Open zjz2020 opened 3 years ago
That error indicates SMJobBless failed. The most likely cause is that your app is sandboxed, which doesn't support privileged helper tools. So make sure you remove the sandbox capability from the host app in Xcode. That means you won't be able to put your app in the AppStore without getting special approval from Apple, which for security reasons, they are hesitant to grant.
Also check that your helper tool has actually been copied into your app's bundle.
Another thing that might help is to use the Swift script from my fork of this project instead of the shell script from this one. It does a couple of extra checks that the shell script doesn't do, but more importantly, it can actually emit error messages that show up in your build log. It's possible one of the extra checks may catch something, or that there are errors the shell script is catching but unable to emit errors for, because it's using stdout for string building. You can find it in the Scripts folder, along with its own README describing how to use it.
@chipjarred can you explain a bit more about about how to remove the sandbox?
Also, can you expand on Also check that your helper tool has actually been copied into your app's bundle.
?
@abstertee Regarding removing the sandbox, you do that by clicking on your project in Xcode's project navigator (side-bar on the left that shows files and folders). That will bring up your project settings in the editor view. In that view, select your main app's target, then click the "Signing & Capabilities" tab. By default a macOS app project will contain a signing section at the top followed by App Sandbox followed by Hardened Runtime. All the way on the right side of the editor view, across from App Sandbox there is little "x" you can click to remove the sandbox. Don't remove Hardened Runtime... you want that. Just remove App Sandbox. Now after rebuilding the app, it will run directly in macOS, not in a container. If there is no App Sandbox there, then its already removed.
Just as a quick reminder, if you remove the sandbox, Apple won't allow your app in the App Store, so if this not just project for your personal use, you'll have to distribute it independently.
As for checking that the helper tool has been copied to your app's bundle... As you probably already know, when you build your app, the actual executable binary is not the only thing Xcode builds. It creates a bundle, a special directory that appears as a file in the Finder, and places the executable binary inside of it along with other resources like storyboards, assets, etc... It has a specific folder structure. If your build is working correctly, your helper tool should be copied into that bundle as well.
So to check if its there, you have to open the bundle, which means finding the built app. It's buried deep inside a bunch of build subfolders inside your Library folder, so the easiest way to do that is to right-click on the app's product in Xcode's project navigator, then select Show in Finder. Then in Finder right-click on the app and select "Show Package Contents". There should be a Library Folder containing LaunchServices folder. That's where your helper should be
@chipjarred is the CodeSignUpdate.swift file supposed to only work with "Mac Developer" certs or can we use a "Developer ID Application" cert?
@abstertee It's set up to work with "Mac Developer:"
and "Apple Development:"
, which I think are what SMJobBless
(or maybe it's launchd
) needs to launch the helper tool. But I have to admit my memory of cert details are little hazy at this point. If you want to experiment with values that you have reason to believe are correct, the function to modify is
func isValidDeveloperCN(_ s: String) -> Bool
{
let appleDeveloper = "Apple Development:"
let macDeveloper = "Mac Developer:"
if isValidDeveloperCN(s, withPrefix: appleDeveloper) { return true }
if isValidDeveloperCN(s, withPrefix: macDeveloper) { return true }
return false
}
@chipjarred Either way I keep getting the following error with an SMJobBless check: SMJobBlessUtil.py: tool designated requirement (anchor apple generic and identifier "com.xxx.xxx.helper" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = S*********W)) doesn't match entry in 'SMPrivilegedExecutables' (identifier "com.xxx.xxx.helper" and anchor apple generic and certificate leaf[subject.CN] = "Developer ID Application: XXX, Inc. Enterprise (S*********W)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */)
When the application is launched there is prompt to install the helper tool, but any attempt to install fails with the same error of this issue Failed to install helper with error: Error Domain=CFErrorDomainLaunchd Code=4 "(null)"
@abstertee What follows involves editing your Info plists. I recommend using a plain text editor for this particular task rather than plist editor for this (so not Xcode). I'm using BBEdit, but you could use TextEdit, vi, emacs, whatever you like, as long as its showing the raw XML of the plist.
Let's break down what you're getting from SMJobBlessUtil.py
. The first thing I notice is an identifier mismatch, so we'll sort that out.
If your project is based on this repo, this file is probably called Helper-Info.plist
.
Make sure the CFBundleIdentifier
and CFBundleName
entries are the set to the helper tool's ID. Based on the error text, that appears to be com.xxx.xxx.helper
for your project. Don't use quotes.
<key>CFBundleIdentifier</key>
<string>com.xxx.xxx.helper</string>
and
<key>CFBundleName</key>
<string>com.xxx.xxx.helper</string>
Also delete the SMAuthorizedClients
entry (both <key>
part and the entire <array>
immediately below it. CodeSignUpdate
(either shell or Swift version) will rebuild it.
So the last few lines of your file should change from this
<key>CFBundleVersion</key>
<string>1</string>
<key>SMAuthorizedClients</key>
<array>
<string>some certificate info here</string>
</array>
</dict>
</plist>
to this
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
Save it
Info.plist
fileCheck that the CFBundleIdentifier
value is $(PRODUCT_BUNDLE_IDENTIFIER)
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
Delete the SMPrivilegedExecutables
entry, so the last few lines of the file should change from this
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<dict>
<key> com.xxx.xxx.helper </key>
<string>some certificate info here</string>
</dict>
</dict>
</plist>
to this
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
Save it
Make sure your target's Bundle Identifier
is set to the id you've chosen for your main app. This should not be the same as your helper tool's bundle identifier. Perhaps this is com.xxx.xxx.NameOfYourApp
. Xcode uses this field to set its $(PRODUCT_BUNDLE_IDENTIFIER)
environment variables.
Set Signing Certificate
to Development
. We can play with this more later. Get a working build first.
Set your helper tool's Bundle Identifier
to the same one you used in the plist, so continuing my assumption about that, it would be com.xxx.xxx.helper
Set Signing Certificate
to Development
Run Script
Build PhaseIf you're using CodeSignUpdate.swift
from my fork, make sure the Run Script phase for both targets looks like this (continuing with previously assumed bundle identifiers):
export MAIN_BUNDLE_ID="com.xxx.xxx.NameOfYourApp"
export HELPER_BUNDLE_ID="com.xxx.xxx.helper"
swift "${SRCROOT}"/Scripts/CodeSignUpdate.swift
Also make sure that the Run Script phase is after the Dependencies phase, but before the Compile phase.
@abstertee Once you've gotten a working build, if you want to use a different certificate, you'll probably need to modify CodeSignUpdate.swift
to use it.
func isValidDeveloperCN(_ s: String) -> Bool
{
let appleDeveloper = "Apple Development:"
let macDeveloper = "Mac Developer:"
let customDeveloper = "Developer ID Application:" // <-- Add this for you to try
if isValidDeveloperCN(s, withPrefix: appleDeveloper) { return true }
if isValidDeveloperCN(s, withPrefix: macDeveloper) { return true }
if isValidDeveloperCN(s, withPrefix: customDeveloper) { return true } // <-- Add this too
return false
}
I'll probably re-write that to use an array of prefixes to check in a loop instead of the straight sequential series of if
statements.
Although I think CodeSignUpdate.swift
completely replaces SMAuthorizedClients
and SMPrivilegedExecutables
each time it runs, I've slept since then, so I'd need to review the code to be sure. For now I recommend deleting those sections of the plists whenever you change signing certificates.
@chipjarred Thank you for the very detailed instructions. This was very helpful. What I found in making these changes is that all works as it should when the Signing Cert selected is the "Apple Development", but we want to use the "Developer ID Application" cert. When the "Developer ID" cert is selected, nothing works and we get that same error message regarding the mismatch from SMJobBless.py.
What is the difference between the two certs and what is preventing us from using the "Developer ID Application" cert? I should add that this application is for internal use and will not be in the AppStore.
I'm glad the instructions were helpful. As for why one certificate works and the other doesn't, I don't know. I wouldn't think a particular certificate would be a hard requirement for SMJobBless
or launchd
but maybe it is. Did you try the changes to CodeSignUpdate.swift
from my follow up? And of course both the tool and main app would need to use the same cert.
Of course, since the app has to run without a sandbox to use SMJobBless
, it couldn't go on the AppStore anyway, but if it's for internal use, does it actually matter which cert you use? I'm sure you have your reasons. Just a question worth pondering. I would think it would matter more if you were planning to distribute it outside of your company.
Hi @abstertee ,Sorry for resurrecting this thread, but I'm facing the same issue you mentioned. Despite initially working with a Development certificate
, when I switched to a Developer ID Application
, I encountered the error. I intend to use a Developer ID Application
for notarizing the application and distributing it outside the Appstore. Have you found a solution? Please share it with me. Thanks!
Failed to install helper with error: Error Domain=CFErrorDomainLaunchd Code=4 "(null)" How to deal with this problem