Post

Replies

Boosts

Views

Activity

Reply to UserDefaults.standard setValue forKey crash on iOS 15 only.
After further investigation, I have confirmed the following (submitted as FB12348064): The AppStorage property wrapper uses KVO to observe UserDefaults. This makes writing to UserDefaults unsafe on non-main threads, even if the AppStorage is not using the same key as is being written. Any write to UserDefaults causes a lookup of all observers. If the AppStorage is deallocated during that lookup, there will be a crash. The following reliably crashes for me within a few hundred iterations, tested with both Xcode 14.3.1 and 15b1: import SwiftUI struct ContentView: View { @State var n: Int = 0 // This song-and-dance is to make sure that AppStorageView is destroyed and recreated. // The two views are visually identical to make the output easier to read. One has @AppStorage, // the other does not. This causes very fast registering/deregistering from UserDefaults KVO. func bodyView() -> AnyView { if n % 2 == 0 { return AnyView( ForEach(0..<10) { _ in AppStorageView(n: n) }) } else { return AnyView( ForEach(0..<10) { _ in NoAppStorageView(n: n) }) } } var body: some View { bodyView() .task { // Churn UserDefaults on a background thread. Task.detached { while true { UserDefaults.standard.set(Date(), forKey: "randomOtherUserDefaultsKey") await Task.yield() } } // At the same time, churn the Views to create and destroy AppStorage observations. while true { n += 1 await Task.yield() } } } } // View with @AppStorage observation struct AppStorageView: View { var n: Int @AppStorage("appStorageValue") var appStorageValue = false var body: some View { LogView(n: n) } } // View without @AppStorage observation struct NoAppStorageView: View { var n: Int var body: some View { LogView(n: n) } } struct LogView: View { var n: Int var body: some View { HStack { Text("App Storage Test: \(n)") Spacer() } } }
Jun ’23
Reply to Instruments: why? “Failed to gain authorization”
To automate @stragerneds's workaround, you can add the following script to the Pre-action for Profiling: # Make sure to set the shell to zsh, not bash # # For Instruments, re-sign binary with get-task-allow entitlement codesign -s - -v -f --entitlements =(echo -n '<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd"\> <plist version="1.0"> <dict> <key>com.apple.security.get-task-allow</key> <true/> </dict> </plist>') ${TARGET_BUILD_DIR}/${PRODUCT_NAME} I've written up a more complete discussion at https://cocoaphony.micro.blog/2022/10/29/solving-required-kernel.html.
Oct ’22
Reply to SectionFetchRequest/Result value vs reference
I think I've finally figured out what's going on, but I'm at a loss for how I would be able to guess this subtlety from the code or the documentation. I believe what is happening is that, as was noted in the session, quakes has a mutable getter ("changes to the request are committed whenever the results getter is called"). So if you wrote the obvious code: // This is fine because quakes hasn't changed yet quakes.sectionIdentifier = sortBy.section // This would apply the `sectionIdentifier` change and trigger an unnecessary fetch (?) quakes.sortDescriptors = sortBy.descriptors So the recommended code avoids this by only touching the property wrapper once. Despite quakes and config both being structs, they have reference semantics. But despite both referencing the same "object," they have different access semantics due to the property wrapper. This seems incredibly subtle, contrary to standard Swift value/reference semantics, and unmentioned in the documentation. Am I understanding it correctly? Is there any way I should be able guess this behavior?
Jun ’21
Reply to xcframework: how to wrap a static library?
xcodebuild determines this based on the -destination option. How do we apply this when a static library is not built with Xcode? I have a static library (in C) built for several architectures, but I haven't found any way to build an xcframework that associates the correct architecture with the simulator. When I try to construct an xcframework just by appending them all: for lib in *.a; do xcodebuild -create-xcframework -library $lib -headers include -output PEQ.xcframework; done the linking product that consumes them throws a warning: ld: warning: ignoring file .../Release-iphonesimulator/PEQ-x8664.a, missing required architecture arm64 ... And cannot find the symbols. I've also tried linking just the x8664 and arm64 versions in a single call: xcodebuild -create-xcframework -library PEQ-arm64.a -headers include -library PEQ-x86_64.a -headers include -output PEQ.xcframework (If I try to include the i386 or armv7 versions, it complains that they "represent two equivalent library definitions") There is no xcodeproj for these static libraries. They are proprietary and do not even have source code for them. How do I associate the correct static library with each platform and simulator? @dhoerl, this seems to be very similar to your issue (in that the libraries are built outside of Xcode), so did this solution work? Note that I don't actually care about creating an xcframework. I don't need to distribute this framework. I'm just trying to link existing static libraries into our application, and have it work on device and simulator.
Oct ’20