Xcode 15.2b Transformable Properties Crash App

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...

Replies

Here is an image of the crash I'm getting on app launch. A few things I've tried with no luck:

  • Removing Migration Plan, this has no effect and the issue is still there
  • Removing the model container when setting up the app, has no effect and the issue is still there

I'm convinced this is linked to using non-default types like Date with transformable. Since I've tested out a project where I have a Date as a transformable property, this doesn't crash. But if I use something from UIKit or my own custom type it crashes.

  • ToDosApp.body.getter shouldn't be initing any objects

  • It should, since i’m prefilling the database. The comment doesn’t offer any advice so please remove it to prevent causing confusion.

Add a Comment

I’m seeing the same behavior on the released version of Xcode 15.2. It works just fine on the iOS 17.0 simulator, but 17.2 crashes on launch with "EXC_BAD_ACCESS".

hey it's actually quite easy you just have to override transformedValueClass() -> AnyClass as

    override class func transformedValueClass() -> AnyClass {
        return NSData.self
    }

and then simply return NSData from transformedValue(_ value: Any?) -> Any? as shown below

 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 as NSData
    } catch {
        return nil
    }
}

and while doing reverse transform you have to do one additional casting from NSData to Data then you good to go

override func reverseTransformedValue(_ value: Any?) -> Any? {
    guard let nsData = value as? NSData else { return nil }
    let data = nsData as Data
    do {
        let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data)
        return color
    } catch {
        return nil
    }
}
Add a Comment

Actually now I'm facing the problem described here https://developer.apple.com/forums/thread/743142