20

I'm creating a UITableViewController with Swift language and in a method

override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell?

I'm getting this error

NSIndexPath? does not have a member name 'row' error in Swift

and I don't understand why.

This is my code

import UIKit

class DPBPlainTableViewController: UITableViewController {

    var dataStore: NSArray = NSArray()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.dataStore = ["one","two","three"]

        println(self.dataStore)
    }


    // #pragma mark - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {

        // Return the number of sections.
        return 1
    }

    override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {

        // Return the number of rows in the section.
        return self.dataStore.count
    }


    override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell? {
        let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")

        cell.textLabel.text = self.dataStore[indexPath.row]

        return cell
    }

}

Then, how can set the cell.text with the array dataStore element?

nhgrif
  • 61,578
  • 25
  • 134
  • 173
dpbataller
  • 1,197
  • 4
  • 12
  • 23
  • 2
    "and I don't understand why" Because `NSIndexPath?` is not `NSIndexPath`. Remember, an Optional is an enum _wrapping_ the "real" value. To access the value, you need to unwrap it. – matt Jun 04 '14 at 18:39
  • The extensions to NSIndexPath for row and section are extended in UIKit in the UITableView.h file. – Alex Zavatone Mar 18 '16 at 15:25

4 Answers4

15

You can either unwrap the optional indexPath parameter with if let...:

if let row = indexPath?.row {
    cell.textLabel.text = self.dataStore[row]
}

or if you're sure indexPath isn't nil, you can force the unwrapping with !:

cell.textLabel.text = self.dataStore[indexPath!.row]

Just keep in mind that indexPath! on a nil value will be a runtime exception, so it's better practice to unwrap it as in the first example.

Nate Cook
  • 92,417
  • 32
  • 217
  • 178
  • 2
    Alternatively, if you are sure that `indexPath` is defined (i.e. not `nil`), you can unwrap it inline using the `!` syntax like so: `dataStore[indexPath!.row]` – aapierce Jun 04 '14 at 15:15
  • Thanks Nate Cook, I begin to understand the meaning of an ? in a variable. I'll continue testing. Thanks to all – dpbataller Jun 04 '14 at 19:55
  • No, no, no, no. The extensions to NSIndexPath for row and section are extended in UIKit in the UITableView.h file. – Alex Zavatone Mar 18 '16 at 15:24
  • @AlexZavatone Yes. However, the issue here is that OP is trying to access the row and section of an `Optional`, not that `NSIndexPath` doesn't have those properties. – Nate Cook Mar 18 '16 at 15:30
5

You can use the optional chaining syntax for this call (setting cell.textLabel.text to nil if indexPath is nil):

cell.textLabel.text = indexPath? ? self.dataStore[indexPath!.row] : nil

or explicitly unwrap it (causing a runtime error if indexPath is nil):

cell.textLabel.text = self.dataStore[indexPath!.row]

or use the more verbose if let syntax suggested by @NateCook.

ipmcc
  • 29,581
  • 5
  • 84
  • 147
  • 1
    Does the optional chaining work in your first example? I think `indexPath?.row` evaluates as an `Int?` and won't work as a subscript. – Nate Cook Jun 04 '14 at 18:26
  • @NateCook: You're right... you have to explicitly test. I've edited to correct. It looks like the optional chaining subscripting is not smart enough to propagate the `nil` through a subscript in the optional chain. It occurs to me that you might be able to make an extension on `dataStore` that implements `subscript(index: Int?) -> String?`, but I'll leave that as an exercise for the reader. – ipmcc Jun 05 '14 at 11:03
0

Use .item instead of .row

cell.textLabel.text = self.dataStore[indexPath.item]

Akshay Phulare
  • 1,359
  • 2
  • 10
  • 15
-2

All you have to do to access NSIndexPath's row and section are to import the header of the file where these extensions to the base NSIndexPath class are defined.

If you don't, your class will act like row and section just don't exist on an instance of NSIndexPath.

The row and section extensions to NSIndexPath are declared within the UIKit framework inside UITableView.h.

To fix this problem, all you need to do is import UITableView.h into your class. That's it.

Here is where the extensions to the class are defined in UITableView.h in Objective-C. I'm sure Swift has a similar section.

// This category provides convenience methods to make it easier to use an NSIndexPath to represent a section and row
@interface NSIndexPath (UITableView)

+ (instancetype)indexPathForRow:(NSInteger)row inSection:(NSInteger)section;

@property (nonatomic, readonly) NSInteger section;
@property (nonatomic, readonly) NSInteger row;

@end
JAL
  • 41,701
  • 23
  • 172
  • 300
Alex Zavatone
  • 4,106
  • 36
  • 54
  • Hah. Thanks JAL. You and I were both adding the code block formatting at the same time but you beat me to it. – Alex Zavatone Mar 18 '16 at 15:47
  • 1
    This isn't the issue. `UITableView.h` is already imported by importing UKit. This question is old and not relevant anymore anyway because the `UITableViewDataSource` method parameters aren't optional anymore. – dan Mar 18 '16 at 15:51
  • If you are accessing properties within NSIndexPath that are extended through UITableView.h or UICollectionView.h, and you are accessing these properties outside of your view controller, then you need to know what file to import and why. I just needed to do this in a class, so I don't know why you're telling me it's irrelevant. I don't want to import all of UIKit into my data container if all I need is one category off of NSIndexPath. Isn't that reasonable? – Alex Zavatone Mar 18 '16 at 17:26
  • 1
    Well the asker is accessing these properties from inside his view controller and is already importing UIKit so I'm not sure how anything you are saying is relevant to this situation. – dan Mar 18 '16 at 17:51