Search code examples
swiftxcodedecodeplistswift5

Decoding a Plist Dictionary in Swift 5


I'm writing a Mac command line tool in Swift 5 and I'm trying to parse a Plist which contains a nested dictionary of values named "TestValues". I want to add each key pair from this dictionary to an array but I'm just hitting a brick wall.

The Plist looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>TestValues</key>
    <dict>
        <key>TestString</key>
        <string>This is my test string value</string>
        <key>TestInt</key>
        <integer>4</integer>
        <key>AnotherTestString</key>
        <string>This is my other test string value</string>
        <key>TestBool</key>
        <true/>
    </dict>
</dict>
</plist>

And my code:

struct CustomPrefs: Codable {
    
    var TestValues: [TestValuePrefs]
    
}

struct TestValuePrefs: Codable {
    
    var TestString: String
    var TestInt: Int
    var AnotherTestString: String
    var TestBool: Bool
    
}


func getCustomPrefs() -> CustomPrefs? {
    
    let decoder = PropertyListDecoder()
    let url = URL(fileURLWithPath: "/Library/Preferences/com.test.preferences.plist")
    
    if let data = try? Data(contentsOf: url) {
        if let customPreferences = try? decoder.decode(CustomPrefs.self, from: data) {
            print("Returning custom prefs")
            return customPreferences
        }
    }
    print("Returning nil")
    return nil
    
}


let preferenceArray = (getCustomPrefs()?.TestValues)
print(preferenceArray ?? "")

But I just keep getting nil from the "getCustomPrefs" function. Can anyone see where I'm going wrong?


Solution

  • The TestValues var in CustomPrefs isn't an array or dictionary but rather the TestValuePrefs object.

    The following should do the trick:

    struct CustomPrefs: Decodable {
        let TestValues: TestValuePrefs
        
        struct TestValuePrefs: Decodable {
            let TestString: String
            let TestInt: Int
            let AnotherTestString: String
            let TestBool: Bool
        }
    }
    
    func getCustomPrefs() -> CustomPrefs? {
        let decoder = PropertyListDecoder()
        let url = URL(fileURLWithPath: "/Library/Preferences/com.test.preferences.plist")
        
        do {
            let data = try Data(contentsOf: url)
            let customPreferences = try decoder.decode(CustomPrefs.self, from: data)
            return customPreferences
        }
        catch {
            print("Error: \(error)")
        }
        return nil
    }
    

    I'd also recommend using CodingKeys to avoid using capitalized variables.