我可以使用Core Data替代用户默认值吗?

时间:2018-02-16 06:19:27

标签: swift core-data userdefaults

我使用swift编写了一些应用程序,我使用用户默认值保存了属性,随着我开始使用更多变量,代码变得有点长。我现在正在开发一个具有6个属性且有100个播放器的播放器类的应用程序,所以不是为用户默认编写600个保存和600个加载行,我想尝试使用CoreData和SQLite来保存数据。 / p>

我开始创建一个较小的概念证明应用程序,以确保我可以为2名玩家保存并加载player.name和player.attack,以保持简单,我遇到了麻烦。我尝试了以下内容:

main View Controller

主视图控制器的示例代码如下

import UIKit
import CoreData
import GameplayKit

var player = Character(context: CoreDataStack.context)
var monster = Character(context: CoreDataStack.context)

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var statsLabel: UILabel!
    @IBOutlet weak var playerAttackButton: UIButton!
    @IBOutlet weak var playerNameField: UITextField!
    @IBOutlet weak var playerHealthButton: UIButton!

    @IBOutlet weak var montserAttackButton: UIButton!
    @IBOutlet weak var monsterNameField: UITextField!
    @IBOutlet weak var monsterHealthButton: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        fetchData()

        if player.name == nil {player.name = ""}
        if monster.name == nil {monster.name = ""}

        updateStatsLabel()

        // Dismiss keyboard on return when typing character name
        //Looks for single or multiple taps.
        let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))

        view.addGestureRecognizer(tap)
        self.playerNameField.delegate = self
        self.monsterNameField.delegate = self

    }

    // MARK: Fetching Data
    func fetchData() -> Void {

        let moc = CoreDataStack.context
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Character")

        do {
            let fetchedPerson = try moc.fetch(fetchRequest) as! [Character]

            print(fetchedPerson.count)
            for object in fetchedPerson {
player.name = object.name
                print(object.name!)
            }

        } catch {
            fatalError("Failed to fetch characters: \(error)")
        }

    }



    // MARK: Delete Data Records

    func deleteRecords() -> Void {
        let moc = CoreDataStack.context
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Character")

        let result = try? moc.fetch(fetchRequest)
        let resultData = result as! [Character]

        for object in resultData {
            moc.delete(object)
        }

        do {
            try moc.save()
            print("saved!")
        } catch let error as NSError  {
            print("Could not save \(error), \(error.userInfo)")
        } catch {

        }

    }

    // MARK: Update Data
    func updateRecords() -> Void {
        let moc = CoreDataStack.context

        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Character")

        let result = try? moc.fetch(fetchRequest)

        let resultData = result as! [Character]
        for object in resultData {
            object.name! = player.name!
            print(object.name!)
        }
        do{
            try moc.save()
            print("saved")
        }catch let error as NSError {
            print("Could not save \(error), \(error.userInfo)")
        }


    }


    @IBAction func deleteButton(_ sender: Any) {
        deleteRecords()
    }
    @IBAction func saveButton(_ sender: Any) {
        fetchData()
        updateRecords()
    }

    @objc func dismissKeyboard() {
        player.name = playerNameField.text!
        monster.name = monsterNameField.text!
        updateStatsLabel()
        //Causes the view (or one of its embedded text fields) to resign the first responder status.
        view.endEditing(true)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        self.view.endEditing(true)
        return false
    }

    @IBAction func playerRollAttackButton(_ sender: Any) {
        let random = GKRandomSource()
        let roll = GKGaussianDistribution(randomSource: random, lowestValue: 1, highestValue: 20)
        player.attack = Int64(roll.nextInt())
        updateStatsLabel()
    }

    @IBAction func playerRollHealth(_ sender: Any) {
        let random = GKRandomSource()
        let roll = GKGaussianDistribution(randomSource: random, lowestValue: 1, highestValue: 8)
        player.health = Int64(roll.nextInt())
        updateStatsLabel()
    }

    @IBAction func monsterRollAttackButton(_ sender: Any) {
        let random = GKRandomSource()
        let roll = GKGaussianDistribution(randomSource: random, lowestValue: 1, highestValue: 12)
        monster.attack = Int64(roll.nextInt())
        updateStatsLabel()
    }

    @IBAction func monsterRollHealth(_ sender: Any) {
        let random = GKRandomSource()
        let roll = GKGaussianDistribution(randomSource: random, lowestValue: 1, highestValue: 20)
        monster.health = Int64(roll.nextInt())
        updateStatsLabel()
    }

    func updateStatsLabel() {
        statsLabel.text = "\(player.name!) health: \(player.health)\n\n\(player.name!)'s attack: \(player.attack)\n\n\(monster.name!) health: \(monster.health)\n\n\(monster.name!)'s attack: \(monster.attack)"
    }

}

对于CoreDataStack视图控制器:

import CoreData

class CoreDataStack {

    static let persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "CoreDataSample")
        container.loadPersistentStores { (_, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        }
        return container
    }()

    static var context: NSManagedObjectContext { return persistentContainer.viewContext }

    class func saveContext () {
        let context = persistentContainer.viewContext

        guard context.hasChanges else {
            return
        }

        do {
            try context.save()
        } catch {
            let nserror = error as NSError
            fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
        }
    }
}

我希望我能够为播放器和持有于player.name和monster.name变量的怪物指定一个名称,使用CoreData保存它们,然后将它们加载回变量中app打开下一个(其他变量也一样)。更改名称将导致CoreData模型中的覆盖,使旧数据丢失并被新数据替换。我遇到了以下困难:

  • 保存似乎导致创建多个记录,但不是每次保存时,只有每次关闭并打开应用程序时都会保存。
  • 当我第一次打开应用程序时,正确保存的数据未加载到视图中,我认为我可以通过修改“player.name = object.name”来先修改对象,但我没有因为我上面的项目有问题,所以能够测试。
  • 删除记录会产生以下错误: CoreDataSample [5879:362306] CoreData:错误:在从上下文中删除托管对象0xd0000000005c0000(0x604000290310)后对其进行变更。

任何帮助将不胜感激!感谢您查看我的问题:)

0 个答案:

没有答案