0

This is how I have set up my data structure:

class Commit: NSObject, NSCoding {

//MARK: Properties
var contents : String
var repeatStatus : Bool
var completionStatus : Bool

//MARK: Initialization
init(contents: String, repeatStatus: Bool, completionStatus: Bool) {

    self.contents = contents
    self.repeatStatus = repeatStatus
    self.completionStatus = completionStatus

    super.init()

}

//MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder){
    aCoder.encodeObject(contents, forKey: PropertyKey.contentsKey)
    aCoder.encodeObject(repeatStatus, forKey: PropertyKey.repeatStatusKey)
    aCoder.encodeObject(completionStatus, forKey: PropertyKey.completionStatusKey)

}

required convenience init?(coder aDecoder: NSCoder) {

    let contents = aDecoder.decodeObjectForKey(PropertyKey.contentsKey) as! String
    let repeatStatus = aDecoder.decodeObjectForKey(PropertyKey.repeatStatusKey) as! Bool
    let completionStatus = aDecoder.decodeObjectForKey(PropertyKey.completionStatusKey) as! Bool

    self.init(contents: contents, repeatStatus: repeatStatus, completionStatus: completionStatus)
}

}

class ToDoList: NSObject, NSCoding {

//MARK: Properties
var commitArray : [Commit]
var date : Int

// MARK: Archiving Paths

static let DocumentsDirectory = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("toDoList")

//MARK: Initialization
override init(){

    let defaultCommit = Commit(contents: "", repeatStatus: false, completionStatus: false)
    self.date = dateTodayAsInt
    self.commitArray = [defaultCommit, defaultCommit, defaultCommit]

    super.init()
}

init(commitArray: [Commit], date: Int) {

    self.date = date
    self.commitArray = commitArray

    super.init()

}

//MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder){

    aCoder.encodeObject(commitArray, forKey: PropertyKey.commitArrayKey)
    aCoder.encodeObject(date, forKey: PropertyKey.dateKey)

}

required convenience init?(coder aDecoder: NSCoder) {

    let commitArray = aDecoder.decodeObjectForKey(PropertyKey.commitArrayKey) as! [Commit]
    let date = aDecoder.decodeObjectForKey(PropertyKey.dateKey) as! Int

    self.init(commitArray: commitArray, date: date)
}


}

func findIndexOfActiveToDoList(allToDoLists: [ToDoList]) -> Int {

var j = Int(floor(Double((allToDoLists.count/2))))
var count = 0

repeat {
    count += 1

    if allToDoLists[j].date < dateTodayAsInt {
        j = Int(j + j/2)
    } else if allToDoLists[j].date > dateTodayAsInt {
        j = Int(j - j/2)
    } else { return j }

} while count < Int(sqrt(Double(allToDoLists.count/2)))

print(j)
return j }


// MARK: Types

struct PropertyKey {

static let contentsKey = "contents"
static let repeatStatusKey = "repeatStatus"
static let completionStatusKey = "completionStatus"

static let commitArrayKey = "commitArray"
static let dateKey = "date"

}

var toDoList = [Commit]()
var allToDoLists = [ToDoList]()

Later, when I want to update the allToDoLists array, like this:

FirstCommitTextField.text! = allToDoLists[findIndexOfActiveToDoList(allToDoLists)].commitArray[0].contents

No matter what I do, as soon as I set:

SecondCommitTextField.text! = allToDoLists[findIndexOfActiveToDoList(allToDoLists)].commitArray[1].contents

The commit array's first element's content property is changed as well. I have run the app with print comments printing the commit array's first element's content property right before and after setting the commit array's second element's content property - just setting the second property somehow changed the first one.

This leads me to the conclusion that somehow setting on value of an element in an array changes the value of the second element in that array as well. Is this the case? If yes, how can this be avoided/circumvented?

Thanks in advance!

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Jonas
  • 1,473
  • 2
  • 13
  • 28
  • The provided code `FirstCommitTextField.text! = allToDoLists[....` does not update `allToDoLists`. Perhaps you meant to write `allToDoLists[...]...contents = FirstCommitTextField.text!`. – szym May 04 '16 at 17:50

1 Answers1

0

I suspect you meant to write:

allToDoLists[findIndexOfActiveToDoList(allToDoLists)].commitArray[0].contents = 
    FirstCommitTextField.text!
allToDoLists[findIndexOfActiveToDoList(allToDoLists)].commitArray[1].contents = 
    SecondCommitTextField.text!

If that's the case, the reason why you see the second update affect the contents of commitArray[0] is that you initialized the ToDoList.commitArray:

self.commitArray = [defaultCommit, defaultCommit, defaultCommit]

And your Commit is a regular class not struct so it is passed by reference (see: structure vs class in swift language). Therefore when you change commitArray[0].contents you will see the same value in commitArray[1].contents and commitArray[2].contents.

Note that the object references are preserved by NSCoder so if they were the same object before serialization, they will be the same after deserialization.

Solution

Instead of re-using let defaultCommit = Commit(...) you should make a new instance initialized with the default arguments. For example add a convenience initializer to Commit:

convenience override init() {
    self.init(contents: "", repeatStatus: false, completionStatus: false)
}

(override is needed because NSObject declares a no-argument init already.)

Then use it:

self.commitArray = [Commit(), Commit(), Commit()]
Community
  • 1
  • 1
szym
  • 5,606
  • 28
  • 34
  • If implementing the Commit class as a struct means that it (obviously) can't inherit from NSCoding any more, how could I still make the data persist past restarting the app? – Jonas May 04 '16 at 17:57
  • I updated the answer with a solution that is compatible with `NSObject`. – szym May 04 '16 at 18:01
  • Thank you, that makes sense! One small detail: when I implement the code as you suggested, the debugger shows an error: "Cannot invoke initializer for type "Commit" with no arguments" at the line "self.commitArray = ..." in the designated initializer – Jonas May 04 '16 at 18:22
  • That error indicates that you are missing `Commit.init()` but if you added the convenience initializer it should not be a problem. – szym May 04 '16 at 18:29