Storing a Custom Transformable Array in Core Data Persistently

As an example, in my Core Data model, I have created an entity called Question.


So I have the following 'Question+CoreDataproperties.swift' file. One of the attributes is mediaFiles, stored as a [String].


import Foundation
import CoreData

extension Question {

...
@NSManaged public var mediaFiles: [String]

}


I have set this attribute as a transformable. Then, in the inspector of the mediaFile attribute, under the 'Custom Class' field, I set [String] as well.


I am able to save the attribute after adding some info with no errors. However, when closing and re-entering the app, the information is lost. All other non-transformable attribites (String, Int32, etc) managed to be saved persistently. So the transformable attribute is somehow not storing persistently?


I have read a few other forums showing the use of 'Class NSObject, NScoder' for such transformable data to store them persistently.


However, this is only useful when the 'Custom Class' field in the inspector is set to the default NSObject? Any other ideas on how to go around this problem?

Replies

Sorry I meant 'class NSObject, NSCoding' in the second last line.

I have managed to find the solution on Youtube. https://www.youtube.com/watch?v=Ema17hA0fGQ


Basically, you would still need to transform the string array into coredata readable data using a ValueTransformer class. Details of how to create it are in the video.


Then, under the inspector of the mediaFile attribute, key in the name of the ValueTransformer class you have just created in the 'Value Transformer Name' field.

I tried the solution in the video and it didn't work for me. I have the following setup in my .xcdatamodeld file

I'm trying to work with an array of strings for the notificationUuids property. I'm transforming the values like this

class AttributedStringToDataTransformer: ValueTransformer {
    
    override func transformedValue(_ value: Any?) -> Any? {
        let boxedData = try! NSKeyedArchiver.archivedData(withRootObject: value!, requiringSecureCoding: true)
        return boxedData
    }
    
    override func reverseTransformedValue(_ value: Any?) -> Any? {
        let typedBlob = value as! Data
        let data = try! NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSString.self], from: typedBlob)
        return (data as! String)
    }
    
}

which is almost exactly like it was in the video. I'm trying to store the notificationUuids as an array of [String] and have everything set up to transform from an NSString to a String but it doesn't work. When an Alarm is stored in Core Data with one notificationUuid, everything works as expected. But when it's got more than one notificationUuid, like in the case that recurrence is set to .daily, then nothing gets saved in the notificationUuids array and when that array is fetched during loading it is empty. Why is this?

Hi Hoid,


Sorry for the late reply.


override func reverseTransformedValue(_ value: Any?) -> Any? {  
        let typedBlob = value as! Data  
        let data = try! NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSString.self], from: typedBlob)  
        return (data as! String)  
    } 

In the above, you declared data to be of class NSString. There could be a conflict when you tried to return String instead of NSString? And you should be returning [String] instead of String if your intention is to store as an array. If you archive it as an array, you should unarchive and return an array.


Try this instead [with usage of unarchivedObject(with:...) method]:

class AttributedStringToDataTransformer: ValueTransformer {  
    override func transformedValue(_ value: Any?) -> Any? {  
        let boxedData = NSKeyedArchiver.archivedData(withRootObject: value!)  
        return boxedData  
    }  
    override func reverseTransformedValue(_ value: Any?) -> Any? {  
        let typedBlob = value as! Data        
        let data = NSKeyedUnarchiver.unarchivedObject(with: typedBlob)  
        return (data as! [String])  
    }  
}