Swift 中的持久数据

Persistent data in Swift

我目前正在 swift 中试验持久数据,但我无法保存这些数据并再次检索它。基本上我有两个文本字段,当用户按下提交按钮时,条目将保存到 UITable,在这里用户将能够移动 table 中的条目或删除它们(如果他们愿意) .我的主要问题是保存和加载这些数据。

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 -- 在这里我存储了我的table和它的功能,我也有一个粗略的保存和加载数据功能。

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 -- 这里有我的文本字段和按钮,以及我如何将我的条目添加到 table。

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
    }





}

我使用 NSUserDefaults 创建了一些示例代码来持久化任务。这是一个相当简单的示例,只要您只是在试验,并且只想拥有少于 100 个元素,就应该没问题。使用下面的代码,您应该能够显示、删除和保存任务。

但是,在未来,我强烈建议您阅读更多 Core Data. There are many great tutorials out there, like this 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
    }
}