Swift CoreData编辑/更新实体,只有一个实例

Mar*_*k L 1 core-data ios swift

这个问题似乎已经被问了很多.但我找不到快速解决方案.

我有一个名为"用户"的实体,它应该只有一个.因此,它是第一次创建后.我需要编辑/更新它的值,而不是创建更多.

我确实在youtube上找到了一个视频.这是下面提供的尝试.但它不起作用.

这是我之前在viewController类中的代码 viewDidLoad()

// Core data setup
let context = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext!
var newUser : User? = nil
var results = NSArray()

var hasAccount = Bool()
Run Code Online (Sandbox Code Playgroud)

这是我的代码 viewDidLoad()

的println(NEWUSER)

    // Core data fetching
    var request = NSFetchRequest(entityName: "User")
    request.returnsObjectsAsFaults = false
    results = context.executeFetchRequest(request, error: nil)!
    if results.count > 0 {
        hasAccount = true
        var res = results[0] as NSManagedObject
        println(res.valueForKeyPath("email")!)
    }
    else {
        println("No Account")
    }
Run Code Online (Sandbox Code Playgroud)

我使用上面的代码,来确定User实体中是否已存在某些内容(然后设置hasAccount为"true").但是,如果你碰巧知道更好的方法来做到这一点.非常感谢!

我打印出"newUser",希望我能打印出User对象.所以我可以检查if newUser != nil { do this }但是我在上述解决方法之外获取"用户"的所有尝试都失败了.

现在,我无法以任何方式开展工作的代码是:

let ent = NSEntityDescription.entityForName("User", inManagedObjectContext: context)

if hasAccount == false {
                    println("Creating a new User Object")
                    let newUser = User(entity: ent!, insertIntoManagedObjectContext: context)
                    newUser.firstName = firstNameTextField.text
                    newUser.lastName = lastNameTextField.text
                    newUser.email = emailTextField.text
                    newUser.password = passwordTextField.text
                    newUser.accountType = 0
                    context.save(nil)
                } else {
                    println("Updating existing User Object")
                    newUser?.firstName = firstNameTextField.text
                    newUser?.lastName = lastNameTextField.text
                    newUser?.email = emailTextField.text
                    newUser?.password = passwordTextField.text
                    newUser?.accountType = 0
                    context.save(nil)
                }
                println(newUser)
Run Code Online (Sandbox Code Playgroud)

正在创建新User对象的部分.应该管用.(但是因为我把它移到if语句中未经测试)

但是应该更新实体的部分不起作用.我知道它的运行是由于println.

但它不会改变用户实体.

任何帮助将不胜感激!

Dan*_*sko 5

首先让我说Core Data是一个非常强大的框架,用于持久化和维护对象图.不幸的是,它确实需要大量的努力才能开始.出于这个特殊原因,我通常建议初学者看看MagicalRecord.这是一个令人愉快的库,可以删除几乎所有需要设置堆栈以及维护和创建上下文的样板代码.

Mattt Thompson(我确实想念他的写作)写了一篇有关何时应该考虑CoreData与NSKeyedArchiver等的有见地的帖子.总是很方便保持参考.

我把我的解决方案分成两部分,在第一部分我回顾了你的原始问题,只是为了给你一些基本的指示.最后一部分是我处理MagicalRecord,因为我觉得它可能更适合你,因为它的学习曲线较轻.毕竟我们在这里运送应用程序,随着您深入了解iOS,学习核心数据.

问题回顾

在我深入研究之前,我想探讨一些我们可以通过您的解决方案纠正的错误,这样您就可以理解为什么它不起作用了.

// Core data setup
let context = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext!
Run Code Online (Sandbox Code Playgroud)

我知道Apple将上下文放入AppDelegate,但它是一个非常混乱的黑客,我通常会发现最好在某个地方包含一个CoreData单例来包装你的上下文.很高兴能够忠实于单一责任原则,App Delegate负责处理App Events并实例化ViewController层次结构.不用于管理核心数据堆栈.

var newUser : User? = nil
var results = NSArray()
Run Code Online (Sandbox Code Playgroud)

这很快,为什么不var results:[AnyObject] = []

var hasAccount = Bool()
Run Code Online (Sandbox Code Playgroud)

你的意思是,var hasAccount = false....

但是让我们快进,你不需要预先定义所有这些变量,任何阅读你代码的人都必须扫描这么多行才能真正找到你想要实现的目标.让我们清理它:

// Core data fetching
let userRequest = NSFetchRequest(entityName: "User")
userRequest.fetchLimit = 1
if let user = context.executeFetchRequest(request, error: nil)?.first as? User {
   //now we have a user
} else {
   //no user
}
Run Code Online (Sandbox Code Playgroud)

新手没有意识到的一个重要问题是NSManagedObjectContext保存操作只能节省一个级别.如果该上下文直接从持久存储中下降,那么您的更改将保存到磁盘,但如果不是,则该保存需要以递归方式调用该上下文父项的保存等.这是文档:

如果上下文的父存储是持久性存储协调器,则会将更改提交到外部存储.如果上下文的父存储是另一个受管对象上下文,则save:仅更新该父存储中的受管对象.要提交对外部存储的更改,必须将更改保存在上下文链中,包括父级为持久性存储协调器的上下文.

因此,如果您的上下文有一个parentContext,那么您的保存操作永远不会将用户保存到磁盘:)

输入MagicalRecord

Magical Record入门非常简单:

MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreNamed(MyCoreDataStoreName)
Run Code Online (Sandbox Code Playgroud)

这通常在您的App Delegate中.他们还在大多数Core Data类上拥有大量令人惊叹的类别,用于常见的Core Data操作,以及一些经过深思熟虑的基础架构,用于处理多线程问题.

所以现在你想在你的数据库中建立一个只应该有一个记录.在开始之前,必须确保只有一个对象负责访问此用户属性,或者至少确保只有一个对象可以创建此用户属性.

为简单起见,我们可以将此类称为UserManager.用户管理器将负责管理我们希望与用户执行的所有操作.现在它只需要确保在我们访问它时,数据库中总有一个.

class UserManager {
    class var sharedManager : UserManager {
        struct Static {
            static let instance : UserManager = UserManager()
        }
        return Static.instance
    }

    func currentUser() -> CurrentUser {
        if let user = CurrentUser.MR_findFirst() as? CurrentUser {
            return user
        } else {
            let user = CurrentUser.MR_createEntity() as CurrentUser
            user.managedObjectContext!.MR_saveToPersistentStoreAndWait()
            return user
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们有一个单例,它将始终保证我们在数据库中有一个用户.您还提到您希望能够更新您的用户,可以将一个简单的实现添加到我们的UserManager:

func updateUser(userUpdateHandler: ((user: CurrentUser) -> Void)) {
    MagicalRecord.saveWithBlock { (context) -> Void in
        let user = self.currentUser()
        userUpdateHandler(user: user)
        //as the app grows you could probably post a notification
        //here so any interested parties could update their info
        //if the user changes....
    }
}
Run Code Online (Sandbox Code Playgroud)

从您这样调用View Controller是微不足道的:

    //the updateUser function isn't called
    //on the main thread so we need to capture
    //the values from the UI so we can safely use
    //them in the background
    let firstName = firstNameTextField.text
    let lastName = lastNameTextField.text
    let email = emailTextField.text
    let password = passwordTextField.text
    UserManager.sharedManager.updateUser { (user) -> Void in
        user.firstName = firstName
        user.lastName = lastName
        user.email = email
        user.password = password
        user.accountType = 0
    }
Run Code Online (Sandbox Code Playgroud)

瞧,您现在已经实现了一套非常标准的功能来处理应用中的用户.