4

I'm having a real problem in getting a simple command line OSX Swift program to read data from a simple .plist XML file - in fact the program can't even see the file despite it seemingly being present and copied into the bundle.

The data is handled by a class that is meant to read and copy the plist data into an array where it can be retrieved without the plist needing to be queried again:

//  Datadump.swift

import Foundation

class Datadump {

    //array of dictionaries containing string keys and any datatype of value
    var dataArray : Array<Dictionary<String,AnyObject>>

    init () {
        //copies data from plist
        let bundle = NSBundle.mainBundle()
        let path = bundle.pathForResource("Hardware", ofType: "plist")
        //check file located
        print("File Location: \(path)   " )
        let productArray = NSArray(contentsOfFile: path!)

        dataArray = productArray as! Array<Dictionary<String,AnyObject>>
        }

    //returns tuple based on product id
    func getData(prodId: Int) -> (String, Int) {

        //returned vars
        var price = 0
        var name : String = ""

        //iterate through array until relevant product is found
        for dict in dataArray{
            if dict["ProdId"] as! Int == prodId{
                price = dict["Price"] as! Int
                name = dict["Name"] as! String
                break
            }
        }
        return (name, price);
    }

}

However the program fails with a fatal error as soon as the:

 let productArray = NSArray(contentsOfFile: path!)

line is reached, with the output:

File Location: nil fatal error: unexpectedly found nil while unwrapping an Optional value

The print statement confirms that the program cannot find the Hardware.plist file.

The main program just creates and instance of the class and runs a function but obviously fails as soon as the init () of the class runs as mentioned above:

//  main.swift

import Foundation

//creates instance of Datadump class
var myData = Datadump ()

//queries data, copies result to tuple
let (nam, pri) = myData.getData(123)
print ("Name: \(nam)  Price: \(pri)")

The Hardware.plist file looks like this:

<?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>Name</key>
        <string>Intel i7 CPU</string>
        <key>Price</key>
        <integer>13995</integer>
        <key>ProdId</key>
        <integer>123</integer>
    </dict>
    <dict>
        <key>Name</key>
        <string>19&quot; Monitor</string>
        <key>Price</key>
        <integer>9995</integer>
        <key>ProdId</key>
        <integer>456</integer>
    </dict>
    <dict>
        <key>Name</key>
        <string>8GB DDR Ram</string>
        <key>Price</key>
        <integer>4595</integer>
        <key>ProdId</key>
        <integer>789</integer>
    </dict>
</array>
</plist>

I've tried everything I can think of including looking at the settings for the .plist file and reimporting it in different ways.

Any help would be greatly appreciated!

Many thanks, Kwangle

Kwangle
  • 329
  • 2
  • 13
  • "despite it seemingly being present and copied into the bundle" But there is no "seemingly". Is it or isn't it? My bet is that it isn't. You can open up the built app and look directly into it and see, you know. – matt Dec 06 '15 at 17:46
  • its visible in the file navigator and I used both the 'Add File' feature and directly dragging it into the project window. How would I check this definitively? Thanks for your help. – Kwangle Dec 06 '15 at 17:48
  • "I used bot the 'Add File' feature and directly dragging it into the project window" That adds it to the _project_, but does not necessarily add it to the _app target_. – matt Dec 06 '15 at 17:52
  • Here is a link to a screen grab of the file list: http://imgur.com/hMgLoHI – Kwangle Dec 06 '15 at 18:03
  • How do I properly add the .plist file to the app? Despite hours of research and experimentation I've been unable to find any info on how to do this. Thanks for your help. – Kwangle Dec 06 '15 at 18:05

2 Answers2

12

Normally, this is what you do:

In the Project Navigator (on the left), select the plist file. In the File Inspector (on the right), examine the target membership settings. You will probably find that the app target is not checked. This means that the plist file is not being copied into the app bundle when you build it. Check the app target, and all will be well.

But you can't do that, because this is a command line program. It is an executable and no more. There is no bundle to copy into. What you're trying to do is metaphysically impossible.

matt
  • 447,615
  • 74
  • 748
  • 977
  • 1
    Compare my answer here (with screen shot): http://stackoverflow.com/a/30146303/341994 – matt Dec 06 '15 at 17:51
  • 1
    Yes but the App checkbox is greyed out and cannot be checked. – Kwangle Dec 06 '15 at 18:00
  • 1
    Here is a screen grab of the plist file info, note the greyed out checkbox that can't be changed: http://imgur.com/ghMjagl – Kwangle Dec 06 '15 at 18:09
  • 1
    Wow, @Kwangle, I've never seen that before. Nevertheless, what I'm saying is right; you _must_ get that checkbox checked, or the file won't end up copied into the app when you build. I suggest you start with a whole new file: make a blank text file right within the Xcode and copy the plist contents into it, maybe. – matt Dec 06 '15 at 18:38
  • 1
    I know. It's driving me crazy. Seems like its something basic I'm missing or a setting that needs to be set correctly. I though it might be because the app is a command line program (which may not be compatible with .plist files) but I don't think that's the case. I'm truly stumped, and as this is the second time I've posted about this, without success, I'm thinking its real oddity. Presumably writing XML files in a text editor and copying them between programs is normal. Many thanks for looking at this. – Kwangle Dec 06 '15 at 18:43
  • 1
    Oh, well, it _is_ because it's a command line program. You have no bundle to copy into. – matt Dec 06 '15 at 18:49
  • 1
    Revised my answer. Sorry I didn't notice about the command line part before. – matt Dec 06 '15 at 18:53
  • Thanks Matt, That explains it. – Kwangle Dec 06 '15 at 22:04
3

In Swift 3.0

Get .plist path

func getPath() -> String {
        let plistFileName = "fileName.plist"
        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        let documentPath = paths[0] as NSString
        let plistPath = documentPath.appendingPathComponent(plistFileName)
        return plistPath
}

To check whether .plist file exist or not in Documents directory.

  let plistPath = self.getPath()
        if FileManager.default.fileExists(atPath: plistPath) {
           //fileName.plist Found
        }
Ashok R
  • 16,501
  • 8
  • 65
  • 66
  • So where must we store the plist in order to use this? User/Documents? This works! But how could this possibly be put into production code? – Fluidity Nov 12 '16 at 15:43
  • File System Programming Guide and `FileManager`? I would like to make a folder in the Documents (or elsewhere) to store plists for each project. – Fluidity Nov 12 '16 at 15:52