Swift Plist Reading and Initializing Arrays as Objects for Key
Asked Answered
R

2

13

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'

Next option:

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

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

Finally:

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.

Refreshing answered 13/6, 2014 at 16:2 Comment(2)
the row and section properties of NSIndexPath are defined in UIKit and not Foundation. Does adding import UIKit to your file solve this issue?Stein
@JackWu UIKit has already been imported; it is the first lineRefreshing
H
19

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>
}
Hyaloplasm answered 13/6, 2014 at 20:24 Comment(0)
H
1

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

Handler answered 15/8, 2014 at 17:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.