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
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.