Starting point
I have an app that is in production that has a single entity called CDShift. This is the class:
@Model
final class CDShift {
var identifier: UUID = UUID()
var date: Date = Date()
...
}
This is how this model is written in the current version.
Where I need to go
Now, I'm updating the app and I have to do some modifications, that are:
add a new entity, called DayPlan
add the relationship between DayPlan and CDShift
What I did is this:
enum SchemaV1: VersionedSchema {
static var versionIdentifier = Schema.Version(1, 0, 0)
static var models: [any PersistentModel.Type] {
[CDShift.self]
}
@Model
final class CDShift {
var identifier: UUID = UUID()
var date: Date = Date()
}
}
To encapsulate the current CDShift in a version 1 of the schema. Then I created the version 2:
enum SchemaV2: VersionedSchema {
static var versionIdentifier = Schema.Version(2, 0, 0)
static var models: [any PersistentModel.Type] {
[CDShift.self, DayPlan.self]
}
@Model
final class DayPlan {
var identifier: UUID = UUID()
var date: Date = Date()
@Relationship(inverse: \CDShift.dayPlan) var shifts: [CDShift]? = []
}
@Model
final class CDShift {
var identifier: UUID = UUID()
var date: Date = Date()
var dayPlan: DayPlan? = nil
}
}
The migration plan
Finally, I created the migration plan:
enum MigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[SchemaV1.self, SchemaV2.self]
}
static let migrateV1toV2 = MigrationStage.custom(
fromVersion: SchemaV1.self,
toVersion: SchemaV2.self) { context in
// willMigrate, only access to old models
} didMigrate: { context in
// didMigrate, only access to new models
let shifts = try context.fetch(FetchDescriptor<SchemaV2.CDShift>())
for shift in shifts {
let dayPlan = DayPlan(date: shift.date)
dayPlan.shifts?.append(shift)
context.insert(dayPlan)
}
}
static var stages: [MigrationStage] {
print("MigrationPlan | stages called")
return [migrateV1toV2]
}
}
The ModelContainer
Last, but not least, how the model container is created in the App:
struct MyApp: App {
private let container: ModelContainer
init() {
container = ModelContainer.appContainer
}
var body: some Scene {
WindowGroup {
...
}
.modelContainer(container)
}
}
This is the extension of ModelContainer:
extension ModelContainer {
static var appContainer: ModelContainer {
let schema = Schema([
CDShift.self,
DayPlan.self
])
let modelConfiguration = ModelConfiguration(
schema: schema,
isStoredInMemoryOnly: Ecosystem.current.isPreview,
groupContainer: .identifier(Ecosystem.current.appGroupIdentifier)
)
do {
// let container = try ModelContainer(for: schema, configurations: modelConfiguration)
let container = try ModelContainer(for: schema, migrationPlan: MigrationPlan.self, configurations: modelConfiguration)
AMLogger.verbose("SwiftData path: \(modelConfiguration.url.path)")
return container
} catch (let error) {
fatalError("Could not create ModelContainer: \(error)")
}
}
}
The error
This has always worked perfectly until the migration. It crashes on the fatalError line, this is the error:
Unable to find a configuration named 'default' in the specified managed object model.
Notes
It seems that the version of the store is never updated to 2, but it keeps staying on 1. I tried also using the lightweight migration, no crash, it seems it recognizes the new entity, but the store version is always 1.
iCloud is enabled
I thought that the context used in the custom migration blocks is not the "right" one that I use when I create my container
If I use the lightweight migration, everything seems to work fine, but I have to manually do the association between the DayPlan and the CDShift objects
Do you have an idea on how to help in this case?
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
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.
GM,
I'm kinda new in SwiftData,
I'm trying to build an app with Steppers and I need to acess the value of steppers in other views and save them.
I started with this code but it doen't save the results, can someone please help? Thanks alot.
import SwiftData
@main
struct TesteStepApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: GroceryListItem.self)
}
}
@Model
class GroceryListItem {
let stepperValue: Int
init(stepperValue: Int = 0) {
self.stepperValue = stepperValue
}
}
struct ContentView: View {
@Environment(\.modelContext) var context
@State private var stepperValue: Int = 0
var body: some View {
VStack {
Stepper("Value: \(stepperValue)", value: $stepperValue, in: 0...100)
.padding()
}
.padding()
.onChange(of: stepperValue) { oldValue, newValue in
insertValue(newValue)
}
}
private func insertValue(_ value: Int) {
}
}
#Preview {
ContentView()
}
Topic:
App & System Services
SubTopic:
iCloud & Data
Since ios 17 I can’t choose the destination of airdropped files anymore.
why this error persists?
TL;DR: Does Apple keep a list of ubiquity containers that a user has deleted, and prevent apps from creating the same ubiquity containers again?
If so, where is this list, and how can I reset it?
Long version:
I'm developing an app that relies on having an ubiquity container (iCloud Drive folder). This was working fine until I decided to rename the folder.
I deleted the existing folder with the old name, changed the value of "NSUbiquitousContainerName," and expected my app's code to create the new folder with the new name, as it had done originally.
But the result has been a disaster.
Now, in the simulator, all of my code is running without errors. The app thinks the iCloud Drive folder exists and can read and write files to it, but the folder only seems to exist in the simulator and is not visible via any iCloud Drive UI.
When I run the exact same code on my iPhone, the code fails with the error 'You don’t have permission to save the file “Documents” in the folder “[whatever]”.'
I have tried changing my app's bundle identifier and everything works swimmingly: the iCloud Drive folder is created instantly and is visible everywhere.
So something is preventing things from working with the original bundle identifier and I need to figure out what it is and how to fix it. Does anybody have any ideas? Thanks in advance.
hi
After upgrading Xcode 16 beta 2 and iOS 18 beta 2, my project will run a hit assertion after executing
sqlite3_finalize
Assertion failed: (0), function isBulkReadStatement, file SQLiteDatabaseTracking.cpp, line 710.
Xcode 15 does not have this problem.
I have a simple app that uses SwiftUI and SwiftData to maintain a database. The app runs on multiple iPhones and iPads and correctly synchronises across those platforms. So I am correct setting Background Modes and Remote Notifications. I have also correctly setup my Model Configuration and ModelContainer (Otherwise I would expect syncing to fail completely).
The problem arises when I run on a Mac (M1 or M3) either using Mac Designed for iPad or Mac Catalyst. This can be debugging in Xcode or running the built app. Then the app does not reflect changes made in the iPhone or iPad apps unless I follow a specific sequence. Leave the app, (e.g click on a Finder window), then come back to the app (i.e click on the app again). Now the app will show the changes made on the iPhone/iPad.
It looks like the app on the Mac is not processing remote notifications when in the background - it only performs them when the app has just become active. It also looks like the Mac is not performing these sync operations when the app is active. I have tried waiting 30 minutes and still the sync doesn't happen unless I leave the app and come back to it.
I am using the same development CloudKit container in all cases
Topic:
App & System Services
SubTopic:
iCloud & Data
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.
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
CloudKit
Cloud and Local Storage
CloudKit Dashboard
SwiftData
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.
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
}
}
Hello,
I'm experiencing an issue with iOS 18 Beta 3 and SwiftData.
I have a model with some attributes marked as externalStorage because they are pretty large.
In iOS 17 I could display a list of all my Models and it would not prefetch the externalStorage attributes.
Now in iOS 18 it seems the attributes are fetched even though I don't use them in my list.
It's an issue because the memory use of my app goes from 100mo on iOS 17 to more than 1gb on iOS 18 because of this.
My app is configured to sync with iCloud.
Anyone else experiencing the issue?
Thanks
Gil
I have an app with fairly typical requirements - I need to insert some data (in my case from the network but could be anything) and I want to do it in the background to keep the UI responsive.
I'm using SwiftData.
I've created a ModelActor that does the importing and using the debugger I can confirm that the data is indeed being inserted.
On the UI side, I'm using @Query and a SwiftUI List to display the data but what I am seeing is that @Query is not updating as the data is being inserted. I have to quit and re-launch the app in order for the data to appear, almost like the context running the UI isn't communicating with the context in the ModelActor.
I've included a barebones sample project. To reproduce the issue, tap the 'Background Insert' button. You'll see logs that show items being inserted but the UI is not showing any data.
I've tested on the just released iOS 18b3 seed (22A5307f).
The sample project is here:
https://hanchor.s3.amazonaws.com/misc/SwiftDataBackgroundV2.zip
I have implemented iCloud syncing in my app.
I am noticing a lot of "CKErrorInternalError" codes in my production logs. The description is "Failed user key sync" or "Failed to sync user keys". I have no idea what they mean or what is causing it.
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 have a Multiplatform app for iOS and macOS targets.
I am using CloudKit with CoreData and have successfully established a private and public database.
The app has successfully synced private and public data for months between macOS (dev machine), an iPhone 13 Pro and an iPad Pro 12.9inch 2nd gen. The public data also syncs perfectly to simulator instances running under other iCloud accounts.
Recently I added a new entity in the public DB and here is where I seemed to have made a mistake.
I entered data into the new public database via my developer UI built into the macOS app running on my MBP before I indexed the necessary fields.
Side note - I find it necessary to index the following for each Entity to ensure iCloud sync works as expected on all devices...
modifiedTimestamp - Queryable
modifiedTimestamp - Sortable
recordName - Queryable
Realising my mistake, I indexed the above CKRecord fields for the new Entity.
Since then, the macOS target has remained in some way "frozen" (for want of a better term). I can add new public or private records in the macOS app but they do not propagate to the public or private stores in iCloud.
I have attempted many fixed, some summarised below:
clean build folder from Xcode;
remove all files from the folder /Users//Library/Containers/, place in recycle bin, empty recycle bin, then build and run;
build and run on iPhone and iPad targets to ensure all apps are current dev version, then repeat above processes.
I've read through the console logging when I build and run the macOS app many many times to see whether I can find any hint. The closest thing I can find is...
BOOL _NSPersistentUIDeleteItemAtFileURL(NSURL *const __strong) Failed to stat item: file:///Users/<me>/Library/Containers/com.me.AppName/Data/Library/Saved%20Application%20State/com.me.AppName.savedState/restorecount.plist
but my research on this and other forums suggests this is not relevant.
Through this, the app still functions as expected on iOS devices and both private and public database additions and modifications propagate to iCloud stores and to other devices.
I expect that removing the macOS app entirely from my dev machine would trigger a complete sync with all existing data. Imagine I bought a new macOS device and chose to install my app where before I had run this only on my iOS devices. My current problem suggests that I could not do this, but I know that this is not the intended behaviour.
This scenario makes me think there is a setting file for my macOS app that I'm not aware of and that this impeding the sync of all existing app data back to the fresh install of the macOS app? But that is a wild guess.
Running public releases (no betas)
Xcode 15.4 (15F31d)
macOS Sonoma 14.5
physical iOS devices running iOS 17.5.1
Any words of wisdom on how I might go about trying to solve this problem please?
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
CloudKit
Cloud and Local Storage
SwiftUI
Core Data
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~
I'm having trouble resolving the error.
If I save data in view A and open view B, I get an error.
If I open only view A, or only view B, there is no problem, but when I open a second view (A to B, B to A, etc.) I get an error.
Attached is the code that reproduces the problem.
We would appreciate your cooperation.
No simulator is used.
Xcode: Version 15.4 (15F31d)
iPone11: 17.5.1
Thank you.
Error
error: Error: Persistent History (2) has to be truncated due to the following entities being removed: (
A
)
warning: Warning: Dropping Indexes for Persistent History
warning: Warning: Dropping Transactions prior to 2 for Persistent History
warning: Warning: Dropping Changes prior to TransactionID 2 for Persistent History
Code
import SwiftUI
import SwiftData
@main
struct issueApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.modelContainer(for: [A.self, B.self])
}
}
}
import SwiftUI
import SwiftData
struct ContentView: View {
@State var showAView: Bool = false
@State var showBView: Bool = false
var body: some View {
VStack {
Button("show A"){
showAView.toggle()
}
.sheet(isPresented: $showAView){
AView()
}
Button("show B"){
showBView.toggle()
}
.sheet(isPresented: $showBView){
BView()
}
}
}
}
struct AView: View {
@Environment(\.modelContext) var context
@Query private var list: [A]
let query = QueryData<A>()
var body: some View {
VStack {
if list.count > 0 {
VStack {
List {
ForEach(list, id: \.number){ item in
VStack {
Text(item.number)
}
}
}
Button("clear"){
try? context.delete(model: A.self)
}
}
} else {
Button("set"){
query.set(data: A(number: "1"))
}
}
}
}
}
struct BView: View {
@Environment(\.modelContext) var context
@Query private var list: [B]
let query = QueryData<B>()
var body: some View {
VStack {
if list.count > 0 {
VStack {
List {
ForEach(list, id: \.number){ item in
VStack {
Text(item.number)
}
}
}
Button("clear"){
try? context.delete(model: B.self)
}
}
} else {
Button("set"){
query.set(data: B(number: "1"))
}
}
}
}
}
class QueryData<T: PersistentModel> {
private var container: ModelContainer?
init() {
self.container = try? ModelContainer(for: T.self)
}
func set(data: T) {
guard let container = self.container else { return }
let context = ModelContext(container)
do {
try context.delete(model: T.self)
context.insert(data)
try context.save()
} catch {
print("error: \(error)")
}
}
}
@Model
final class A {
var number: String
init(number: String) {
self.number = number
}
}
@Model
final class B {
var number: String
init(number: String) {
self.number = number
}
}
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!
Hello!
I am an inexperienced programmer learning swiftUI and swiftData so apologies in advance if this answer can be found elsewhere.
I want to display a list of recent transcriptions, stored in modelContainer, in reversed order. I attempted to do transcriptionsList.reversed() in a ForEach (which is in a swiftUI List), but .onDelete gets messed up as it is deleting the items as per the normal order.
The question is, is there a way when using context.insert(), that I can specify to insert at index 0? Or is there a better way of resolving this?
Thanks in advance!
The code when pasted in here appears to not be formatted correctly so I didn't include code
I've already submitted this as a bug report to Apple, but I am posting here so others can save themselves some troubleshooting. This is submitted as FB14337982 with an attached complete Xcode project to replicate.
In iOS 17 we use a ModelActor to download data which is saved as an Event, and then save it to SwiftData with a relationship to a Location. In iOS 18 22A5307d we are seeing that this code no longer persists the relationship to the Location, but still saves the Event. If we put a breakpoint in that ModelActor we see that the object graph is correct within the ModelActor stack trace at the time we call modelContext.save(). However, after saving, the relationship is missing from the default.store SQLite file, and of course from the app UI.
Here is a toy example showing how inserting an Employee into a Company using a ModelActor gives unexpected results in iOS 18 22A5307d but works as expected in iOS 17.
It appears that no relationships data survives being saved in a ModelActor.ModelContext.
Also note there seems to be a return of the old bug that saving this data in the ModelActor does not update the @Query in the UI in iOS 18 but does so in iOS 17.
Models
@Model
final class Employee {
var uuid: UUID = UUID()
@Relationship(deleteRule: .nullify) public var company: Company?
/// For a concise display
@Transient var name: String {
self.uuid.uuidString.components(separatedBy: "-").first ?? "NIL"
}
init(company: Company?) {
self.company = company
}
}
@Model
final class Company {
var uuid: UUID = UUID()
@Relationship(deleteRule: .cascade, inverse: \Employee.company)
public var employees: [Employee]? = []
/// For a concise display
@Transient var name: String {
self.uuid.uuidString.components(separatedBy: "-").first ?? "NIL"
}
init() { }
}
ModelActor
import OSLog
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "SimpleModelActor")
@ModelActor
final actor SimpleModelActor {
func addEmployeeTo(CompanyWithID companyID: PersistentIdentifier?) {
guard let companyID,
let company: Company = self[companyID, as: Company.self] else {
logger.error("Could not get a company")
return
}
let newEmployee = Employee(company: company)
modelContext.insert(newEmployee)
logger.notice("Created employee \(newEmployee.name) in Company \(newEmployee.company?.name ?? "NIL")")
try! modelContext.save()
}
}
ContentView
import OSLog
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "View")
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var companies: [Company]
@Query private var employees: [Employee]
@State private var simpleModelActor: SimpleModelActor!
var body: some View {
ScrollView {
LazyVStack {
DisclosureGroup("Instructions") {
Text("""
Instructions:
1. In iOS 17, tap Add in View. Observe that an employee is added with Company matching the shown company name.
2. In iOS 18 beta (22A5307d), tap Add in ModelActor. Note that the View does not update (bug 1). Note in the XCode console that an Employee was created with a relationship to a Company.
3. Open the default.store SQLite file and observe that the created Employee does not have a Company relationship (bug 2). The relationship was not saved.
4. Tap Add in View. The same code is now executed in a Button closure. Note in the XCode console again that an Employee was created with a relationship to a Company. The View now updates showing both the previously created Employee with NIL company, and the View-created employee with the expected company.
""")
.font(.footnote)
}
.padding()
Section("**Companies**") {
ForEach(companies) { company in
Text(company.name)
}
}
.padding(.bottom)
Section("**Employees**") {
ForEach(employees) { employee in
Text("Employee \(employee.name) in company \(employee.company?.name ?? "NIL")")
}
}
Button("Add in View") {
let newEmployee = Employee(company: companies.first)
modelContext.insert(newEmployee)
logger.notice("Created employee \(newEmployee.name) in Company \(newEmployee.company?.name ?? "NIL")")
try! modelContext.save()
}
.buttonStyle(.bordered)
Button("Add in ModelActor") {
Task {
await simpleModelActor.addEmployeeTo(CompanyWithID: companies.first?.persistentModelID)
}
}
.buttonStyle(.bordered)
}
}
.onAppear {
simpleModelActor = SimpleModelActor(modelContainer: modelContext.container)
if companies.isEmpty {
let newCompany = Company()
modelContext.insert(newCompany)
try! modelContext.save()
}
}
}
}