Posts under App & System Services topic

Post

Replies

Boosts

Views

Activity

SwiftUI & SwiftData: Fatal Error "Duplicate keys of type" Occurs on First Launch
I'm developing a SwiftUI app using SwiftData and encountering a persistent issue: Error Message: Thread 1: Fatal error: Duplicate keys of type 'Bland' were found in a Dictionary. This usually means either that the type violates Hashable's requirements, or that members of such a dictionary were mutated after insertion. Details: Occurrence: The error always occurs on the first launch of the app after installation. Specifically, it happens approximately 1 minute after the app starts. Inconsistent Behavior: Despite no changes to the code or server data, the error occurs inconsistently. Data Fetching Process: I fetch data for entities (Bland, CrossZansu, and Trade) from the server using the following process: Fetch Bland and CrossZansu entities via URLSession. Insert or update these entities into the SwiftData context. The fetched data is managed as follows: func refleshBlandsData() async throws { if let blandsOnServer = try await DataModel.shared.getBlands() { await MainActor.run { blandsOnServer.forEach { blandOnServer in if let blandOnLocal = blandList.first(where: { $0.code == blandOnServer.code }) { blandOnLocal.update(serverBland: blandOnServer) } else { modelContext.insert(blandOnServer.bland) } } } } } This is a simplified version of my StockListView. The blandList is a @Query property and dynamically retrieves data from SwiftData: struct StockListView: View { @Environment(\.modelContext) private var modelContext @Query(sort: \Bland.sname) var blandList: [Bland] @Query var users: [User] @State private var isNotLoaded = true @State private var isLoading = false @State private var loadingErrorState = "" var body: some View { NavigationStack { List { ForEach(blandList, id: \.self) { bland in NavigationLink(value: bland) { Text(bland.sname) } } } .navigationTitle("Stock List") .onAppear { doIfFirst() } } } // This function handles data loading when the app launches for the first time func doIfFirst() { if isNotLoaded { loadDataWithAnimationIfNotLoading() isNotLoaded = false } } // This function ensures data is loaded with an animation and avoids multiple triggers func loadDataWithAnimationIfNotLoading() { if !isLoading { isLoading = true Task { do { try await loadData() } catch { // Capture and store any errors during data loading loadingErrorState = "Data load failed: \(error.localizedDescription)" } isLoading = false } } } // Fetch data from the server and insert it into the SwiftData model context func loadData() async throws { if let blandsOnServer = try await DataModel.shared.getBlands() { for bland in blandsOnServer { // Avoid inserting duplicate keys by checking for existing items in blandList if !blandList.contains(where: { $0.code == bland.code }) { modelContext.insert(bland.bland) } } } } } Entity Definitions: Here are the main entities involved: Bland: @Model class Bland: Identifiable { @Attribute(.unique) var code: String var sname: String @Relationship(deleteRule: .cascade, inverse: \CrossZansu.bland) var zansuList: [CrossZansu] @Relationship(deleteRule: .cascade, inverse: \Trade.bland) var trades: [Trade] } CrossZansu: @Model class CrossZansu: Equatable { @Attribute(.unique) var id: String var bland: Bland? } Trade: @Model class Trade { @Relationship(deleteRule: .nullify) var user: User? var bland: Bland } User: class User { var id: UUID @Relationship(deleteRule: .cascade, inverse: \Trade.user) var trades: [Trade] } Observations: Error Context: The error occurs after the data is fetched and inserted into SwiftData. This suggests an issue with Hashable requirements or duplicate keys being inserted unintentionally. Concurrency Concerns: The fetch and update operations are performed in asynchronous tasks. Could this cause race conditions? Questions: Could this issue be related to how @Relationship and @Attribute(.unique) are managed in SwiftData? What are potential pitfalls with Equatable implementations (e.g., in CrossZansu) when used in SwiftData entities? Are there any recommended approaches for debugging "Duplicate keys" errors in SwiftData? Additional Info: Error Timing: The error occurs only during the app's first launch and consistently within the first minute.
3
1
641
Apr ’25
XPC listener initialized in System Extesnion invalidates incoming connection under certain conditions
I found a problem where a process tries to connect to System Extension and connection is invalidated. XPC listener has to be disposed and initialized again. This happens when System Extension executes tasks in following order: NSXPCListener initialized NSXPCListener.resume() NSProvider.startSystemExtensionMode() Result: Connection is invalidated and not only that the client has to retry connection, nut also System Extension must reinitialize listener (execute step 1 and 2). However if I call NSProvider.startSystemExtensionMode() NSXPCListener initialized NSXPCListener.resume() It works as expected and even if the connection is invalidated/interrupted, client process can always reconnect and no other action is necessary in System Extension (no need to reinitialize XPC listener), In Apple docs about NSProvider.startSystemExtensionMode() it says that this method starts handling request, but in another online article written by Scott Knight I found that startSystemExtensionMode() also starts listener server. Is that right? PLease could you add this info into the docs if it is so? https://knight.sc/reverse%20engineering/2019/08/24/system-extension-internals.html I would like to use following logic: Call NSProvider.startSystemExtensionMode() only under certain circumstances - I have received some configuration that I need to process and do some setup. If I don't receive it, there is no reason to call startSystemExtensionMode() yet, I don't need to handle handleNewFlow() yet. Connect XPC client to System Extension under certain conditions. Ideally communicate with client even though System Extension is not handling network requests yet, that is without receiving handleNewFlow(). Basically I consider XPC and System Extension handling network requests as separate things. Is that correct, are they separate and independent? Does XPC communication really depend on calling startSystemExtensionMode()? Another potential issue: Is it possible that XPC listener fails to validate connection when client tries to connect before System Extension manages to complete init and park the main thread in CFRunLoop? Note: These querstions arose mostly from handling upgrades of System Extension (extension is already running, network filter is created and is connected and new version of the app upgrades System Exension). Thanks.
5
0
1.4k
Apr ’25
Defining custom file types
On iOS:When one receives a file of type .pages by email, Mail displays a large Pages icon and tapping on it opens Pages. (A long-press brings up the more complicated Actions screen).When one receives a file of type .vcf by email, Mail displays a large Contacts icon and tapping on it opens Contacts. (A long-press brings up the more complicated Actions screen).I have my own custom file type, .ripf, and I want to have the same behaviour because that is what my users will expect. Accordingly, in my app's Info.plist I have a CFBundleDocumentTypes dictionary providing a one-element LSItemContentTypes array referring to the name 'com.universalis.ripcard', and a UTExportedTypeDeclarations dictionary associating the UTTypeIdentifier 'com.universalis.ripcard' with a public.filename-extension 'ripf' and a public.mime-type 'text/vnd.universalis.ripcard'. All the other entries in those two dictionaries are present and correct as far as I can tell. Both CFBundleDocumentTypes[0].CFBundleTypeIconFiles and UTExportedTypeDeclarations[0].UTTypeIconFiles contain a list of icon files for the file type.(That rather long paragraph is to avoid boring people by including the entire Info.plist!)Some things do work..ripf files received via AirDrop bring up a suitable "Open with..." message which mentions my app, and tapping the message opens the app..ripf files received as an email attachment display as an icon. But it is the app's icon and not the icon of the file type.BUTTapping on a received file's icon does not open the app, but only opens the generic Actions screen, offering Message, Mail, WhatsApp, Notes, and only then (after the user has scrolled sideways) "Copy to..." my app.Now, the whole apparatus of CFBundleDocumentTypes and UTExportedTypeDeclarations is obscure and under-documented, and indeed the main documenation for the latter has a big warning at the top saying that it is obsolete and not being updated. That doesn't matter so much. What I need to know is:(Less important): How do I get the right file icon?(More important): How do I get my app to open when the icon is tapped, as Pages and Contacts do? There must be a way – unless special cases for those two apps are wired into iOS itself.
10
0
6.5k
Apr ’25
Return the results of a Spotlight query synchronously from a Swift function
How can I return the results of a Spotlight query synchronously from a Swift function? I want to return a [String] that contains the items that match the query, one item per array element. I specifically want to find all data for Spotlight items in the /Applications folder that have a kMDItemAppStoreAdamID (if there is a better predicate than kMDItemAppStoreAdamID > 0, please let me know). The following should be the correct query: let query = NSMetadataQuery() query.predicate = NSPredicate(format: "kMDItemAppStoreAdamID > 0") query.searchScopes = ["/Applications"] I would like to do this for code that can run on macOS 10.13+, which precludes using Swift Concurrency. My project already uses the latest PromiseKit, so I assume that the solution should use that. A bonus solution using Swift Concurrency wouldn't hurt as I will probably switch over sometime in the future, but won't be able to switch soon. I have written code that can retrieve the Spotlight data as the [String], but I don't know how to return it synchronously from a function; whatever I tried, the query hangs, presumably because I've called various run loop functions at the wrong places. In case it matters, the app is a macOS command-line app using Swift 5.7 & Swift Argument Parser 1.5.0. The Spotlight data will be output only as text to stdout & stderr, not to any Apple UI elements.
1
0
201
Apr ’25
App Groups in Provisioning Profile
I'll preface by saying I am new to MacOS development. I've struggled with this issue for several days and have nowhere else to go for help. My MacOS app is an Electron build. It needs application-groups entitlement for IPC. But the developer portal, when generating the provisioning profile, always appends "groups." to the start and I am unable to remove it. This renders my provisioning profile invalid and causes my app to be rejected by Transporter because it is not supposed to start with "groups", but with my team identified for MacOS. Maybe I can still use the provisioning profile as is, but I've not found any way to do that. So I'm stuck unable to deliver. Any help with this is appreciated.
1
0
139
Apr ’25
Network Service Order for IoT Ethernet device
I am making a USB attached IoT device that follows the Matter approach to connectivity (IP/mDNS/DHCP). I am having conflicts with it as it appears to MacOS as an Ethernet adapter and this is causing it to be assigned as a "default" route, interfering with routing when my Mac is connected to NAT based WiFi. I'd like to be able to hint to MacOS & iPadOS that this is not a routable private network, the subnet should be respected and a default route should not be assigned to it, otherwise the order of the device connection is used by the IP routing tables and I am concerned my non-routable private network will initialize before Wifi and block NAT based internet connectivity. How can I hint to MacOS/iPadOS "this is not a routable private network, this is not a NAT, do not assign me a default route beyond the subnet I have provided you."
1
0
126
Apr ’25
Launch Host App from Share Extension
Hi! Could you please point me to the official documentation or recommended approach for launching the host app from a Share Extension? The scenario is: The user is sharing some text to my app. I need launch App and show this text. At the moment, I'm using the following hack: let selector = NSSelectorFromString("sharedApplication") if let app = UIApplication.perform(selector)?.takeUnretainedValue() as? UIApplication, app.responds(to: #selector(UIApplication.open(_:options:completionHandler:))) { app.open(url, options: [:], completionHandler: nil) } This does work, but it's terrible. So, the question: What is the official way to open the host app from within a Share Extension? Thanks!
2
0
180
Apr ’25
date(byAdding:value:to:options:) Method Behaves Strangely
Let’s try calculating one day after "2023/11/04 12:00 New York time". let timeZone = TimeZone(identifier: "America/New_York")! var calendar = Calendar(identifier: .gregorian) calendar.timeZone = timeZone var dateFormatter = DateFormatter() dateFormatter.timeZone = timeZone dateFormatter.locale = .init(identifier: "ja_JP") dateFormatter.dateStyle = .short dateFormatter.timeStyle = .short var dateComponents = DateComponents() dateComponents.year = 2023 dateComponents.month = 11 dateComponents.day = 4 dateComponents.hour = 12 // At New York 2023/11/04 12:00 let date1 = calendar.date(from: dateComponents)! print(dateFormatter.string(from: date1)) // Add 1 day let date2 = calendar.date(byAdding: .day, value: 1, to: date1)! print(dateFormatter.string(from: date2))``` The output is: 2023/11/04 12:00 2023/11/05 12:00 Now, let’s try the following—also to get the time one day later: let date2 = calendar.date(byAdding: .minute, value: 24 * 60, to: date1)! print(dateFormatter.string(from: date2)) This outputs: 2023/11/04 12:00 2023/11/05 11:00 What's Causing This Difference? It’s likely due to Daylight Saving Time (DST). But why do we get different results between the first and second examples?
1
0
231
Apr ’25
Access BSSID MacOS
I don't understand what permissions need to be given for this code to operate. I cannot seem to work out why I'm not able to see a BSSID. I think I've given sandbox the appropriate permissions AND I've added some to the Target Properties for good measure. Yet, cannot get BSSID. import SwiftUI import CoreWLAN import CoreLocation struct ContentView: View { @State private var currentBSSID: String = "Loading..." var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Current BSSID:") Text(currentBSSID) } .padding() .onAppear(perform: fetchBSSID) } func fetchBSSID() { if let iface2 = CWWiFiClient.shared().interface() { print("✅ Found Wi-Fi interface: \(iface2.interfaceName ?? "nil")") } else { print("❌ No Wi-Fi interface found") } if let iface = CWWiFiClient.shared().interface(), let bssid = iface.bssid() { currentBSSID = bssid } else { currentBSSID = "Not connected" print("✅ BSSID: \(currentBSSID)") } } } #Preview { ContentView() } Output - WifI interface is found but BSSID is not found.
2
0
219
Apr ’25
Issue Integrating Apple Pay JS – `merchantSession` Blocke
Hello Apple Devs, We’re currently trying to integrate Apple Pay on the web using Apple Pay JS. We've followed the official documentation closely, but we're running into a blocker during the merchantSession validation phase. We successfully retrieved a merchantSession, which looks like this: json { "displayName": "Our Name", "domainName": "https://pay.ourdomain.co", "epochTimestamp": , "expiresAt": ****************, "merchantIdentifier": "", "merchantSessionIdentifier": ", "nonce": "", "operationalAnalyticsIdentifier": our name "t:", "pspId": "", "retries": 0, "signature": "*****************..." } Issue: Shortly after initiating the session, we receive a cancel event with the following info: ApplePayCancelEvent { type: "cancel", sessionError: { code: "unknown", info: {} } } We're unsure what causes the cancellation. There are no clear error messages or hints in the logs to identify what went wrong. What We’ve Checked: The merchantSession is returned successfully from our backend. The domainName matches our frontend domain (https://pay.durdomain.co). The session hasn’t expired when tested. We're using Apple Pay JS APIs as described in the documentation. Help Needed: What can trigger an ApplePayCancelEvent with an "unknown" error code? Any insight or guidance would be deeply appreciated. Thanks in advance!
1
0
134
Apr ’25
SwiftData migration crashes when working with relationships
The following complex migration consistently crashes the app with the following error: SwiftData/PersistentModel.swift:726: Fatal error: What kind of backing data is this? SwiftData._KKMDBackingData<SwiftDataMigration.ItemSchemaV1.ItemList> My app relies on a complex migration that involves these optional 1 to n relationships. Theoretically I could not assign the relationships in the willMigrate block but afterwards I am not able to tell which list and items belonged together. Steps to reproduce: Run project Change typealias CurrentSchema to ItemSchemaV2 instead of ItemSchemaV1. Run project again -> App crashes My setup: Xcode Version 16.2 (16C5032a) MacOS Sequoia 15.4 iPhone 12 with 18.3.2 (22D82) Am I doing something wrong or did I stumble upon a bug? I have a demo Xcode project ready but I could not upload it here so I put the code below. Thanks for your help typealias CurrentSchema = ItemSchemaV1 typealias ItemList = CurrentSchema.ItemList typealias Item = CurrentSchema.Item @main struct SwiftDataMigrationApp: App { var sharedModelContainer: ModelContainer = { do { return try ModelContainer(for: ItemList.self, migrationPlan: MigrationPlan.self) } catch { fatalError("Could not create ModelContainer: \(error)") } }() var body: some Scene { WindowGroup { ContentView() } .modelContainer(sharedModelContainer) } } This is the migration plan enum MigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [ItemSchemaV1.self, ItemSchemaV2.self] } static var stages: [MigrationStage] = [ MigrationStage.custom(fromVersion: ItemSchemaV1.self, toVersion: ItemSchemaV2.self, willMigrate: { context in print("Started migration") let oldlistItems = try context.fetch(FetchDescriptor<ItemSchemaV1.ItemList>()) for list in oldlistItems { let items = list.items.map { ItemSchemaV2.Item(timestamp: $0.timestamp)} let newList = ItemSchemaV2.ItemList(items: items, name: list.name, note: "This is a new property") context.insert(newList) context.delete(list) } try context.save() // Crash indicated here print("Finished willMigrate") }, didMigrate: { context in print("Did migrate successfully") }) ] } The versioned schemas enum ItemSchemaV1: VersionedSchema { static var versionIdentifier = Schema.Version(1, 0, 0) static var models: [any PersistentModel.Type] { [Item.self] } @Model final class Item { var timestamp: Date var list: ItemSchemaV1.ItemList? init(timestamp: Date) { self.timestamp = timestamp } } @Model final class ItemList { @Relationship(deleteRule: .cascade, inverse: \ItemSchemaV1.Item.list) var items: [Item] var name: String init(items: [Item], name: String) { self.items = items self.name = name } } } enum ItemSchemaV2: VersionedSchema { static var versionIdentifier = Schema.Version(2, 0, 0) static var models: [any PersistentModel.Type] { [Item.self] } @Model final class Item { var timestamp: Date var list: ItemSchemaV2.ItemList? init(timestamp: Date) { self.timestamp = timestamp } } @Model final class ItemList { @Relationship(deleteRule: .cascade, inverse: \ItemSchemaV2.Item.list) var items: [Item] var name: String var note: String init(items: [Item], name: String, note: String = "") { self.items = items self.name = name self.note = note } } } Last the ContentView: struct ContentView: View { @Query private var itemLists: [ItemList] var body: some View { NavigationSplitView { List { ForEach(itemLists) { list in NavigationLink { List(list.items) { item in Text(item.timestamp.formatted(date: .abbreviated, time: .complete)) } .navigationTitle(list.name) } label: { Text(list.name) } } } .navigationTitle("Crashing migration demo") .onAppear { if itemLists.isEmpty { for index in 0..<10 { let items = [Item(timestamp: Date.now)] let listItem = ItemList(items: items, name: "List No. \(index)") modelContext.insert(listItem) } try! modelContext.save() } } } detail: { Text("Select an item") } } }
1
1
183
Apr ’25
SwiftData data crashes with @Relationship
I've noticed that SwiftData's @Relationship seems to potentially cause application crashes. The crash error is shown in the image. Since this crash appears to be random and I cannot reproduce it under specific circumstances, I can only temporarily highlight that this issue seems to exist. @Model final class TrainInfo { @Relationship(deleteRule: .cascade, inverse: \StopStation.trainInfo) var stations: [StopStation]? } @Model final class StopStation { @Relationship var trainInfo: TrainInfo? } /// some View var origin: StopStationDisplayable? { if let train = train as? TrainInfo { return train.stations?.first(where: { $0.isOrigin }) ?? train.stations?.first(where: { $0.isStarting }) } return nil } // Some other function or property func someFunction() { if let origin, let destination { // Function implementation } }
1
0
153
Apr ’25
Converting Payed App to Freemium
I just converted my App to Freemium, but the method is used to give user, that bought the App before it was free, access to the paid futures seems not to be working. I am getting the original Purchase Date from the AppTransaction (https://developer.apple.com/documentation/storekit/apptransaction/originalpurchasedate) and comparing that to a set date where the App Model changed. In Test Flight this is working without any Problems but on the Live System, users that have purchased the App do not get access! let shared = try await AppTransaction.refresh() if case .verified(let appTransaction) = shared { result(appTransaction.originalPurchaseDate.ISO8601Format()). } I am using flutter to develop the App and the result()... send the string back to the flutter side. Here is the code of the Flutter side: Future<void> restorePurchases() async { SubscriptionProvider().updatePayment(PurchaseStatus.pending); await InAppPurchase.instance.restorePurchases(); if (Platform.isIOS) { DateTime changedToFreemium = DateTime.utc(2025, 4, 7, 11, 0, 0); String? purchaseDateRaw = await IosFlutterChannel().getOriginalPurchaseDate(); if (kDebugMode) { print("Purchase Date Raw: $purchaseDateRaw"); } if (purchaseDateRaw != null) { DateTime purchaseDate = DateTime.parse(purchaseDateRaw); if (purchaseDate.isBefore(changedToFreemium)) { if (kDebugMode) { print("Restoring legacy purchases"); } SubscriptionProvider().update(true, SubscriptionStatus.active, SubscriptionType.fs_lifetime); } else { if (kDebugMode) { print("Not restoring legacy purchases"); } } } } SubscriptionProvider().updatePayment(PurchaseStatus.purchased); } Console Log when running in Test Environment: flutter: Purchase Date Raw: 2013-08-01T07:00:00Z flutter: Restoring legacy purchases Thanks!
1
0
154
Apr ’25
Apple Pay - PKAddPaymentPassViewController doesn't show correct list of devices
Hi, We are trying to make the PKAddPaymentPassViewController to show the correct list of devices to where the pass can be added. We have analysed the documentation and we are using the PrimaryAccountIdentifier field which is the field that supposedly controls this behavior but the list of devices presented in the view controller always include one iPhone and one Apple Watch, regardless of where the card has been already added. We are initializing the PKAddPaymentPassRequestConfiguration object with: PKEncryptionScheme PrimaryAccountIdentifier CardholderName PrimaryAccountSuffix LocalizedDescription PaymentNetwork PrimaryAccountIdentifier CardholderName PrimaryAccountSuffix LocalizedDescription We have also verified the configuration in our payment pass processor and everything should be ok. We would like to have some help on achieving the desired flow for Apple Pay, which is to present the PKAddPaymentPassViewController with the correct list of available devices and not the full list. Thank you.
1
0
577
Apr ’25
Improving And Scaling App Intent Support
Platform and Version iOS Development Environment: Xcode 16.2.0, macOS 15.3 Run-time Configuration: iOS 18.3, 17.x Description of Problem We have started migrating some of the app’s core functionality over to App Intents. Our first release of App Intent support focused on two settings a user can modify on their Bose products, Audio Modes and Immersive Audio, giving users the ability to modify these settings via Siri and shortcuts. The implementation uses two separate shortcuts for each setting type, with each shortcut supporting a single phrase for Siri each: “Change my Bose mode to ” and “Change my Bose immersive audio to ”. Each shortcut uses their own App Intent, and each App Intent has support for optionally providing both a product and a setting when performing the intent. Failing to provide a device, which happens when the intent is performed via Siri, simply auto selects a currently connected Bose product. Failing to provide a setting, like in cases where a user says “Change my Bose ” without providing a setting will simply have Siri confirm the setting the user wants to change before changing the setting. We are using AppEntity to identify a Bose product for both App Intents. Because the App Intent for the Audio Modes setting has a larger number of supported values (up to 15 maximum), we are also using AppEntity to identify these settings. We are using AppEnum to identify available settings for the Immersive Audio App Intent, as only 3 static values are supported. Our original implementation of App Intent support had quite a few phrases supported for each shortcut. We had explicit support for direct synonyms of the verb “Change” in other phrases, supporting words like “Switch” and “Set”. We also had support for words that are like the word “Change”, but not directly related, like the word “Toggle” for instance. We also had support for phrases with or without the setting in each phrase. However, early on we had a lot of trouble with phrase detection with Siri. Siri had a hard time identifying what shortcut was being requested, as well as not being able to identify what settings the user was providing for the setting parameter of each App Intent. While researching potential fixes for this issue, we found a response to a thread in the Apple forums (https://developer.apple.com/forums/thread/759909) that seemed to indicate that Siri phrase recognition was very much an aggregate process. With the total number of phrases supported combined with the available settings for each phrase further compounding the total number of phrases Siri needs to learn to recognize for each shortcut. So, to hopefully improve Siri phrase detection, we added logic to limit the amount of Audio Mode settings supported based on what Audio Modes the user had setup on their Bose products. But, more importantly, we limited the number of explicit phrases supported for each shortcut to just a single phrase. In our testing, not only did this improve phrase recognition, but support for synonyms like “Set” or “Switch” seemed to implicitly still be recognized by Siri. The issues we ran into with Siri phrase detection above has us a bit concerned about scaling App Intent support to other settings and features for our products in the future. Our app supports the ability to modify a large number of settings on their Bose products, with support constantly expanding to new products as they are released. Our roadmap for App Intent support was initially very ambitious, supporting much more than just the two settings mentioned above. But our initial experience with App Intents has us tapering our expectations a little bit as far as how much can be supported in total for App Intents. One thing we also noticed is less than optimal display of default shortcuts in the Shortcuts app. The default shortcuts appeared like so, with shortcuts displayed based on available settings fro each shortcut: However, we could not find a way to indicate to users that one particular section pertained specifically to the Audio Mode setting and the other to the Immersive Audio setting. The only information the user has to make this determination for themselves is the available settings (or shortcuts) for each. This may not be immediately clear to a new customer who might be using one of our products for the first time. This display of default shortcuts in the Shortcuts app has us wondering if our shortcuts implementation is what is intended as far as support for the Shortcuts app is concerned. We did survey default shortcuts displayed by other third-party applications and they mostly dealt with navigation with a single section containing default options clearly indicating where the user can navigate with a shortcut. We couldn’t find an example of an application supporting the ability to change different setting types, with each setting type having their own available values for each. So, to summarize the questions we have concerning App Intent support: What can we do with our App Intents and Shortcuts implementation to guarantee optimal performance with Siri? What is an ideal number of phrases to support for each Shortcut. What limitations should we be placing as far as the total number of available settings for each Shortcut. Are there phrases that might work better than others for what we’re trying to achieve with App Intent support? i.e. Is “Change my Bose mode” or “Change my Bose immersive audio” a good phrase to use for this kind of functionality? Or should we be using different verbs or wording? Assuming optimal support of each Shortcut above. What is a reasonable expectation as far as how many different supported shortcuts we can scale to support at the same time. One issue we ran into early on was Siri confusing one shortcut with the other and triggering the wrong App Intent at times. While this was ultimately resolved, this outcome seems much more likely the greater the number of individual shortcuts supported. Are there any recommendations on how to display these App Intents to customers as far as default shortcuts in the Shortcuts app is concerned? Is what we currently display for default shortcuts in the Shortcuts app what was initially intended for third party support for App Intents? If what we are currently displaying is expected, would it be possible to support the ability to provide additional context to each section of default shortcuts displayed? We would like to indicate to the user that one set of shortcuts pertains to the Audio Modes settings, and the other to Immersive Audio. Something along the lines of a section header like some of the first-party apps use. Are there any recommendations or tips for supporting App Intents, particularly phrases for Siri, in other languages?
0
0
200
Apr ’25
Accuracy of IBI Values Measured by Apple Watch
I am currently developing an app that measures HRV to estimate stress levels. To align the values more closely with those from Galaxy devices, I decided not to use the heartRateVariabilitySDNN value provided by HealthKit. Instead, I extracted individual interbeat intervals (IBI) using the HKHeartBeatSeries data. Can I obtain accurate IBI data using this method? If not, I would like to know how I can retrieve more precise data. Any insights or suggestions would be greatly appreciated. Here is a sample code I tried. @Observable class HealthKitManager: ObservableObject { let healthStore = HKHealthStore() var ibiValues: [Double] = [] var isAuthorized = false func requestAuthorization() { let types = Set([ HKSeriesType.heartbeat(), HKQuantityType.quantityType(forIdentifier: .heartRateVariabilitySDNN)!, ]) healthStore.requestAuthorization(toShare: nil, read: types) { success, error in DispatchQueue.main.async { self.isAuthorized = success if success { self.fetchIBIData() } } } } func fetchIBIData() { var timePoints: [TimeInterval] = [] var absoluteStartTime: Date? let dateFormatter = DateFormatter() dateFormatter.timeZone = TimeZone(identifier: "Asia/Seoul") dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" var calendar = Calendar.current calendar.timeZone = TimeZone(identifier: "Asia/Seoul") ?? .current var components = DateComponents() components.year = 2025 components.month = 4 components.day = 3 components.hour = 15 components.minute = 52 components.second = 0 let startTime = calendar.date(from: components)! components.hour = 16 components.minute = 0 let endTime = calendar.date(from: components)! let predicate = HKQuery.predicateForSamples(withStart: startTime, end: endTime, options: .strictStartDate) let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false) let query = HKSampleQuery(sampleType: HKSeriesType.heartbeat(), predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: [sortDescriptor]) { (_, samples, _) in if let sample = samples?.first as? HKHeartbeatSeriesSample { absoluteStartTime = sample.startDate let startDateKST = dateFormatter.string(from: sample.startDate) let endDateKST = dateFormatter.string(from: sample.endDate) print("series start(KST):\(startDateKST)\tend(KST):\(endDateKST)") let seriesQuery = HKHeartbeatSeriesQuery(heartbeatSeries: sample) { query, timeSinceSeriesStart, precededByGap, done, error in if !precededByGap { timePoints.append(timeSinceSeriesStart) } if done { for i in 1..<timePoints.count { let ibi = (timePoints[i] - timePoints[i-1]) * 1000 // Convert to milliseconds // Calculate absolute time for current beat if let startTime = absoluteStartTime { let beatTime = startTime.addingTimeInterval(timePoints[i]) let beatTimeString = dateFormatter.string(from: beatTime) print("IBI: \(String(format: "%.2f", ibi)) ms at \(beatTimeString)") } self.ibiValues.append(ibi) } } } self.healthStore.execute(seriesQuery) } else { print("No samples found for the specified time range") } } self.healthStore.execute(query) } }
1
0
115
Apr ’25
Detection of file eviction
I want to implement quota feature to my file provider extension. I am able to keep track of materialized files total size. (Content download and edit operations) However I cannot detect file eviction operation (User right click to file and select "Remove Download"). Is there anyway to detect this action Or any suggestion to keep track of materialized files total size?
2
0
189
Apr ’25
Guidance on Continuous Location & Direction Updates to watchOS App Without Screen Dimming
Dear Apple Developer Support, I am reaching out for guidance on implementing continuous directional and location updates in a watchOS app designed as part of a mapping/navigation solution. Current Scenario: We send continuous location and direction updates from our iOS app to the watchOS companion app. When viewing directions on the Apple Watch, the screen dims after a short period, and the live data stops updating consistently, even though the user is actively looking at the screen. This negatively impacts the usability of real-time navigation on watchOS. Our Objective: Prevent screen dimming (or extend screen-on time) while the user is viewing navigation directions. Ensure reliable, continuous data updates on the watch screen during active navigation sessions. Request: We would appreciate your guidance on: The recommended method to keep the watchOS screen active while displaying real-time navigation data. Proper use of APIs such as WKExtendedRuntimeSession, WorkoutSession, or any other mechanism suitable for this use case. Any best practices or App Store review considerations for apps that require extended screen time and continuous updates. How such use cases were traditionally handled on watchOS and what has or hasn’t worked. We want to ensure we're implementing this in a battery-efficient, user-respectful, and Apple-compliant manner. Thank you for your assistance and guidance.
1
0
99
Apr ’25
UNEXPECTED_CANCEL_AFTER_completeMerchantValidation
Hi, We are trying to make payment from ecomm merchant. The last request during process is { "sessionData": { "epochTimestamp": "1741082241", "expiresAt": "1741092241", "merchantSessionIdentifier": "SSH88312C485D_7E0DD10173", "nonce": "3f6dc197", "merchantIdentifier": "5F9BC6BAF8", "domainName": "libertybank.ge", "displayName": "Apple Pay Purchase", "signature": "3080060000", "operationalAnalyticsIdentifier": "Apple Pay Purchase:5F9BC6BAF8", "retries": 0, "pspId": "5F9BC6BAF8" } } which is successfully validated applePaySession.completeMerchantValidation(data.sessionData) After this, the "oncancel" handler is triggered in applePay. Please help us to understand what is wrong. Please note the domain where the applepay button is located is at txpg.libertypay.ge Which is successfully verified.
1
0
106
Apr ’25
Issues with Siri Shortcuts: Confirmation Prompt, Inconsistent Behavior
Hello Apple Developer Community, I’m working on integrating Siri into my React Native app using native iOS code and bridging to React Native. I’ve followed the necessary steps to set up Siri support, including: Adding the Siri capability. Adding Siri usage descriptions in Info.plist. Using AppIntent and AppShortcutsProvider to define shortcuts. However, I’m facing the following issues: Siri Prompts for Confirmation When a user says a phrase, Siri asks, "Turn on 'MyApp' shortcuts with Siri?" instead of directly recognizing the phrase. Is this expected behavior? If so, how can I reduce friction for users and make the experience more seamless? Inconsistent Behavior for Existing Users For users updating to a version with Siri support: When the app is closed, Siri says, "MyApp hasn't added support for that with Siri." When the app is open, Siri prompts, "Turn on shortcut for MyApp?" and rest all working fine Why does Siri not recognize the shortcut when the app is closed, even though the shortcut is defined in AppShortcutsProvider? How can I ensure that Siri recognizes the shortcut regardless of whether the app is open or closed? Other than using AppIntent and AppShortcutsProvider should i try Donating shortcuts(will that helps for updated user case). Please help me on this
8
1
632
Apr ’25
SwiftUI & SwiftData: Fatal Error "Duplicate keys of type" Occurs on First Launch
I'm developing a SwiftUI app using SwiftData and encountering a persistent issue: Error Message: Thread 1: Fatal error: Duplicate keys of type 'Bland' were found in a Dictionary. This usually means either that the type violates Hashable's requirements, or that members of such a dictionary were mutated after insertion. Details: Occurrence: The error always occurs on the first launch of the app after installation. Specifically, it happens approximately 1 minute after the app starts. Inconsistent Behavior: Despite no changes to the code or server data, the error occurs inconsistently. Data Fetching Process: I fetch data for entities (Bland, CrossZansu, and Trade) from the server using the following process: Fetch Bland and CrossZansu entities via URLSession. Insert or update these entities into the SwiftData context. The fetched data is managed as follows: func refleshBlandsData() async throws { if let blandsOnServer = try await DataModel.shared.getBlands() { await MainActor.run { blandsOnServer.forEach { blandOnServer in if let blandOnLocal = blandList.first(where: { $0.code == blandOnServer.code }) { blandOnLocal.update(serverBland: blandOnServer) } else { modelContext.insert(blandOnServer.bland) } } } } } This is a simplified version of my StockListView. The blandList is a @Query property and dynamically retrieves data from SwiftData: struct StockListView: View { @Environment(\.modelContext) private var modelContext @Query(sort: \Bland.sname) var blandList: [Bland] @Query var users: [User] @State private var isNotLoaded = true @State private var isLoading = false @State private var loadingErrorState = "" var body: some View { NavigationStack { List { ForEach(blandList, id: \.self) { bland in NavigationLink(value: bland) { Text(bland.sname) } } } .navigationTitle("Stock List") .onAppear { doIfFirst() } } } // This function handles data loading when the app launches for the first time func doIfFirst() { if isNotLoaded { loadDataWithAnimationIfNotLoading() isNotLoaded = false } } // This function ensures data is loaded with an animation and avoids multiple triggers func loadDataWithAnimationIfNotLoading() { if !isLoading { isLoading = true Task { do { try await loadData() } catch { // Capture and store any errors during data loading loadingErrorState = "Data load failed: \(error.localizedDescription)" } isLoading = false } } } // Fetch data from the server and insert it into the SwiftData model context func loadData() async throws { if let blandsOnServer = try await DataModel.shared.getBlands() { for bland in blandsOnServer { // Avoid inserting duplicate keys by checking for existing items in blandList if !blandList.contains(where: { $0.code == bland.code }) { modelContext.insert(bland.bland) } } } } } Entity Definitions: Here are the main entities involved: Bland: @Model class Bland: Identifiable { @Attribute(.unique) var code: String var sname: String @Relationship(deleteRule: .cascade, inverse: \CrossZansu.bland) var zansuList: [CrossZansu] @Relationship(deleteRule: .cascade, inverse: \Trade.bland) var trades: [Trade] } CrossZansu: @Model class CrossZansu: Equatable { @Attribute(.unique) var id: String var bland: Bland? } Trade: @Model class Trade { @Relationship(deleteRule: .nullify) var user: User? var bland: Bland } User: class User { var id: UUID @Relationship(deleteRule: .cascade, inverse: \Trade.user) var trades: [Trade] } Observations: Error Context: The error occurs after the data is fetched and inserted into SwiftData. This suggests an issue with Hashable requirements or duplicate keys being inserted unintentionally. Concurrency Concerns: The fetch and update operations are performed in asynchronous tasks. Could this cause race conditions? Questions: Could this issue be related to how @Relationship and @Attribute(.unique) are managed in SwiftData? What are potential pitfalls with Equatable implementations (e.g., in CrossZansu) when used in SwiftData entities? Are there any recommended approaches for debugging "Duplicate keys" errors in SwiftData? Additional Info: Error Timing: The error occurs only during the app's first launch and consistently within the first minute.
Replies
3
Boosts
1
Views
641
Activity
Apr ’25
XPC listener initialized in System Extesnion invalidates incoming connection under certain conditions
I found a problem where a process tries to connect to System Extension and connection is invalidated. XPC listener has to be disposed and initialized again. This happens when System Extension executes tasks in following order: NSXPCListener initialized NSXPCListener.resume() NSProvider.startSystemExtensionMode() Result: Connection is invalidated and not only that the client has to retry connection, nut also System Extension must reinitialize listener (execute step 1 and 2). However if I call NSProvider.startSystemExtensionMode() NSXPCListener initialized NSXPCListener.resume() It works as expected and even if the connection is invalidated/interrupted, client process can always reconnect and no other action is necessary in System Extension (no need to reinitialize XPC listener), In Apple docs about NSProvider.startSystemExtensionMode() it says that this method starts handling request, but in another online article written by Scott Knight I found that startSystemExtensionMode() also starts listener server. Is that right? PLease could you add this info into the docs if it is so? https://knight.sc/reverse%20engineering/2019/08/24/system-extension-internals.html I would like to use following logic: Call NSProvider.startSystemExtensionMode() only under certain circumstances - I have received some configuration that I need to process and do some setup. If I don't receive it, there is no reason to call startSystemExtensionMode() yet, I don't need to handle handleNewFlow() yet. Connect XPC client to System Extension under certain conditions. Ideally communicate with client even though System Extension is not handling network requests yet, that is without receiving handleNewFlow(). Basically I consider XPC and System Extension handling network requests as separate things. Is that correct, are they separate and independent? Does XPC communication really depend on calling startSystemExtensionMode()? Another potential issue: Is it possible that XPC listener fails to validate connection when client tries to connect before System Extension manages to complete init and park the main thread in CFRunLoop? Note: These querstions arose mostly from handling upgrades of System Extension (extension is already running, network filter is created and is connected and new version of the app upgrades System Exension). Thanks.
Replies
5
Boosts
0
Views
1.4k
Activity
Apr ’25
Defining custom file types
On iOS:When one receives a file of type .pages by email, Mail displays a large Pages icon and tapping on it opens Pages. (A long-press brings up the more complicated Actions screen).When one receives a file of type .vcf by email, Mail displays a large Contacts icon and tapping on it opens Contacts. (A long-press brings up the more complicated Actions screen).I have my own custom file type, .ripf, and I want to have the same behaviour because that is what my users will expect. Accordingly, in my app's Info.plist I have a CFBundleDocumentTypes dictionary providing a one-element LSItemContentTypes array referring to the name 'com.universalis.ripcard', and a UTExportedTypeDeclarations dictionary associating the UTTypeIdentifier 'com.universalis.ripcard' with a public.filename-extension 'ripf' and a public.mime-type 'text/vnd.universalis.ripcard'. All the other entries in those two dictionaries are present and correct as far as I can tell. Both CFBundleDocumentTypes[0].CFBundleTypeIconFiles and UTExportedTypeDeclarations[0].UTTypeIconFiles contain a list of icon files for the file type.(That rather long paragraph is to avoid boring people by including the entire Info.plist!)Some things do work..ripf files received via AirDrop bring up a suitable "Open with..." message which mentions my app, and tapping the message opens the app..ripf files received as an email attachment display as an icon. But it is the app's icon and not the icon of the file type.BUTTapping on a received file's icon does not open the app, but only opens the generic Actions screen, offering Message, Mail, WhatsApp, Notes, and only then (after the user has scrolled sideways) "Copy to..." my app.Now, the whole apparatus of CFBundleDocumentTypes and UTExportedTypeDeclarations is obscure and under-documented, and indeed the main documenation for the latter has a big warning at the top saying that it is obsolete and not being updated. That doesn't matter so much. What I need to know is:(Less important): How do I get the right file icon?(More important): How do I get my app to open when the icon is tapped, as Pages and Contacts do? There must be a way – unless special cases for those two apps are wired into iOS itself.
Replies
10
Boosts
0
Views
6.5k
Activity
Apr ’25
Return the results of a Spotlight query synchronously from a Swift function
How can I return the results of a Spotlight query synchronously from a Swift function? I want to return a [String] that contains the items that match the query, one item per array element. I specifically want to find all data for Spotlight items in the /Applications folder that have a kMDItemAppStoreAdamID (if there is a better predicate than kMDItemAppStoreAdamID > 0, please let me know). The following should be the correct query: let query = NSMetadataQuery() query.predicate = NSPredicate(format: "kMDItemAppStoreAdamID > 0") query.searchScopes = ["/Applications"] I would like to do this for code that can run on macOS 10.13+, which precludes using Swift Concurrency. My project already uses the latest PromiseKit, so I assume that the solution should use that. A bonus solution using Swift Concurrency wouldn't hurt as I will probably switch over sometime in the future, but won't be able to switch soon. I have written code that can retrieve the Spotlight data as the [String], but I don't know how to return it synchronously from a function; whatever I tried, the query hangs, presumably because I've called various run loop functions at the wrong places. In case it matters, the app is a macOS command-line app using Swift 5.7 & Swift Argument Parser 1.5.0. The Spotlight data will be output only as text to stdout & stderr, not to any Apple UI elements.
Replies
1
Boosts
0
Views
201
Activity
Apr ’25
App Groups in Provisioning Profile
I'll preface by saying I am new to MacOS development. I've struggled with this issue for several days and have nowhere else to go for help. My MacOS app is an Electron build. It needs application-groups entitlement for IPC. But the developer portal, when generating the provisioning profile, always appends "groups." to the start and I am unable to remove it. This renders my provisioning profile invalid and causes my app to be rejected by Transporter because it is not supposed to start with "groups", but with my team identified for MacOS. Maybe I can still use the provisioning profile as is, but I've not found any way to do that. So I'm stuck unable to deliver. Any help with this is appreciated.
Replies
1
Boosts
0
Views
139
Activity
Apr ’25
Network Service Order for IoT Ethernet device
I am making a USB attached IoT device that follows the Matter approach to connectivity (IP/mDNS/DHCP). I am having conflicts with it as it appears to MacOS as an Ethernet adapter and this is causing it to be assigned as a "default" route, interfering with routing when my Mac is connected to NAT based WiFi. I'd like to be able to hint to MacOS & iPadOS that this is not a routable private network, the subnet should be respected and a default route should not be assigned to it, otherwise the order of the device connection is used by the IP routing tables and I am concerned my non-routable private network will initialize before Wifi and block NAT based internet connectivity. How can I hint to MacOS/iPadOS "this is not a routable private network, this is not a NAT, do not assign me a default route beyond the subnet I have provided you."
Replies
1
Boosts
0
Views
126
Activity
Apr ’25
Launch Host App from Share Extension
Hi! Could you please point me to the official documentation or recommended approach for launching the host app from a Share Extension? The scenario is: The user is sharing some text to my app. I need launch App and show this text. At the moment, I'm using the following hack: let selector = NSSelectorFromString("sharedApplication") if let app = UIApplication.perform(selector)?.takeUnretainedValue() as? UIApplication, app.responds(to: #selector(UIApplication.open(_:options:completionHandler:))) { app.open(url, options: [:], completionHandler: nil) } This does work, but it's terrible. So, the question: What is the official way to open the host app from within a Share Extension? Thanks!
Replies
2
Boosts
0
Views
180
Activity
Apr ’25
date(byAdding:value:to:options:) Method Behaves Strangely
Let’s try calculating one day after "2023/11/04 12:00 New York time". let timeZone = TimeZone(identifier: "America/New_York")! var calendar = Calendar(identifier: .gregorian) calendar.timeZone = timeZone var dateFormatter = DateFormatter() dateFormatter.timeZone = timeZone dateFormatter.locale = .init(identifier: "ja_JP") dateFormatter.dateStyle = .short dateFormatter.timeStyle = .short var dateComponents = DateComponents() dateComponents.year = 2023 dateComponents.month = 11 dateComponents.day = 4 dateComponents.hour = 12 // At New York 2023/11/04 12:00 let date1 = calendar.date(from: dateComponents)! print(dateFormatter.string(from: date1)) // Add 1 day let date2 = calendar.date(byAdding: .day, value: 1, to: date1)! print(dateFormatter.string(from: date2))``` The output is: 2023/11/04 12:00 2023/11/05 12:00 Now, let’s try the following—also to get the time one day later: let date2 = calendar.date(byAdding: .minute, value: 24 * 60, to: date1)! print(dateFormatter.string(from: date2)) This outputs: 2023/11/04 12:00 2023/11/05 11:00 What's Causing This Difference? It’s likely due to Daylight Saving Time (DST). But why do we get different results between the first and second examples?
Replies
1
Boosts
0
Views
231
Activity
Apr ’25
Access BSSID MacOS
I don't understand what permissions need to be given for this code to operate. I cannot seem to work out why I'm not able to see a BSSID. I think I've given sandbox the appropriate permissions AND I've added some to the Target Properties for good measure. Yet, cannot get BSSID. import SwiftUI import CoreWLAN import CoreLocation struct ContentView: View { @State private var currentBSSID: String = "Loading..." var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Current BSSID:") Text(currentBSSID) } .padding() .onAppear(perform: fetchBSSID) } func fetchBSSID() { if let iface2 = CWWiFiClient.shared().interface() { print("✅ Found Wi-Fi interface: \(iface2.interfaceName ?? "nil")") } else { print("❌ No Wi-Fi interface found") } if let iface = CWWiFiClient.shared().interface(), let bssid = iface.bssid() { currentBSSID = bssid } else { currentBSSID = "Not connected" print("✅ BSSID: \(currentBSSID)") } } } #Preview { ContentView() } Output - WifI interface is found but BSSID is not found.
Replies
2
Boosts
0
Views
219
Activity
Apr ’25
Issue Integrating Apple Pay JS – `merchantSession` Blocke
Hello Apple Devs, We’re currently trying to integrate Apple Pay on the web using Apple Pay JS. We've followed the official documentation closely, but we're running into a blocker during the merchantSession validation phase. We successfully retrieved a merchantSession, which looks like this: json { "displayName": "Our Name", "domainName": "https://pay.ourdomain.co", "epochTimestamp": , "expiresAt": ****************, "merchantIdentifier": "", "merchantSessionIdentifier": ", "nonce": "", "operationalAnalyticsIdentifier": our name "t:", "pspId": "", "retries": 0, "signature": "*****************..." } Issue: Shortly after initiating the session, we receive a cancel event with the following info: ApplePayCancelEvent { type: "cancel", sessionError: { code: "unknown", info: {} } } We're unsure what causes the cancellation. There are no clear error messages or hints in the logs to identify what went wrong. What We’ve Checked: The merchantSession is returned successfully from our backend. The domainName matches our frontend domain (https://pay.durdomain.co). The session hasn’t expired when tested. We're using Apple Pay JS APIs as described in the documentation. Help Needed: What can trigger an ApplePayCancelEvent with an "unknown" error code? Any insight or guidance would be deeply appreciated. Thanks in advance!
Replies
1
Boosts
0
Views
134
Activity
Apr ’25
SwiftData migration crashes when working with relationships
The following complex migration consistently crashes the app with the following error: SwiftData/PersistentModel.swift:726: Fatal error: What kind of backing data is this? SwiftData._KKMDBackingData<SwiftDataMigration.ItemSchemaV1.ItemList> My app relies on a complex migration that involves these optional 1 to n relationships. Theoretically I could not assign the relationships in the willMigrate block but afterwards I am not able to tell which list and items belonged together. Steps to reproduce: Run project Change typealias CurrentSchema to ItemSchemaV2 instead of ItemSchemaV1. Run project again -> App crashes My setup: Xcode Version 16.2 (16C5032a) MacOS Sequoia 15.4 iPhone 12 with 18.3.2 (22D82) Am I doing something wrong or did I stumble upon a bug? I have a demo Xcode project ready but I could not upload it here so I put the code below. Thanks for your help typealias CurrentSchema = ItemSchemaV1 typealias ItemList = CurrentSchema.ItemList typealias Item = CurrentSchema.Item @main struct SwiftDataMigrationApp: App { var sharedModelContainer: ModelContainer = { do { return try ModelContainer(for: ItemList.self, migrationPlan: MigrationPlan.self) } catch { fatalError("Could not create ModelContainer: \(error)") } }() var body: some Scene { WindowGroup { ContentView() } .modelContainer(sharedModelContainer) } } This is the migration plan enum MigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [ItemSchemaV1.self, ItemSchemaV2.self] } static var stages: [MigrationStage] = [ MigrationStage.custom(fromVersion: ItemSchemaV1.self, toVersion: ItemSchemaV2.self, willMigrate: { context in print("Started migration") let oldlistItems = try context.fetch(FetchDescriptor<ItemSchemaV1.ItemList>()) for list in oldlistItems { let items = list.items.map { ItemSchemaV2.Item(timestamp: $0.timestamp)} let newList = ItemSchemaV2.ItemList(items: items, name: list.name, note: "This is a new property") context.insert(newList) context.delete(list) } try context.save() // Crash indicated here print("Finished willMigrate") }, didMigrate: { context in print("Did migrate successfully") }) ] } The versioned schemas enum ItemSchemaV1: VersionedSchema { static var versionIdentifier = Schema.Version(1, 0, 0) static var models: [any PersistentModel.Type] { [Item.self] } @Model final class Item { var timestamp: Date var list: ItemSchemaV1.ItemList? init(timestamp: Date) { self.timestamp = timestamp } } @Model final class ItemList { @Relationship(deleteRule: .cascade, inverse: \ItemSchemaV1.Item.list) var items: [Item] var name: String init(items: [Item], name: String) { self.items = items self.name = name } } } enum ItemSchemaV2: VersionedSchema { static var versionIdentifier = Schema.Version(2, 0, 0) static var models: [any PersistentModel.Type] { [Item.self] } @Model final class Item { var timestamp: Date var list: ItemSchemaV2.ItemList? init(timestamp: Date) { self.timestamp = timestamp } } @Model final class ItemList { @Relationship(deleteRule: .cascade, inverse: \ItemSchemaV2.Item.list) var items: [Item] var name: String var note: String init(items: [Item], name: String, note: String = "") { self.items = items self.name = name self.note = note } } } Last the ContentView: struct ContentView: View { @Query private var itemLists: [ItemList] var body: some View { NavigationSplitView { List { ForEach(itemLists) { list in NavigationLink { List(list.items) { item in Text(item.timestamp.formatted(date: .abbreviated, time: .complete)) } .navigationTitle(list.name) } label: { Text(list.name) } } } .navigationTitle("Crashing migration demo") .onAppear { if itemLists.isEmpty { for index in 0..<10 { let items = [Item(timestamp: Date.now)] let listItem = ItemList(items: items, name: "List No. \(index)") modelContext.insert(listItem) } try! modelContext.save() } } } detail: { Text("Select an item") } } }
Replies
1
Boosts
1
Views
183
Activity
Apr ’25
SwiftData data crashes with @Relationship
I've noticed that SwiftData's @Relationship seems to potentially cause application crashes. The crash error is shown in the image. Since this crash appears to be random and I cannot reproduce it under specific circumstances, I can only temporarily highlight that this issue seems to exist. @Model final class TrainInfo { @Relationship(deleteRule: .cascade, inverse: \StopStation.trainInfo) var stations: [StopStation]? } @Model final class StopStation { @Relationship var trainInfo: TrainInfo? } /// some View var origin: StopStationDisplayable? { if let train = train as? TrainInfo { return train.stations?.first(where: { $0.isOrigin }) ?? train.stations?.first(where: { $0.isStarting }) } return nil } // Some other function or property func someFunction() { if let origin, let destination { // Function implementation } }
Replies
1
Boosts
0
Views
153
Activity
Apr ’25
Converting Payed App to Freemium
I just converted my App to Freemium, but the method is used to give user, that bought the App before it was free, access to the paid futures seems not to be working. I am getting the original Purchase Date from the AppTransaction (https://developer.apple.com/documentation/storekit/apptransaction/originalpurchasedate) and comparing that to a set date where the App Model changed. In Test Flight this is working without any Problems but on the Live System, users that have purchased the App do not get access! let shared = try await AppTransaction.refresh() if case .verified(let appTransaction) = shared { result(appTransaction.originalPurchaseDate.ISO8601Format()). } I am using flutter to develop the App and the result()... send the string back to the flutter side. Here is the code of the Flutter side: Future<void> restorePurchases() async { SubscriptionProvider().updatePayment(PurchaseStatus.pending); await InAppPurchase.instance.restorePurchases(); if (Platform.isIOS) { DateTime changedToFreemium = DateTime.utc(2025, 4, 7, 11, 0, 0); String? purchaseDateRaw = await IosFlutterChannel().getOriginalPurchaseDate(); if (kDebugMode) { print("Purchase Date Raw: $purchaseDateRaw"); } if (purchaseDateRaw != null) { DateTime purchaseDate = DateTime.parse(purchaseDateRaw); if (purchaseDate.isBefore(changedToFreemium)) { if (kDebugMode) { print("Restoring legacy purchases"); } SubscriptionProvider().update(true, SubscriptionStatus.active, SubscriptionType.fs_lifetime); } else { if (kDebugMode) { print("Not restoring legacy purchases"); } } } } SubscriptionProvider().updatePayment(PurchaseStatus.purchased); } Console Log when running in Test Environment: flutter: Purchase Date Raw: 2013-08-01T07:00:00Z flutter: Restoring legacy purchases Thanks!
Replies
1
Boosts
0
Views
154
Activity
Apr ’25
Apple Pay - PKAddPaymentPassViewController doesn't show correct list of devices
Hi, We are trying to make the PKAddPaymentPassViewController to show the correct list of devices to where the pass can be added. We have analysed the documentation and we are using the PrimaryAccountIdentifier field which is the field that supposedly controls this behavior but the list of devices presented in the view controller always include one iPhone and one Apple Watch, regardless of where the card has been already added. We are initializing the PKAddPaymentPassRequestConfiguration object with: PKEncryptionScheme PrimaryAccountIdentifier CardholderName PrimaryAccountSuffix LocalizedDescription PaymentNetwork PrimaryAccountIdentifier CardholderName PrimaryAccountSuffix LocalizedDescription We have also verified the configuration in our payment pass processor and everything should be ok. We would like to have some help on achieving the desired flow for Apple Pay, which is to present the PKAddPaymentPassViewController with the correct list of available devices and not the full list. Thank you.
Replies
1
Boosts
0
Views
577
Activity
Apr ’25
Improving And Scaling App Intent Support
Platform and Version iOS Development Environment: Xcode 16.2.0, macOS 15.3 Run-time Configuration: iOS 18.3, 17.x Description of Problem We have started migrating some of the app’s core functionality over to App Intents. Our first release of App Intent support focused on two settings a user can modify on their Bose products, Audio Modes and Immersive Audio, giving users the ability to modify these settings via Siri and shortcuts. The implementation uses two separate shortcuts for each setting type, with each shortcut supporting a single phrase for Siri each: “Change my Bose mode to ” and “Change my Bose immersive audio to ”. Each shortcut uses their own App Intent, and each App Intent has support for optionally providing both a product and a setting when performing the intent. Failing to provide a device, which happens when the intent is performed via Siri, simply auto selects a currently connected Bose product. Failing to provide a setting, like in cases where a user says “Change my Bose ” without providing a setting will simply have Siri confirm the setting the user wants to change before changing the setting. We are using AppEntity to identify a Bose product for both App Intents. Because the App Intent for the Audio Modes setting has a larger number of supported values (up to 15 maximum), we are also using AppEntity to identify these settings. We are using AppEnum to identify available settings for the Immersive Audio App Intent, as only 3 static values are supported. Our original implementation of App Intent support had quite a few phrases supported for each shortcut. We had explicit support for direct synonyms of the verb “Change” in other phrases, supporting words like “Switch” and “Set”. We also had support for words that are like the word “Change”, but not directly related, like the word “Toggle” for instance. We also had support for phrases with or without the setting in each phrase. However, early on we had a lot of trouble with phrase detection with Siri. Siri had a hard time identifying what shortcut was being requested, as well as not being able to identify what settings the user was providing for the setting parameter of each App Intent. While researching potential fixes for this issue, we found a response to a thread in the Apple forums (https://developer.apple.com/forums/thread/759909) that seemed to indicate that Siri phrase recognition was very much an aggregate process. With the total number of phrases supported combined with the available settings for each phrase further compounding the total number of phrases Siri needs to learn to recognize for each shortcut. So, to hopefully improve Siri phrase detection, we added logic to limit the amount of Audio Mode settings supported based on what Audio Modes the user had setup on their Bose products. But, more importantly, we limited the number of explicit phrases supported for each shortcut to just a single phrase. In our testing, not only did this improve phrase recognition, but support for synonyms like “Set” or “Switch” seemed to implicitly still be recognized by Siri. The issues we ran into with Siri phrase detection above has us a bit concerned about scaling App Intent support to other settings and features for our products in the future. Our app supports the ability to modify a large number of settings on their Bose products, with support constantly expanding to new products as they are released. Our roadmap for App Intent support was initially very ambitious, supporting much more than just the two settings mentioned above. But our initial experience with App Intents has us tapering our expectations a little bit as far as how much can be supported in total for App Intents. One thing we also noticed is less than optimal display of default shortcuts in the Shortcuts app. The default shortcuts appeared like so, with shortcuts displayed based on available settings fro each shortcut: However, we could not find a way to indicate to users that one particular section pertained specifically to the Audio Mode setting and the other to the Immersive Audio setting. The only information the user has to make this determination for themselves is the available settings (or shortcuts) for each. This may not be immediately clear to a new customer who might be using one of our products for the first time. This display of default shortcuts in the Shortcuts app has us wondering if our shortcuts implementation is what is intended as far as support for the Shortcuts app is concerned. We did survey default shortcuts displayed by other third-party applications and they mostly dealt with navigation with a single section containing default options clearly indicating where the user can navigate with a shortcut. We couldn’t find an example of an application supporting the ability to change different setting types, with each setting type having their own available values for each. So, to summarize the questions we have concerning App Intent support: What can we do with our App Intents and Shortcuts implementation to guarantee optimal performance with Siri? What is an ideal number of phrases to support for each Shortcut. What limitations should we be placing as far as the total number of available settings for each Shortcut. Are there phrases that might work better than others for what we’re trying to achieve with App Intent support? i.e. Is “Change my Bose mode” or “Change my Bose immersive audio” a good phrase to use for this kind of functionality? Or should we be using different verbs or wording? Assuming optimal support of each Shortcut above. What is a reasonable expectation as far as how many different supported shortcuts we can scale to support at the same time. One issue we ran into early on was Siri confusing one shortcut with the other and triggering the wrong App Intent at times. While this was ultimately resolved, this outcome seems much more likely the greater the number of individual shortcuts supported. Are there any recommendations on how to display these App Intents to customers as far as default shortcuts in the Shortcuts app is concerned? Is what we currently display for default shortcuts in the Shortcuts app what was initially intended for third party support for App Intents? If what we are currently displaying is expected, would it be possible to support the ability to provide additional context to each section of default shortcuts displayed? We would like to indicate to the user that one set of shortcuts pertains to the Audio Modes settings, and the other to Immersive Audio. Something along the lines of a section header like some of the first-party apps use. Are there any recommendations or tips for supporting App Intents, particularly phrases for Siri, in other languages?
Replies
0
Boosts
0
Views
200
Activity
Apr ’25
Accuracy of IBI Values Measured by Apple Watch
I am currently developing an app that measures HRV to estimate stress levels. To align the values more closely with those from Galaxy devices, I decided not to use the heartRateVariabilitySDNN value provided by HealthKit. Instead, I extracted individual interbeat intervals (IBI) using the HKHeartBeatSeries data. Can I obtain accurate IBI data using this method? If not, I would like to know how I can retrieve more precise data. Any insights or suggestions would be greatly appreciated. Here is a sample code I tried. @Observable class HealthKitManager: ObservableObject { let healthStore = HKHealthStore() var ibiValues: [Double] = [] var isAuthorized = false func requestAuthorization() { let types = Set([ HKSeriesType.heartbeat(), HKQuantityType.quantityType(forIdentifier: .heartRateVariabilitySDNN)!, ]) healthStore.requestAuthorization(toShare: nil, read: types) { success, error in DispatchQueue.main.async { self.isAuthorized = success if success { self.fetchIBIData() } } } } func fetchIBIData() { var timePoints: [TimeInterval] = [] var absoluteStartTime: Date? let dateFormatter = DateFormatter() dateFormatter.timeZone = TimeZone(identifier: "Asia/Seoul") dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" var calendar = Calendar.current calendar.timeZone = TimeZone(identifier: "Asia/Seoul") ?? .current var components = DateComponents() components.year = 2025 components.month = 4 components.day = 3 components.hour = 15 components.minute = 52 components.second = 0 let startTime = calendar.date(from: components)! components.hour = 16 components.minute = 0 let endTime = calendar.date(from: components)! let predicate = HKQuery.predicateForSamples(withStart: startTime, end: endTime, options: .strictStartDate) let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false) let query = HKSampleQuery(sampleType: HKSeriesType.heartbeat(), predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: [sortDescriptor]) { (_, samples, _) in if let sample = samples?.first as? HKHeartbeatSeriesSample { absoluteStartTime = sample.startDate let startDateKST = dateFormatter.string(from: sample.startDate) let endDateKST = dateFormatter.string(from: sample.endDate) print("series start(KST):\(startDateKST)\tend(KST):\(endDateKST)") let seriesQuery = HKHeartbeatSeriesQuery(heartbeatSeries: sample) { query, timeSinceSeriesStart, precededByGap, done, error in if !precededByGap { timePoints.append(timeSinceSeriesStart) } if done { for i in 1..<timePoints.count { let ibi = (timePoints[i] - timePoints[i-1]) * 1000 // Convert to milliseconds // Calculate absolute time for current beat if let startTime = absoluteStartTime { let beatTime = startTime.addingTimeInterval(timePoints[i]) let beatTimeString = dateFormatter.string(from: beatTime) print("IBI: \(String(format: "%.2f", ibi)) ms at \(beatTimeString)") } self.ibiValues.append(ibi) } } } self.healthStore.execute(seriesQuery) } else { print("No samples found for the specified time range") } } self.healthStore.execute(query) } }
Replies
1
Boosts
0
Views
115
Activity
Apr ’25
Detection of file eviction
I want to implement quota feature to my file provider extension. I am able to keep track of materialized files total size. (Content download and edit operations) However I cannot detect file eviction operation (User right click to file and select "Remove Download"). Is there anyway to detect this action Or any suggestion to keep track of materialized files total size?
Replies
2
Boosts
0
Views
189
Activity
Apr ’25
Guidance on Continuous Location & Direction Updates to watchOS App Without Screen Dimming
Dear Apple Developer Support, I am reaching out for guidance on implementing continuous directional and location updates in a watchOS app designed as part of a mapping/navigation solution. Current Scenario: We send continuous location and direction updates from our iOS app to the watchOS companion app. When viewing directions on the Apple Watch, the screen dims after a short period, and the live data stops updating consistently, even though the user is actively looking at the screen. This negatively impacts the usability of real-time navigation on watchOS. Our Objective: Prevent screen dimming (or extend screen-on time) while the user is viewing navigation directions. Ensure reliable, continuous data updates on the watch screen during active navigation sessions. Request: We would appreciate your guidance on: The recommended method to keep the watchOS screen active while displaying real-time navigation data. Proper use of APIs such as WKExtendedRuntimeSession, WorkoutSession, or any other mechanism suitable for this use case. Any best practices or App Store review considerations for apps that require extended screen time and continuous updates. How such use cases were traditionally handled on watchOS and what has or hasn’t worked. We want to ensure we're implementing this in a battery-efficient, user-respectful, and Apple-compliant manner. Thank you for your assistance and guidance.
Replies
1
Boosts
0
Views
99
Activity
Apr ’25
UNEXPECTED_CANCEL_AFTER_completeMerchantValidation
Hi, We are trying to make payment from ecomm merchant. The last request during process is { "sessionData": { "epochTimestamp": "1741082241", "expiresAt": "1741092241", "merchantSessionIdentifier": "SSH88312C485D_7E0DD10173", "nonce": "3f6dc197", "merchantIdentifier": "5F9BC6BAF8", "domainName": "libertybank.ge", "displayName": "Apple Pay Purchase", "signature": "3080060000", "operationalAnalyticsIdentifier": "Apple Pay Purchase:5F9BC6BAF8", "retries": 0, "pspId": "5F9BC6BAF8" } } which is successfully validated applePaySession.completeMerchantValidation(data.sessionData) After this, the "oncancel" handler is triggered in applePay. Please help us to understand what is wrong. Please note the domain where the applepay button is located is at txpg.libertypay.ge Which is successfully verified.
Replies
1
Boosts
0
Views
106
Activity
Apr ’25
Issues with Siri Shortcuts: Confirmation Prompt, Inconsistent Behavior
Hello Apple Developer Community, I’m working on integrating Siri into my React Native app using native iOS code and bridging to React Native. I’ve followed the necessary steps to set up Siri support, including: Adding the Siri capability. Adding Siri usage descriptions in Info.plist. Using AppIntent and AppShortcutsProvider to define shortcuts. However, I’m facing the following issues: Siri Prompts for Confirmation When a user says a phrase, Siri asks, "Turn on 'MyApp' shortcuts with Siri?" instead of directly recognizing the phrase. Is this expected behavior? If so, how can I reduce friction for users and make the experience more seamless? Inconsistent Behavior for Existing Users For users updating to a version with Siri support: When the app is closed, Siri says, "MyApp hasn't added support for that with Siri." When the app is open, Siri prompts, "Turn on shortcut for MyApp?" and rest all working fine Why does Siri not recognize the shortcut when the app is closed, even though the shortcut is defined in AppShortcutsProvider? How can I ensure that Siri recognizes the shortcut regardless of whether the app is open or closed? Other than using AppIntent and AppShortcutsProvider should i try Donating shortcuts(will that helps for updated user case). Please help me on this
Replies
8
Boosts
1
Views
632
Activity
Apr ’25