Swift 3 / iOS 10,正确使用核心数据

Posted

技术标签:

【中文标题】Swift 3 / iOS 10,正确使用核心数据【英文标题】:Swift 3 / iOS 10, Proper Core Data Usage 【发布时间】:2016-12-01 20:11:07 【问题描述】:

在将我的应用程序升级到 Swift 3 和 ios 10 之前,我在使用 CoreData 作为简单对象的数据存储方面没有任何问题。轻量级迁移很简单,保存和获取也很简单,等等。但是自从最近的升级以来,我一直在使用 CoreData 遇到麻烦。

我的问题分为两部分。首先,有没有人知道任何好的资源来帮助我了解 CoreData 如何在幕后工作,以便我可以更好地调试它? Apple 的文档非常有限,我阅读的所有文章都像新的 CoreData 一样简单。我在关系数据库方面有很好的经验,所以 CoreData 为我添加了一个不舒服的抽象层。

其次,下面的代码有什么问题?使用此代码,轻量级迁移不像 iOS 10 之前那样工作。对象正在保存到CoreData(保存后我可以在应用程序中与它们交互),但在应用程序重新启动后会消失。

lazy var persistentContainer: NSPersistentContainer = 

    let description = NSPersistentStoreDescription()

    description.shouldInferMappingModelAutomatically = true
    description.shouldMigrateStoreAutomatically = true

    let container = NSPersistentContainer(name: "MY_APP")
    container.persistentStoreDescriptions = [description]
    let description = NSPersistentStoreDescription()

    description.shouldInferMappingModelAutomatically = true
    description.shouldMigrateStoreAutomatically = true

    container.persistentStoreDescriptions = [description]
    container.loadPersistentStores(completionHandler:  (storeDescription, error) in
        if let error = error as NSError? 
            fatalError("Unresolved error \(error), \(error.userInfo)")
        
    )
    return container
()

// MARK: - Core Data Saving support

func saveContext () 
    let context = persistentContainer.viewContext
    if context.hasChanges 
        do 
            try context.save()
         catch 
            let nserror = error as NSError
            fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
        
    

我正在使用一个单独的文件来抽象​​我的对象的存储:

class Repository



func getContext () -> NSManagedObjectContext

    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    return appDelegate.persistentContainer.viewContext



func delete<T>(_ a: some T)


    getContext().delete(a as! NSManagedObject)




// -----------   Password Repo ----------


func savePassword(name: String, hint: String, un: String) 
    let context = getContext()

    //retrieve the entity that we just created
    let entity =  NSEntityDescription.entity(forEntityName: "Password", in: context)

    let transc = NSManagedObject(entity: entity!, insertInto: context)

    //set the entity values
    transc.setValue(name, forKey: "name")
    transc.setValue(hint, forKey: "thing")
    transc.setValue(un, forKey: "newThing")


    //save the object
    do 
        try context.save()
     catch let error as NSError  
        print("Could not save \(error), \(error.userInfo)")
     catch 

    



func updatePassword(pas: Password) -> Password

    let context = getContext()
    //        sec.is_new = false

    // TODO,  add updates

    // Try updating the model in the DB
    do 
        try context.save()
     catch 
        print(error)

    

    return pas



func fetchPasswords() -> [Password]


    let context = getContext()
    //create a fetch request, telling it about the entity
    let fetchRequest: NSFetchRequest<Password> = Password.fetchRequest() as! NSFetchRequest<Password>
    let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
    fetchRequest.sortDescriptors = [sortDescriptor]

    do 
        //go get the results
        let searchResults = try getContext().fetch(fetchRequest)
        return searchResults

     catch 
        print("Error with request: \(error)")
    

    return []





// -----------   End Password Repo ----------





// -----------   Hints Repo ----------

func saveHint (name: String, hint: String) 
    let context = getContext()

    //retrieve the entity that we just created
    let entity =  NSEntityDescription.entity(forEntityName: "Hint", in: context)

    let transc = NSManagedObject(entity: entity!, insertInto: context)

    //set the entity values
    transc.setValue(value1, forKey: "some_string")
    transc.setValue(value2, forKey: "some_thing")

    //save the object
    do 
        try context.save()
     catch let error as NSError  
        print("Could not save \(error), \(error.userInfo)")
     catch 

    





func fetchHints() -> [Hint]


    let context = getContext()
    //create a fetch request, telling it about the entity
    let fetchRequest: NSFetchRequest<Hint> = Hint.fetchRequest() as! NSFetchRequest<Hint>
    let sortDescriptor = NSSortDescriptor(key: "my_key", ascending: true)
    fetchRequest.sortDescriptors = [sortDescriptor]

    do 
        //go get the results
        let searchResults = try getContext().fetch(fetchRequest)
        return searchResults

     catch 
        print("Error with request: \(error)")
    

    return []



然后我这样称呼这个 Repository 类:

Repository().savePassword(name: nameText.text!, hint: hintSoFarLabel.text!, un: "Hey")

Repository 类正在工作,直到我重新启动应用程序...

我也在尝试迁移到我的核心数据模型的新版本,它只是添加了一个非可选的字符串属性(具有默认值),这将是 iOS 9/Swift 2 中的一个简单的轻量级迁移。什么是我错过了 swift 3 轻量级迁移?

毫无疑问,问题在于我对 iOS 10 中的 CoreData 理解不够好。我做了一段时间的软件工程师,但我只使用 iOS 和 Swift 几个月,所以请温柔。提前致谢!

【问题讨论】:

如果它让事情变得更容易,NSPersistentContainer 不是 Swift 3 的东西,也不是必需的。您可以继续使用与早期 iOS 版本中相同的类。它们仍然有效且未被弃用。此外,消失的物体与迁移无关。如果迁移现有的持久性存储失败,应用将在启动时崩溃。 @TomHarrington。谢谢,这是一个很好的建议,我会试试看。然而,这个特殊的应用程序是从 Xcode 8、Swift 3 和 iOS 10 目标开始的。所以我认为我应该坚持这些范式前进。我想弄清楚如何使用这样的应用程序进行简单的轻量级迁移。在此之前,我使用 iOS 9 编写了一个不同的应用程序,这就是我了解 iOS9 迁移与 iOS10 的方式。 我正在开发一个简单的应用程序和新的 Coredata (swift3),当我将 ShouldInferMapping... 和 ShouldMigrateStoreAu... 设置为 true 以描述并将它们设置为 container.persistenStoreDescriptions 作为 Griffin 时,我注意到每个人上面指出,我的数据不会持久。但是,当我评论这些行(将两个属性设置为 true)时,我的所有数据都回来了(仍然存在)。有什么想法吗? 【参考方案1】:

好吧,我觉得自己像个白痴。我添加了一个非可选字符串并且没有给它一个默认值。我总是给非字符串类型提供默认值,但是 Xcode 的设置方式使它看起来如果没有给出其他值,它只会给字符串一个默认值“”。

为新属性添加默认值允许迁移在 Swift3/iOS10 中工作。菜鸟的错误,但也许它会在未来帮助这里的人。

【讨论】:

以上是关于Swift 3 / iOS 10,正确使用核心数据的主要内容,如果未能解决你的问题,请参考以下文章

为 FCM 检索令牌的正确方法 - iOS 10 Swift 3

Swift 3 iOS 10 - 带有图像和文本的 UICollectionView 无法正确显示

ios、swift、核心数据+多表

具有核心数据关系的 iOS 8 Swift NSFetchResult 谓词无法访问内部 ID

View 中的 IOS Swift 3 UILabel 无法正确呈现

iOS Swift 3核心数据-尝试递归调用-save的致命错误:上下文中止