I'm encountering an issue with a one-to-many relationship between two models using SwiftData. Here's a simple example of my models:
@Model
final class TVSeries {
var title: String
@Relationship(deleteRule: .cascade, inverse: \Episode.tvSeries) var episodes: [Episode]
init(title: String, episodes: [Episode] = []) {
self.title = title
self.episodes = episodes
}
}
@Model
final class Episode {
var title: String
var tvSeries: TVSeries // <- Works fine when optional
init(title: String, tvSeries: TVSeries) {
self.title = title
self.tvSeries = tvSeries
}
}
After creating and saving a TVSeries instance with an associated Episode, fetching the TVSeries instance shows that the episodes array remains empty whereas the back-link to the TVSeries works as expected. Here's the relevant test case:
struct SwiftDataTestingTests {
@Test func testFullInit() async throws {
// Configure SwiftData context
let config = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try ModelContainer(for: TVSeries.self, Episode.self, configurations: config)
let context = ModelContext(container)
context.autosaveEnabled = false
// Create entries
let tvSeries = TVSeries(title: "New Series")
let episode = Episode(title: "Episode 1", tvSeries: tvSeries)
context.insert(episode)
try context.save()
// Fetch tv series
let tvSeriesDescriptor = FetchDescriptor<TVSeries>()
let tvSeriesResult = try context.fetch(tvSeriesDescriptor)
#expect(tvSeriesResult.count == 1)
let fetchedTVSeries = try #require(tvSeriesResult.first)
#expect(fetchedTVSeries.episodes.count == 1) // <- Expectation failed: (fetchedTVSeries.episodes.count → 0) == 1
// Fetch episodes
let episodeDescriptor = FetchDescriptor<Episode>()
let episodeResult = try context.fetch(episodeDescriptor)
#expect(episodeResult.count == 1)
let fetchedEpisode = try #require(episodeResult.first)
#expect(fetchedEpisode.tvSeries.title == "New Series")
}
}
Everything seems fine when I make the tvSeries attribute in the Episode model optional, but I would prefer to leave it explicit.
I tested this on the latest XCode and XCode Beta versions running macOS Sonoma
XCode Version: 15.4 (15F31d)
XCode Beta Version: 16.0 beta 3 (16A5202i)
MacOS Version: 14.5 (23F79)
Any insights or suggestions on what might be causing this issue would be greatly appreciated. Thank you!
SwiftData
RSS for tagSwiftData is an all-new framework for managing data within your apps. Models are described using regular Swift code, without the need for custom editors.
Posts under SwiftData tag
200 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hi all, I am working on a macOS/iOS sandboxed app with SwiftUI/SwiftData. The app saves its' data into a ModelDocument file. But I want to also save large binary data files into this file.
I create the DocumentGroup scene with:
DocumentGroup(editing: Entry.self, contentType: .myDocument) {
MainWindowView()
}
In my MainWindowView, I use
@Environment(\.documentConfiguration) private var documentConfiguration
to get the URL to the user created ModelDocument file URL.
When I need to add large binary file into the ModelDocument file, I call a method in my model:
func saveReferencedData(_ data: Data, documentURL: URL?) throws {
let logger = Logger(subsystem: "saveReferencedData", category: "Asset")
if let documentURL {
let referencedFileName = "\(entryIdentifier)_\(assetIdentifier).\(assetType)"
let tempFileURL = documentURL.appending(components: referencedFileName)
if documentURL.startAccessingSecurityScopedResource() {
do {
try data.write(to: tempFileURL, options: [])
} catch {
documentURL.stopAccessingSecurityScopedResource()
throw AssetFileOperationError.unableToSaveReferenceFile
}
self.referencedFileLocation = referencedFileName
logger.debug("Successfully saved image data to: \(referenceFileURL)")
}
documentURL.stopAccessingSecurityScopedResource()
} else {
logger.debug("ERROR! Unable to save referenced image file because document URL is nil.")
}
}
When this method is called, the data file is saved, but immediately I gets this diablog box:
If I click on any of the options in the dialog box, the binary data file is removed.
I think this is a permission problem and I am not writing to the ModelDocument file correctly. But I can not find any information online to experiment more.
Does anyone have experience with a customized ModelDocument file with more data files written into it? Or if you have any insight or advice, it would be greatly appreciated.
Thank you so much for reading.
Hey everyone,
I'm a new developer working on my app. I'm facing an unusual problem during the App Store review process. The app works perfectly on my physical devices, running iOS 18.0 developer beta, but it consistently crashes on simulators with iOS 17.5. Due to this issue, my app has been rejected for iOS 17.5 because of the crashes. I know I should have avoided developing and testing the app on different versions, so please go easy on me :(
Crash Details:
Steps Leading to Crash:
Opened the app
Went to the Stocks section
Clicked on Buy
App crashed
The Breakpoint Exception:
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x21b9f1620)
Generated Infrastructure Code Section Triggering Crash:
{
@storageRestrictions(accesses: _$backingData, initializes: _stock)
init(initialValue) {
_$backingData.setValue(forKey: \.stock, to: initialValue)
_stock = _SwiftDataNoType()
}
get {
_$observationRegistrar.access(self, keyPath: \.stock)
return self.getValue(forKey: \.stock)
}
set {
_$observationRegistrar.withMutation(of: self, keyPath: \.stock) {
self.setValue(forKey: \.stock, to: newValue)
}
}
}
The crash appears to be related to Swift Data handling during the trade operation in my app's TradeView.
While I've managed to temporarily fix the issue by running the app on iOS 18.0, this is not a practical solution for the App Store release, which is my immediate concern.
Has anyone else experienced similar issues with Swift Data on iOS 17.5? If yes, how did you solve it? Is there a known workaround or a better approach to ensure compatibility with iOS 17.5?
Thank you in advance for your valuable assistance. I truly appreciate your help!
Hi
I want to update item value after insert.
Solution(False):
I want to add "isAutosaveEnabled" prop to modelContainer, but modelContainer just have one parm.
Solution2(False):
There doesn't have API to update SwiftData, I just find insert, delete and save.
I change the city_name and run "try? modelContext.save()".
But it replace after I reopen the app and maybe it doesn't work even before I close app.
How can I update the city_name?
/// --------- App ---------
import SwiftUI
import SwiftData
@main
struct MyApp: App {
var container: ModelContainer
init(){
let schema = Schema([
SD_City.self,
])
let modelConfiguration = ModelConfiguration(schema: schema)
do {
container = try ModelContainer(for: schema, configurations: [modelConfiguration])
let context = ModelContext(container)
var city : SD_City
city = SD_City(city_id: 1, city_name: "city1")
context.insert(city)
city = SD_City(city_id: 2, city_name: "city2")
context.insert(city)
} catch {
fatalError("Could not create ModelContainer: ")
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(container)
}
}
/// --------- ContentView ---------
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@State var sortOrder_city = SortDescriptor(\SD_City.city_id)
@Query(sort: [SortDescriptor(\SD_City.city_id)]) var citylist: [SD_City]
@State var city_id = 0
@State var city_name = "city_name"
var body: some View {
HStack {
Button("Change \ncity name", action: {
// Update the city_name of city1 to city3
// SD_City(city_id: 1, city_name: "city1") --> SD_City(city_id: 1, city_name: "city3")
})
Text(city_name)
}
}
}
/// ---------swiftdata---------
import SwiftUI
import SwiftData
@Model
final class SD_City{
@Attribute(.unique) let city_id: Int = 1
var city_name: String = ""
init(city_id: Int , city_name: String) {
self.city_id = city_id
self.city_name = city_name
}
}
Thanks~
Dear Apple Developer Forum community,
I have a Multiplatform SwiftUI app that runs on both iOS and macOS. The app is available in the Mac App Store, and I aim to maintain backward compatibility. I use App Groups to synchronize data between the main app, where users configure content, and the widget, which displays this content. The data is stored using SwiftData.
With macOS Sequoia now in beta testing, I have encountered a breaking change that affects my app.
In macOS Sequoia, apps must use the team identifier number $(TeamIdentifierPrefix) as the prefix for App Groups on macOS. I cannot properly test future versions of my app without instructing my beta testers to turn off System Integrity Protection (SIP). This presents a significant issue for my Multiplatform SwiftUI app. On iOS, the app group identifier must start with group.identifier. Before macOS Sequoia, you could name your app group freely, and testing with TestFlight and publishing to the App Store was straightforward. Now, however, testing an app intended for the App Store is complicated by this rule. On macOS, you must use $(TeamIdentifierPrefix) to bypass this rule and allow for widgets to be tested and allow for synchronization between SwiftData. While on iOS, this approach is not allowed as the App Group becomes considered invalid.
Additionally, this annoying popup appears every time a beta tester tries to open the app if they have SIP turned on:
Instead of prompting for the app extensions, it rejects it. Rejecting this popup also prevents the main SwiftData app from opening.
I am unsure how to proceed. If I want to test widgets (which is a primary focus of the app), I must use macOS Sequoia. I am particularly concerned about the implications if I decide to stop supporting macOS Sonoma in the future.
Thank you in advance,
LocalWE
I am a develop beginner. Recently, my App used SwiftData's MigraitonPlan, which caused it to crash when I opened it for the first time after updating in TestFlight or the store. After clicking it a second time, I could enter the App normally, and I could confirm that all the models were the latest versions.However, when I tested it in Xcode, everything was normal without any errors.
Here is my MigrationPlan code:
import Foundation
import SwiftData
enum MeMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[MeSchemaV1.self, MeSchemaV2.self, MeSchemaV3.self, MeSchemaV4.self]
}
static var stages: [MigrationStage] {
[migrateV1toV2, migrateV2toV3, migrateV3toV4]
}
//migrateV1toV2, because the type of a data field in MeSchemaV1.TodayRingData.self was modified, the historical data was deleted during the migration, and the migration work was successfully completed.
static let migrateV1toV2 = MigrationStage.custom(
fromVersion: MeSchemaV1.self,
toVersion: MeSchemaV2.self,
willMigrate: { context in
try context.delete(model: MeSchemaV1.TodayRingData.self)
},
didMigrate: nil
)
//migrateV2toV3, because a new Model was added, it would crash at startup when TF and the official version were updated, so I tried to delete the historical data during migration, but the problem still exists.
static let migrateV2toV3 = MigrationStage.custom(
fromVersion: MeSchemaV2.self,
toVersion: MeSchemaV3.self,
willMigrate: { context in
try context.delete(model: MeSchemaV2.TodayRingData.self)
try context.delete(model: MeSchemaV2.HealthDataStatistics.self)
try context.delete(model: MeSchemaV2.SportsDataStatistics.self)
try context.delete(model: MeSchemaV2.UserSettingTypeFor.self)
try context.delete(model: MeSchemaV2.TodayRingData.self)
try context.delete(model: MeSchemaV2.TodayHealthData.self)
try context.delete(model: MeSchemaV2.SleepDataSource.self)
try context.delete(model: MeSchemaV2.WorkoutTargetData.self)
try context.delete(model: MeSchemaV2.WorkoutStatisticsForTarget.self)
try context.delete(model: MeSchemaV2.HealthDataList.self)
},
didMigrate: nil
)
//migrateV3toV4, adds some fields in MeSchemaV3.WorkoutList.self, and adds several new Models. When TF and the official version are updated, it will crash at startup. Continue to try to delete historical data during migration, but the problem still exists.
static let migrateV3toV4 = MigrationStage.custom(
fromVersion: MeSchemaV3.self,
toVersion: MeSchemaV4.self,
willMigrate: { context in
do {
try context.delete(model: MeSchemaV3.WorkoutList.self)
try context.delete(model: MeSchemaV3.HealthDataStatistics.self)
try context.delete(model: MeSchemaV3.SportsDataStatistics.self)
try context.delete(model: MeSchemaV3.UserSettingTypeFor.self)
try context.delete(model: MeSchemaV3.TodayRingData.self)
try context.delete(model: MeSchemaV3.TodayHealthData.self)
try context.delete(model: MeSchemaV3.SleepDataSource.self)
try context.delete(model: MeSchemaV3.WorkoutTargetData.self)
try context.delete(model: MeSchemaV3.WorkoutStatisticsForTarget.self)
try context.delete(model: MeSchemaV3.HealthDataList.self)
try context.delete(model: MeSchemaV3.SleepStagesData.self)
try context.save()
} catch {
print("Migration from V3 to V4 failed with error: \(error)")
throw error
}
},
didMigrate: nil
)
}
Hello folks, absolute newbie here.
I'm following a tutorial that I'm trying to adjust to my own needs. In the tutorial the app is sat up in a way that you can add a new entry to a note taking app by tapping on a button which brings in an edit view.
What I'm trying to do is to bring in the edit view by adding it as a tab on the bottom instead.
Here's my ContentView:
import SwiftData
import SwiftUI
struct ContentView: View {
let example = Entry(content: "Example Entry")
var body: some View {
TabView {
LifeEventsView()
.tabItem {
Label("Life events", systemImage: "house")
}
EditEntryView(entry: example)
.tabItem {
Label("Add new event", systemImage: "plus")
}
MattersView()
.tabItem {
Label("What matters", systemImage: "house")
}
}
}
}
#Preview {
ContentView()
}
And here's the EditEntryView:
import SwiftData
struct EditEntryView: View {
@Bindable var entry: Entry
var body: some View {
Form {
TextField("Entry", text: $entry.content, axis: .vertical)
}
.navigationTitle("Edit entry")
.navigationBarTitleDisplayMode(.inline)
}
}
#Preview {
do {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try ModelContainer(for: Entry.self, configurations: config)
let example = Entry(content: "Example Entry")
return EditEntryView(entry: example)
.modelContainer(container)
} catch {
fatalError("Failed to create model container.")
}
}
The build succeeds but I get an error while running saying: Thread 1: Fatal error: failed to find a currently active container for Entry
I'm sure this is an easy fix but I've been thinking with it for a while with no luck.
I'm building an app which has both iOS and macOS versions along with extensions on both platforms. I want the main app to be able to share data the extension using the group container and I want the app on both platforms sync data over CloudKit.
CloudKit synchronization works like a dream. The data sharing between the app and extension on iOS works also exactly as intended. However on macOS every time the app is launched I get “MyApp” would like to access data from other apps. alert dialog.
I tried initializing the ModelConfiguration both with an explicit and automatic app group container identifiers. Same results.
Hi, I'm trying to make some changes to my SwiftData model and I want to add a new non-optional property to one of my model classes.
My current model was not part of a VersionedSchema so I first encapsulated it into one
public enum FeynnDataModelsSchemaV1: VersionedSchema {
public static var versionIdentifier: Schema.Version = .init(1, 0, 0)
public static var models: [any PersistentModel.Type] {
[FeynnDataModelsSchemaV1.Workout.self, FeynnDataModelsSchemaV1.Activity.self, FeynnDataModelsSchemaV1.ActivityRecord.self, FeynnDataModelsSchemaV1.WorkoutSession.self]
}
}
Then I run the app and everything works as expected.
Secondly, I create the V2 and add the new property:
public enum FeynnDataModelsSchemaV2: VersionedSchema {
public static var versionIdentifier: Schema.Version = Schema.Version(2, 0, 0)
public static var models: [any PersistentModel.Type] {
[FeynnDataModelsSchemaV2.Workout.self, FeynnDataModelsSchemaV2.Activity.self, FeynnDataModelsSchemaV2.ActivityRecord.self, FeynnDataModelsSchemaV2.WorkoutSession.self]
}
}
extension FeynnDataModelsSchemaV2 {
@Model
final public class Activity: Hashable {
...
public var activityType: ActivityType = ActivityType.traditionalStrengthTraining
...
}
}
Lastly, I create the schema migration plan and add it to my modelContainer:
public enum FeynnDataModelsMigrationPlan: SchemaMigrationPlan {
public static var schemas: [VersionedSchema.Type] = [
FeynnDataModelsSchemaV1.self,
FeynnDataModelsSchemaV2.self
]
public static var stages: [MigrationStage] = [migrateV1toV2]
public static var migrateV1toV2 = MigrationStage.custom(fromVersion: FeynnDataModelsSchemaV1.self, toVersion: FeynnDataModelsSchemaV2.self, willMigrate: {moc in
let activities = try? moc.fetch(FetchDescriptor<Activity>())
print("\(activities?.count ?? 919191991)")
}, didMigrate: {moc in
let activities = try? moc.fetch(FetchDescriptor<Activity>())
print("\(activities?.count ?? 88888888)")
activities?.forEach { activity in activity.activityType = .traditionalStrengthTraining }
try? moc.save()
})
}
if let container = try? ModelContainer(
for: Workout.self, Activity.self, ActivityRecord.self, WorkoutSession.self,
migrationPlan: FeynnDataModelsMigrationPlan.self,
configurations: ModelConfiguration(cloudKitDatabase: .automatic)) {
self.container = container
} else {
self.container = try ModelContainer(
for: Workout.self, Activity.self, ActivityRecord.self, WorkoutSession.self,
migrationPlan: FeynnDataModelsMigrationPlan.self,
configurations: ModelConfiguration(cloudKitDatabase: .none))
}
After running this, the application runs as expected, but as soon as I render a view that references Activity.activityType the app crashes trying to get the value for my existing activities given that it is nil, pointing out that the migration stage was not ran?
None of the print statements in the didMigrate or willMigrate can be found within the logs either.
I have tried several approaches creating more VersionedSchemas and I run into more issues such as Cannot use stuck migration with an unknown model version.
I feel like the current way of handling schema versions of SwiftData is very confusing and opaque for the developer to know what is going on. I don't know if the underlying database is picking the un-versioned schema, the V1 or the V2.
Is there anything I'm doing wrong? What can I do apart from making the new property optional and having a computed property that unwraps it giving it a default value when nil?
Thank you!
I'm currently using Swiftdata to store data for an app I've deployed to the app store.
The problem is that the app does not build when I add a case of the Enum type to the model, so I decided to apply a MigrationPlan. I also decided that it is not a good idea to store the Enum type itself because of future side effects.
The app is deployed in the app store with a model without a VersionedSchema, so the implementation is complete until we modify it to a model with a VersionedSchema.
This is Version1.
public enum TeamSchemaV1: VersionedSchema {
public static var versionIdentifier: Schema.Version = .init(1, 0, 0)
public static var models: [any PersistentModel.Type] {
[TeamSchemaV1.Team.self,
TeamSchemaV1.Lineup.self,
TeamSchemaV1.Player.self,
TeamSchemaV1.Human.self]
}
@Model
public final class Lineup {
...
public var uniform: Uniform
...
}
And you're having trouble migrating to Version2. The change is to rename the Uniform to DeprecatedUniform (the reason for the following change is to migrate even if it's just a property name)
public enum TeamSchemaV2: VersionedSchema {
public static var versionIdentifier: Schema.Version = .init(1, 0, 1)
public static var models: [any PersistentModel.Type] {
[TeamSchemaV2.Team.self,
TeamSchemaV2.Lineup.self,
TeamSchemaV2.Player.self,
TeamSchemaV2.Human.self]
}
@Model
public final class Lineup {
...
@Attribute(originalName: "uniform")
public var deprecatedUniform: Uniform
...
When you apply this plan and build the app, EXC_BAD_ACCESS occurs.
public enum TeamMigrationPlan: SchemaMigrationPlan {
public static var schemas: [VersionedSchema.Type] {
[TeamSchemaV1.self, TeamSchemaV2.self]
}
public static var stages: [MigrationStage] {
[migrateV1toV2]
}
public static let migrateV1toV2 = MigrationStage.lightweight(fromVersion: TeamSchemaV1.self,
toVersion: TeamSchemaV2.self)
}
I'm currently unable to migrate the data, which is preventing me from updating and promoting the app. If anyone knows of this issue, I would really appreciate your help.
Want to know if it's possible to have multiple variables of the same type? This code works but when the app loads the data again on relaunch, both variables have both the new cards and discarded cards.
@Model
class Player {
let id = UUID()
let name: String
@Relationship(deleteRule: .cascade, inverse: \Card.player) var cards: [Card] = []
@Relationship(deleteRule: .cascade, inverse: \Card.player) var discardedCards: [Card] = []
init(name: String, cards: [Card]) {
self.name = name
self.cards = cards
}
}
@Model
class Card {
let id = UUID()
let name: String
var player: Player?
init(name: String) {
self.name = name
}
}
Hi, I'm using CloudKit to create an app that backs up and records your data to iCloud.
Here's what I'm unsure about:
I understand that the 'CloudKit Dashboard' has 'Security Roles'. I thought these were meant to set permissions for accessing and modifying users' data, but I found there was no change even when I removed all 'Permissions' from 'Default Roles'. Can you clarify?
I'd like to know what _world, _icloud, and _creator in Default Roles mean respectively.
I would like to know what changes the creation, read, and write permissions make.
Is it better to just use the default settings?
Here's what I understand so far:
Default Roles:
_world: I don't know
_icloud: An account that is not my device but is linked to my iCloud
_creator: My Device
Permissions:
create: Create data
read: Read data
write: Update and delete data.
I'm not sure if I understand this correctly. Please explain.
Task:
A child view should show depending on a boolean flag the corresponding view. The flag is calculated from a model which is given by parent view.
Problem:
The flag is false in init, which is correct. However in body it's true, which is incorrect. Why value in body isn't consistent to init? Is there a race condition? The child view's is rendered thrice, that's another issue, but flag is in init and body as described before.
Parent view looks like this:
struct ParentView: View {
private var groupedItems: [GroupedItems] = []
init(items: [Item]) {
Dictionary(grouping: items) { $0.category.name }
.forEach {
let groupedItems = GroupedItems(categoryName: $0.key, items: $0.value)
self.groupedItems.append(groupedItems)
}
}
var body: some View {
ChildView(groupedItems)
}
}
Child view looks like this:
struct ChildView: View {
@State private var showItems: Bool
init(_ groupedItems: GroupedItems) {
self._showItems = State(initialValue: !groupedItems.completed)
}
var body: some View {
if showItems {
AView()
} else {
BView()
}
}
}
Model looks like this:
@Model
final class Item: Identifiable {
@Attribute(.unique) public var id: String
public var name: String
public var completed = false
}
struct GroupedItems {
var categoryName: String
var items: [Item]
var completed: Bool {
items.filter { !$0.completed }.isEmpty
}
}
I have a background thread that is updating a swift data model Item using a ModelActor. The background thread runs processing an Item and updates the Item's status field. I notice that if I have a view like
struct ItemListView: View {
@Query private var items: [Items]
var body: some View {
VStack {
ForEach(items) { item in
ItemDetailView(item)
}
}
}
}
struct ItemDetailView: View {
var item: Item
var body: some View {
// expected: item.status automatically updates when the background thread updates the `Item`'s `status`.
Text(item.status)
// actual: This text never changes
}
}
Then background updates to the Item's status in SwiftData does not reflect in the ItemDetailView. However, if I inline ItemDetailView in ItemListView like this:
struct ItemListView: View {
@Query private var items: [Items]
var body: some View {
VStack {
ForEach(items) { item in
// Put the contents of ItemDetailView directly in ItemListView
Text(item.status)
// result: item.status correctly updates when the background thread updates the item.
}
}
}
}
Then the item's status text updates in the UI as expected. I suspect ItemDetailView does not properly update the UI because it just takes an Item as an input. ItemDetailView would need additional understanding of SwiftData, such as a ModelContext.
Is there a way I can use ItemDetailView to show the Item's status and have the UI show the status as updated in the background thread?
In case details about my background thread helps solve the problem, my thread is invoked from another view's controller like
@Observable
class ItemCreateController {
func queueProcessingTask() {
Task {
let itemActor = ItemActor(modelContainer: modelContainer)
await itemActor.setItem(item)
await itemActor.process()
}
}
}
@ModelActor
actor ItemActor {
var item: Item?
func setItem(_ item: Item) {
self.item = modelContext.model(for: item.id) as? Item
}
func process() async {
// task that runs processing on the Item and updates the Item's status as it goes.
}
Hello! I have been working on an app for quite some time now, and one of my views is of a bunch of different articles that my brand has written. The articles can be found on their website, and so as of late, I have just been copying and pasting all of the data from each article into a JSON file in my app; However, as the list of articles grow, I need to fetch the data directly from the website and have it integrated with my code, so every time a new article is published, users dont have to update their app. Is there any way someone could help with this? I've been struggling for a while now. Thanks!
try to update my app from iOS 17 to io 18, I'm using SwiftData to save the data on the memory, on iOS 17 all works fine but I tried to export my app to iOS 18 and I get a strange error when I try to delate a playlist from SwiftData memory.
Thread 10: Fatal error: Never access a full future backing data - PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata://4885953A-BDB2-4CD1-9299-B2FBBB899EB7/PlaylistItem/p372), implementation: SwiftData.PersistentIdentifierImplementation) with Optional(B2A80504-2FE1-4C86-8341-3DDE8B6AB913)
the code where this error happen is very simple,
func deletePlaylist(_ playlist: PlayListModel) async throws {
print("attempt to delate :, \(playlist.playlistName)")
context.delete(playlist)
try context.save() // error here on the save
}
Error only happen on iOS 18 not on the 17.
Anyone successfully able to add two or more ModelConfigurations to a ModelContainer?
DESCRIPTION OF PROBLEM:
When you create two ModelConfigurations for two different models and combine them into one ModelContainer, it seems the @Query fails to find the models.
Crashes app with error:
“Thread 1: "NSFetchRequest could not locate an NSEntityDescription for entity name 'NumberModel'"
STEPS TO REPRODUCE
1 - Create a new iOS project.
2 - In ContentView.swift add this code:
import SwiftData
import SwiftUI
struct ContentView: View {
@Query private var colors: [ColorModel]
@Query private var numbers: [NumberModel]
var body: some View {
List {
ForEach(colors) { color in
Text(color.name)
}
ForEach(numbers) { number in
Text(number.name)
}
}
}
}
#Preview {
ContentView()
.modelContainer(for: [ColorModel.self, NumberModel.self])
}
3 - In App file, add this code:
import SwiftData
import SwiftUI
@Model
class ColorModel {
var name: String = ""
init(name: String) {
self.name = name
}
}
@Model
class NumberModel {
var name: String = ""
init(name: String) {
self.name = name
}
}
@main
struct MultipleModelConfigsApp: App {
private var container: ModelContainer
init() {
do {
let config1 = ModelConfiguration(for: ColorModel.self)
let config2 = ModelConfiguration(for: NumberModel.self)
let container = try ModelContainer(
for: ColorModel.self, NumberModel.self,
configurations: config1, config2
)
self.container = container
} catch {
fatalError("ModelContainer creation failed.")
}
}
var body: some Scene {
WindowGroup {
ContentView()
.modelContainer(container)
}
}
}
4 - Now run the app and observe the crash and the error stated above.
VERSION OF XCODE
Version 15.1 (15C65)
FEEDBACK REPORT
FB: FB13504577
(Xcode project attached to FB)
My app started crashing a ton with Xcode 16 beta 1 / iOS 18 because of "Thread 1: Fatal error: Never access a full future backing data". I was hoping this would be resolved with beta 2, but unfortunately this is not the case. I'm having a tough time reproducing this bug in a small sample project – I'd appreciate any hints as to what might be causing this.
Full error:
Thread 1: Fatal error: Never access a full future backing data - PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata://10A5A93C-DC7F-40F3-92DB-F4125E1C7A73/MyType/p2), implementation: SwiftData.PersistentIdentifierImplementation) with Optional(3BF44A2D-256B-4C40-AF40-9B7518FD9FE6)
I'm trying to use SwiftData for a new app after ~20 years of Core Data (and EOF before that). So while I'm new to SwiftData, I'm not new to Apple persistence frameworks.
I've got a pretty typical workflow - need to load some JSON from the network, convert that into model objects.
I've created an actor using the @ModelActor macro and I'm using that to do the network fetch and insert. I can set breakpoints in this code and see that it does indeed run and it's running on a non-main queue. That all seems fine.
The problem is that my @Query powered variable in my SwiftUI user interface does not get updated when this data is loaded and saved as I would expect it to.
I'm passing in the container to the actor using modelContext.container from the same modelContext that is powering the view / in the environment. My understanding was that like Core Data before it, the SwiftData framework was listening for the relevant notifications passed by the container/context, processing those and updating the UI but I can only see my data if I quit and relaunch the app.
This seems like it should be a very common use case but I've not found much online. Not sure if that means I'm just doing something fundamentally wrong or what.
Tested on both iOS 18 and 17 with the same results.
Anyone else doing this successfully? What could I be doing wrong?
I wanted to get my SwiftData previews working in my primary project, so I started modeling them after the SampleTrips project.
After messing around with that & being unable to make it work, I brought the same (working) sample code into my main project, unfortunately that's not working...
I've attached the preview error (note, there's nothing in the Diagnostic Reports).
//Sample code that works in it's own project, but not my primary target.
import SwiftUI
import SwiftData
struct TestSwiftDataStuffView: View {
let trip: Trip
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, \(trip.name)!")
.padding()
.foregroundColor(.red)
}
.padding()
}
}
#Preview(traits: .sampleDataSecondary) {
@Previewable @Query var trips: [Trip]
TestSwiftDataStuffView(trip: trips.first!)
}
actor DataModelSecondary {
struct TransactionAuthor {
static let widget = "widget"
}
static let shared = DataModelSecondary()
private init() {}
nonisolated lazy var modelContainer: ModelContainer = {
let modelContainer: ModelContainer
let schema = Schema([
Trip.self
])
do {
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false, cloudKitDatabase: .none)
modelContainer = try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Failed to create the model container: \(error)")
}
return modelContainer
}()
}
@Model class Trip {
@Attribute(.preserveValueOnDeletion)
var name: String
init(name: String) {
self.name = name
}
}
extension Trip {
static var previewTrips: [Trip] {
[
Trip(name: "Revenant"),
Trip(name: "Newcastle"),
Trip(name: "Bianca")
]
}
}
struct SampleDataSecondary: PreviewModifier {
static func makeSharedContext() throws -> ModelContainer {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try ModelContainer(
for: Trip.self,
configurations: config
)
SampleDataSecondary.createSampleData(into: container.mainContext)
return container
}
func body(content: Content, context: ModelContainer) -> some View {
content.modelContainer(context)
}
static func createSampleData(into modelContext: ModelContext) {
Task { @MainActor in
let sampleDataTrips: [Trip] = Trip.previewTrips
let sampleData: [any PersistentModel] = sampleDataTrips //+ sampleDataLA + sampleDataBLT
sampleData.forEach {
modelContext.insert($0)
}
try? modelContext.save()
}
}
}
@available(iOS 18.0, *)
extension PreviewTrait where T == Preview.ViewTraits {
@MainActor static var sampleDataSecondary: Self = .modifier(SampleDataSecondary())
}
Xcode16Preview.txt