Search code examples
iosxcodeswiftnscodingnskeyedunarchiver

Swift NSCoding issue with single custom object


I was reading some other posts and I can't seem to figure out why object saved to disk using NSKeyedUnarchiver on the following is always printing blank lines to the console. My end goal is to store a single user object to disk using NSKeyedArchiver.

I am currently learning how to use Firebase to store some data. I have a custom class that represents the user as follows:

class User: NSObject, NSCoding {
let key: String
let email: String
let familyKey: String
let username: String
let provider: String
let role: String



// Initialize from arbitrary data
init(key: String, email: String, fKey: String, uName:String, provider:String, role: String) {
    self.key = key
    self.email = email
    self.familyKey = fKey
    self.username = uName
    self.provider = provider
    self.role = role
    super.init()
}

required init?(coder aDecoder: NSCoder) {
    self.key = aDecoder.valueForKey("key") as! String
    self.email = aDecoder.valueForKey("email") as! String
    self.familyKey = aDecoder.valueForKey("familyKey") as! String
    self.username = aDecoder.valueForKey("username") as! String
    self.provider = aDecoder.valueForKey("provider") as! String
    self.role = aDecoder.valueForKey("role") as! String
}
func encodeWithCoder(aCoder: NSCoder) {
    aCoder.encodeObject(self.key, forKey: "key")
    aCoder.encodeObject(self.email, forKey: "email")
    aCoder.encodeObject(self.familyKey, forKey: "familyKey")
    aCoder.encodeObject(self.username, forKey: "username")
    aCoder.encodeObject(self.provider, forKey: "provider")
    aCoder.encodeObject(self.role, forKey: "role")
}

In my sign up view controller I create the user and authenticate the user with Firebase with no issues. Within the authentication block I initialize the user object using the first custom initializer above. I have tried the following to get it to save so I believe my issue lies in my code above with the encoding/decoding of the data since none of these work.

Archiving file locally based on this link from HackingWithSwift. FBPath.shared.docDir is just a NSString link to my doc directory.

let userPath = FBPath.shared.docDir.stringByAppendingPathComponent("currentUser")
                    let userData = NSKeyedArchiver.archivedDataWithRootObject(newUser)
                    userData.writeToFile(userPath, atomically: true)

Trying to retrieve this using

let userPath = FBPath.shared.docDir.stringByAppendingPathComponent("currentUser")
    if let user = NSKeyedUnarchiver.unarchiveObjectWithFile(userPath) as? User {
        print(user.username)
    }

This didn't work so I tried to store it to disk using the post from Whasssaaahhh here post by Whasssaaahhh. Here is the code that actually works, but stores an emtpy file.

required convenience init?(coder aDecoder: NSCoder) {

    guard let aKey = aDecoder.decodeObjectForKey("userKey") as? String else {return nil}
    guard let anEmail = aDecoder.decodeObjectForKey("userEmail") as? String else {return nil}
    guard let aFamilyKey = aDecoder.decodeObjectForKey("usersFamilyKey") as? String else {return nil}
    guard let userName = aDecoder.decodeObjectForKey("username") as? String else {return nil}
    guard let aProvider = aDecoder.decodeObjectForKey("userProvider") as? String else {return nil}
    guard let aRole = aDecoder.decodeObjectForKey("userRole") as? String else {return nil}

    self.init(key: aKey, email: anEmail, fKey: aFamilyKey, uName:userName, provider:aProvider, role: aRole)
}

private class func getCurrentUserURL() -> NSURL {
    let documentsDirectory = NSFileManager().URLsForDirectory((.DocumentDirectory), inDomains: .UserDomainMask).first!
    let userPath = documentsDirectory.URLByAppendingPathComponent("currentUser")
    return userPath
}

class func saveCurrentUserToDisk(user:User){
    let success = NSKeyedArchiver.archiveRootObject(user, toFile: User.getCurrentUserURL().path!)
    if !success {
        print("failed to save user to disk")
    }
}
class func loadCurrentUserFromDisk() -> User?{
    return NSKeyedUnarchiver.unarchiveObjectWithFile(User.getCurrentUserURL().path!) as? User
}

In my sign up VC to store the user to disk:

let newUser = User(key: authData.uid, email: self.emailField.text!, fKey: newFamily.key, uName: self.usernameField.text!, provider: authData.provider, role: "parent")
                    newUser.createUserRecords()
                    User.saveCurrentUserToDisk(newUser)

Then to try and access the data:

if let user = User.loadCurrentUserFromDisk() {
        print(user)
    }

See below edit This prints a blank line. I am at a loss on what to do to save this user to disk. Based on this attempt it appears it isn't a good idea to try and save this in user defaults and its such a small data set as a single user that it won't hurt to store the data object on the disk anyway. Just need to figure out why I can't get this to work. I also tried updating all instance variables from let to var and assigning "" as the string values. I am hoping a second set of eyes may help me see what I am missing here. Thanks in advance!

EDIT: When I changed instance variables to vars and set their value to an empty string, now the object is not getting returned and nothing is printed to the console. If I change them back then the user prints a blank line to console eventhough I have not overwritten description. If I try to print instance variables it again prints an empty string as well.

So I also decided to check the location. The values are all there. It is just not recovering them with the NSKeyedUnarchiver. I printed the file location to verify its trying to access the correct location and all is good. The XML of the document isn't showing nicely here. I am new to the site, I don't believe I can upload the file. But I looked through the plist generated and its all in there. I'm not far from giving up on this and just manually storing the data to a plist. Not sure what issues that could cause when compared to the benefits of NSKeyedArchiver and using NSCoding


Solution

  • The issue was with the guard statments stopping the initialization of the object being unarchived. I changed from guard statements to let statements assigning an empty string if it couldn't decode the value as Michael suggested in the comments above. This helped me find the value that had an improper key so I could update the code. All is well now.