How to implement a generic way of saving different Dictionaries in Swift using NSKeyedArchiver?

619 views Asked by At

Sorry, if this is an easy question, but i couldn't find an answer for three days. I'm just new to Swift.

Problem: I'd like to use the Swift-Enum-Type as the KEY for different Swift-Dictionaries AND save this dictionaries using NSKeydArchiver in a generic way.

First of all I show some easy sample code for the enum and dictionaries (I know, I could integrate the dictionaries values directly into the enum declaration as rawValues, but this is not my problem, my problem is to SAVE the dictionary with NSKeydArchiver -> see function below)

// ------------------------------------------------------
// Enumerations and Dictionaries
// ------------------------------------------------------
enum helloOptions {
    case option1
    case option2
    case option3
}

enum someOtherEnum {
    case green
    case red
    case blue
}

var myDictionary01 : Dictionary<helloOptions, String> = [helloOptions.option1 : "Hello", helloOptions.option2 : "Welcome", helloOptions.option3 : "Nice to see you"]

var myDictionary02 : Dictionary<someOtherEnum, Int> = [someOtherEnum.green : 22, someOtherEnum.red : 11, someOtherEnum.blue : 999]

And here is my working implementation of a savingFunction, but unfortunately it is not a generic way, because I would need to implement this function many times for all Enums in the App.

func saveDictionary(myDictionary: Dictionary<helloOptions, AnyObject>, myFileName: String, myDataFolder: DataFolder) -> Bool {

    let completeDataPath = makeCompletePathToFile(myFileName, myDataFolder)

    let saveSuccess : Bool = NSKeyedArchiver.archiveRootObject(myDictionary, toFile: completeDataPath)
    if !saveSuccess {
        println("Some problem saving Dictionary")
    }
    return saveSuccess
}

So I would like to have some generic Type of Enums, like "AnyEnumType" in order to use that function in a generic way:

func saveDictionary(myDictionary: Dictionary<AnyEnumType, AnyObject>, myFileName: String, myDataFolder: DataFolder) -> Bool {
 ... 
}

But this - of course - causes a Compiler-Error "Use of undeclared type "AnyEnumType""

I tried a lot with typeAlias, with TypePlaceholders <T : Hashable> and so on, but I couldn't implement it.

How can I implement the function like this

func saveDictionary(myDictionary: Dictionary<AnyKeyType, AnyObject>, myFileName: String, myDataFolder: DataFolder) -> Bool { ..
}

by declaring this generic Type "AnyKeyType" is following the protocol Hashable, Equitable, ... that enables me to use a String or a Enum.Member for Key ???

1

There are 1 answers

0
Gregory Higley On BEST ANSWER

Something like this might work:

protocol EnumType {
    typealias RawType
    init?(rawValue: RawType)
    var rawValue: RawType { get }
}

enum Hello: String, EnumType {
    case Option1 = "Option1"
    case Option2 = "Option2"
}

func toArchivableDictionaryWithEnumerationKeys<E: EnumType, T>(dictionary: Dictionary<E, T>) -> Dictionary<E.RawType, T> {
    var result = Dictionary<E.RawType, T>()
    // Tried to use reduce here, but couldn't make it work
    for (key, value) in dictionary {
        result[key.rawValue] = value
    }
    return result
}

func fromArchivedDictionaryWithEnumerationKeys<E: EnumType, T>(dictionary: Dictionary<E.RawType, T>) -> Dictionary<E, T>? {
    var result = Dictionary<E, T>()
    for (key, value) in dictionary {
        if let enumKey = E(rawValue: key) {
            result[enumKey] = value
        } else {
            return nil
        }
    }
    return result
}

let dict = toArchivableDictionaryWithEnumerationKeys([Hello.Option1: "Bob"])
let data = NSKeyedArchiver.archivedDataWithRootObject(dict)
let value = NSKeyedUnarchiver.unarchiveObjectWithData(data) as Dictionary<String, String>
if let unarchived: Dictionary<Hello, String> = fromArchivedDictionaryWithEnumerationKeys(value) {
    // Blah blah
}

Also, you should call your enumeration HelloOptions not helloOptions, and call the enumeration values Option1, not option1. All types in Swift should be capitalized. You'll notice that this convention is followed religiously by the designers of Swift. You should do so, too.