Value of Type Bool has no subscripts. What does it mean in context?

Hello:


I am getting the error Value of Type Bool has no subscripts in bellow code but can't figure out how to resolve it. Any help will be appreciated.

The error is on line # 19. That line is supposed to call first record and continue with subsequest records in the plist.



func preloadData() {
        print ("Call to preload Data Starts Now")
        let preloadedDataKey = "didPreloadData"
        UserDefaults.standard.removeObject(forKey: preloadedDataKey)
        let userDefaults = UserDefaults.standard
        if userDefaults.bool(forKey: preloadedDataKey) == false {
            guard Bundle.main.url(forResource: "SCQ", withExtension:"plist") != nil else {
            return
        }
            var propertyListFormat = PropertyListSerialization.PropertyListFormat.xml //format the property list
            var plistData: [String: AnyObject] = [:] // the data
            print(plistData)
            let plistUrl = Bundle.main.url (forResource: "SCQ", withExtension: "plist") // path of the data
            let fileManager = FileManager.default
            if let plistData = NSData(contentsOf: plistUrl!) {
                var format = PropertyListSerialization.PropertyListFormat.xml
                do {
                    let items = PropertyListSerialization.propertyList(plistData, isValidFor: format)
                    let item = items[0]// error here
          
                    if let attribute = item ["answer"] {
                        print (item)
                    }
                    if let attribute = item ["distractor1"] {
                                 print (item)
                    }
                    if let attribute = item ["distractor2"] {
                                 print (item)
                    }
                    if let attribute = item ["distractor3"] {
                                 print (item)
                    }
                    if let attribute = item ["distractor4"] {
                                 print (item)
                    }
                    if let attribute = item ["distractor5"] {
                                print (item)
                    }
                    if let attribute = item ["grade"] {
                                print (item)
                    }
                    if let attribute = item ["id"] {
                                print (item)
                    }
                    if let attribute = item ["answer"] {
                                print (item)
                    }
                    if let attribute = item ["qid"] {
                                print (item)
                    }
                    if let attribute = item ["question"] {
                                print (item)
                    }
                    if let attribute = item ["qValue"] {
                                 print (item)
                     }
                     if let attribute = item ["skill"] {
                                 print (item)
                     }
                     if let attribute = item ["subject"] {
                                 print (item)
                    }
                     if let attribute = item ["topic"] {
                                 print (item)
                    }
                                     
                } catch {
                    print(error)
                }
            }

Accepted Reply

As already noted, `PropertyListSerialization.propertyList(_:isValidFor:)` returns Bool, which is used when serializing your Objective-C object and checks if it is serializable with given format.


In your case, you want to deserialize a plist Data, so it is completely useless in your case.


You need to use this method.

class func propertyList(from: Data, options: PropertyListSerialization.ReadOptions, format: UnsafeMutablePointer<PropertyListSerialization.PropertyListFormat>?) -> Any



You can use it like this:

func preloadData() {
    print("Call to preload Data Starts Now")
    let preloadedDataKey = "didPreloadData"
    UserDefaults.standard.removeObject(forKey: preloadedDataKey)
    let userDefaults = UserDefaults.standard
    if userDefaults.bool(forKey: preloadedDataKey) == false {
        guard let plistUrl = Bundle.main.url(forResource: "SCQ", withExtension:"plist") else {
            return
        }
        do {
            let plistData = try Data(contentsOf: plistUrl)
            var format = PropertyListSerialization.PropertyListFormat.xml
            if
                let items = try PropertyListSerialization.propertyList(from: plistData, format: &format) as? [[String: Any]],
                let item = items.first
            {
                if let answer = item["answer"] {
                    print(answer)
                }
                //...
            } else {
                print("plist is not an Array of Dictionary or is empty")
            }
        } catch {
            print(error)
        }
    }
}

Replies

Looking at the doc, you see that propertyList(Any, isValidFor: PropertyListSerialization.PropertyListFormat) returns a Bool:


class func propertyList(Any, isValidFor: PropertyListSerialization.PropertyListFormat) -> Bool

Returns a Boolean value that indicates whether a given property list is valid for a given format.


you see that items is a Bool:

let items = PropertyListSerialization.propertyList(plistData, isValidFor: format)

It is not an array, so you cannot call items[0].

Hence the eror.


May be you want to use

class func data(fromPropertyList plist: Any, format: PropertyListSerialization.PropertyListFormat, options opt: PropertyListSerialization.WriteOptions)

In doc:

Declaration

class func data(fromPropertyList plist: Any, format: PropertyListSerialization.PropertyListFormat, options opt: PropertyListSerialization.WriteOptions) throws -> Data

Parameters

plist

A property list object.

format

A property list format. For possible values, see

PropertyListSerialization.PropertyListFormat
.
opt

The

opt
parameter is currently unused. No options should be specified.
error

If the method does not complete successfully, upon return contains an

NSError
object that describes the problem.

Return Value

An

NSData
object containing
plist
in the format specified by
format
.

As already noted, `PropertyListSerialization.propertyList(_:isValidFor:)` returns Bool, which is used when serializing your Objective-C object and checks if it is serializable with given format.


In your case, you want to deserialize a plist Data, so it is completely useless in your case.


You need to use this method.

class func propertyList(from: Data, options: PropertyListSerialization.ReadOptions, format: UnsafeMutablePointer<PropertyListSerialization.PropertyListFormat>?) -> Any



You can use it like this:

func preloadData() {
    print("Call to preload Data Starts Now")
    let preloadedDataKey = "didPreloadData"
    UserDefaults.standard.removeObject(forKey: preloadedDataKey)
    let userDefaults = UserDefaults.standard
    if userDefaults.bool(forKey: preloadedDataKey) == false {
        guard let plistUrl = Bundle.main.url(forResource: "SCQ", withExtension:"plist") else {
            return
        }
        do {
            let plistData = try Data(contentsOf: plistUrl)
            var format = PropertyListSerialization.PropertyListFormat.xml
            if
                let items = try PropertyListSerialization.propertyList(from: plistData, format: &format) as? [[String: Any]],
                let item = items.first
            {
                if let answer = item["answer"] {
                    print(answer)
                }
                //...
            } else {
                print("plist is not an Array of Dictionary or is empty")
            }
        } catch {
            print(error)
        }
    }
}

Hello 00per:


Thanks for the code. Seems I still need to add spme code because the print statements for the items do not print.

What do you see as the reason? Could it be because the keys are not arranged in the same order as in the code?


<?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">

<array>

<dict>

<key>zgrade</key>

<string>2</string>

<key>ztopic</key>

<string>SPELLING</string>

<key>z_pk</key>

<string>2</string>

<key>zdistractor1</key>

<string>R</string>

<key>zqid</key>

<string>1</string>

<key>zdistractor2</key>

<string>Wr</string>

<key>zdifficultyLevel</key>

<string>92</string>

<key>zdistractor3</key>

<string>DK</string>

<key>zid</key>

<string>403</string>

<key>zdistractor4</key>

<string>not here</string>

<key>zqValue</key>

<string>1000</string>

<key>z_ent</key>

<string>3</string>

<key>zdistractor5</key>

<string>not here</string>

<key>z_opt</key>

<string>4</string>

<key>zskill</key>

<string>Spell Terrible Correctly</string>

<key>zquestion</key>

<string>If the word is spelled correctly choose R. If it is not spelled correctly choose Wr. If you don’t know choose DK. The noise was TERRIBLE.</string>

<key>zanswer</key>

<string>1</string>

<key>zsubject</key>

<string>ELA</string>

</dict>

<array>

</plist>

The second `<array>` should be `</array>`, and assuming that is fixed.


Your plist does not have an entry for "answer", but has "zanswer". All the same for other keys.

I do not know why all the keys are prefixed with "z", but you need to specify the keys exactly as in the plist.


                if let answer = item["zanswer"] {
                    print(answer)
                }

Hello 00per:


I found out the reason for the lack of print output. The plist keys were inserted with the "z" sqlite letter appended to each field.


However only the first record is printed so Included a loop

However only the first record is printed.

In your original code, you specified `items[0]` and has no loop going through all the `items`, my code has followed it.

By the way, if you use `Codable`, you can write something like this:

struct Scq: Codable {
    var answer: String
    var distractor1: String
    var distractor2: String
    var distractor3: String
    var distractor4: String
    var distractor5: String
    var grade: String
    var id: String
    var qid: String
    var question: String
    var qValue: String
    var skill: String
    var subject: String
    var topic: String
    
    enum CodingKeys: String, CodingKey {
        case answer = "zanswer"
        case distractor1 = "zdistractor1"
        case distractor2 = "zdistractor2"
        case distractor3 = "zdistractor3"
        case distractor4 = "zdistractor4"
        case distractor5 = "zdistractor5"
        case grade = "zgrade"
        case id = "zid"
        case qid = "zqid"
        case question = "zquestion"
        case qValue = "zqValue"
        case skill = "zskill"
        case subject = "zsubject"
        case topic = "ztopic"
    }
}

func preloadScq() {
    print("Call to preload Data Starts Now")
    let preloadedDataKey = "didPreloadData"
    UserDefaults.standard.removeObject(forKey: preloadedDataKey)
    let userDefaults = UserDefaults.standard
    if userDefaults.bool(forKey: preloadedDataKey) == false {
        guard let plistUrl = Bundle.main.url(forResource: "SCQ", withExtension:"plist") else {
            return
        }
        do {
            let plistData = try Data(contentsOf: plistUrl)
            let scqList = try PropertyListDecoder().decode([Scq].self, from: plistData)
            print(scqList)
        } catch {
            print(error)
        }
    }
}