Post

Replies

Boosts

Views

Activity

Reply to How to reduce cell height (vertical margins) when using UIListContentConfiguration
Thanks, I should note I want to reduce the vertical margins in the cell as opposed to setting an absolute fixed height dimension, in order to properly support dynamic type text sizes. This is the compositional layout I'm using: let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in let config = UICollectionLayoutListConfiguration(appearance: .insetGrouped) return NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment) }
Topic: UI Frameworks SubTopic: UIKit Tags:
Aug ’25
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 How to reduce cell height (vertical margins) when using UIListContentConfiguration
Setting directionalLayoutMargins top/bottom to a number less than 15 (the default value) has no effect (even negative numbers). A number greater than 15 does increase it beyond the default margins. Seems the system enforces minimum vertical margins :/
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
Boosts
Views
Activity
Aug ’25
Reply to How to reduce cell height (vertical margins) when using UIListContentConfiguration
Thanks, I should note I want to reduce the vertical margins in the cell as opposed to setting an absolute fixed height dimension, in order to properly support dynamic type text sizes. This is the compositional layout I'm using: let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in let config = UICollectionLayoutListConfiguration(appearance: .insetGrouped) return NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment) }
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
Boosts
Views
Activity
Aug ’25
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?
Replies
Boosts
Views
Activity
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.)
Replies
Boosts
Views
Activity
Aug ’25
Reply to Issue with layoutMarginsGuide under iOS 26
Seems like a good bug report to me! I don’t see anything missing, or have an explanation for the different behavior, so seems it could be not working as expected.
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
Boosts
Views
Activity
Jul ’25
Reply to Different toolbar item placement for iPhone vs iPad
Thanks, but in this app I don’t have a split view. No sidebar and no plans to add one.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Jul ’25
Reply to How to hide scroll edge effect until scroll down
Found it! .scrollEdgeEffectHidden(scrollEdgeEffectHidden, for: .top) does the trick setting scrollEdgeEffectHidden via .onScrollGeometryChange(for: CGFloat.self) looking at the geometry.contentOffset.y
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Jul ’25
Reply to Why does a purchase result in success unverified?
+1 I too would like to know in what scenarios would it be successful but not verified. I’m ignoring that scenario in my app, not unlocking the content unless it’s a verified purchase.
Topic: App & System Services SubTopic: StoreKit Tags:
Replies
Boosts
Views
Activity
Jul ’25
Reply to AppIntent perform function is not invoked from ControlWidget
It seems perform is not invoked because the system does not support showing request value dialogs to ask the user which object they want to edit when using this intent in a ControlWidget. Not sure if that's documented somewhere.
Replies
Boosts
Views
Activity
Jul ’25
Reply to Widget error upon restore iPhone: The file "Name.sqlite" couldn't be opened
Thanks for all your help! 🙏
Replies
Boosts
Views
Activity
Jul ’25
Reply to Widget error upon restore iPhone: The file "Name.sqlite" couldn't be opened
destroyPersistentStore isn't the right API that deletes the data lol Can you clarify what you mean by "destroy those CoreData object"?
Replies
Boosts
Views
Activity
Jun ’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 } } } } } }
Replies
Boosts
Views
Activity
Jun ’25
Reply to How to update the last compatible version on AppStore
There is not. Releasing an app update always replaces the current version. Those users would have to update to the newer OS to get the newest update (which could require buying a new device). For that reason you typically would not increase the deployment target to 26 when it is released, you’d give time for your users to update first.
Replies
Boosts
Views
Activity
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).
Replies
Boosts
Views
Activity
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).
Replies
Boosts
Views
Activity
Jun ’25