Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Extending Bundle to add extra plist file

I imagine this is going to be simple, but I’m still learning swift.

I’ve created a Preferences.plist file that I want to pull in like Info.plist, but separate, ie:

Bundle.main.prefsDictionary

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Or something like that … I have a Bundle+Extension.swift file already, that contains:

import Foundation

extension Bundle {
    var releaseVersionNumber: String? {
        infoDictionary?["CFBundleShortVersionString"] as? String
    }
}

So what I’m figuring I want to do is add a new var that references this extra plist file, but for all my searching, I cannot seem to find any suitable example to do this … a lot of stuff on how to merge / append Dictionaries together, but I want to keep this separate, just easily referenceable, just like Bundle.main.infoDictionary is …

If swift was an open source compiler, I’d pop out and find the appropriate code for ‘var infoDictionary’ and just copy / change that to reference the new file, but alas, that seems to be impossible.

I tried adding to my Bundle extension something as simple as:

   var prefsDictionary: Dictionary {
        let path = Bundle.main.path(forResource: "Preferences", ofType: "plist")!
        return NSDictionary(contentsOfFile: path)
   }

based on examples I’d found … XCode tells me I need to add "<<#Key: Hashable#>, Any>", making it:

    var prefsDictionary: Dictionary<<#Key: Hashable#>, Any> {
        let path = Bundle.main.path(forResource: "Preferences", ofType: "plist")!
        return NSDictionary(contentsOfFile: path)
    }

Leading to the next error:

error: Unexpected token #>, at 22:52 in XCode/app/Sources/Helpers/Bundle+Extensions.swift.

Even though that is what it told me to put … so I’m sort of going around in circles, and not even sure if I’m even close to doing it right.

A good article about this would be great … pointers always appreciated …

Thanks …

>Solution :

To fix the error add the generic types and (bridge) cast the result type

var prefsDictionary: Dictionary<String,Any> {
    let path = Bundle.main.path(forResource: "Preferences", ofType: "plist")!
    return NSDictionary(contentsOfFile: path)! as [String:Any]
}

However the API NSDictionary(contentsOfFile is strongly discouraged in Swift. The recommended API is PropertyListSerialization and it’s also recommended to use the URL related API

var prefsDictionary: Dictionary<String,Any> {
    let url = Bundle.main.url(forResource: "Preferences", withExtension: "plist")!
    let data = try! Data(contentsOf: url)
    return try! PropertyListSerialization.propertyList(from: data, format: nil) as! [String:Any]
}

But there is a still better way. Declare a struct where the members represent the keys for example

struct MyPrefs: Decodable {
    var versionString : String
}

and decode the data with PropertyListDecoder

var preferences: MyPrefs {
    let url = Bundle.main.url(forResource: "Preferences", withExtension: "plist")!
    let data = try! Data(contentsOf: url)
    return try! PropertyListDecoder().decode(MyPrefs.self, from: data)
}

Side note:

Force unwrapping the types is fine in this case. The code must not crash because the files in the application bundle are immutable at runtime. A crash reveals a design mistake which can be fixed instantly.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading