iOS — локальное сохранение массива пар ключ-значение

В приложении, которое я разрабатываю, пользователю необходимо добавить «предприятие», чтобы использовать приложение. Однако они могут добавить в приложение более одного «предприятия». Каждому добавленному предприятию нужны две вещи: имя и корпоративный ключ API.

Я создал класс "Предприятие":

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")
    }

}

Следует отметить одну вещь об этом классе: в примере, который я использовал для создания этого класса, они подклассифицировали класс как NSCoder, а также NSObject, однако, если я добавил «NSCoder» в класс, я получаю следующую ошибку:

Множественное наследование от классов NSObject и NSCoder.

При сохранении массива я использую следующее:

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")
}

Кажется, это работает при сохранении одного значения, но когда я проверяю, сколько элементов находится в массиве при запуске, отображается только 1.

Любые идеи, как я могу это сделать? Конечный результат, который я ищу, будет примерно таким:

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

    Enterprise
        name: def
        apiKey: 23oih9823tng893g2gd

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

С возможностью удалить конкретное предприятие из списка, если пользователь решит удалить его.

ИЗМЕНИТЬ

Вот что сработало, используя метод Леонардо (ниже и принятый ответ):

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")
}

person matcartmill    schedule 12.02.2015    source источник
comment
Вы реализуете NSCoding, а не подкласс NSCoder   -  person Wain    schedule 12.02.2015


Ответы (2)


Вы должны использовать словарь, иначе вы можете получить одно и то же предприятие с разными ключами. Я создал класс для загрузки и сохранения словарей по умолчанию. Вы должны сделать следующее:

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)")
}
person Leo Dabus    schedule 12.02.2015
comment
Это выглядит как хорошее начало, но если пользователь добавляет предприятия по одному, как создается var enterprise:[String:String]? Мне нужно будет извлечь существующие записи, добавить их в массив, а затем добавить новую в этот массив? - person matcartmill; 12.02.2015
comment
var enterprise - это словарь, и я думаю, что вам вообще не нужен массив. В моем примере я начал с 3 предприятий и добавил в словарь только одно. - person Leo Dabus; 12.02.2015
comment
ключ — это название предприятия, а значение — это apikey - person Leo Dabus; 12.02.2015
comment
другим вариантом было бы хранить только имена предприятий в массиве и сохранять каждый apikey в пользовательских значениях по умолчанию в виде строки. Но вам нужно будет выполнить поиск перед добавлением нового элемента в массив, чтобы предотвратить дублирование. Таким образом, вы сможете легко отсортировать массив, если это необходимо. - person Leo Dabus; 12.02.2015
comment
Итак, если у меня есть 3 предприятия, и я собираюсь добавить четвертое, мне сначала придется создать словарь с помощью функции Load.dictionary, правильно? Затем просто добавьте мои новые значения в словарь и сохраните словарь, который теперь содержит 4 записи? Когда я пытаюсь это сделать, я делаю: for (key, value) inloadEnterprises { enterprise.setObject(key, forKey: value) } Я получаю нулевой развернутый сбой. Если я распечатаю ключ и значение, все работает нормально. Сбивает с толку. Но это дает мне ошибку: значение $T5 не идентично String:String - person matcartmill; 12.02.2015
comment
Хорошо, у меня все заработало, используя ваш метод с некоторыми обновлениями! Я опубликую свое редактирование, чтобы показать, что сработало. Спасибо за вашу помощь. - person matcartmill; 12.02.2015

Вы всегда переопределяете «Предприятие» для ключа «предприятие», используйте массив:

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

РЕДАКТИРОВАТЬ:

работает для меня, но это не очень хорошее решение:

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")
    }
}
person iOSfleer    schedule 12.02.2015
comment
Когда я пытаюсь это сделать, я получаю следующую ошибку NSInternalInconsistencyException', причина: '-[__NSCFArray insertObject:atIndex:]: метод мутации, отправленный в неизменяемый объект' - person matcartmill; 12.02.2015