I've already used this method in Swift 2
var myDict: NSDictionary?
if let path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist") {
myDict = NSDictionary(contentsOfFile: path)
}
But don't know how to read plist in Swift3 without using NSDictionary(contentsOfFile: path)
The native Swift way is to use PropertyListSerialization
if let url = Bundle.main.url(forResource:"Config", withExtension: "plist") {
do {
let data = try Data(contentsOf:url)
let swiftDictionary = try PropertyListSerialization.propertyList(from: data, format: nil) as! [String:Any]
// do something with the dictionary
} catch {
print(error)
}
}
You can also use NSDictionary(contentsOf:
with a type cast:
if let url = Bundle.main.url(forResource:"Config", withExtension: "plist"),
let myDict = NSDictionary(contentsOf: url) as? [String:Any] {
print(myDict)
}
but you explicitly wrote: without using NSDictionary(contentsOf...
Basically don't use NSDictionary
without casting in Swift, you are throwing away the important type information.
Meanwhile (Swift 4+) there is still more comfortable PropertyListDecoder
which is able to decode Plist directly into a model.
PropertyListDecoder can be used to decode plist file directly to Objects.
1: Sample Plist File (sample.plist)
<?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>key1</key>
<string>valua for key1</string>
<key> key2</key>
<string>valua for key1</string>
<key>CustomClass1</key>
<dict>
<key>customClass1_key</key>
<string>customClass1_value</string>
</dict>
<key>CustomClass2</key>
<dict>
<key>customClass2_kek</key>
<string>customClasse_value</string>
</dict>
</dict>
</plist>
2: Corespoinding Models for plist
struct PlistConfiguration: Codable {
var key1:String?
var customClass1Obj: CustomClass1?
var customClass2Obj: CustomClass2?
private enum CodingKeys : String, CodingKey {
case key1 = "key1"
case customClass1Obj = "CustomClass1"
case customClass2Obj = "CustomClass2"
}
}
2.1: Nested Model
struct CustomClass1: Codable {
var customClass1_key:String?
private enum CodingKeys : String, CodingKey {
case customClass1_key = "customClass1_key"
}
}
2.2: Nested Model
struct CustomClass2: Codable {
var customClass2_key: String?
private enum CodingKeys : String, CodingKey {
case customClass2_key = "customClass2_key"
}
}
3: Read Plist from main app bundle
func parseConfig() -> PlistConfiguration {
let url = Bundle.main.url(forResource: "sample", withExtension: "plist")!
let data = try! Data(contentsOf: url)
let decoder = PropertyListDecoder()
return try! decoder.decode(PlistConfiguration.self, from: data)
}
In a modern Swift environment I use stuff like this:
import Foundation
public extension Bundle {
func plist<As>(from resource: String) -> As? where As: Decodable {
guard let plist = Bundle.main.url(forResource: resource, withExtension: "plist") else { return nil }
let decoder = PropertyListDecoder()
do {
let data = try Data(contentsOf: plist)
return try decoder.decode(As.self, from: data)
} catch { return nil }
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With