Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Plist Reading and Initializing Arrays as Objects for Key

I'm developing a small UITableView-based app in Swift. I started out hard-coding some basic arrays as such (included is the basic class declaration and the UITableView outlet declaration:

import UIKit

class ScenesViewController: UITableViewController {

@IBOutlet var myTableView: UITableView

var sceneName = ["Scene 1", "Scene 2", "Scene 3", "Scene 4", "Scene 5"]
var sceneDetail = ["Hi", "Hello", "Bye", "My Bad", "Happy"]

This code is inside a UITableViewController which has an outlet that is all hooked up to a UITableView. I have created a plist for this called "Scenes.plist" and have attempted to replace the code. In Objective-C, I would do this:

NSString *path = [[NSBundle mainBundle] pathForResource:@"Scenes" ofType:@"plist"];

NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
sceneName = [dict objectForKey:@"SceneName"];
sceneDetail = [dict objectForKey:@"SceneDetail"];

I started doing this in Swift like this:

let path = NSBundle.mainBundle().pathForResource("Scenes", ofType:"plist")

let dict = NSDictionary(contentsOfFile:path)  // Error: 'ScenesViewController.Type' does not have a member named 'path'

I scoured the Internet for a solution but was unable to find one. So I took up the next course of action - an 'almost' one-liner:

let dict = NSDictionary(contentsOfFile: 
    NSBundle.mainBundle().pathForResource("Scenes", ofType:"plist"))

var sceneName = dict["SceneName"] // Error: 'ScenesViewController.Type' does not have a member named 'dict'

Now, the only solution I can almost resort to is having all of that on one line for each array:

var sceneName = NSDictionary(contentsOfFile: 
    NSBundle.mainBundle().pathForResource("Scenes", ofType:"plist").objectForKey("SceneName")
// Warning: Variable 'sceneName' inferred to have type 'AnyObject!', which may be unexpected
// Fix-It: Add an explicit type annotation to silence this warning (var sceneName: AnyObject! = ...)

So I added the explicit type annotation just to find out that there were more errors. Inside the cellForRowAtIndexPath: function:

override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell? {
    let cell: UITableViewCell = tableView!.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) as UITableViewCell

    // Configure the cell...

    cell.textLabel.text = sceneName[indexPath!.row] // Error: could not find member 'row'
    return cell
}

There are actually two errors; I do not know whether this is accidentally duplicated or not. The next error(s) are in the the prepareForSegue:sender: method:

override func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {
    if segue?.identifier == "ShowDetails" {
        var detailVC: ScenesDetailViewController = segue!.destinationViewController as ScenesDetailViewController

        var myIndexPath: NSIndexPath = myTableView.indexPathForSelectedRow()

        var row: Int = myIndexPath.row

        detailVC.detailModal = [sceneDetail[row], sceneName[row]] // Error: Could not find an overload for 'subscript' that accepts the supplied arguments
    }
}

Again, there are two errors; however, I believe this is because sceneDetail and sceneName are both used. There seem to be errors all over the place with reading and using plists in files that aren't there (or that I haven't come across) in Objective-C.

like image 629
virenabhyankar Avatar asked Jun 13 '14 16:06

virenabhyankar


2 Answers

To resolve your initial problem, put the code inside a func, like:

var memoryName = []
var memoryDetail = []

override func awakeFromNib() {
    super.awakeFromNib()

    let path = NSBundle.mainBundle().pathForResource("Memories", ofType:"plist")
    let dict = NSDictionary(contentsOfFile:path)

    memoryName = dict["MemoryName"] as Array<String>
    memoryDetail = dict["MemoryDetail"] as Array<String>
}
like image 55
paiv Avatar answered Nov 08 '22 02:11

paiv


You can solve this by moving your code into a function that is called after your objects initialization.

Properties are initialized as part of the initialization of your object. You have created path and dict as properties and are trying to initialize them with code that does not meet the requirements for initialization.

An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.

--The Swift Programming Language - Initialization - Safety check 4

Also, it seems you only want and need sceneName and sceneDetail to be properties, so you can move them completely within the scope of the method you use to populate sceneDetail and sceneName after initialization.

It's hard for me to say what the subscript error is exactly because I don't see what type of data detailVC.detailModal is supposed to contain; even so, it seems the wrong type of object is being passed into the detailModal array. A good little description of this issue is Swift and arrays

like image 1
timeSmith Avatar answered Nov 08 '22 03:11

timeSmith