Persistent data in Swift

I am currently experimenting with persistent data in swift, but I am unable to save this data and return it back. Basically, I have two text fields, and when the user clicks the submit button, the record will be saved in UITable, here the user will be able to move the records in the table or delete them if they wish. My main problem is saving and loading this data.

Taskmanager.swift - My main types are stored here

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 - here I saved my table and its functions, I also have an approximate sketch of the functions for saving and loading data.

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 - Here I have text fields and buttons and how I add my records to the 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
    }





}
+4
1

, NSUserDefaults . : 100 , . , , .

. , .

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
    }
}
+1

Source: https://habr.com/ru/post/1662808/


All Articles