Swift中的持久化数据

4
我目前正在尝试使用Swift进行持久数据实验,但我在保存和检索数据方面遇到了麻烦。基本上我有两个文本字段,当用户按提交按钮时,输入将保存到UITable中,在这里用户可以移动表中的条目或删除它们(如果他们想这么做)。我的主要问题是保存和加载这些数据。
Taskmanager.swift - 这里我存储了我的基本类型。
import Foundation

import UIKit

var taskMgr: TaskManager = TaskManager()

struct task {
    var name = "Name"
    var year = "Year"
}


//setting data
let defaults = UserDefaults.standard
//defaults.synchronize()


//getting data

class TaskManager: NSObject {
    var tasks = [task]()

    func addTask(name: String, year: String){
        tasks.append(task(name: name, year: year))
    }
}

ThirdViewController.swift -- 这里我存储了我的表格及其功能,还有保存和加载数据函数的草图。

import Foundation

import UIKit

class ThirdViewController:UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet var tableView: UITableView!

    @IBAction func deleteT(_ sender: Any) {

        if(tableView.isEditing == true){
        tableView.setEditing(false, animated: true)
        }else{
        tableView.setEditing(true, animated: true)
        }


    }

    func saveData() {

        let data = NSMutableData()

        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        let path = paths[0]
        let file = (path as NSString).appendingPathComponent("Persistent.plist")

        //2
        let archiver = NSKeyedArchiver(forWritingWith: data)
        archiver.encode(G, forKey: "name")
        archiver.endode(year, forKey: "year")
        archiver.finishEncoding()
        data.write(toFile: file, atomically: true)
    }


    func loadData() {
        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        let path = paths[0]
        let file = (path as NSString).appendingPathComponent("Persistent.plist")

        // 1
        if FileManager.default.fileExists(atPath: file) {
            if let data = NSData(contentsOfFile: file) {
                let unarchiver = NSKeyedUnarchiver(forReadingWith: data as Data)
                name = unarchiver.decodeObjectForKey("name") as! [String]
                 year = unarchiver.decodeObjectForKey("year") as! [String]


                unarchiver.finishDecoding()
            }
        }
    }





     func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
        return true
    }

     func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
        return false
    }

     func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {


    }

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.reloadData()
        loadData()
    }

    override func viewWillAppear(_ animated: Bool) {
        self.tableView.reloadData()
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
        return taskMgr.tasks.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{

        let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "TableView")

        //Assign the contents of our var "items" to the textLabel of each cell
        cell.textLabel!.text = taskMgr.tasks[indexPath.row].name
        cell.detailTextLabel!.text = taskMgr.tasks[indexPath.row].year
        //cell.editing = tableView(tableView, canMoveRowAtIndexPath: indexPath)

        return cell

    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath){
        if (editingStyle == UITableViewCellEditingStyle.delete){

            taskMgr.tasks.remove(at: indexPath.row)
            tableView.reloadData()
        }
    }
}

FourthViewController.swift -- 在这里,我有我的文本框和按钮以及如何将我的条目添加到表格中。

import Foundation

import UIKit

class FourthViewController: UIViewController, UITextFieldDelegate{

    @IBOutlet var addT: UITextField!
    @IBOutlet var addY: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    @IBAction func confTask(_ sender:UIButton){
         if (addT.text == ""){
         }else{
        //add record
        let name: String = addT.text!
        let Year: String = addY.text!
        //taskMgr.addTask(name:name)
        taskMgr.addTask(name:name, year:Year)

        }

        //dismiss keyboard and reset fields

        self.view.endEditing(true)
        addT.text = nil
        addY.text = nil

    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.view.endEditing(true)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool{
        textField.resignFirstResponder()
        return true
    }





}

请查看此链接:http://stackoverflow.com/questions/40756178/how-can-i-delete-object-from-core-data-in-swift-3/40756625#40756625,也许您可以找到解决方案。 - Raj Joshi
谢谢您的快速回复,但我正在尝试向plist文件添加数据(不一定是plist,但我认为这是最简单的方法?),并在关闭它后从那里检索它。我不完全理解您的答案,我该如何操作才能使其与我的工作相匹配?谢谢! - smarty_pants
实际上,我给了你核心数据的引用。 - Raj Joshi
每次只写入一个条目到plist中,因此其他数据将被覆盖。您需要读/写数组。为此,您需要创建一个类来存储您的名称,并将该类的实例放入数组中。您的类还需要实现“NSCoding”;您应该只使用Core Data,这样长期来看会更容易。 - Paulw11
这个应用程序可以帮助理解如何使用Core Data插入、删除和更新数据。https://github.com/AshokICreate/CoreDataNoteApp - Ashok R
尝试使用 Realm - Nikhil Manapure
1个回答

2
我已经使用NSUserDefaults创建了一些示例代码来持久化任务。只要你在进行实验并且只想拥有少于100个元素,这是一个相当简单的示例,应该可以胜任。使用下面的代码,您应该能够显示、删除和保存任务。
但是,在未来,我强烈建议您阅读更多关于Core Data的内容。有许多很棒的教程,比如这个one
我已经创建了一个Task对象模型和TaskManager用于读取、保存和删除任务。
import Foundation

// Task Data Model
class Task: NSObject, NSCoding {
    let name: String
    let year: String

    required init(name: String, year: String) {
        self.name = name
        self.year = year
    }

    required init(coder decoder: NSCoder) {
        self.name = decoder.decodeObject(forKey: "name") as? String ?? ""
        self.year = decoder.decodeObject(forKey: "year") as? String ?? ""
    }

    func encode(with coder: NSCoder) {
        coder.encode(name, forKey: "name")
        coder.encode(year, forKey: "year")
    }
}

class TaskManager {
    ///  UserDefaults instance
    private let defaults = UserDefaults.standard

    /// Singleton instance, class **should** be accessed by this property
    static let shared = TaskManager()

    /// Indetifier of tasks container in `defaults` 
    private let kTasksIdentifier = "tasks"

    /// Add a new task to your container and syncronize it into `defaults`
    ///
    /// - Parameters:
    ///   - name: Name of the task
    ///   - year: Year of the task
    func save(taskName name: String, year: String) {
        let task = Task(name: name, year: year)

        // Check if there is already saved tasks
        guard let data = defaults.value(forKey: kTasksIdentifier) as? Data, var tasks = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Task] else {
            // If not, save it as the first one
            syncronizeTasks(tasks: [task])
            return
        }

        tasks.append(task)
        syncronizeTasks(tasks: tasks)
    }

    /// Remove a task at an index
    ///
    /// - Parameters:
    ///   - index: The index of the removeable task
    func remove(at index: Int) {

        guard let data = defaults.value(forKey: kTasksIdentifier) as? Data, var tasks = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Task] else {
            fatalError("Unable to retrive tasks from defaults")
        }

        tasks.remove(at: index)
        syncronizeTasks(tasks: tasks)        
    }

    /// Read all tasks elements
    /// If there are tasks in memory, it returns the one from memory
    /// Otherwise reads it from `UserDefaults`
    ///
    /// - Returns: all tasks elements available, return empty array if no elements found
    func readAllTasks() -> [Task] {
        let data = UserDefaults.standard.value(forKey: kTasksIdentifier)
        let allTasks = NSKeyedUnarchiver.unarchiveObject(with: data as! Data) 
        return allTasks as? [Task] ?? [Task]()
    }


    private func syncronizeTasks(tasks: [Task]) {
        let data = NSKeyedArchiver.archivedData(withRootObject: tasks)
        defaults.set(data, forKey: kTasksIdentifier)
        defaults.synchronize()
    }
}

我已经稍微修改了你已有的ThirdViewController

import UIKit
import Foundation

class ThirdViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet var tableView: UITableView!
    /// Your tasks being updated in this collection every time `refreshTasks()` is being called
    private var tasks = [Task]()

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.refreshTasks()
        self.tableView.reloadData()
    }

    func refreshTasks() {
        self.tasks = TaskManager.shared.readAllTasks()
    }

    @IBAction func deleteT(_ sender: Any) {
        if(tableView.isEditing == true) {
            tableView.setEditing(false, animated: true)
        } else {
            tableView.setEditing(true, animated: true)
        }
    }

    func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
        return false
    }

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
        return tasks.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{

        let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "TableView")

        //Assign the contents of our var "items" to the textLabel of each cell
        cell.textLabel!.text = tasks[indexPath.row].name
        cell.detailTextLabel!.text = tasks[indexPath.row].year
        //cell.editing = tableView(tableView, canMoveRowAtIndexPath: indexPath)

        return cell

    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath){
        if (editingStyle == UITableViewCellEditingStyle.delete) {
            self.tableView.beginUpdates()
            TaskManager.shared.remove(at: indexPath.row)
            refreshTasks()
            self.tableView.deleteRows(at: [indexPath], with: .fade)
            self.tableView.endUpdates()
        }
    }
}

另外,为了保险起见,也编辑了您的FourthViewController

import Foundation
import UIKit

class FourthViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var addT: UITextField!
    @IBOutlet var addY: UITextField!

    /// User has pressed `Submit` button 
    ///
    /// - Parameter sender: the pressed button
    @IBAction func confTask(_ sender: UIButton) {
        // Check if textfields are containing text
        guard let nameText = addT.text, let yearText = addY.text, !nameText.isEmpty, !yearText.isEmpty else {
            print("at least one of the textFields is not filled")
            return
        }

        // Save the tasks 
        TaskManager.shared.save(taskName: nameText, year: yearText)

        //dismiss keyboard and reset fields
        self.view.endEditing(true)
        addT.text = nil
        addY.text = nil
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.view.endEditing(true)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool{
        textField.resignFirstResponder()
        return true
    }
}

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