Posts

Post not yet marked as solved
4 Replies
599 Views
This possibly seems like a regression but from iOS 17.1.0+, I'm having issues from Xcode 15.2 Beta &where when using a transformable property I'm getting a crash when trying to create a model container. This worked fine for me in Xcode 15.1 Beta when testing on iOS OS 17.0.1 and below. I have a simple model where I'm trying to save a UIColor, below is an example of this model. class Category: Codable { @Attribute(.unique) var title: String var items: [Item]? @Attribute(.transformable(by: ColorValueTransformer.self)) var color: UIColor? init(title: String = "", color: UIColor) { self.title = title self.color = color } enum CodingKeys: String, CodingKey { case title } required init(from decoder: Decoder) throws { ... } func encode(to encoder: Encoder) throws { ... } } Within my value transformer, I'm handling setting and getting the value. final class ColorValueTransformer: ValueTransformer { static let name = NSValueTransformerName(rawValue: String(describing: ColorValueTransformer.self)) override func transformedValue(_ value: Any?) -> Any? { guard let color = value as? UIColor else { return nil } do { let data = try NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: true) return data } catch { return nil } } override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? Data else { return nil } do { let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) return color } catch { return nil } } public static func register() { let transformer = ColorValueTransformer() ValueTransformer.setValueTransformer(transformer, forName: name) } } Then within my root app entry point, I register this transformer. @main struct ToDosApp: App { ...... init() { ColorValueTransformer.register() } ...... Unfortunately in my custom container object I get a crash on this line let container = try ModelContainer(for: ....) With an error of Thread 1: EXC_BAD_ACCESS (code=1, address=0x0) Like i said before previously this was working fine but now it's not... I have a feedback open also FB13471979 but it would be great if someone on the SwiftData team at Apple could look into this issue since it's a pretty big regression...
Posted
by tundsdev.
Last updated
.
Post not yet marked as solved
4 Replies
1.6k Views
Like the title says, I've realised that when I try to use filter or sort on properties that aren't standard supported data types i.e. Using a transformable or a value type like an enum, I seem to be getting the following crash... SwiftData/DataUtilities.swift:1140: Fatal error: Unexpected type for Expansion: Optional<UIColor> Xcode expands and shows me when trying to access the wrapped value it's crashing. I'm assumung that the query property wrapper can't handle these custom data types @Query private var items: [Item] { get { _items.wrappedValue <--- Crash here } } Which seems to be pointing to a transferable property in one of my models. Below are my two models i'm using. enum Priority: Int, Codable, Identifiable, CaseIterable { case low case medium case high var title: String { switch self { case .low: return "Low" case .medium: return "Medium" case .high: return "High" } } var image: Image? { switch self { case .medium: return Image(systemName: "exclamationmark.2") case .high: return Image(systemName: "exclamationmark.3") default: return nil } } var id: Self { self } } @Model final class Item: Codable { var title: String @Attribute(originalName: "timestamp") var dueDate: Date var isCompleted: Bool var isFlagged: Bool = false var isArchived: Bool = false var isCritical: Bool? var priority: Priority? @Relationship(deleteRule: .nullify, inverse: \Category.items) var category: Category? @Attribute(.externalStorage) var image: Data? enum CodingKeys: String, CodingKey { case title case timestamp case isCritical case isCompleted case category case imageName } init(title: String = "", dueDate: Date = .now, priority: Priority? = nil, isCompleted: Bool = false) { self.title = title self.dueDate = dueDate self.priority = priority self.isCompleted = isCompleted } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.title = try container.decode(String.self, forKey: .title) self.dueDate = Date.randomDateNextWeek() ?? .now self.isCompleted = try container.decode(Bool.self, forKey: .isCompleted) self.category = try container.decodeIfPresent(Category.self, forKey: .category) if let imageName = try container.decodeIfPresent(String.self, forKey: .imageName), let imageData = UIImage(named: imageName) { self.image = imageData.jpegData(compressionQuality: 0.8) } if let isCritical = try container.decodeIfPresent(Bool.self, forKey: .isCritical), isCritical == true { self.priority = .high } } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(title, forKey: .title) try container.encode(dueDate, forKey: .timestamp) try container.encode(isCompleted, forKey: .isCompleted) try container.encode(category, forKey: .category) } } @Model class Category: Codable { @Attribute(.unique) var title: String var items: [Item]? @Attribute(.transformable(by: ColorValueTransformer.self)) var color: UIColor? init(title: String = "", color: UIColor) { self.title = title self.color = color } enum CodingKeys: String, CodingKey { case title } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.title = try container.decode(String.self, forKey: .title) self.color = UIColor(possibleColors.randomElement()!) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(title, forKey: .title) } } And below is an example of me sorting based on my enum (Priority) & Relationship (Category name) func sort() -> [SortDescriptor<Item>]{ switch self { case .title: [SortDescriptor(\Item.title)] case .date: [SortDescriptor(\Item.dueDate)] case .category: [SortDescriptor(\Item.category?.title)] case .priority: [SortDescriptor(\Item.priority?.rawValue)] } } And a filter example below creating a predicate that we will execute to return and matches found in the title or category title let highPriority = Priority.high if let query { return #Predicate { $0.priority == highPriority && ($0.title.contains(query) || $0.category?.title.contains(query) == true) && $0.isArchived == false } } I'm pretty sure this is a SwiftData bug since when using strings, bools and dates it's all fine using anything outside of that box causes these crashes...
Posted
by tundsdev.
Last updated
.
Post not yet marked as solved
1 Replies
914 Views
I may have found a bug within Xcode 15.0.1. I am working with CoreLocation in order to access a user's location. I have all the code working and the permissions needed within the info.plist. Below is a checklist of everything that I have. Info.plist I don't need to but I added all 3 keys into my project's info.plist to explain the reason why I need access to the user's location: NSLocationAlwaysAndWhenInUseUsageDescription NSLocationWhenInUseUsageDescription NSLocationAlwaysUsageDescription This was just to cover all basis. Requesting Permission After doing that I have a simple class that requests access to the users location. @Observable class LocationPermissionManager: NSObject { private let manager = CLLocationManager() override init() { super.init() manager.delegate = self } func requestAuthorisation() { manager.requestWhenInUseAuthorization() } } Nothing too wild just a simple class to ask for authorisation permission. When I create an instance of this class and call the function to request authorisation. I get the system dialogue to ask for permission and I'm able to select an option. The Problem The issue starts to happen after. When I go to the settings on the simulator the app doesn't show up or appear. But if I go to the settings on my actual iPhone the app does show up and appear and i'm able to change the different permissions you want to give the app. Requesting Permission Something weird I did find is a work around to make your app appear in the settings. And it involves you requesting permission for another API i.e ATT (App Tracking Transparency) this does work and you're able to see your app appear in the settings on the simulator and you'll see both the ATT & Location permission options as well. It's worth nothing that I'm using SwiftUI for this and this possible bug might be a big one since if someone doesn't have a physical iPhone they're not able to change the permissions for their location on the simulator. Could someone in Apple please investigate this issue?
Posted
by tundsdev.
Last updated
.
Post marked as solved
4 Replies
1.5k Views
I've been stuck on this one for quite a while now, and I'm starting to become more and more convinced this is an issue with Xcode 15 Beta RC. Just to give you some context I seem to be getting the following crash after modifying my schema (adding new properties) for the the second time. Below is the crash that is occurring on my device & simulator. Unresolved error loading container Error Domain=NSCocoaErrorDomain Code=134130 "Persistent store migration failed, missing source managed object model." UserInfo={URL=file:///Users/xxxxx/Library/Developer/CoreSimulator/Devices/23B8CDDD-CC5F-4A1C-B0F4-CF89C77B7ECF/data/Containers/Data/Application/235A14D7-6492-439F-BB4D-B18498D80970/Library/Application%20Support/default.store, metadata={ NSPersistenceFrameworkVersion = 1327; NSStoreModelVersionChecksumKey = "dia3s8Q2+lqw669j9+RcPLQ+06yu0x6BBTZ4cXoQ1os="; NSStoreModelVersionHashes = { Category = {length = 32, bytes = 0x187754bb 36c51a62 85ede16f 4b2a3912 ... 57326030 2de7ef77 }; Item = {length = 32, bytes = 0xa7e4be4d ddd86d36 f71799b0 bc69dcb4 ... 83d47dfe d433fc01 }; }; NSStoreModelVersionHashesDigest = "G/Tk4lzyeNBXzf5+7qxbd+isF8uFnSaC5LtUCCkC8GQwaG1d9Di0eJ10NQEyPgwRczoYeYAMYG8ai4RooEhH9w=="; NSStoreModelVersionHashesVersion = 3; NSStoreModelVersionIdentifiers = ( "1.2.0" ); NSStoreType = SQLite; NSStoreUUID = "A99894EA-FA7B-4CA7-AEB7-6DEE42843EC0"; "_NSAutoVacuumLevel" = 2; }, reason=Can't find model for source store} I currently have 5 versions of my Schemas there's two of them Item Category Below is a minified changelog of what has changed between versions. Version 1 (Initial Version) Version 2 (Lightweight Migration - Property is renamed in Item) Version 3 (Custom Migration - New Property is added to Item both are bools) Version 4 (Custom Migration - New Property is added to Category which is a transformable) Version 5 (Custom Migration - New Property is added to Item which is a string) It's quite a large file with all of the version schema's etc so i've created a gist here for you to see all of the changes that have been made between Version 1 up until Version 5. The problem that I'm seeing is that between migration for V1 up until V4 everything is fine. It's only until SwiftData attempt to migrate V4 to V5 and I get the crash that I have provided above, and V5 only has a new string property which shouldn't be causing a crash since I've done 2 custom migrations that were all fine before V5 so this seems really strange even tho my migration plan is setup properly which you can see below. enum ToDosMigrationPlan: SchemaMigrationPlan { static var schemas: [VersionedSchema.Type] { [ToDosSchemaV1.self, ToDosSchemaV2.self, ToDosSchemaV3.self, ToDosSchemaV4.self, ToDosSchemaV5.self] } static var stages: [MigrationStage] { [ migrateV1toV2, migrateV2toV3, migrateV3toV4, migrateV4toV5 ] } // V1 to V2 static let migrateV1toV2 = MigrationStage.lightweight( fromVersion: ToDosSchemaV1.self, toVersion: ToDosSchemaV2.self ) // V2 to V3 static let migrateV2toV3 = MigrationStage.custom( fromVersion: ToDosSchemaV2.self, toVersion: ToDosSchemaV3.self, willMigrate: nil, didMigrate: { context in let items = try? context.fetch(FetchDescriptor<ToDosSchemaV3.Item>()) items?.forEach { item in item.isFlagged = false item.isArchived = false } try? context.save() }) static let migrateV3toV4 = MigrationStage.custom( fromVersion: ToDosSchemaV3.self, toVersion: ToDosSchemaV4.self, willMigrate: nil, didMigrate: { context in let categories = try? context.fetch(FetchDescriptor<ToDosSchemaV4.Category>()) categories?.forEach { category in category.color = UIColor(possibleColors.randomElement()!) } try? context.save() }) static let migrateV4toV5 = MigrationStage.custom( fromVersion: ToDosSchemaV4.self, toVersion: ToDosSchemaV5.self, willMigrate: nil, didMigrate: { context in // TODO: Handle setting some custom data here with defaults }) } Has anyone come across this or got any ideas as to what the problem may be?
Posted
by tundsdev.
Last updated
.
Post not yet marked as solved
5 Replies
2.2k Views
It seems that when you delete model objects with a relationship there seems to be a crash occurring if the collection your observing is tied to a ForEach. Below is an example of the models in SwiftData @Model class Category { @Attribute(.unique) var title: String var items: [Item]? init(title: String = "") { self.title = title } } The relationship is defined between the category and the item @Model final class Item { var title: String var timestamp: Date var isCritical: Bool var isCompleted: Bool @Relationship(.nullify, inverse: \Category.items) var category: Category? init(title: String = "", timestamp: Date = .now, isCritical: Bool = false, isCompleted: Bool = false) { self.title = title self.timestamp = timestamp self.isCritical = isCritical self.isCompleted = isCompleted } } So if I were to create a category individually so i can later tag it to an item i'm just following the standard proceedure of creating a category and inserting it into the context as you can see below Button("Add Category") { let category = Category(title: title) modelContext.insert(category) } .disabled(title.isEmpty) Then later on when i create my item I associated an existing category with an item and insert that into the context so now there is a possibility to have many items associated to a single category as you can see below @State var item = Item() // Item is bound to form elements .... Button("Create") { modelContext.insert(item) item.category = selectedCategory.title == "None" ? nil : selectedCategory dismiss() } This is where the problem occurs... If i try to delete a category now after inserting it @Query private var categories: [Category] .... ForEach(categories) { category in Text(category.title) .swipeActions(allowsFullSwipe: true) { Button(role: .destructive) { withAnimation { modelContext.delete(category) } } label: { Label("Delete", systemImage: "trash.fill") } } } The application crashes because the foreach is still being executed by the Query property wrapper as if the item still exists even though it has been deleted, but it's trying to access the title on an object that doesn't exist anymore. I've also noticed that when trying to insert duplicate items using the Atrribute macros this causes a crash too. It's almost as if the query collection just isn't being updated with the new values of a category being deleted. I've filed a feedback under FB12286699 (Crash when deleting items in relationships) in Feedback assistent hopefully this can get picked up.
Posted
by tundsdev.
Last updated
.