I needed help creating the predicate in order to query my swift data model for the record with the largest integer. Is this possible, and is it any more efficient than just fetching all the data and then doing the filtering?
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Post
Replies
Boosts
Views
Activity
I've created a container, enabled CloudKit in xCode and added code to reference the container and save. Every time I run the app I get this error when I close the game:
Snapshot request 0x2814167c0 complete with error: <NSError: 0x28144df80; domain: BSActionErrorDomain; code: 1 ("response-not-possible")>
The app is not saved and i cannot see any action with the container. My app is built in Unity with C# code and builds with no errors.
I would appreciate and assistance!
I'm developing an app with dynamic intents fetched from a SwiftData container with data that is configured inside the app. However, the following happens when .fetch() is run during widget configuration.
When creating a new instance of the model and inserting it into the container, it disappears when the app is relaunched.
When creating a new instance of the model and inserting it into the container, and then attempting to delete that instance, the app crashes. This error shows up Could not cast value of type 'Swift.Optional<Any>' (0x2003ddfc0) to 'Foundation.UUID' (0x200442af8).
This is a snippet of the query code.
func entities(for identifiers: [ExampleEntity.ID]) async throws -> [ExampleEntity] {
let modelContext = ModelContext(Self.container)
let examples = try? modelContext.fetch(FetchDescriptor<ExampleModel>())
return examples?.map {
ExampleEntity(example: $0)
} ?? []
}
SwiftData works as expected as long as the .fetch() is not called in the EntityQuery.
Hi, I followed the instructions here: https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories
but when trying to get access to each file inside the selected folder (tried iCloud and iPhone) it always fails. Instead, if I specify a type of file on the Controller (say .audio) I can read the file fine...
Did anything change which is not documented in that page? Do I have to set any other permission or capability? (I have iCloud Documents)
Thanks!!
I have v3 models in coredata (model: Event, Lecture), and make new model in v4 with swiftdata but model name, property is different (model: EventModel, LectureModel).
For migration, I add V3Schema, V4Schema and MigrationPlan.
enum V4Schema: VersionedSchema {
static var models: [any PersistentModel.Type] = [LectureModel.self, EventModel.self ]
static var versionIdentifier = Schema.Version(4, 0, 0)
}
enum V3Schema: VersionedSchema {
static var models: [any PersistentModel.Type] = [Event.self, Lecture.self]
static var versionIdentifier = Schema.Version(3, 5, 2)
}
enum ModelMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] = [V3Schema.self, V4Schema.self]
static var stages: [MigrationStage] = [migrateV3ToV4]
}
extension ModelMigrationPlan {
static let migrateV3ToV4 = MigrationStage.custom(fromVersion: V3Schema.self,
toVersion: V4Schema.self,
willMigrate: willMigrate,
didMigrate: { _ in Log.debug(message: "Migration Complete") })
}
private func willMigrate(context: ModelContext) throws {
try migrateLectures(context: context)
try migrateEvents(context: context)
try context.save()
}
private func migrateEventTypes(context: ModelContext) throws {
// create new v4 event model using v3 event model.
}
private func migrateLectures(context: ModelContext) throws {
// create new v4 lecture model using v3 lecture model.
}
static let release: ModelContainer = {
let v4Schema = Schema(V4Schema.models)
let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "app group id")
let databaseURL = containerURL?.appending(path: "**.sqlite")
let configuration = ModelConfiguration("**",
schema: v4Schema,
url: databaseURL!,
cloudKitDatabase: .private("**"))
#if DEBUG
do {
try autoreleasepool {
let desc = NSPersistentStoreDescription(url: configuration.url)
let opts = NSPersistentCloudKitContainerOptions(containerIdentifier: configuration.cloudKitContainerIdentifier!)
desc.cloudKitContainerOptions = opts
desc.shouldAddStoreAsynchronously = false
if let model = NSManagedObjectModel.makeManagedObjectModel(for: V4Schema.models) {
let container = NSPersistentCloudKitContainer(name: "**", managedObjectModel: model)
container.persistentStoreDescriptions = [desc]
container.loadPersistentStores(completionHandler: { _, err in
if let err {
Log.error(message: "Store open failed: \(err.localizedDescription)")
}
})
try container.initializeCloudKitSchema()
Log.debug(message: "Initialize Cloudkit Schema")
if let store = container.persistentStoreCoordinator.persistentStores.first {
try container.persistentStoreCoordinator.remove(store)
}
}
}
} catch {
Log.error(message: "Failed: \(error.localizedDescription)")
}
#endif
return try! ModelContainer(for: v4Schema, migrationPlan: ModelMigrationPlan.self, configurations: configuration)
}()
But when I run, I got error message.
CoreData: CloudKit: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _scheduleAutomatedExportWithLabel:activity:completionHandler:]_block_invoke(3508): <NSCloudKitMirroringDelegate: 0x1036b1540> - Finished automatic export - ExportActivity - with result: <NSCloudKitMirroringResult: 0x1035da810> storeIdentifier: ***** success: 0 madeChanges: 0 error: Error Domain=NSCocoaErrorDomain Code=134407 "Request '*****' was cancelled because the store was removed from the coordinator." UserInfo={NSLocalizedFailureReason=Request '****' was cancelled because the store was removed from the coordinator.}
I don't know why store was removed from the coordinator.
Any have solutions?
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.
My app is using SwiftData, but I deployed it to the app store with no VersionedSchema applied without thinking about migrating the model. Now I need to migrate the data and I need help from someone who has experience moving from non-versioned to versioned.
Assuming I currently have a version1, version2 schema, it works fine for the initial install situation, but when I migrate to version1, version2 in an app that is listed on the app store, I run into problems.
I don't have any logs to show for it. Thread 1: EXC_BAD_ACCESS (code=2, address=0x16a6578f0) If anyone has had the same experience as above, please respond, thanks!
Let me know what kind of logs you need and I'll add them as a comment.
I have two versionedSchema V1VersionedSchema, V2VersionedSchema.
When i create schema with versionedSchema like code below and check version using breakpoint, V1, V2 Schema encodingVersion is same.
let v3Schema = Schema(versionedSchema: V1VersionedSchema.self)
// encodingVersion: 1.0.0
// schemaEncodingVersion: 1.0.0
let v2Schema = Schema(versionedSchema: V2VersionedSchema.self)
// encodingVersion: 1.0.0
// schemaEncodingVersion: 2.0.0
Why encoding version is same? (I think migration plan v1 to v2 isn't work becaus of that)
In Xcode 15.0.1, I created a new project to start working with SwiftData. I did this by creating a default App project and checking the Use SwiftData checkbox. The resulting project contains just three files: an app entry point file, a ContentView SwiftUI view file, and an Item model file.
The only change I made was to annotate the default Item timestamp property with a .transformable attribute.
Here is the resulting model:
@Model
final class Item {
@Attribute(.transformable(by: TestVT.self)) var timestamp: Date // Only updated this line
init(timestamp: Date) {
self.timestamp = timestamp
}
}
And here is the definition of TestVT. It is a basic ValueTransformer that simply tries to store the Date as a NSNumber:
// Added this
class TestVT: ValueTransformer {
static let name = NSValueTransformerName("TestVT")
override class func transformedValueClass() -> AnyClass {
NSNumber.self
}
override class func allowsReverseTransformation() -> Bool {
true
}
override func transformedValue(_ value: Any?) -> Any? {
guard let date = value as? Date else {
return nil
}
let ti = date.timeIntervalSince1970
return NSNumber(value: ti)
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let num = value as? NSNumber else {
return nil
}
let ti = num.doubleValue as TimeInterval
return Date(timeIntervalSince1970: ti)
}
}
And finally, I made sure to register my ValueTransformer but updating the sharedModelContainer definition in the App:
var sharedModelContainer: ModelContainer = {
ValueTransformer.setValueTransformer(TestVT(), forName: TestVT.name) // Only added this line
let schema = Schema([
Item.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
Prior to Xcode 15.1, this was working fine. However, now when I try to create an item when running the app I get the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "timestamp"; desired type = NSNumber; given type = __NSTaggedDate; value = 2023-12-14 01:47:11 +0000.'
I'm unsure of why this stopped working. The error seems to be complaining about the input being of type Date when NSNumber was expected, but I thought that's what the ValueTransformer was supposed to be doing.
Important note: prior to Xcode 15.1, I did not originally override the transformedValueClass() and everything was working but in the new Xcode when launching the app I was getting a Thread 1: EXC_BAD_ACCESS (code=1, address=0x0) on the return try ModelContainer(...) line. Removing the .transformable property from my model fixed the issue. That's why I added the override here, because I think the docs indicate overriding it as well and I missed that the first time. This being said, I think the code I have is what a correct ValueTransformer would look like.
If anyone has experienced this issue, or has a working ValueTransformer for SwiftData in Xcode 15.1, please let me know. Appreciate any help with this issue. Thanks so much!
Hey, I might have a super dumb question but I am very new to SwiftData and Swift in general. So I have the following code in a View:
@State private var selectedMonth: String = ""
@State private var selectedYear: String = ""
@Query(
filter: #Predicate<Transaction> { transaction in
transaction.date.monthString == selectedMonth && transaction.date.yearString == selectedYear
},
sort: \Transaction.date
) var transactions: [Transaction]
My @State vars selectedMonth and selectedYear get changed whenever the user selects options from a menu. I've had trouble getting this query/filter to work, does anyone know if filtering on dynamic (@State) variables is supported in SwiftData? If so, am I missing something here?
I've also tried the pattern of having the transactions array live elsewhere in a regular swift file with the @Published macro and calling an update function whenever the @State vars selectedMonth or selectedYear get updated using .onChange(of:) . Unfortunately, I've not been able to find any documentation or examples about setting up a regular swift file (not SwiftUI) to work with SwiftData (e.g. querying from it, accessing the context/container, etc)
Would appreciate anyone giving tips or pointing me in the right direction. Sorry if its a noob questions, thanks so much for any help
Xcode: 15.1
I've got (simplified) model with relationship many-to-many defined as follows
@Model
final class Item {
var title: String
@Relationship(inverse: \Tag.items) var tags: [Tag]?
init(_ title: String) {
self.title = title
}
}
@Model
final class Tag {
var name: String
var items: [Item]?
init(_ name: String) {
self.name = name
}
}
and a view with a query
struct ItemsView: View {
@Query var items: [Item]
var body: some View {
List {...}
}
init(searchText: String) {
_items = Query(filter: #Predicate<Item> { item in
if (searchText.isEmpty) {
return true
} else {
return item.tags!.contains{$0.name.localizedStandardContains(searchText)}
}
})
}
}
This code compiles but fails at runtime with an error:
Query encountered an error: SwiftData.SwiftDataError(_error: SwiftData.SwiftDataError._Error.unsupportedPredicate)
It looks like Predicate does not like optionals cause after changing tags and items to non optionals and the predicate line to
item.tags.contains{$0.name.localizedStandardContains(searchText)}
everything works perfectly fine.
So, my question is, does anybody know how to make it work with optionals?
Full code: https://github.com/m4rqs/PredicateWithOptionals.git
My app uses a significant amount of space in a private CloudKit database, and I need to be able to show users how much free space they have. Is there any way to get this value programmatically?
I am trying to use ArchiveStream.process on MacOS as outlined in the documentation here:
https://developer.apple.com/documentation/accelerate/decompressing_and_extracting_an_archived_directory
I have been able to successfully do the following:
Take objects and create Data objects
Create a UIDocument that is FileWrapper based
Compress the UIDocument as an .aar archive
Upload it to iCloud as a CKRecord
Download it as an .aar and decode it back to a directory
Decode the directory as individual data items and back to objects
The problem is that I sometimes can only download from iCloud once and have the decompression function - other times it may work two or three times but ultimately fails when trying to call ArchiveStream.process
The error reported by ArchiveStream.process is simply 'ioError' but on the console I see the following:
[0xa5063c00] Truncated block header (8/16 bytes read)
[0xa503d000] NOP received
[0xa5080400] processStream
[0xa7019000] decoder failed
[0xbd008c00] istream read error
[0xbd031c00] refill buffer
[0x90008000] archive stream read error (header)
[0xc8173800] stream cancelled
The test data I am using does not change so it does not appear to be related to what I am compressing.
But I am at a loss how to prevent the error.
This is IOS 17 running on MacOS (as iPad) and on IOS 17 devices.
Expected behaviour: When I press the button I expect the label change from "Original" to "Edited"
Obtained behaviour: This only happens in scenario A) and B) not C), the desired.
Scenario A: action: update2(item:) and both labels
Scenario B: action: update(itemID:container:) and label: Text(item.name)
Scenario C: action: update(itemID:container:) and label: ItemRow(item:)
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
var body: some View {
NavigationSplitView {
List {
ForEach(items) { item in
Button(action: {
update(itemID: item.id, container: modelContext.container)
// update2(item: item)
},
label: {
ItemRow(item: item)
// Text(item.name)
})
}
.onDelete(perform: deleteItems)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
} detail: {
Text("Select an item")
}
}
private func addItem() {
withAnimation {
let newItem = Item(name: "Original")
modelContext.insert(newItem)
//modelContext.save()
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(items[index])
}
}
}
}
struct ItemRow: View {
let item: Item
var body: some View {
Text(item.name)
}
}
@Model
final class Item {
var name: String
init(name: String) {
self.name = name
}
}
func update(itemID: PersistentIdentifier, container: ModelContainer){
let editModelContext = ModelContext(container)
editModelContext.autosaveEnabled = false
let editModel = editModelContext.model(for: itemID) as! Item
editModel.name = "Edited"
try! editModelContext.save()
}
func update2(item: Item){
item.name = "Edited"
}
When I logged into my cloudkit console to inspect the database for some debugging work I couldn't access the private database. It keeps saying "failed to access iCloud data, please signi n again". No matter how many times I sign in again, whether with password or passwordless key it keeps saying the same thing. It says that message when I click on Public database, and private and shared databases are below it. I only noticed this a couple of days ago. It's done this in the past, but I eventually got back into the database but I don't know what changed to make it work.
I'll preface by saying I'm a new Swift developer, having a go at my first app. Just a simple memory tracker.
I'm building it using SwiftData + iCloud Syncing. I had set up my model like this:
@Model
final class Memory {
var content: String = ""
var dateCreated: Date = Date.now
var dateUpdated: Date = Date.now
var tags: [Tag]? = [Tag]()
@Attribute(.externalStorage) var images: [Data] = [Data]()
init(
content: String = "",
dateCreated: Date = .now,
dateUpdated: Date = .now,
tags: [Tag] = [Tag](),
images: [Data] = [Data]()
) {
self.content = content
self.dateCreated = dateCreated
self.dateUpdated = dateUpdated
self.tags = tags
self.images = images
}
}
But I discovered that led to a massive performance issue as soon as someone added a few images to a Memory. Maybe SwiftData isn't correctly putting an ARRAY of Data into external storage? My memory usage would just balloon with each photo added. All the examples I've seen just use a singular Data type for external storage, so not sure.
Anyway, I played around with different options and figured out that a new MemoryPhoto struct was probably best, so I put the old model in a V1 schema and my NEW V2 model looks like this...
enum DataSchemaV2: VersionedSchema {
static var versionIdentifier = Schema.Version(2, 0, 0)
static var models: [any PersistentModel.Type] {
[Memory.self, Tag.self, MemoryPhoto.self]
}
@Model
final class Memory {
var content: String = ""
var dateCreated: Date = Date.now
var dateUpdated: Date = Date.now
var tags: [Tag]? = [Tag]()
@Relationship(deleteRule: .cascade) var photos: [MemoryPhoto]? = [MemoryPhoto]()
@Attribute(.externalStorage) var images: [Data] = [Data]()
init(
content: String = "",
dateCreated: Date = .now,
dateUpdated: Date = .now,
tags: [Tag] = [Tag](),
images: [Data] = [Data](),
photos: [MemoryPhoto] = [MemoryPhoto]()
) {
self.content = content
self.dateCreated = dateCreated
self.dateUpdated = dateUpdated
self.tags = tags
self.images = images
self.photos = photos
}
}
@Model
final class MemoryPhoto {
@Attribute(.externalStorage) var originalData: Data?
@Relationship(inverse: \Memory.photos) var memory: Memory?
init(originalData: Data? = nil, memory: Memory? = nil) {
self.originalData = originalData
self.memory = memory
}
}
Here's my migration, currently, which does work, because as best I can tell this is a lightweight migration...
enum DataMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[DataSchemaV1.self, DataSchemaV2.self]
}
static var stages: [MigrationStage] {
[migrateV1toV2]
}
static let migrateV1toV2 = MigrationStage.lightweight(fromVersion: DataSchemaV1.self, toVersion: DataSchemaV2.self)
}
But what I'm trying to figure out now is to migrate the former memory.images of type [Data] to the new memory.photos of type [MemoryPhoto], and been struggling. Any type of custom migration I do fails, sometimes inconsistently. I can try to get the exact errors if helpful but at this point not even a simple fetch to existing memories and updating their content as a part of the migration works.
Is there a way to write a hypothetical V2 to V3 migration that just takes the images and puts them in the photos "slot"? For instance, what I do have working is this function that basically runs a "migration" or sorts when a given memory appears and it has the former images property.
....
.onAppear {
convertImagesToPhotos()
}
}
private func convertImagesToPhotos() {
guard !memory.images.isEmpty && memory.unwrappedPhotos.isEmpty else { return }
let convertedPhotos = memory.images.map { imageData in
MemoryPhoto(originalData: imageData)
}
memory.photos?.append(contentsOf: convertedPhotos)
memory.images.removeAll()
}
Any help or pointers appreciated for this newbie swift developer. If helpful, here's the main App struct too...
@main
struct YesterdaysApp: App {
@Environment(\.scenePhase) var scenePhase
@AppStorage("writingRemindersEnabled") var writingRemindersEnabled: Bool = false
let container: ModelContainer
init() {
do {
container = try ModelContainer(
for: Memory.self,
migrationPlan: DataMigrationPlan.self
)
} catch {
fatalError("Failed to initialize model container.")
}
}
var body: some Scene {
WindowGroup {
OuterMemoryListView()
.yesterdaysPremium()
}
.modelContainer(container)
}
}
This session was supposed to be a code-along, but the starter code is missing
I have a flutter app I developed and some of the users cannot use our backup system which uses iCloud Key-Value storage. These users all have one thing in common. Full iCloud. If they turn off the app from iCloud from backup details page in iCloud settings then it works. But if their iCloud is 100% full and they can't even access the page then it does not work.
Can someone tell me why this is happening? My app is made in flutter. It uses this package: https://pub.dev/packages/cloud_kit.
Thanks.
I get the "Permission Failure" error ("Invalid bundle ID for container") below only when running my app from my iOS Target, and not my watchkit target. The watch app is able to sync/create/delete items, but my iOS target is unable to even read the data from the cloud. The cloudkit Container ID matches my iOS Target Bundle ID, which is the apps Bundle ID.
Looking at the output of CKContainer, the container matches the iOS Bundle ID.
I have tried reseting multiple times, creating different cloud containers, reseting the Signing and Capabilites online. It always results in the same issue... What am I missing here?
<CKError 0x281b69590: "Partial Failure" (2/1011); "Failed to modify some record zones"; uuid = D57676F8-455E-4039-9DF4-824E3BAD42BE; container ID = "iCloud.companyName.AppName"; partial errors: {
com.apple.coredata.cloudkit.zone:__defaultOwner__ = <CKError 0x281b771e0: "Permission Failure" (10/2007); server message = "Invalid bundle ID for container"; op = 8CCAD43B495AADC0; uuid = D57676F8-455E-4039-9DF4-824E3BAD42BE>
}>
If one offline device increments a counter (starting at 0) value by 5 and a second offline device decrements the counter by 2, what can I do so that the final counter value is 3 (once the devices come back online)?
I have read about operational transformations for conflict resolution. Is there any example code I can look at that implements it?