1

In an app I'm developing the user needs to add an "enterprise" to use the app. However, they can add more than one "enterprise" to the app. Every enterprise added needs two things: a name, and an enterprise API key.

I have created an "Enterprise" class:

class Enterprise : NSObject {

    var name:String!
    var apiKey:String!

    init (name:String, apiKey:String) {
        self.name = name
        self.apiKey = apiKey
    }

    required init(coder decoder: NSCoder) {
        self.name = decoder.decodeObjectForKey("name") as String
        self.apiKey = decoder.decodeObjectForKey("apiKey") as String
        super.init()
    }

    func encodeWithCoder(coder: NSCoder) {
        coder.encodeObject(self.name, forKey: "name")
        coder.encodeObject(self.apiKey, forKey: "apiKey")
    }

}

One thing to note about this class, in the example I followed to build this class they have sub-classed the class as NSCoder as well as NSObject, however if I added "NSCoder" to the class, I get the following error:

Multiple inheritance from classes 'NSObject' and 'NSCoder'

When saving the array I'm using the following:

let name = self.enterpriseName.text
let key = self.apiKey.text
if name != "" {
    if key != "" {
        let defaults = NSUserDefaults.standardUserDefaults()
        let objectKey = "enterprise"
        var ent = [Enterprise(name: name, apiKey: key)]
        var entData = NSKeyedArchiver.archivedDataWithRootObject(ent)
        defaults.setObject(entData, forKey: objectKey)
        defaults.synchronize()
    }
    else {
        println("No API key")
    }
}
else {
    println("No name")
}

This seems to work when storing one value, but when I check how many items are in the array at launch, it only shows 1.

Any ideas how I can do this? The end result I'm looking for would be something like:

Enterprises:
    Enterprise
        name: abc
        apiKey: 184nfh692j6j31))8dx

    Enterprise
        name: def
        apiKey: 23oih9823tng893g2gd

    Enterprise:
        name: ghi
        apiKey: sfgYSS4yw44gw31!#%q

With the ability to remove a particular enterprise from the list if the user choose to delete it.

EDIT

Here's what worked, using Leonardo's (below, and accepted answer) method:

let name = self.enterpriseName.text
let apiKey = self.apiKey.text
if name != "" {
    if apiKey != "" {
        var enterprises = NSMutableDictionary()
        var loadedEnterprises = Load.dictionary("enterprises")
        if loadedEnterprises != nil {
            if(loadedEnterprises.count > 0) {
                for (key, value) in loadedEnterprises {
                    enterprises.setObject(value, forKey: key as String)
                }
            }
        }
        enterprises.setObject(apiKey, forKey: name)     
        Save.dictionary("enterprises", enterprises)
    }
    else {
        println("No API key")
    }
}
else {
    println("No name")
}
matcartmill
  • 1,573
  • 2
  • 19
  • 38

2 Answers2

1

You should use a Dictionary otherwise you may end up with the same enterprise with different keys. I have created a class to load and save dictionaries to the user default. You should do as follow:

class Load {
    class func dictionary(key:String) -> NSDictionary! {
        return NSUserDefaults.standardUserDefaults().objectForKey(key) as? NSDictionary
    }
}

class Save {
    class func dictionary(key:String, _ value:NSDictionary) {
        NSUserDefaults.standardUserDefaults().setObject(value, forKey: key)
    }
}

class Remove {
    class func object(key:String) {
        NSUserDefaults.standardUserDefaults().removeObjectForKey(key)
    }
}


var enterprises:[String:String] = ["abc":"184nfh692j6j31))8dx","def":"23oih9823tng893g2gd","ghi":"sfgYSS4yw44gw31!#%q"]
Save.dictionary("enterprises", enterprises)

name = "jkl"
apiKey = "9a4giNifjKJq6v8G4fb"

if !name.isEmpty {
    if !apiKey.isEmpty {
        enterprises[name] = apiKey
        Save.dictionary("enterprises", enterprises)
    } else {
        println("No API key")
    }
} else {
    println("No name")
}

for (key, value) in enterprises {
    println("\(key)=\(value)")
}

let loadedDictionary = Load.dictionary("enterprises")

for (key, value) in loadedDictionary {
    println("\(key)=\(value)")
}
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • That looks like a good start, but if the user is adding the enterprises one at a time, how is the "var enterprises:[String:String] being built? Will I need to pull in the existing entries, add them to the array, and then append the new one to that array? – matcartmill Feb 12 '15 at 16:12
  • var enterprise is a dictionary and I think you don't need an array at all. In my example I started with 3 enterprises and added just one to the dictionary. – Leo Dabus Feb 12 '15 at 16:16
  • key is the name of the enterprise and value is the apikey – Leo Dabus Feb 12 '15 at 16:18
  • another option would be to store just the names of the enterprises in an array and save each apikey into the userdefaults as string. But you would need to do a find before adding a new element to the array to prevent duplicates. This way you would be able to easily sort the array if needed. – Leo Dabus Feb 12 '15 at 16:39
  • So if I have 3 enterprises and I go to add a fourth, I will first have to build the dictionary using the Load.dictionary function, correct? Then simply add my new values to the dictionary and save the Dictionary which now contains 4 entries? When I try that I do: for (key, value) in loadedEnterprises { enterprises.setObject(key, forKey: value) } I get a nil unwrapped crash. If I print out the key and value, it works fine. Confusing. But that gives me an error: Value $T5 not identical to String:String – matcartmill Feb 12 '15 at 16:41
  • Alright, I got it working using your method with some updates! I will post my edit to show what worked. Thanks for your help. – matcartmill Feb 12 '15 at 16:52
0

You always override 'Enterprise" for 'enterprise' Key, use an array:

var entArray = defaults.objectForKey(objectKey)
entArray?.addObject(entData)
defaults.setObject(entArray, forKey: objectKey)

EDIT:

works for me, but it's not good solution:

func getEnterprise() {
    let objectKey = "enterprise"
    // get stored array
    let defaults = NSUserDefaults.standardUserDefaults()
    var array = defaults.objectForKey(objectKey) as? Array<NSData>
    if array != nil {
        for data in array! {
            var enterprise = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? Enterprise
            println("\(enterprise!.name)")
        }
    }
}

func saveEnterprise() {
    let name = self.enterpriseName
    let key = self.apiKey
    if name != "" && name != nil {
        if key != "" && key != nil {
            let defaults = NSUserDefaults.standardUserDefaults()
            let objectKey = "enterprise"
            var ent = Enterprise(name: name!, apiKey: key!)

            // get stored array
            var array = defaults.objectForKey(objectKey) as? Array<NSData>
            if array == nil {
                array = Array()
            }
            var entData = NSKeyedArchiver.archivedDataWithRootObject(ent)
            array?.append(entData)
            defaults.setObject(array, forKey: objectKey)
            defaults.synchronize()
        }
        else {
            println("No API key")
        }
    }
    else {
        println("No name")
    }
}
iOSfleer
  • 408
  • 6
  • 20
  • When I try this, I get the following error NSInternalInconsistencyException', reason: '-[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object' – matcartmill Feb 12 '15 at 15:59