Reputation: 21
Using SwiftData and a shared SQLite database in an app and its share extension. If you merely tap Share in Safari, and the extension is shown, when you activate the app, one view throws:
DraftsView : getDrafts(): Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: Error Domain=NSCocoaErrorDomain Code=256 "The file “default.store” couldn’t be opened." UserInfo={NSFilePath=/private/var/mobile/Containers/Shared/AppGroup/455BCEA6-209A-41EA-852D-0488375BD111/Library/Application Support/default.store, NSSQLiteErrorDomain=1}
error: -executeRequest: encountered exception = I/O error for database at /private/var/mobile/Containers/Shared/AppGroup/455BCEA6-209A-41EA-852D-0488375BD111/Library/Application Support/default.store. SQLite error code:1, 'no such table: ZDRAFT' with userInfo = { NSFilePath = "/private/var/mobile/Containers/Shared/AppGroup/455BCEA6-209A-41EA-852D-0488375BD111/Library/Application Support/default.store"; NSSQLiteErrorDomain = 1; }
None of that view's items are opened. Because the view saves the model context, all those items then appear permanently deleted.
The strange thing is the main view is unaffected.
To avoid concurrency issues, there are no @Query vars in either the app or the share extension, only fetches as needed.
SwiftData model for the view which fails:
@Model final class ClassA {
@Attribute(.unique)
var id: String
@Relationship(deleteRule: .cascade, inverse: \ClassB.parent)
var things: [ClassB] = [ClassB]()
}
@Model final class ClassB {
@Attribute(.unique)
var id: String
// Inverse link to parent
var parent: ClassA?
@Relationship(deleteRule: .cascade, inverse: \ClassC.parent)
var otherThings: [ClassC] = [ClassC]()
}
@Model final class ClassC {
@Attribute(.unique)
var id: String
// Inverse link to parent
var parent: ClassB?
}
For the view that's unaffected:
@Model final class Item {
@Attribute(.unique)
var id: UUID
var itemUrl: String
}
Share extension is all one Swift class:
class ShareViewController: SLComposeServiceViewController {
private let modelContainer: ModelContainer
private let modelContext: ModelContext
required init?(coder: NSCoder) {
do {
modelContainer = try ModelContainer(for: SavedItem.self, configurations: ModelConfiguration(isStoredInMemoryOnly: false))
} catch {
fatalError("Failed to create the model container: \(error)")
}
modelContext = modelContainer.mainContext
super.init(coder: coder)
}
[…]
func saveItem(itemUrl: String) {
var fetch = FetchDescriptor<Item>(
predicate: #Predicate { item in item.itemUrl == itemUrl }
)
fetch.includePendingChanges = true
let dupes = try? modelContext.fetch(fetch)
guard dupes?.count == 0 else { return }
let id = UUID()
let item = Item(id: id)
modelContext.insert(item)
do {
try modelContext.save()
} catch(let error) {
print("error: \(error.localizedDescription)")
}
}
}
Tried model actors, was a crashy hairball.
If you never view the share extension icon in Safari, app works fine.
Anyone have SwiftData share extension sample code that works?
Upvotes: 1
Views: 1003
Reputation: 21
Adding the crashing data model class to the schema parameter fixed it:
let config = ModelConfiguration(groupContainer: .identifier("group.org.foo.appname"))
modelContainer = try ModelContainer(for: ClassA.self, ClassB.self, configurations: config)
Upvotes: 1