How to Read Plist Without Using Nsdictionary in Swift

How to Read Plist without using NSDictionary in Swift?

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.

How do I get a plist as a Dictionary in Swift?

In swift 3.0 Reading from Plist.

func readPropertyList() {
var propertyListFormat = PropertyListSerialization.PropertyListFormat.xml //Format of the Property List.
var plistData: [String: AnyObject] = [:] //Our data
let plistPath: String? = Bundle.main.path(forResource: "data", ofType: "plist")! //the path of the data
let plistXML = FileManager.default.contents(atPath: plistPath!)!
do {//convert the data to a dictionary and handle errors.
plistData = try PropertyListSerialization.propertyList(from: plistXML, options: .mutableContainersAndLeaves, format: &propertyListFormat) as! [String:AnyObject]

} catch {
print("Error reading plist: \(error), format: \(propertyListFormat)")
}
}

Read More
HOW TO USE PROPERTY LISTS (.PLIST) IN SWIFT.

How can I read data from a PLIST in Swift?

First, your Root object in the plist is an NSArray not a NSDictionary.

Second, if you want to use KVC on Foundation Collections (I don't believe this works with Swift's Array) you need to call valueForKeyPath.

let chapterPath = NSBundle.mainBundle().pathForResource("chapterMapping", ofType: "plist")
if let arrayOfItems: [AnyObject] = NSArray(contentsOfFile: chapterPath!) {
let chapterNames: [String] = arrayOfItems.valueForKeyPath("chapterName") as NSArray as [String]
let pageNumbers: [Int] = arrayOfItems.valueForKeyPath("pageNumber") as NSArray as [Int]
}

Third, the swift-y way of doing this would be with the map function, but arrayOfItems would need to be a strongly-defined type and it might be more work than it's worth. Example:

let array: [ChapterMetaData] = // define it here
let chapterImages = array.map { $0.chapterImage }

Swift 5: How to read variables in plist files?

Your property list format is not very convenient. As you want an array anyway create the property list with an array for key animals (and name all keys lowercased)

<?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>animals</key>
<array>
<dict>
<key>name</key>
<string>Tiger</string>
<key>picture</key>
<string>tiger_running</string>
</dict>
<dict>
<key>name</key>
<string>Jaguar</string>
<key>picture</key>
<string>jaguar_sleeping</string>
</dict>
</array>
</dict>
</plist>

Then create two structs

struct Root : Decodable {
let animals : [Animal]
}

struct Animal : Decodable {
let name, picture : String
}

and the data source array

var animals = [Animal]()

And decode the stuff

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let url = Bundle.main.url(forResource: "Animals", withExtension:"plist")!
do {
let data = try Data(contentsOf: url)
let result = try PropertyListDecoder().decode(Root.self, from: data)
self.animals = result.animals
} catch { print(error) }
}

PropertyListDecoder and PropertyListSerialization are state of the art in Swift. The NSDictionary/NSArray API is objective-c-ish and outdated.

read plist file from outside project directory

You can only read files from your bundle or from one of the other sandboxed directories. You could read/write plist files from your documents directory, for example.

Loop through plist in swift

First of all do not use the NSDictionary/NSArray API to read property lists in Swift at all.

NSDictionary(contentsOfFile returns an optional, you have to unwrap it to use it in a loop, that's what the error is telling you.

And there is no dictionary but the root object in the property list. All other collection types are arrays (clearly stated in the screenshot)

The highly recommended API to read property lists in Swift is PropertyListSerialization or even PropertyListDecoder in Swift 4+

let url = Bundle.main.url(forResource: "LetterPoints", withExtension: "plist")!
let data = try! Data(contentsOf: url)
let letterPoints = try! PropertyListSerialization.propertyList(from: data, format: nil) as! [String:[[Int]]]
for (_, outerArray) in letterPoints {
for innerArray in outerArray {
let x = innerArray[0]
let y = innerArray[1]
print("x: \(x); y: \(y)")
}
}

As the property list is (immutable) in the application bundle force unwrapping the optionals is fine. If the code crashes it reveals a design mistake which can be fixed at once.

Alternative to the objective-NSDictionary(contentsOfFile) method in swift

The (recommended) alternative is PropertyListSerialization

let url = Bundle.main.url(forResource: Constants.plistName, withExtension: Constants.plistType)!
let data = try! Data(contentsOf: url)
let myDictionary = try! PropertyListSerialization.propertyList(from: data, format: nil) as! [String:Any]

I can't Unwrap a value from a plist key value

Depending on your style preference...

var nsDictionary: NSDictionary?
if let path = Bundle.main.path(forResource: "AppData", ofType: "plist") {
nsDictionary = NSDictionary(contentsOfFile: path)
}
if let dict = nsDictionary {
let vTitle = dict["LbVacationsTitle"] as? String
if let vt = vTitle {
// ...
}
}

...or...

var nsDictionary: NSDictionary?
if let path = Bundle.main.path(forResource: "AppData", ofType: "plist") {
nsDictionary = NSDictionary(contentsOfFile: path)
}
guard let dict = nsDictionary else {
print("Couldn't get a valid dictionary")
return
}
let vTitle = dict["LbVacationsTitle"] as? String
guard let vt = vTitle else {
print("Couldn't find a string matching LbVacationsTitle")
return
}
// ...


Related Topics



Leave a reply



Submit