Post

Replies

Boosts

Views

Activity

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
129
1w
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
109
2w
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
468
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
333
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
307
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
326
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
323
Aug ’24
Can someone explain why this state property is nil despite being updated before the other state property?
I took CKShare with Zone example - https://github.com/apple/sample-cloudkit-sharing Modified it a little bit so code looks like this: struct ResourceView: View { @State private var showingShare: Bool = false @State private var shareView: CloudSharingView? ... var body: some View { HStack { Button(action: { Task { let (share,container) = try! await shareConfiguration() shareView = CloudSharingView(container: container, share: share) showingShare = true } }) { Label("Share", systemImage: "circle") } ... .sheet(isPresented: $showingShare) { if let shareView = shareView { shareView } else { Text("No sheet to show") } } And the first time I click on Share button I am getting "No sheet to show" despite showingShare boolean being set after shareView variable. Presumably because shareView is nil. The second time I click on Share button it shows the sharing view.
1
0
273
Aug ’24
SwiftData updates from CKSyncEngine (or any other background thread)
I'm currently managing two independent ModelContext instances in my app—one dedicated to CKSyncEngine and another for the Main/UI thread. While this setup works to some extent, I'm encountering a couple of issues: UI Updates: When SwiftData is updated via CKSyncEngine, the UI doesn't automatically refresh. To address this, I've had to implement .refreshable() and write imperative Swift code to (re)fetch data. This approach feels counterintuitive since it prevents me from fully leveraging SwiftUI's declarative nature, such as using @Query and user must explicitly trigger refresh. Deletion Logic: If users delete data via the UI, I have to manage a different delete code path. Specifically, I need to ensure that the object is removed from the UI's ModelContext without triggering a deletion in CKSyncEngine's ModelContext. This dual-path deletion logic feels unnecessarily complex. Also, I intend to later re-use CKSyncEngine part for Command Line tool app that will not have UI at all. What is the correct way to manage SwiftData in a background process like CKSyncEngine while maintaining a seamless and declarative approach in SwiftUI?
1
0
386
Aug ’24
Managing Duplicate Objects in Core Data (or SwiftData) with CloudKit Sync When Devices were Offline during object creation
Suppose I have two iPhones that are offline. On the first iPhone, at 1 PM, I create a Person object with the details: name: "John", lastName: "Smith", and age: 40. Then, on the second iPhone, which is also offline, I also create Person object at 2 PM with the same name: "John" and lastName: "Smith", but with a different age: 30. Both iPhones come online at 3 PM and sync with CloudKit. I would expect CloudKit to reconcile these two records and end up with a single record—specifically, Person(name: "John", lastName: "Smith", age: 30), assuming a "last writer wins" approach. Any guidance or best practices for handling this situation would be greatly appreciated! My idea is that I could generate a 128bit UUID as hash from first name and last name and then I would have to force this UUID to be used as recordName in CKRecord as this would trigger a conflict on CloudKit side and prevent two instance to be created. But how do I accomplish this with SwiftData or CoreData?
3
0
717
Aug ’24
Do CloudKit subscriptions work from command line tool application?
I am able to fetch CloudKit records from my MacOS command line tool/daemon. However, I would like CloudKit to notify my daemon whenever CKRecords were altered so I would not have to poll periodically. In CloudKit console I see that my app successfully created CloudKit subscription, but the part that confuses me is where in my app do I define callback function that gets called whenever CloudKit attempted to notify my app of CloudKit changes? My first question - do I need to define callback in my implementation of UNUserNotificationCenterDelegate? NSApplicationDelegate? Something else? My second question, would CKSyncEngine work from command line application?
1
0
393
Jul ’24
Is it possible to use CloudKit from "Command Line Tool" application?
In Xcode I have created UI-less application. I tried to add following code: import CloudKit let container = CKContainer.default() And it is failing with: In order to use CloudKit, your process must have a com.apple.developer.icloud-services entitlement. The value of this entitlement must be an array that includes the string "CloudKit" or "CloudKit-Anonymous". If I go to project and select my Command Line Tool target I don't see CloudKit capability that I usually see in UI based applications. So, is it impossible to use CloudKit from Command Line tools?
1
0
546
Jul ’24
How can DeviceActivityMonitor extension communicate with my main app?
At 11:37 in this video - https://developer.apple.com/videos/play/wwdc2021/10123/ - Nolan instantiates MyModel() in swiftui view file. And then at 12:02 he just uses MyModel from extension. I have the exact same code and when I try to build my project, it fails with error that MyModel() could not be found. I shared my MyModel.swift file between extension target and main app. Now it builds. However, it seems there are two separate instances of MyModel. What is proper way for DeviceActivityMonitor extension to pass data to main app? I simply want to increment counter from extension every minute and let the main app to know that. Or even better, - is there a way to use SwiftData from Device Activity Monitor extension?
1
0
601
Feb ’24