Post

Replies

Boosts

Views

Activity

SwiftData changes made in widget via AppIntent are not reflected in main app until full relaunch
Hi, I’m using SwiftData with an @Observable DatabaseManager class that is shared between my app and a widget. This class is located inside a Swift package and looks roughly like this: public final class DatabaseManager { public static let shared = DatabaseManager() private init() { let groupID = "group.com.yourcompany.myApp" let config = ModelConfiguration(groupContainer: .identifier(groupID)) let c = try! ModelContainer(for: MyModel.self, configurations: config) self.container = c self.modelContext = c.mainContext } public private(set) var container: ModelContainer public private(set) var modelContext: ModelContext } In the main app, I inject the container and context like this: struct MyApp: App { var body: some Scene { WindowGroup { ContentView() .modelContainer(DatabaseManager.shared.container) .modelContext(DatabaseManager.shared.modelContext) } } } Both the widget and the main app import the same package, and both use DatabaseManager.shared for reading and writing objects. The problem: When the widget updates an object using an AppIntent, the change is not reflected in the main app unless I fully terminate and relaunch it. If I just bring the app back to the foreground, it still shows stale data. Is there a recommended way to make the main app observe or reload SwiftData changes that were made in the widget (via the same shared app group and container)? I’m already using .modelContainer(...) and .modelContext(...) in the app, and everything else works fine — it’s just the syncing that doesn’t happen unless I force-relaunch the app. Thanks!
2
0
283
Jul ’25
NavigationSplitView content column renders list in plain style – even on iPhone
Hi everyone, I’m building an iOS app that originally targeted iPhone using NavigationStack. Now I’m adapting it for iPad and switched to using NavigationSplitView to support a three-column layout. The structure looks like this: NavigationSplitView { A // Sidebar } content: { B // Middle column – this shows a list } detail: { C // Detail view } The issue is with the list shown in view B (the content column). It appears completely unstyled, as if it’s using .listStyle(.plain) — with no background material, no grouped sections, and a very flat look. I can understand that this might be intentional on iPad to visually distinguish the three columns. However, the problem is that this same unstyled list also appears on iPhone, even though iPhone only shows a single column view at a time! I tried explicitly setting .listStyle(.insetGrouped) or .listStyle(.grouped) on the list in view B, but it makes no difference. When I go back to NavigationStack, the list in B is styled properly, just as expected — but then I lose the enhanced iPad layout. What I’m looking for: I’d like to keep using NavigationSplitView, but I want the list in the content column (view B) to use the default iOS list styling, at least on iPhone. Is there any way to achieve this? Thanks!
0
0
72
Aug ’25
NavigationSplitView content column renders list in plain style – even on iPhone
Hi everyone, I’m building an iOS app that originally targeted iPhone using NavigationStack. Now I’m adapting it for iPad and switched to using NavigationSplitView to support a three-column layout. The structure looks like this: NavigationSplitView { A // Sidebar } content: { B // Middle column – this shows a list } detail: { C // Detail view } The issue is with the list shown in view B (the content column). It appears completely unstyled, as if it’s using .listStyle(.plain) — with no background material, and a very flat look. I can understand that this might be intentional on iPad to visually distinguish the three columns. However, the problem is that this same unstyled list also appears on iPhone, even though iPhone only shows a single column view at a time! I tried explicitly setting .listStyle(.insetGrouped) or .listStyle(.grouped) on the list in view B, but it makes no difference. When I go back to NavigationStack, the list in B is styled properly, just as expected — but then I lose the enhanced iPad layout. What I’m looking for: I’d like to keep using NavigationSplitView, but I want the list in the content column (view B) to use the default iOS list styling, at least on iPhone. Is there any way to achieve this? Thanks!
0
0
77
Aug ’25
Interactive Widget with SwiftData Not Triggering iCloud Sync
Hello everyone, I'm developing an app for iOS 18 using SwiftData, with iCloud synchronization enabled. My app also includes an interactive widget that allows users to mark tasks as complete, similar to Apple's Reminders widget. I'm facing a specific issue with how iCloud sync is triggered when changes are made from the widget. My Setup: Xcode 16 Swift 6 / iOS 18 SwiftData with iCloud Sync enabled. An interactive widget using App Intents to modify the SwiftData model. What's working correctly: App to Widget (Same Device): If I mark a task as complete in the main app, the widget on the same device updates instantly. Widget to App (Same Device): If I mark a task as complete using the interactive widget, the main app on the same device reflects this change immediately. App to App (Across Devices): If I make a change in the app on my iPhone, it syncs correctly via iCloud and appears in the app on my iPad. The Problem: The synchronization issue occurs specifically when an action is initiated from the widget and needs to be reflected on other devices, or when a change from another device needs to be reflected in the widget. Widget Change Not Syncing to Other Devices: If I mark a task as complete in the widget on my iPhone, the change does not sync to my iPad. The task on the iPad only updates after I manually open the main app on the iPhone. Remote Change Not Syncing to Widget: Similarly, if I mark a task as complete in the app on my iPad, the widget on my iPhone does not update to show this change. The widget only refreshes with the correct state after I open the main app on the iPhone. It seems that the widget's AppIntent correctly modifies the local SwiftData store, but this action isn't triggering the necessary background process to push the changes to iCloud. The sync only seems to happen when the main app, with its active ModelContainer, is brought to the foreground. My goal is for any change made in the widget to be reflected across all of the user's devices in near real-time, without requiring them to launch the main app to initiate the sync. Is there a specific API I need to call from my AppIntent to force a SwiftData sync, or a project capability I might be missing to allow the widget extension to trigger iCloud pushes? Any help or guidance would be greatly appreciated. Thank you!
1
0
129
Sep ’25
Issues with Searchable Modifier Placement and State in TabView on iOS 26
Hi everyone, I'm updating my app to adopt the new search bar design in iOS 26 and I'm running into some UI issues depending on the implementation pattern. I'm using Xcode 26.0.1 and SwiftUI 6. I've tried two main approaches for search, and each has a specific problem related to TabView. Pattern 1: Searchable View Inside a Standard Tab In this pattern, the search bar is specific to one of the main tabs. The Code: struct ContentView: View { var body: some View { TabView { Tab("Main", systemImage: "list.bullet") { MainView() } Tab("View1", systemImage: "gearshape") { Text("View1") } Tab("View2", systemImage: "gearshape") { Text("View2") } } } } struct MainView: View { @State private var searchText = "" var body: some View { NavigationStack { List { Text("Text 1") Text("Text 2") } .searchable(text: $searchText, placement: .toolbar) .navigationTitle("Main") } } } The Problem: When I preview MainView directly, the search bar correctly appears at the bottom, matching the new iOS 26 design. However, when MainView is presented inside the TabView in ContentView, two issues occur: Incorrect Position: The search bar reverts to the old style, appearing at the top of the view, attached to the navigation bar. Initially Hidden: Often, on the first appearance of the view, the search bar is hidden until I actively pull down on the list. It seems like the TabView environment is interfering with the expected placement and initial state of the searchable modifier. Pattern 2: Dedicated Global Search Tab (role: .search) Here, I'm using a dedicated tab for a global search experience, with the searchable modifier on the TabView itself. The Code: struct ContentView: View { @State private var searchText: String = "" var body: some View { TabView { Tab(role: .search) { SearchView() } Tab("Main", systemImage: "list.bullet") { MainView() } Tab("View1", systemImage: "gearshape") { Text("View1") } Tab("View2", systemImage: "gearshape") { Text("View2") } } .searchable(text: $searchText) } } struct MainView: View { var body: some View { NavigationStack { List { Text("Text 1") Text("Text 2") } .navigationTitle("Main") } } } The Problem: The search state is leaking into other tabs in an unexpected way. Steps to Reproduce: Run the app and tap on the "Search" tab. Tap the search bar to activate it and bring up the keyboard. Now, tap on the "Main" tab. Result: The app switches to MainView, but the search bar remains active and focused at the top of the MainView. This is incorrect; the search UI should be dismissed when switching away from the search context. Interestingly, if I tap on "View1" or "View2" (which don't have a NavigationStack), the search bar is correctly dismissed. This suggests the .searchable modifier on the TabView is attaching its UI to the first available NavigationStack it finds in the selected tab. My Questions: For Pattern 1, is there a correct way to ensure the new bottom-placed search bar appears reliably inside a TabView? For Pattern 2, how can I ensure the search state is properly dismissed when navigating away from the search tab, even to a tab that contains a NavigationStack? Is this a potential bug, or am I misusing the APIs for these scenarios? Any guidance or workarounds would be greatly appreciated. Thanks!
1
0
92
Oct ’25
Correct SwiftData Concurrency Logic for UI and Extensions
Hi everyone, I'm looking for the correct architectural guidance for my SwiftData implementation. In my Swift project, I have dedicated async functions for adding, editing, and deleting each of my four models. I created these functions specifically to run certain logic whenever these operations occur. Since these functions are asynchronous, I call them from the UI (e.g., from a button press) by wrapping them in a Task. I've gone through three different approaches and am now stuck. Approach 1: @MainActor Functions Initially, my functions were marked with @MainActor and worked on the main ModelContext. This worked perfectly until I added support for App Intents and Widgets, which caused the app to crash with data race errors. Approach 2: Passing ModelContext as a Parameter To solve the crashes, I decided to have each function receive a ModelContext as a parameter. My SwiftUI views passed the main context (which they get from @Environment(\.modelContext)), while the App Intents and Widgets created and passed in their own private context. However, this approach still caused the app to crash sometimes due to data race errors, especially during actions triggered from the main UI. Approach 3: Creating a New Context in Each Function I moved to a third approach where each function creates its own ModelContext to work on. This has successfully stopped all crashes. However, now the UI actions don't always react or update. For example, when an object is added, deleted, or edited, the change isn't reflected in the UI. I suspect this is because the main context (driving the UI) hasn't been updated yet, or because the async function hasn't finished its work. My Question I'm not sure what to do or what the correct logic should be. How should I structure my data operations to support the main UI, Widgets, and App Intents without causing crashes or UI update failures? Here is the relevant code using my third (and current) approach. I've shortened the helper functions for brevity. // MARK: - SwiftData Operations extension DatabaseManager { /// Creates a new assignment and saves it to the database. public func createAssignment( name: String, deadline: Date, notes: AttributedString, forCourseID courseID: UUID, /*...other params...*/ ) async throws -> AssignmentModel { do { let context = ModelContext(container) guard let course = findCourse(byID: courseID, in: context) else { throw DatabaseManagerError.itemNotFound } let newAssignment = AssignmentModel( name: name, deadline: deadline, notes: notes, course: course, /*...other properties...*/ ) context.insert(newAssignment) try context.save() // Schedule notifications and add to calendar _ = try? await scheduleReminder(for: newAssignment) newAssignment.calendarEventIDs = await CalendarManager.shared.addEventToCalendar(for: newAssignment) try context.save() await MainActor.run { WidgetCenter.shared.reloadTimelines(ofKind: "AppWidget") } return newAssignment } catch { throw DatabaseManagerError.saveFailed } } /// Finds a specific course by its ID in a given context. public func findCourse(byID id: UUID, in context: ModelContext) -> CourseModel? { let predicate = #Predicate<CourseModel> { $0.id == id } let fetchDescriptor = FetchDescriptor<CourseModel>(predicate: predicate) return try? context.fetch(fetchDescriptor).first } } // MARK: - Helper Functions (Implementations omitted for brevity) /// Schedules a local user notification for an event. func scheduleReminder(for assignment: AssignmentModel) async throws -> String { // ... Full implementation to create and schedule a UNNotificationRequest return UUID().uuidString } /// Creates a new event in the user's selected calendars. extension CalendarManager { func addEventToCalendar(for assignment: AssignmentModel) async -> [String] { // ... Full implementation to create and save an EKEvent return [UUID().uuidString] } } Thank you for your help.
5
0
188
Nov ’25
SwiftData crash when enabling CloudKit for existing users (Free to Pro upgrade)
Hi, I am implementing a premium feature in my app where CloudKit syncing is available only for "Pro" users. The Workflow: Free Users: I initialize the ModelContainer with cloudKitDatabase: .none so their data stays local. Pro Upgrade: When a user purchases a subscription, I restart the container with cloudKitDatabase: .automatic to enable syncing. The Problem: If a user starts as "Free" (creates local data) and later upgrades to "Pro", the app crashes immediately upon launch with the following error: Fatal error: Failed to create ModelContainer: SwiftDataError(_error: SwiftData.SwiftDataError._Error.loadIssueModelContainer, _explanation: nil) It seems that SwiftData fails to load the existing data once the configuration changes to expect a CloudKit-backed store. My Question: Is there a supported way to "toggle" CloudKit on for an existing local dataset without causing this crash? I want the user's existing local data to start syncing once they pay, but currently, it just crashes. My code: import Foundation import SwiftData public enum DataModelEnum: String { case task, calendar public static let container: ModelContainer = { let isSyncEnabled = UserDefaults.isProUser let config = ModelConfiguration( groupContainer: .identifier("group.com.yourcompany.myApp"), cloudKitDatabase: isSyncEnabled ? .automatic : .none ) do { return try ModelContainer(for: TaskModel.self, CalendarModel.self, configurations: config) } catch { fatalError("Failed to create ModelContainer: \(error)") } }() }
1
0
74
1w