Post

Replies

Boosts

Views

Activity

Reply to MKReverseGeocodingRequest and CNPostalAddress from MKMapItem
I would try to see if you can make MKAddressRepresentations work for your needs, and if you could present the customization options to your customers based that line up with what that class offers I'll pursue this route, thanks! Existing customers will need to change their settings to choose one of the predetermined formats that most closely matches their use case. As folks reach out to report what they can't achieve anymore, I can file feedback reports requesting more formats be added to MKAddressRepresentations. The only thing I'm struggling to come up with is a friendly name to show in the UI for selecting the predetermined format of "city with context" both the short and full variants. Best I have is City, City and State, or City and State and Region - the word "state" is probably not appropriate everywhere. 🤔 it is very easy to naively construct geographically incorrect place descriptions ... we want to discourage folks from doing these string concatenation techniques, and instead rely on the framework to provide you with the correctly formatted information through MKAddressRepresentations Makes sense! With the approach to select specific date components, I took inspiration from DateFormatter.setLocalizedDateFormatFromTemplate(_:) where you specify which components you want and you'll get an appropriately formatted localized string. I thought I achieved that fairly well by preserving the order of components in the postal address just filtering out undesired ones, but can certainly imagine there could be scenarios where the resulting string doesn't really work for different locations around the world. I did file FB8648023 (Template API desired to format addresses with different components, similar to DateFormatter.setLocalizedDateFormatFromTemplate) hoping to replace my solution with a native API, but maybe that's infeasible given address complexity?
Aug ’25
Reply to MKReverseGeocodingRequest and CNPostalAddress from MKMapItem
Hi Ed! The use case in my app is to get the address for where a photo was taken. The user selects which address component(s) they want included. So I look at each attribute from the full attributed address to see if the CNPostalAddressPropertyAttribute is a component the user selected if so I add it to an array. Once enumeration is complete I join the array with commas to separate the components. For examples, if they selected zip it would be just 92101, or if they selected street and city and state it’d be 3835 N Harbor Dr, San Diego, CA. (The selected attributes aren’t guaranteed to be in the final string and that is acknowledged.)
Aug ’25
Reply to Widget error upon restore iPhone: The file "Name.sqlite" couldn't be opened
Thank you, this is very helpful. To confirm we're aligned: Open the database in read-only mode If loadPersistentStores fails, try it again in read-write mode If that succeeds destroy those CoreData object and do a read-only open again If that fails show an error message to the user destroy those CoreData object Is that accomplished via container.persistentStoreCoordinator.destroyPersistentStore, so all together like so? let container = NSPersistentCloudKitContainer(name: "AppName") let sharedStoreURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.domain.appname")!.appendingPathComponent("\(container.name).sqlite") guard FileManager.default.fileExists(atPath: sharedStoreURL.path) else { // Show error to user return } let description = container.persistentStoreDescriptions.first! description.url = sharedStoreURL description.isReadOnly = true container.loadPersistentStores { description, error in if let error { container.persistentStoreDescriptions.first?.isReadOnly = false container.loadPersistentStores { description, error in if let error { // Show error to user } else { try? container.persistentStoreCoordinator.destroyPersistentStore(at: sharedStoreURL, type: .sqlite) container.persistentStoreDescriptions.first?.isReadOnly = true container.loadPersistentStores { description, error in if let error { // Show error to user } } } } } }
Jun ’25
Reply to Widget error upon restore iPhone: The file "Name.sqlite" couldn't be opened
Thanks Kevin! I captured a sysdiagnose and found this: error 2025-06-17 08:33:06.224277 -0600 MyAppWidget os_unix.c:46922: (2) open(/private/var/mobile/Containers/Shared/AppGroup/FC8E38EF-1FEF-4C9A-9712-D10421E30FE5/Name.sqlite-wal) - No such file or directory error 2025-06-17 08:33:06.224284 -0600 MyAppWidget unable to open database file in "SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_METADATA'" error 2025-06-17 08:33:06.224863 -0600 MyAppWidget error: (14) I/O error for database at /private/var/mobile/Containers/Shared/AppGroup/FC8E38EF-1FEF-4C9A-9712-D10421E30FE5/Name.sqlite. SQLite error code:14, 'unable to open database file' I confirmed upon restore the sqlite file exists but sqlite-wal does not - it won’t open without that file. But that file does get created once you open the app, and then you can reinitialize the widget by restarting the iPhone (that’s the only way I know to make the system cold start the widget process). You’re exactly right, setting isReadOnly is what causes this behavior. If I do not put it in read-only mode, the error does not occur because it is apparently able to open the database file without the sqlite-wal file, the same way the app is able to. So I can work around the issue by setting isReadOnly only when the sqlite-wal file exists. That means it would be in read/write mode until the user restarts the iPhone then it’ll be read-only. I’m wondering if that's a bad idea. I don’t attempt to write to the database from the widget, but the widget does not sync (cloudKitContainerOptions is not set), so are there any implications in making the widget open in read/write mode, creating the wal file in the process? If the data has been changed on iCloud and thus is out of date, would that cause the app to fail to sync or have some other effect when it's opened? 🤔 Is it expected for the sqlite-wal file to be missing after restore? It seems if that were preserved the issue would not occur (maybe the sqlite-shm file is also required, that too is missing).
Jun ’25
Reply to Widget error upon restore iPhone: The file "Name.sqlite" couldn't be opened
It's interesting huh! One option would be to simply delete the file and let CoreData pull the data from the cloud … you might want to consider just excluding the file from the backup entirely I think this is not an option because the user can turn off iCloud and NSPersistentCloudKitContainer still works to store data without syncing to iCloud. So not all users can get this data back from iCloud, it may only exist in their backup. What does your app actually "do" and, most importantly, what if any background modes/work does it use? You can think of it as a very simple "todo" app where you create a todo, it shows up in the widget, and tapping a button marks it complete (via the main app process). Note the widget's access to the database is read-only and cloudKitContainerOptions is not set so the widget extension process does not sync with iCloud (only the main app process does). The only background modes/work used in the app is the "remote notifications" capability that allows CloudKit to silently notify the app when there's new content enabling NSPersistentCloudKitContainer to do its magic - there's no additional background tasks implemented. On its own I'm not sure how restarting would have changed anything, but having your app run FIRST (before the widget did) might have changed something. Does that fit in with any of what your app is doing? I did more testing here. If I follow the steps and install the app from the App Store after restore, the widget is run and shows the error, now if you restart the iPhone again the widget is run and shows the same error. So restarting the iPhone only resolves the problem after the main app has been run. If you never open the app, the widget error never resolves. You have to open the app and then restart the device to resolve it. The only code differences I have for persistence setup in the app vs the widget is to migrate the file at the default store URL to the shared app groups URL if it hasn't been migrated yet, set cloudKitContainerOptions, and it does not set isReadOnly. Does your Widget try to setup myPersistentCloudKitContainer again at any of those points? The widget sets up the NSPersistentCloudKitContainer and calls loadPersistentStores only once upon init, which happens at the time WidgetKit calls getSnapshot, getTimeline, or relevance (whichever is called first).
Jun ’25
Reply to Widget error upon restore iPhone: The file "Name.sqlite" couldn't be opened
Hi Ziqiao Chen! It seems the URL does change after reset (but is still valid) and the URL does not change upon restarting iPhone to get the successful case. I added debug text to my widget to show the URL and whether it can be accessed, then performed these steps: Run the app on my iPhone from Xcode and verify there is no error in the widget, note the sqlite file URL Perform iCloud backup Reset iPhone and restore from that backup The app is not automatically installed because only apps downloaded from the App Store are reinstalled, so I had to enable Developer Mode then run the app from Xcode The error message appears in the widget - the sqlite file couldn't be opened, it is a different URL than it was before the iPhone was reset, and yet FileManager says the file exists, the file is readable and writable, and the contents at that path are non-nil Restart iPhone The error message is not shown in the widget and everything else is the same (the URL, file exists, readable, writable, non-nil contents) The URL in step 1 was file:///private/var/mobile/Containers/Shared/AppGroup/FAF64427-9826-4C86-9C2E-D7E5285BA7EC/MyApp.sqlite The URL in step 5 and 7 was file:///private/var/mobile/Containers/Shared/AppGroup/FDE4F3AF-E775-4D5F-842D-1C5AA77BE26F/MyApp.sqlite The URL is set like this myPersistentCloudKitContainer.persistentStoreDescriptions[0].url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.mydomain.myapp")!.appendingPathComponent("\(container.name).sqlite"). Note if you install the app from the App Store instead of installing it via Xcode in step 4, you also see the error in the widget. But if you first delete the app and then install it, you of course wouldn't see the error since there's no existing data from the app. While this may not replicate the issue exactly, it seems to be close enough with the same end result - the app can access the database but the widget fails to until you restart iPhone. It seems it can only be fully tested with the production app that's live on the App Store, since that's the only way iOS will reinstall it upon restore from backup. I can't put debug text in my widget for customers to see. 😀
Jun ’25