15

I have a small app that has a few saving functionalities. I have a data model class called: Closet:

class Department: NSObject, NSCoding {
   var deptName = ""
   var managerName = ""

   var Task: [Assignment]?   // <----- assignment class is in example 2

   func encodeWithCoder(aCoder: NSCoder) {

    aCoder.encodeObject(deptName, forKey: "deptName")
    aCoder.encodeObject(managerName, forKey: "mngName")
   // aCoder.encodeObject(Task, forKey: "taskArray")

}

  required init(coder aDecoder: NSCoder) {

     super.init()

    course = aDecoder.decodeObjectForKey("deptName") as! String
    instructor = aDecoder.decodeObjectForKey("mngName") as! String
   // Task = aDecoder.decodeObjectForKey("tasKArray") as? [Assignment]

}

override init() {
    super.init()
}

}

So this is the main controller data model which in the first View Controller, a user is able to tap the "+" button to add a department name and manager name. The problem is not with saving this as i save it successfully using NSKeyedArchive and loads it back when the app starts.

The Problem:

I want to add an array of assignments on this data model Department called Assignment which would have a title and a notes variable. This is the Data model for Assignment:

Assignment.swift

class Assignment: NSObject, NSCoding {
     var title = ""
     var notes = ""

      func encodeWithCoder(aCoder: NSCoder) {

    // Methods
    aCoder.encodeObject(title, forKey: "Title")
    aCoder.encodeObject(notes, forKey: "notepad")

}

required init(coder aDecoder: NSCoder) {


// Methods
    title = aDecoder.decodeObjectForKey("Title") as! String
    notes = aDecoder.decodeObjectForKey("notepad") as! String

    super.init()
}

override init() {
    super.init()
}


 }

So what i am essentially trying to achieve is an app where a user enters different departments with different manager names which work now in my app, but within a department, the user can click the "+" button to add an assignment title and notes section that can be editable when clicked which i can handle afterwards. These assignments are different from department to department.

My big problem is achieving this functionality. I can't seem to get this working.

I want this array assigment property to be part of the Department Class so each cell can have their own sort of To-Do list. any help would definitely help me out a lot. Thanks :)

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Amit
  • 310
  • 1
  • 3
  • 15

2 Answers2

20

You are using NSCoder correctly, but there are two errors in capitalization. The first error affects the functionality of the application, and the second error is a stylistic mistake. You encoded Task with the key "taskArray", but you tried to decode it with the key "tasKArray". If you fix the capital K in the latter, then your code will work.

The second capitalization error is a stylistic mistake: Task, like all properties in Swift, should be written in lowerCamelCase (llamaCase).

Be sure to pay close attention to indentation. In programming, there are special indentation rules we follow that help make code clear. Here is the corrected code with proper capitalization and indentation:

class Department: NSObject, NSCoding {
    var deptName = ""
    var managerName = ""

    var task: [Assignment]?

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(deptName, forKey: "deptName")
        aCoder.encodeObject(managerName, forKey: "mngName")
        aCoder.encodeObject(task, forKey: "taskArray")
    }

    required init(coder aDecoder: NSCoder) {
        super.init()

        course = aDecoder.decodeObjectForKey("deptName") as! String
        instructor = aDecoder.decodeObjectForKey("mngName") as! String
        task = aDecoder.decodeObjectForKey("taskArray") as? [Assignment]
    }

    override init() {
        super.init()
    }
}

class Assignment: NSObject, NSCoding {
    var title = ""
    var notes = ""

    func encodeWithCoder(aCoder: NSCoder) {
        // Methods
        aCoder.encodeObject(title, forKey: "Title")
        aCoder.encodeObject(notes, forKey: "notepad")
    }

    required init(coder aDecoder: NSCoder) {
        // Methods
        title = aDecoder.decodeObjectForKey("Title") as! String
        notes = aDecoder.decodeObjectForKey("notepad") as! String

        super.init()
    }

    override init() {
        super.init()
    }
}
Tone416
  • 540
  • 3
  • 10
  • I will try to refactor my code and see if it works. But essentially what i am trying to achieve, is save data within data. Parent cells with child cells when taping a parent cell, it would show child cells with the 'Assignment' class as its data model to hold a 'title' and 'notes' – Amit Sep 01 '15 at 20:48
0

Updated for Swift 5 / Xcode Version 12.4 (12D4e)

Thanks for the example above Tone416 -- I've reworked it for Swift 5 as the protocols and methods have changed. I've also included a simple test to prove it out so you should be able to just cut and paste this into a playground a run it.


import Foundation

class Department: NSObject, NSCoding {
    var deptName = ""
    var managerName = ""
    
    var task: [Assignment]?
    
    func encode(with coder: NSCoder) {
        coder.encode(deptName, forKey: "deptName")
        coder.encode(managerName, forKey: "mngName")
        coder.encode(task, forKey: "taskArray")
    }
    
    required init(coder aDecoder: NSCoder) {
        super.init()
        
        deptName = aDecoder.decodeObject(forKey: "deptName") as! String
        managerName = aDecoder.decodeObject(forKey: "mngName") as! String
        task = aDecoder.decodeObject(forKey: "taskArray") as? [Assignment]
    }
    
    override init() {
        super.init()
    }
    
    convenience init(deptName: String, managerName: String, task: [Assignment]?) {
        self.init()
        
        self.deptName = deptName
        self.managerName = managerName
        self.task = task
    }
}

class Assignment: NSObject, NSCoding {
    var title = ""
    var notes = ""
    
    func encode(with coder: NSCoder) {
        // Methods
        coder.encode(title, forKey: "Title")
        coder.encode(notes, forKey: "notepad")
    }
    
    required init(coder aDecoder: NSCoder) {
        // Methods
        title = aDecoder.decodeObject(forKey: "Title") as! String
        notes = aDecoder.decodeObject(forKey: "notepad") as! String
        
        super.init()
    }
    
    override init() {
        super.init()
    }
    
    convenience init(title: String, notes: String) {
        self.init()
        
        self.title = title
        self.notes = notes
    }
}

// Create some data for testing
let assignment1 = Assignment(title: "title 1", notes: "notes 1")
let assignment2 = Assignment(title: "title 2", notes: "notes 2")
let myDepartment = Department(deptName: "My Dept", managerName: "My Manager", task: [assignment1, assignment2])

// Try archive and unarchive
do {
    // Archive
    let data = try NSKeyedArchiver.archivedData(withRootObject: myDepartment, requiringSecureCoding: false)
    print ("Bytes in archive: \(data.count)")
    
    // Unarchive
    let obj = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! Department
    
    // Print the contents of the unarchived object
    print("Department: \(obj.deptName)    Manager: \(obj.managerName)")
    if let task = obj.task {
        for i in 0...task.count-1 {
            print("Task: \(task[i].title) \(task[i].notes)")
        }
    }
    
} catch {
    
    let nsError = error as NSError
    fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}

Enjoy