将字符串数组保存到CoreData

9

我正在尝试将一个包含6个字符串的数组保存到CoreData中,代码如下:

     let imageUrls = ["image-url....", "image-url....", ..., "image-url"]

我已经尝试了在如何将数组保存到CoreData?最佳实践? - 数组/字典作为Core Data实体属性中提到的所有方法,但都没有成功。

这是我正在尝试的方法。

在我的.xcdatamodeld文件中,我声明了imageUrl作为可转换类型,请参见链接中的图片(无法附加到帖子中,对此我很抱歉!):https://drive.google.com/file/d/1VJey55oD9KhOy1KDy59h8PweMMQnaK2-/view?usp=sharing

在我的NSManagedObject类中,我有:

    @objc(Entity)
    public class Entity: NSManagedObject {
    @NSManaged public var imageUrls: [String]?
    ....

这是我在保存到CoreData之前创建实体的方法:
    entity.imageUrls = ["test", "test", "test"]

当我尝试获取图像URL时,获取的实体entity.imageUrls值为nil。我不理解这个。期望的结果是与我保存到CoreData中的相同数组。

1
你在模型的属性中将自定义类设置为[String]了吗? - Joakim Danielson
是的,我做了,忘记提到了! - Casper Lindberg
也许你需要清理并重新构建你的项目。我刚刚创建了一个简单的测试项目,使用Transformable并设置自定义类,它可以正常工作。 - Joakim Danielson
我忘记了我必须清理CoreData中的所有空对象... 它起作用了。非常感谢! - Casper Lindberg
嘿,@CoderOgden,我遇到了完全相同的问题。我尝试清理构建文件夹并重新构建,但结果仍然相同。你能否进一步解释一下你是如何解决这个问题的? - unknown
7个回答

12

我的建议是将数组编码/解码为JSON并使用计算属性。从Swift的角度来看,这可能比使用transformable属性更有效,因为后者会将数组桥接到Objective-C运行时,并使用较重的NSCoding

@NSManaged public var imageUrls: String

var urls : [String] {
    get {
        let data = Data(imageUrls.utf8)
        return (try? JSONDecoder().decode([String].self, from: data)) ?? []
    }
    set {
        guard let data = try? JSONEncoder().encode(newValue),
            let string = String(data: data, encoding: .utf8) else { imageUrls = "" }
        imageUrls = string
    }
}

另一个好处是JSON字符串可被搜索。


搜索性能很好,感谢您的观点。 - Joe Pinsonault
可变形也是可以搜索的。所以,这个论点是无效的。 - undefined

5

请查看附件中的".xcdatamodelId"文件的声明。然后在CoreDataProperties.swift文件中声明以下行:

@NSManaged public var thumbnail: [String]?

现在,我们可以将字符串数组初始化为缩略图变量。在xcdatamodelID文件中声明变量


2
为什么使用Array<Any>而不是Array<String>? - Joakim Danielson
1
我们可以使用Array<String>,这也是可行的。我只是使用了Array<Any>,这样我就不需要每次更新模型了。 - Pawan Kumar
对于我的情况,我必须使用非可选类型。 - William Hu
1
此外,将 NSSecureUnarchiveFromData 设置为值转换器(已重命名为“Transformer”)。否则,你可能会收到这个运行时警告:"NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release"。 - Manabu Nakazawa

1

Swift 5

我发现vadian的答案非常有用。我采用了相同的思路,但是用了不同的方法。分享我的代码,希望能对你有所帮助。

  • 选择要将数组存储为字符串的属性类型

enter image description here

// 保存到核心数据

var quizManagedObject : [NSManagedObject] = []

func save(question: String, answers: [String]) {

    guard let data = try? JSONEncoder().encode(answers),
    let answersEncodedString = String(data: data, encoding: .utf8) else { return }

    guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
    let managedContext = appDelegate.persistentContainer.viewContext

    let entity = NSEntityDescription.entity(forEntityName: "Quiz", in: managedContext)!
    let quiz = NSManagedObject(entity: entity, insertInto: managedContext)

    quiz.setValue(question, forKeyPath: "question")
    quiz.setValue(answersEncodedString, forKeyPath: "answers")

    do {
        quizManagedObject.append(quiz)
        try managedContext.save()
    } catch let error as NSError {
        print("Could not save. \(error), \(error.userInfo)")
    }
}

// 从核心数据获取

func fetchCoreData() {

    guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
    let managedContext = appDelegate.persistentContainer.viewContext
    let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Quiz")

    do {
        quizManagedObject = try managedContext.fetch(fetchRequest)

        for i in quizManagedObject {
            if let decodedAnswerString = i.value(forKey: "answers") as? String {
                let data = Data(decodedAnswerString.utf8)
                let answerArray = try? JSONDecoder().decode([String].self, from: data)
                let question = i.value(forKey: "question") as! String

                print("Q : ", question)
                print("A : ", answerArray ?? [""])
            }
        }
    } catch let error as NSError {
        print("Could not fetch. \(error), \(error.userInfo)")
    }
}

1
你需要将它保存为字符串并使用分隔符拆分数组,但要确保字符串不包含该分隔符。
例如,如果你在将数组保存到核心数据时使用了分隔符", ",则需要像这样保存:
coreData.arrayToStringValue = array.joined(separator: ", ")

当您读取数据时,您需要像这样读取:

let arrayFromStringValue:[String] = coreDataValue.split(separator: ", ")

2
嗨,谢谢你的回答!那是个好主意,但这只是一个变通方法。我可能会采用这个解决方案,但我也很想知道为什么我的方法不起作用... - Casper Lindberg

1
将imageUrl的类型更改为BinaryData,然后将您的列表转换为数据并保存。
let urls = NSKeyedArchiver.archivedData(withRootObject: imageUrls)
manageObject.setValue(urls, forKey: "imageUrls")

0

如果您的对象可以在没有任何自定义类的情况下进行转换,您可能希望将其强制转换为[NSObject]并以此方式保存,因为它是可转换的基础类型。但您可能应该将其设置为[String]作为基类。

还有没有证据表明您曾将数据保存到核心数据中?您所展示的只是在模型中分配一个变量。这样做实际上是不应该的,因为它们应该是不可变的,而不是可变的。


-6

以下是我们可以存储的数据类型。

enter image description here

你不能存储数组数据。你需要将其转换为逗号分隔的字符串,然后尝试将其保存到coredata中。在获取时,你可以将其转换为array


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接