Dave G Dave G - 10 days ago 4
iOS Question

Strange Swift Syntax, What's It Doing?

So I'm following this tutorial for In-App-Purchases. Here are a few things I don't get:


  • For the table, in the rowAtIndexPath they use a handler, what is that?

  • They put all the table code in an extension. I don't know why.

  • There's also a weird "buyButtonHandler?(product!)" call on button tap



I'd appreciate any clarification on any of the above points. Below is the table code where they put the table in an extension:

extension MasterViewController {
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return products.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ProductCell

var products = [SKProduct]() //This is actually declared elsewhere
let product = products[(indexPath as NSIndexPath).row]

cell.product = product
cell.buyButtonHandler = { product in
RageProducts.store.buyProduct(product)
}

return cell
}
}


And the above code includes the strange that I'm looking for help understanding:

cell.buyButtonHandler = { product in
RageProducts.store.buyProduct(product)
}


The table cell has a button and in the cell class this is its code:

func buyButtonTapped (_ sender: AnyObject) {
buyButtonHandler?(product!)
}


It references the below line. This button code/reference is gibberish to me:

var buyButtonHandler: ((_ product: SKProduct) -> ())?


I don't get what that buyButtonHandler is doing, it's like 50% parenthesis! Lastly, I'm including the below var declaration, in case it helps for context:

var product: SKProduct? {
didSet {
guard let product = product else { return }

textLabel?.text = product.localizedTitle

if RageProducts.store.isProductPurchased(product.productIdentifier) {
//Setup
} else {
//Alternate setup
}
}
}

Answer

The stuff you're seeing is fairly standard Swift.

Bullet #1: It looks like the table view cells hold a closure, which is a block of code that they save and run later. The IBAction for the cell's button just invokes the handler block. (The term block and closure are interchangeable. Objective-C calls them blocks, Swift calls them closures.)

So the code in cellForRowAtIndexPath is installing a closure into the cell. That lets you configure your cells from outside. It's a neat trick.

Bullet #2: It's considered good form to place the methods that implement a protocol in an extension. That way they're all grouped together and easy to find. It also makes the extension into a nice modular block of code. The extension is probably for the UITableViewDelegate and/or UITableViewDataSource protocol methods.

Bullet #3: Same thing as #1. The cell stores a closure (block of code) in a variable, and when the user taps a button, the button's IBAction invokes the stored closure.

Bullet 1 and Bullet 3 mean that in the table view data source's cellForRowAtIndexPath method you can provide a custom block of code for each cell that gets invoked when the cell's button is tapped. The code in the button IBAction invokes the stored closure and passes it the current product.

Comments