I have an app on the Mac App Store that does uninstallation of Mac applications. Recently the app got rejected saying that it should not uninstall apps which ship with the OS. How can I programmatically retrieve the list of system apps or find out if a given app is a system app? Our technical support query to Apple met with a response that a workaround should be available in the developer forums. Thanks in advance!
Hmmm, that seems strange.
I had a chat with SolShare about how they managed to end up here. I’m not going to go into the details but suffice to say that there are two parts to this question, and one of them falls into DTS’s purview.
The two parts are:
How do you reliably identify built-in apps?
Will an app that removes other apps be allowed on the store?
The first part is a purely technical question, and thus is something that I can address here. The part is about App Review policy, and I can’t comment on that.
With regards the first part, I can’t think of any supported way to answer that question exactly. There is, however, a way to answer a slightly different question, that is, “Is this Apple code?” You can do that by evaluating the
anchor apple code signing requirement. Pasted in at the end of this email is a tiny test project that does just that.
I must stress that this can produce false positives. For example, Xcode is not a built-in app but is reported as Apple code. However, there are no false negatives, that is, every built-in app will be reported as Apple code. I’ll leave it up to you to decide whether this is sufficient for your needs.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"let appleCodeSignedByApple: SecRequirement = {
var reqQ: SecRequirement? = nil
let err = SecRequirementCreateWithString("anchor apple" as NSString, [], &reqQ)
assert(err == err)
return reqQ!
}()
func isAppleCodeSignedByApple(_ url: URL) throws -> Bool {
var codeQ: SecStaticCode? = nil
let err = SecStaticCodeCreateWithPath(url as NSURL, [], &codeQ)
guard err == errSecSuccess else {
throw NSError(domain: NSOSStatusErrorDomain, code: Int(err), userInfo: nil)
}
let code = codeQ!
return SecStaticCodeCheckValidityWithErrors(
code,
SecCSFlags(rawValue: kSecCSBasicValidateOnly),
appleCodeSignedByApple,
nil
) == errSecSuccess
}
let appDir = try! FileManager.default.url(for: .applicationDirectory, in: .localDomainMask, appropriateFor: nil, create: false)
let appDirContents = try! FileManager.default.contentsOfDirectory(at: appDir, includingPropertiesForKeys: nil, options: [])
let appDirApps = appDirContents.filter { $0.pathExtension == "app" }
let sortedAppDirApps = appDirApps.sorted { $0.lastPathComponent < $1.lastPathComponent }
for app in sortedAppDirApps {
let isApple = try! isAppleCodeSignedByApple(app)
print("\(isApple ? "+" : " ") \(app.lastPathComponent)")
}