1

I have two classes : Contact and Bill. Contact has an array of type Bill.

When I persist using NSKeyedArchiver my contacts persist just fine, however, it does not persist the Bill array.

Each time I add a a Bill or a Contact I call the insertNewObject() method in persist.

Here are my classes :

/* Persist.swift */
import Foundation

class Persist {

    static let sharedInstance = Persist()

    let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
    var contactsFilePath : String {
        let manager = NSFileManager.defaultManager()
        let url = manager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first! as NSURL
        return url.URLByAppendingPathComponent("\(delegate.userId):objectsArrayz").path!
    }

    func insertNewObject(){
        NSKeyedArchiver.archiveRootObject(delegate.contacts!, toFile: contactsFilePath)

    }

    func retrieveContracts(){
        if let array = NSKeyedUnarchiver.unarchiveObjectWithFile(contactsFilePath) as? [Contact] {
            delegate.contacts = array
        }
    }

    func deleteContact(rowNum : Int){
        delegate.contacts!.removeAtIndex(rowNum)
        NSKeyedArchiver.archiveRootObject(delegate.contacts!, toFile: contactsFilePath)
    }

/* Contact.swift */    
import Foundation

class Contact : NSObject, NSCoding{

    var image : UIImage?
    var firstName : String?
    var lastName : String?
    var email : String?
    var phoneNumber : String?
    var address : String?
    var bills : [Bill]?

    init(image: UIImage, firstName : String, lastName : String, email : String, phoneNumber : String, address : String, bills : [Bill]) {
        self.image = image
        self.firstName = firstName
        self.lastName = lastName
        self.email = email
        self.phoneNumber = phoneNumber
        self.address = address
        self.bills = bills
    }

    // MARK: NSCoding

    required convenience init?(coder decoder: NSCoder) {
        guard let image = decoder.decodeObjectForKey("image") as? UIImage,
            let firstName = decoder.decodeObjectForKey("firstName") as? String,
            let lastName = decoder.decodeObjectForKey("lastName") as? String,
            let email = decoder.decodeObjectForKey("email") as? String,
            let phoneNumber = decoder.decodeObjectForKey("phoneNumber") as? String,
            let address = decoder.decodeObjectForKey("address") as? String,
            let bills = decoder.decodeObjectForKey("bills") as? [Bill]
        else {
                return nil
        }

        self.init(
            image: image,
            firstName: firstName,
            lastName : lastName,
            email : email,
            phoneNumber : phoneNumber,
            address : address,
            bills : bills

        )
    }

    func encodeWithCoder(coder: NSCoder) {
        coder.encodeObject(self.image, forKey: "image")
        coder.encodeObject(self.firstName, forKey: "firstName")
        coder.encodeObject(self.lastName, forKey: "lastName")
        coder.encodeObject(self.email, forKey: "email")
        coder.encodeObject(self.phoneNumber, forKey: "phoneNumber")
        coder.encodeObject(self.address, forKey: "address")
        coder.encodeObject(self.bills, forKey: "bills")
    }
}


/* Bill.swift*/
import Foundation

class Bill : NSObject, NSCoding{

    var service : String?
    var subtotal : Double?
    var taxes : Double?
    var total : Double?


    init(service : String, subtotal : Double, taxes : Double, total: Double) {
        self.service = service
        self.subtotal = subtotal
        self.taxes = taxes

    }

    // MARK: NSCoding

    required convenience init?(coder decoder: NSCoder) {

        guard let service = decoder.decodeObjectForKey("service") as? String,
            let subtotal = decoder.decodeObjectForKey("subtotal") as? Double,
            let taxes = decoder.decodeObjectForKey("taxes") as? Double,
            let total = decoder.decodeObjectForKey("total") as? Double

            else {
                return nil
        }


        self.init(
            service: service,
            subtotal: subtotal,
            taxes: taxes,
            total: total

        )
    }

    func encodeWithCoder(coder: NSCoder) {
        coder.encodeObject(self.service, forKey: "service")
        coder.encodeObject(self.subtotal, forKey: "subtotal")
        coder.encodeObject(self.taxes, forKey: "taxes")
        coder.encodeObject(self.total, forKey: "total")
    }
}
3
  • Does the same happen if you declare bills as non optional? i.e. var bills : [Bill]! Just be sure that there's a zero-length array there e.g. var bills : [Bill]! = [Bill]() Commented Apr 6, 2016 at 18:46
  • Yes I did change my variables to non-optionals, however it didn't help me out. Commented Apr 6, 2016 at 19:55
  • And what-if you use NSArray / NSMutableArrays explicitly rather than implied Arrays? e.g. declaring bills as an NSArray? Another way of doing this (As far as I know is) by: var bills : [Bill] = []. I could be wrong here.. But explicitly using: var bills: NSArray is a last resort Commented Apr 6, 2016 at 21:18

2 Answers 2

1

Here is a guide from the apple dev docs (in objective-c):

Discussion You must return self from initWithCoder:. If you have an advanced need that requires substituting a different object after decoding, you can do so in awakeAfterUsingCoder:

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Archiving/Articles/codingobjects.html

- (void)encodeWithCoder:(NSCoder *)coder {
    [super encodeWithCoder:coder];
    // Implementation continues
}

- (id)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        _firstName = [coder decodeObjectForKey:ASCPersonFirstName];
        _lastName = [coder decodeObjectForKey:ASCPersonLastName];
        _height = [coder decodeFloatForKey:ASCPersonHeight];
    }
    return self;
}
Sign up to request clarification or add additional context in comments.

1 Comment

He is using the coder designated initialiser. Without it, he wouldn't be able to compile as it wouldn't confirm to the protocol. Secondly, the code is in swift, not Obj-C.
0

A call to super.init() is required in the serializable class's init method.

Snippet from Bill.swift

 required init?(coder decoder: NSCoder) {
        super.init()
        service = decoder.decodeObjectForKey("service") as? String
        subtotal = decoder.decodeObjectForKey("subtotal") as? Double
        taxes = decoder.decodeObjectForKey("taxes") as? Double
        total = decoder.decodeObjectForKey("total") as? Double
        services = decoder.decodeObjectForKey("services") as? [Service]
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.