Post

Replies

Boosts

Views

Activity

modelContext.fetch() hits assert on release builds, but not on debug builds
Exact same app works fine in debug builds, but on release builds I see this stacktrace indicating that assert() was hit. Incident Identifier: *** Distributor ID: com.apple.TestFlight Hardware Model: iPhone14,3 Process: AuditOS [67847] Path: /private/var/containers/Bundle/Application/*** Identifier: *** Version: 1.0 (15) AppStoreTools: 16C5031b AppVariant: 1:iPhone14,3:18 Beta: YES Code Type: ARM-64 (Native) Role: Foreground Parent Process: launchd [1] Coalition: *** Date/Time: 2025-02-11 12:37:54.7801 -0600 Launch Time: 2025-02-11 12:37:33.1737 -0600 OS Version: iPhone OS 18.3 (22D63) Release Type: User Baseband Version: 4.20.03 Report Version: 104 Exception Type: EXC_BREAKPOINT (SIGTRAP) Exception Codes: 0x0000000000000001, 0x000000019d388e2c Termination Reason: SIGNAL 5 Trace/BPT trap: 5 Terminating Process: exc handler [67847] Triggered by Thread: 0 Thread 0 Crashed: 0 libswiftCore.dylib 0x000000019d388e2c _assertionFailure(_:_:file:line:flags:) + 264 (AssertCommon.swift:147) 1 SwiftData 0x0000000261842e04 Schema.KeyPathCache.validateAndCache(keypath:on:) + 2628 (Schema.swift:0) 2 SwiftData 0x000000026178cac4 static PersistentModel.keyPathToString(keypath:) + 360 (DataUtilities.swift:36) 3 SwiftData 0x000000026184c9e4 static PersistentModel.fetchDescriptorKeyPathString(for:) + 36 (FetchDescriptor.swift:51) 4 SwiftData 0x00000002617b9770 closure #1 in PredicateExpressions.KeyPath.convert(state:) + 172 (FetchDescriptor.swift:458) 5 SwiftData 0x00000002617b7f48 PredicateExpressions.KeyPath.convert(state:) + 352 (FetchDescriptor.swift:438) 6 SwiftData 0x00000002617bb7ec protocol witness for ConvertibleExpression.convert(state:) in conformance PredicateExpressions.KeyPath<A, B> + 16 (<compiler-generated>:0) 7 SwiftData 0x00000002617baaa0 PredicateExpression.convertToExpressionOrPredicate(state:) + 716 (FetchDescriptor.swift:219) 8 SwiftData 0x00000002617ba6dc PredicateExpression.convertToExpression(state:) + 32 (FetchDescriptor.swift:237) 9 SwiftData 0x00000002617b7cfc PredicateExpressions.Equal.convert(state:) + 328 (:-1) 10 SwiftData 0x00000002617bba08 protocol witness for ConvertibleExpression.convert(state:) in conformance PredicateExpressions.Equal<A, B> + 64 (<compiler-generated>:0) 11 SwiftData 0x00000002617baaa0 PredicateExpression.convertToExpressionOrPredicate(state:) + 716 (FetchDescriptor.swift:219) 12 SwiftData 0x00000002617b7abc PredicateExpression.convertToPredicate(state:) + 28 (FetchDescriptor.swift:244) 13 SwiftData 0x00000002617b7190 nsFetchRequest<A>(for:in:) + 1204 (FetchDescriptor.swift:64) 14 SwiftData 0x0000000261783358 DefaultStore.fetch<A>(_:) + 292 (DefaultStore.swift:496) 15 SwiftData 0x000000026178322c protocol witness for DataStore.fetch<A>(_:) in conformance DefaultStore + 16 (<compiler-generated>:0) 16 SwiftData 0x00000002617847fc asDataStore #1 <A><A1>(_:) in closure #1 in ModelContext.fetch<A>(_:) + 3152 (ModelContext.swift:2590) 17 SwiftData 0x00000002617a74d8 partial apply for closure #1 in ModelContext.fetch<A>(_:) + 100 (<compiler-generated>:0) 18 SwiftData 0x00000002617a7438 closure #1 in ModelContext.enumerateFetchableStores<A>(_:_:) + 208 (ModelContext.swift:2527) 19 SwiftData 0x00000002617a731c specialized ModelContext.enumerateFetchableStores<A>(_:_:) + 200 (ModelContext.swift:2522) 20 SwiftData 0x00000002617a6f08 ModelContext.fetch<A>(_:) + 144 (ModelContext.swift:2534) 21 SwiftData 0x00000002617a6e70 dispatch thunk of ModelContext.fetch<A>(_:) + 56 (:-1) 22 AuditOS 0x00000001041af3f4 0x10419c000 + 78836 23 AuditOS 0x00000001041bebd5 0x10419c000 + 142293 24 AuditOS 0x00000001041bbbf5 0x10419c000 + 130037 25 AuditOS 0x00000001041d8be5 0x10419c000 + 248805 26 AuditOS 0x00000001041bde6d 0x10419c000 + 138861 27 libswift_Concurrency.dylib 0x00000001aa6bfe39 completeTaskWithClosure(swift::AsyncContext*, swift::SwiftError*) + 1 (Task.cpp:497) The code in question looks like this: func addRecord<T: MyDtoProtocol>(_ someDTO: T) async throws { var zone: ZoneModel? = nil let recordName = someDTO.recordNameType let fetchDescriptor = FetchDescriptor<T.ModelType> (predicate: #Predicate {$0.recordName == recordName}) > var localEntitites: [T.ModelType] = try modelContext.fetch(fetchDescriptor) <---- I have isolated crash to this line. Basically for each swiftdata model type I have associatedType for Data Transfer Object type and vice versa.
4
1
397
2w
Can't deploy CloudKit schema because of empty record? Why?
When I try to promote schema to production, I get following error: Cannot promote schema with empty type 'workspace', please delete the record type before attempting to migrate the schema again However, in hierarchical root record sharing, I think it should be completely legit use case where there is empty root record (in my case workspace) to which other records reference through ->parent reference. Am I missing something? Is this weird constraint imposed on CloudKit?
1
0
269
2w
Can't open crash logs after changing app name and bundle identifier
I changed my application's name and bundle identifier from xcode. I am able to publish this app to testflight (internal) successfully. However, when users of my app report crash and I try to open them from App Store Connect crash section I see following error in Xcode: Xcode failed to locate iOS App with App Store Identifier "XXXXXXX418". If I open in Xcode > Window > Organizer > Crashes I see following error: Upload "YourAPP" to App Store Connect to begin receiving crash logs. It seems that AppStore identifier at one point changed and xcode and appstore connect are out of sync. What solution is there?
0
0
130
2w
Is there a guaranteed order for records in CKSyncEngine's handleFetchedRecordZoneChanges?
I have two recordTypes in CloudKit: Author and Book. The Book records have their parent property set to an Author, enabling hierarchical record sharing (i.e., if an Author record is shared, the participant can see all books associated with that author in their shared database). When syncing with CKSyncEngine, I was expecting handleFetchedRecordZoneChanges to deliver all Author records before their associated Book records. However, unless I’m missing something, the order appears to be random. This randomness forces me to handle two codepaths in my app (opposed to just one) to replicate CloudKit references in my local persistency storage: Book arrives before its Author → I store the Book but defer setting its parent reference until the corresponding Author arrives. Author arrives before its Books → I can immediately set the parent reference when each Book arrives. Is there a way to ensure that Author records always arrive before Book records when syncing with CKSyncEngine? Or is this behavior inherently unordered and I have to implement two codepaths?
1
0
319
2w
How to Share a CloudKit Record with Multiple Participants While Keeping Individual Records Private?
In a CloudKit private database, the Owner creates a custom zone and performs the following actions: Creates CKRecord1 with CKShare1 and invites Participant1 to it. Creates CKRecord2 with CKShare2 and invites Participant2 to it. Creates CKRecordShared, which should be accessible to both Participant1 and Participant2. How can I achieve step 3? I observed that: Setting a regular reference from CKRecord1 (or CKRecord2) to CKRecordShared does not automatically make CKRecordShared accessible to Participant1 (or Participant2). CKRecordShared can only have one parent, so it cannot be directly linked via parent reference to both Participant1 and Participant2 at the same time. One potential solution I see is to have the Owner create a separate CKShare for CKRecordShared and share it explicitly with each participant. However, this approach could lead to user errors, as it requires careful management of multiple shares for each participant. Is there a better way to handle this scenario, ensuring that CKRecordShared is accessible to multiple participants without introducing unnecessary complexity or potential errors?
2
0
446
Jan ’25
swiftdata model polymorphism?
I have a SwiftData model where I need to customize behavior based on the value of a property (connectorType). Here’s a simplified version of my model: @Model public final class ConnectorModel { public var connectorType: String ... func doSomethingDifferentForEveryConnectorType() { ... } } I’d like to implement doSomethingDifferentForEveryConnectorType in a way that allows the behavior to vary depending on connectorType, and I want to follow best practices for scalability and maintainability. I’ve come up with three potential solutions, each with pros and cons, and I’d love to hear your thoughts on which one makes the most sense or if there’s a better approach: **Option 1: Use switch Statements ** func doSomethingDifferentForEveryConnectorType() { switch connectorType { case "HTTP": // HTTP-specific logic case "WebSocket": // WebSocket-specific logic default: // Fallback logic } } Pros: Simple to implement and keeps the SwiftData model observable by SwiftUI without any additional wrapping. Cons: If more behaviors or methods are added, the code could become messy and harder to maintain. **Option 2: Use a Wrapper with Inheritance around swiftdata model ** @Observable class ParentConnector { var connectorModel: ConnectorModel init(connectorModel: ConnectorModel) { self.connectorModel = connectorModel } func doSomethingDifferentForEveryConnectorType() { fatalError("Not implemented") } } @Observable class HTTPConnector: ParentConnector { override func doSomethingDifferentForEveryConnectorType() { // HTTP-specific logic } } Pros: Logic for each connector type is cleanly organized in subclasses, making it easy to extend and maintain. Cons: Requires introducing additional observable classes, which could add unnecessary complexity. **Option 3: Use a @Transient class that customizes behavior ** protocol ConnectorProtocol { func doSomethingDifferentForEveryConnectorType(connectorModel: ConnectorModel) } class HTTPConnectorImplementation: ConnectorProtocol { func doSomethingDifferentForEveryConnectorType(connectorModel: ConnectorModel) { // HTTP-specific logic } } Then add this to the model: @Model public final class ConnectorModel { public var connectorType: String @Transient public var connectorImplementation: ConnectorProtocol? // Or alternatively from swiftui I could call myModel.connectorImplementation.doSomethingDifferentForEveryConnectorType() to avoid this wrapper func doSomethingDifferentForEveryConnectorType() { connectorImplementation?.doSomethingDifferentForEveryConnectorType(connectorModel: self) } } Pros: Decouples model logic from connector-specific behavior. Avoids creating additional observable classes and allows for easy extension. Cons: Requires explicitly passing the model to the protocol implementation, and setup for determining the correct implementation needs to be handled elsewhere. My Questions Which approach aligns best with SwiftData and SwiftUI best practices, especially for scalable and maintainable apps? Are there better alternatives that I haven’t considered? If Option 3 (protocol with dependency injection) is preferred, what’s the best way to a)manage the transient property 2) set the correct implementation and 3) pass reference to swiftdata model? Thanks in advance for your advice!
0
0
245
Jan ’25
how to build pkg file with xcode cloud that is notarized and also has postinstall file?
I am creating a macOS app with the following requirements: Automatic Startup: After initial installation, the app should automatically start, even after the OS restarts. Notarized Installation: The installation package (.pkg) should be notarized to avoid user have to make security exception. In my current setup I’ve created a script, ci_scripts/ci_post_xcodebuild.sh, which uploads the package file $CI_APP_STORE_SIGNED_APP_PATH/<appName>.pkg to GitHub via Xcode Cloud. While I can successfully download the app, I’m encountering two main issues: Notarization (I assume): I’m unsure how to get Xcode Cloud to notarize the .pkg file. Currently, upon opening the .pkg file for the first time, users have to go to System Settings > Privacy & Security to allow an exception for the package, after which installation proceeds successfully on second try. I’d like to automate the notarization process to eliminate this extra step. Adding Additional Files to PKG installer: My current .pkg file only includes the app binary. I need to configure Xcode Cloud to include a postinstall script and a launchd daemon configuration file within the package. This would ensure that necessary files are set up on installation and that the app is properly registered as a launch daemon.
0
0
387
Nov ’24
Is there a way for my macos app to run NSRunningApplication.terminate()?
When my macOS app (currently in TestFlight and set for Mac App Store distribution) tries to terminate another app, both terminate() and forceTerminate() consistently return false. However, I can retrieve a list of all running applications so some related APIs do work. I suspect this limitation is due to sandboxing. I have three questions: Is there any permission or entitlement I can add in Xcode to allow my app to terminate other applications? If no such permission exists, is there a way to guide users on how to launch my app (distributed through the Mac App Store) without sandboxing? For example, could they set it up to launch as a daemon or agent? If unsandboxing is impossible, would I need to create a separate target specifically without sandboxing? In other words, my MacOS app would communicate with my unsandboxed daemon that would do all the terminate()-ing?
1
0
322
Nov ’24
Live queries on SwiftData DB but without @Query macro?
I switched from using @Query to @ModelActor because of the following reasons: Performance Issues: With @Query, my app became unresponsive with large datasets because data fetching occurred on the main thread. Integration with CKSyncEngine: I needed to implement @ModelActor anyway to allow CKSyncEngine to add data to local persistent storage from the background. In my current setup, the onAppear() method for my view calls the getItems() function on my model actor, which returns [ItemsDTO] and then View renders them. However, I'm now facing a challenge in achieving the same automatic data refreshing and view updates that @Query provided. Here are a few potential solutions I'm considering: Periodic Data Fetching: Fetch data at regular intervals to keep the view updated. But this seems expensive. Local Write Monitoring: Monitor all local writes to automatically trigger updates when changes occur. But this is quite a lot of code I would have to write myself. Switch to manual refresh: Users would have to manually trigger UI updates by pressing button. Reintroduce @Query: Writes would happen from ModelActor, but reads would still happen from Main thread. But then again app would become unresponsive on reads. If you have any additional ideas or best practices for maintaining reactivity with @ModelActor, I'd love to hear them!
1
1
820
Sep ’24
Installation on this device is prohibited by ManagedConfiguration
When I try to install my app via USB cable on my child's iPhone 13 device I see following error in Xcode: Installation on this device is prohibited by ManagedConfiguration If I go to VPN & Device Management, I don't see anything. This is my Child's iphone so it is not like it is enrolled in some kind of school or work MDM. Developer mode is ON and I added device ID to Certificates, Identifiers & Profiles > Devices. Domain: IXRemoteErrorDomain Code: 9 Failure Reason: Unhandled error domain IXRemoteErrorDomain, code 9 User Info: { FunctionName = "-[IXSRemoteInstaller initWithRemoteInstallOptions:xpcAssetStream:assetSize:error:]"; SourceFileLine = 149; }``` Is it cellular network provider that has locked something? How to be sure about this?
1
0
631
Sep ’24
How to determine model type in CKSyncEngine's nextRecordZoneChangeBatch?
I’m working on a project where I’m using CKSyncEngine to sync different types of SwiftData models, specifically User and Organization, to CloudKit. Here’s how I schedule these models to be synced: For the User model: let pendingSaves: [CKSyncEngine.PendingRecordZoneChange] = [.saveRecord(user.recordID)] syncEngine.state.add(pendingRecordZoneChanges: pendingSaves) For the Organization model: let pendingSaves: [CKSyncEngine.PendingRecordZoneChange] = [.saveRecord(organization.recordID)] syncEngine.state.add(pendingRecordZoneChanges: pendingSaves) The problem arises in my CKSyncEngineDelegate's nextRecordZoneChangeBatch method where from CKRecord.ID alone I need to create the actual CKRecord that will be synced to CloudKit. This recordID alone doesn’t provide enough information to determine 1) in which local model table I need to fetch actual data to build whole CKRecord; and 2) what to put in CKRecord.recordType - whether it’s a User or an Organization. Question: What is the best practice for passing or determining the model type (e.g., User or Organization) in nextRecordZoneChangeBatch? How should I handle this in a way that effectively differentiates between the different model types being synced? Any advice or examples would be greatly appreciated! Few ideas: embed the Model type in RecordID.recordName string, but this makes my recordNames longer (like resource_29af3932). fetch data by recordID in all local persistent storage, but this seems slow and there is constraint that User and Organization IDs should never be the same. introduce lookup table where from CKRecordID I can look up model type. Somehow extend CKRecordID to add model type field?
1
1
452
Aug ’24
Able to read from CloudKit shared database, but not write.
User A shares zone with User B (influenced from https://github.com/apple/sample-cloudkit-zonesharing, but I have just one zone "Contacts" that I am sharing): private func shareConfiguration() async throws -> (CKShare, CKContainer) { let container = CKContainer(identifier: "iCloud.com.***.syncer") let database = container.privateCloudDatabase let zone = CKRecordZone(zoneName: "Contacts") let fetchedZone = try await database.recordZone(for: zone.zoneID) guard let existingShare = fetchedZone.share else { print("Does not have existing share") let share = CKShare(recordZoneID: zone.zoneID) share[CKShare.SystemFieldKey.title] = "Resources" _ = try await database.modifyRecords(saving: [share], deleting: []) return (share, container) } print("Has existing share") guard let share = try await database.record(for: existingShare.recordID) as? CKShare else { throw NSError(domain: "", code: 0, userInfo: nil) } return (share, container) } ... let (share,container) = try! await shareConfiguration() shareView = CloudSharingView(container: container, share: share) // UIViewControllerRepresentable implementation User B accepts share invitation (borrowed from https://github.com/apple/sample-cloudkit-zonesharing) class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) { guard cloudKitShareMetadata.containerIdentifier == "iCloud.com.***.syncer" else { print("Shared container identifier \(cloudKitShareMetadata.containerIdentifier) did not match known identifier.") return } // Create an operation to accept the share, running in the app's CKContainer. let container = CKContainer(identifier: "iCloud.com.***.syncer") let operation = CKAcceptSharesOperation(shareMetadatas: [cloudKitShareMetadata]) debugPrint("Accepting CloudKit Share with metadata: \(cloudKitShareMetadata)") operation.perShareResultBlock = { metadata, result in let shareRecordType = metadata.share.recordType switch result { case .failure(let error): debugPrint("Error accepting share: \(error)") case .success: debugPrint("Accepted CloudKit share with type: \(shareRecordType)") } } operation.acceptSharesResultBlock = { result in if case .failure(let error) = result { debugPrint("Error accepting CloudKit Share: \(error)") } } operation.qualityOfService = .utility container.add(operation) } } User B through CKSyncEngine is able to read all records. However, when User B tries to write to database through CKSyncEngine, User B on his device gets following error: <CKSyncEngine 0x1282a1400> error fetching changes with context <FetchChangesContext reason=scheduled options=<FetchChangesOptions scope=all group=CKSyncEngine-FetchChanges-Automatic)>>: Error Domain=CKErrorDomain Code=2 "Failed to fetch record zone changes" UserInfo={NSLocalizedDescription=Failed to fetch record zone changes, CKPartialErrors={ "<CKRecordZoneID: 0x3024872a0; zoneName=Contacts, ownerName=_18fb98f978ce4e9c207daaa142be6024>" = "<CKError 0x30249ed60: \"Zone Not Found\" (26/2036); server message = \"Zone does not exist\"; op = DC9089522F9968CE; uuid = 4B3432A4-D28C-457A-90C5-129B24D258C0; container ID = \"iCloud.com.***.syncer\">"; }} Also, in CloudKit console, if I go to Zones, I don't see any zones under Shared Database. Wasn't I supposed to see my zone here? However, I see "Contacts" zone under Private Database. If I expand Zone details I see following: Zone wide sharing is enabled. All records in this zone are being shared with the sharing participants below. And under Participants I see both User A and User B. User B is marked as: Permission READ_WRITE Type USER Acceptance INVITED What puzzles me is why READ works, but not WRITE?
1
1
458
Aug ’24
Can't accept CloudKit share invitation from my SwiftUI application
I am able to send invitation from my device to friend's device. When friend clicks on invitation that was shared through text messages it says: Open "Resources"? User X wants to collaborate. You'll join as User Y (user Y @iCloud.com). |Not Now| |Open| If friend clicks on |Open| then nothing happens. Share remains in "invited" state and the callbacks which I expected to be called are not. The official Apple CloudKit Sharing App - https://github.com/apple/sample-cloudkit-sharing/blob/main/Sharing/App/AppDelegate.swift - is confusing me because it does not have following code like typical SwiftUI app: @main struct MainApp: App { Instead it uses @main for AppDelegate. Here is my code with prints that encode what is going on: class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { print("I see this getting called on App startup") return true } func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { print("I also see this getting called on App startup") return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) { print("I don't see this getting called") } func application(userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) -> Bool { print("However, I expected this to be called when friend opened his CloudKit share invitation") return false } } @main struct MainApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate static let sharedModelActor: ModelActorDatabase = { let schema = Schema([ Resource.self, ]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false, cloudKitDatabase: .none) do { let modelContainer = try ModelContainer(for: schema, configurations: [modelConfiguration]) return ModelActorDatabase(modelContainer: modelContainer) } catch { fatalError("Could not create ModelContainer: \(error)") } }() @StateObject var syncedDatabase: SyncedDatabase = SyncedDatabase(modelContainer: Self.sharedModelActor.modelContainer) var body: some Scene { WindowGroup { ResourceView() .environmentObject(syncedDatabase) } .modelContainer( Self.sharedModelActor.modelContainer ) .database(SharedDatabase.shared.database) } } I was expecting that this would call userDidAcceptCloudKitShareWith, but it is not. Why?
1
0
509
Aug ’24