Hi all,I have a command-line tool that I need to notarize.Well, actually, notarizing is the easy part. The tool is used inside an installer package, which is inside a dmg, and when we notarize the dmg, everything inside the dmg gets notarized transparantly, which is quite nice.Except that for notarizing to pass, the tool needs secure timestamps and hardened runtime enabled.Okay, so far so good. Documentation says that Xcode does this for us. When in debug mode, Xcode enables get-task-allow, and we can debug the tool. When in production mode (via archive/export), Xcode enables secure timestamps and removes get-task-allow.Except, I'm having trouble exporting the tool for production. I can archive using `xcodebuild archive -scheme mytool -archivePath mytool`, and I get an Xcode Archive bundle that looks a lot like one would expect:$ find mytool.xcarchivemytool.xcarchivemytool.xcarchive/Productsmytool.xcarchive/Products/usrmytool.xcarchive/Products/usr/localmytool.xcarchive/Products/usr/local/binmytool.xcarchive/Products/usr/local/bin/mytoolmytool.xcarchive/dSYMsmytool.xcarchive/dSYMs/mytool.dSYMmytool.xcarchive/dSYMs/mytool.dSYM/Contentsmytool.xcarchive/dSYMs/mytool.dSYM/Contents/Resourcesmytool.xcarchive/dSYMs/mytool.dSYM/Contents/Resources/DWARFmytool.xcarchive/dSYMs/mytool.dSYM/Contents/Resources/DWARF/mytoolmytool.xcarchive/dSYMs/mytool.dSYM/Contents/Info.plistmytool.xcarchive/Info.plistBut then when I try to export, I get:$ xcodebuild -exportArchive -archivePath mytool.xcarchive -exportPath prod-mytool -exportOptionsPlist mytool_codesigning_options.plist** EXPORT FAILED **2020-02-13 06:05:23.453 xcodebuild[54323:1508625] [MT] IDEDistribution: -[IDEDistributionLogging _createLoggingBundleAtPath:]: Created bundle at path '/var/folders/mz/kgcq5n9j7yn2s9v0ch850tz80000gq/T/mytool_2020-02-13_06-05-23.453.xcdistributionlogs'.2020-02-13 06:05:23.474 xcodebuild[54323:1508625] [MT] IDEDistributionMethodManager: -[IDEDistributionMethodManager orderedDistributionMethodsForTask:archive:]: Error = Error Domain=IDEDistributionMethodManagerErrorDomain Code=2 "Unknown Distribution Error" UserInfo={NSLocalizedDescription=Unknown Distribution Error}error: exportArchive: exportOptionsPlist error for key 'method': expected one of {}, but found developer-idError Domain=IDEFoundationErrorDomain Code=1 "exportOptionsPlist error for key 'method': expected one of {}, but found developer-id" UserInfo={NSLocalizedDescription=exportOptionsPlist error for key 'method': expected one of {}, but found developer-id}Google says that the error is caused by the Xcode Archive not containing a normal app, and that you can't export a tool.Can I export this tool?If not, then is it still possible to take advantage of Xcode's automatic debug/production switching behavior even though I need secure timestamps and the hardened runtime turned on in production?
Post
Replies
Boosts
Views
Activity
I'd like to add additional metadata regarding image files to Spotlight's index, so that I can see the data in Get Info in Finder. I don't want to replace Apple's image indexer; I just want to add one more field.
I am able to successfully create a new Spotlight indexer for a custom file type, and it works; in particular, I can see the new metadata in the Get Info window for the file in Finder.
When I modify the indexer to also accept jpeg files, I'm seeing that Spotlight is choosing to use macOS' built-in image indexer all the time, and never my custom indexer. (I'm using mdimport -d2 -t sample.jpeg for testing)
Before I start asking about code-level questions, it seems prudent to start with the basics:
Is Spotlight willing to leverage multiple indexers for the same file type?
Does mdimport -d2 -t sample.jpeg only test one single indexer? If not, what is the correct way to test an indexer that accepts file types already accepted by an Apple indexer?
I'm having a problem in my app where the navigation stack is getting erased when certain parts of the view are re-rendered. I've narrowed the problem down to this sample app:
import SwiftUI
let demonstrateProblem = false
@main
struct NavStackApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@State var model = ValueProducer()
var body: some View {
NavigationView {
DivisorCheckView(model: model)
Text("Choose a divisor (detail view)")
}
}
}
struct DivisorCheckView: View {
@ObservedObject var model: ValueProducer
var divisor = 2
var body: some View {
VStack(spacing: 12) {
Text("\(model.value) \(model.value % divisor == 0 ? "is" : "is not") divisible by \(divisor)")
if demonstrateProblem ? Bool.random() : true {
otherDivisorBody(divisor: divisor + 1)
}
else {
otherDivisorBody(divisor: divisor + 1)
}
}
.navigationTitle("Divisibility by \(divisor)")
}
@ViewBuilder
func otherDivisorBody(divisor num: Int) -> some View {
NavigationLink(destination: DivisorCheckView(model: model, divisor: num)) {
Text("Check divisibility by \(num)")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
ContentView()
}
}
}
class ValueProducer: ObservableObject {
@Published var value = 0
init() {
cycle()
}
private func cycle() {
value = type(of: value).random(in: (10...99))
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] () -> Void in
self?.cycle()
}
}
}
If you run this app as-is, the navigation stack operates as expected: every time you tap on a link to a subview, the screen continues to update every 2 seconds as a new number is generated — and the view stack never pops on its own.
If you change demonstrateProblem to true, the behavior I'm seeing in my larger app presents itself here in this demo app: the navigation stack pops on its own.
What seems to be happening is that SwiftUI is sensitive to which fork of the if statement is being executed. When a different fork is being executed, the navigation stack is completely reset to that layer in the navigation stack.
I know SwiftUI does sneaky things such as using view hierarchy deltas to incrementally create and destroy view instances; it feels like SwiftUI is deciding that part of the view hierarchy was completely destroyed — and if that were true, then of course automatically unwinding the navigation stack to that point makes total sense.
I've seen recommendations on forums suggesting that we should disable observer notifications on views when the view is no longer active, but that has an annoying side effect of preventing those views from updating (and then I have to manually trigger a potentially unnecessary update when the user pops the navigation stack for real, which feels overly complicated).
What is going on here? Is there a development pattern I should follow that doesn't trick SwiftUI into thinking that the old view hierarchy was destroyed?
(Skippable) Backstory:
I have an app that customers initially install by:
Going to our website in a browser,
downloading a (notarized) disk image,
mounting it,
opening the (Developer ID-signed) installer package therein, and
following the prompts to completion.
Once installed, this app has a button you can click that will check for updates and possibly install them. When you click this button:
Our app contacts an HTTPS API on our servers to see if there is an update
If there is an update, the API responds with a download URL to the aforementioned notarized disk image, and the SHA-512 hash of the disk image
Our app downloads the disk image, and verifies the SHA-512 hash of the download
Our app mounts the DMG
Our app uses /usr/sbin/pkgutil --check-signature (although I do hope to switch to SecStaticCodeCheckValidity() in the future) to verify that
the OS thinks that the installer package is properly signed, and also that
the organization unit on the leaf certificate for the code signing key used to sign the installer is exactly equal to our Apple Team ID
Our app uses /usr/sbin/installer -package ... -volinfo to verify that the installer package thinks it can install onto the host
Our app uses /usr/sbin/installer -package ... -target / to install the software update
For 98%+ of our users, this update process works great. For a tiny portion of users, we're seeing that hdiutil reports that it failed to attach the disk image. For the even tinier portion of users from whom we have obtained debug logs, one of the errors we've seen is "not recognized"; however, I don't know if that's the most common error, due to the small sample size.
Before I get much further, I think it's prudent to acknowledge that the above system we're using today is pushing a decade old now, and it's probably wise to verify the foundations.
So...
For a Developer-ID-signed macOS app that is distributed outside the Mac App Store using an installer package, what is the "best" container/packaging system for an automated system to obtain and consume the installer package with the goal of a user-initiated self-update? For example:
Notarized disk image (and failures to attach the image need to be bug reports to Apple)
Just the installer package (If I understand correctly, this is bad because it bypasses the automatic propagation (normally performed by macOS) of the DMG's notary ticket to the pkg, right?)
Something else?
Secondly, what are some common developer mistakes to avoid? For example, these come to mind:
When saving the DMG to disk, explicitly enable quarantine on the DMG, so that macOS runs appropriate security checks as intended (is this correct?)
When running the installer package, do not use low-level tools (like cp) to copy the pkg out of the DMG, because macOS won't be able to automatically find the notary ticket when the pkg is installed (is this correct?)
Anything else?
Additional context:
We currently support macOS 10.13+, but we will soon support only macOS 10.15+.
Thank you!