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 8 Swift NSFetchResult 谓词无法访问内部 ID