Adding IBInspectable to a superclass in Swift

Adding IBInspectable to a UICollectionViewCell subclass

One very useful feature I find is to add a IBInspectable background image to my views. In an entertainment app I’m currently building there is a designable cell that looks like this:

@IBDesignable
class TrailerCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var lengthLabel: UILabel!
    @IBInspectable weak var bgImage: UIImage! {
        didSet {
            imgView.frame = self.bounds
            self.addSubview(imgView)
            imgView.image = bgImage
            imgView.contentMode  = UIViewContentMode.Center
            self.sendSubviewToBack(imgView)
        }
    }
}

what’s great about this is it gives me a very easy way to set a background image on my TrailerCollectionViewCell. It shows right up in my storyboards so I can easily set it to my “play” icon graphic.

IBInspectable in Xcode 7

Notice on the top-right of the attributes inspector it adds a “Bg Image” dropdown. When this is changed the didSet method above is triggered and it updates the view inside of Xcode, and when the iOS app runs. This is happening because the decoding from interface builder will also trigger didSet in the same fashion that interface builder does.

What’s not so handy about this approach is that I have two other types of CollectionViewCell subclasses that need similar functionality. So, I have these three classes inherit from one superclass that implements the functionality.

import Foundation
import UIKit
 
class MediaCollectionViewCell: UICollectionViewCell {
    @IBOutlet weak var titleLabel: UILabel!
    var imgView = UIImageView()
    @IBInspectable weak var bgImage: UIImage! {
        didSet {
            imgView.frame = self.bounds
            self.addSubview(imgView)
            imgView.image = bgImage
            imgView.contentMode  = UIViewContentMode.Center
            self.sendSubviewToBack(imgView)
        }
    }
}
 
class TrailerCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var lengthLabel: UILabel!
}
 
class ReviewCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var ratingLabel: UILabel!
}
 
class PlaythroughCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var lengthLabel: UILabel!
    @IBOutlet weak var ratingLabel: UILabel!
}

It doesn’t work!

Here’s the problem though: The field disappears in my storyboard!

I spent some time debugging this issue, and found that I could work around it by overriding the bgImage variable in my subclasses, but it was not neccessary to re-implement anything. Success!

The fix

Just adding this line did the trick:

@IBInspectable override var bgImage: UIImage! { didSet {} }

So now my complete file looks like this:

import Foundation
import UIKit
 
class MediaCollectionViewCell: UICollectionViewCell {
    @IBOutlet weak var titleLabel: UILabel!
    var imgView = UIImageView()
    @IBInspectable weak var bgImage: UIImage! {
        didSet {
            imgView.frame = self.bounds
            self.addSubview(imgView)
            imgView.image = bgImage
            imgView.contentMode  = UIViewContentMode.Center
            self.sendSubviewToBack(imgView)
        }
    }
}
 
class TrailerCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var lengthLabel: UILabel!
    @IBInspectable override var bgImage: UIImage! { didSet {} }
}
 
class ReviewCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var ratingLabel: UILabel!
    @IBInspectable override var bgImage: UIImage! { didSet {} }
}
 
class PlaythroughCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var lengthLabel: UILabel!
    @IBOutlet weak var ratingLabel: UILabel!
    @IBInspectable override var bgImage: UIImage! { didSet {} }
}

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS

Being Swifty with Collection View and Table View Cells

Here’s a common scenario: You have a table view or collection view that has a variety of different types of content. You want to display varying cells based on these types of content, and they’re all mixed within a single section. Pardon the stand-in art, but it looks roughly like this:

In the Objective-C world, it was typical to just use an NSArray to hold whatever records your collection view was going to be using as a data source, and then for each element check what class it is before picking a cell. This case seems particularly not Swifty these days.

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"identifier" forIndexPath:indexPath];
    id record = self.records[indexPath.row];
    if([record isKindOfClass:[PlaythroughItem class]]) {
        // ...
    }
    else if([record isKindOfClass:[ReviewItem class]]) {
        // ...
    }
    else if([record isKindOfClass:[TrailerItem class]]) {
        // ...
    }
    return cell;
}

shudder

Okay so that’s not the most type-safe approach, although it’s surprisingly common in Obj-C iOS code. As a better alternative in Swift, we can use enum cases for the different item types, and perform lookups of the items themeselves as needed. Let’s look at an example.

Example

In an entertainment app I’m working on I’ve got a few types of cells for the types of news items that come in:

enum NewsItem {
  case Trailer(index: Int)
  case Review(index: Int)
  case Playthrough(index: Int)
}

The index is just a way to keep track of which item this it supposed to represent in the database. We take this approach to keep the amount of data needed to produce the collection view down. We don’t need all the associated data with every video to be present when putting together a collection view, we just need the info on what cell is tapped, after it is tapped.

Let’s say we have a simple collection view that shows one of these three and picks a custom cell for each. In a Swift file NewsFeed.swift I have that acts as the dataSource of my collection view for the main news view. Of particular interest is the cellForItemAtIndexPath method, which runs the NewsItem record through a switch and produces the correct type of cell, with the relevant information populated:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let record = records[indexPath.row]
    switch(record) {
    case .Playthrough(let index):
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("PlaythroughCell", forIndexPath: indexPath) as! PlaythroughCollectionViewCell
        let playthrough = MediaDB.playthroughAtIndex(index)
        cell.titleLabel.text = playthrough.title
        cell.lengthLabel.text = playthrough.length.prettyTime
        return cell
    case .Review(let index):
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ReviewCell", forIndexPath: indexPath) as! ReviewCollectionViewCell
        let review = MediaDB.reviewAtIndex(index)
        cell.ratingLabel.text = "\(review.rating) out of 10"
        cell.titleLabel.text = review.title
        return cell
    case .Trailer(let index):
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("TrailerCell", forIndexPath: indexPath) as! TrailerCollectionViewCell
        let trailer = MediaDB.trailerAtIndex(index)
        cell.titleLabel.text = trailer.title
        cell.lengthLabel.text = trailer.length.prettyTime
        return cell
    }
}

This code works well enough, record is of type NewsItem which can be one of three cases for the different news items we support:

enum NewsItem {
  case Trailer(index: Int)
  case Review(index: Int)
  case Playthrough(index: Int)
}

The associated index value is so that we can find the individual item in the DB when the collection view wants to display a cell.

Something about this code didn’t sit right with me though. I felt that too much of the code was boilerplate; in particular the switch felt bulky and like it had too much work being done inside each case.

But what if I created a protocol for any data source that could be presented as a collection view cell? It would change on a view-by-view basis so I don’t actually want this in my model.. but I do like having it on these particular CollectionViewCell subclasses.

So, I created a protocol called NewsCellPresentable, which I can adhere to in extensions with my custom collection view cells:

protocol NewsCellPresentable {
    func configureForIndex(index: Int)
}
extension PlaythroughCollectionViewCell: NewsCellPresentable {
    func configureForIndex(index: Int) {
        let playthrough = MediaDB.playthroughAtIndex(index)
        self.titleLabel.text = playthrough.title
        self.lengthLabel.text = playthrough.length.prettyTime
    }
}
extension ReviewCollectionViewCell: NewsCellPresentable {
    func configureForIndex(index: Int) {
        let review = MediaDB.reviewAtIndex(index)
        self.titleLabel.text = review.title
        self.ratingLabel.text = "\(review.rating) out of 10"
    }
}
extension TrailerCollectionViewCell: NewsCellPresentable {
    func configureForIndex(index: Int) {
        let trailer = MediaDB.trailerAtIndex(index)
        self.titleLabel.text = trailer.title
        self.lengthLabel.text = trailer.length.prettyTime
    }
}

This feels much cleaner already. Now I can go back to my cellForItemAtIndexPath method and trim it down to just the following:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let record = records[indexPath.row]
    var cell: NewsCellPresentable
    switch(record) {
    case .Playthrough(let index):
        cell = collectionView.dequeueReusableCellWithReuseIdentifier("PlaythroughCell", forIndexPath: indexPath) as! PlaythroughCollectionViewCell
        cell.configureForIndex(index)
    case .Review(let index):
        cell = collectionView.dequeueReusableCellWithReuseIdentifier("ReviewCell", forIndexPath: indexPath) as! ReviewCollectionViewCell
        cell.configureForIndex(index)
    case .Trailer(let index):
        cell = collectionView.dequeueReusableCellWithReuseIdentifier("TrailerCell", forIndexPath: indexPath) as! TrailerCollectionViewCell
        cell.configureForIndex(index)
    }
    return (cell as! MediaCollectionViewCell)
}

What do you think? Is this a cleaner approach? Let me know if you have a different method in the comment, or let me know on Twitter. My username is @jquave.

P.S.

If you want to try this out yourself and don’t have the same DB layer as me… guess what? Neither do I! You can easily stub this out like this:

class MediaDB {
    class func titleForRecord(index: Int) -> String {
        return "Title!!"
    }
    class func trailerAtIndex(index: Int) -> Trailer {
        return Trailer()
    }
    class func reviewAtIndex(index: Int) -> Review {
        return Review()
    }
    class func playthroughAtIndex(index: Int) -> Playthrough {
        return Playthrough()
    }
}
struct Trailer {
    let title = "Trailer Title"
    let length = 190
}
struct Review {
    let title = "Review Title"
    let rating = 4
}
struct Playthrough {
    let title = "Playthrough Title"
    let length = 9365
}
enum NewsItem {
    case Trailer(index: Int)
    case Review(index: Int)
    case Playthrough(index: Int)
}

Personally I always stub things out with static values before I do the work of integrating with a backend service or whatever data provider is needed. This makes it much easier to iterate.

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS

Developing tvOS Apps for Apple TV [Part 2]

Looking for some help building your Apple TV tvOS App? I’m available for consulting and development, contact me.

This is part 2 of the tvOS tutorial. If you haven’t gone through part 1 yet, I suggest you run through that first.

Adding Interactivity

In the first section we created a simple TVML document with a few buttons. It looked something like this:

<document>
    <alertTemplate>
        <title>Hello tvOS!</title>
        <button>
            <text>A Button</text>
        </button>
        <button>
            <text>A Second Button</text>
        </button>
    </alertTemplate>
</document>

This is an alert with a few buttons, and they don’t do anything. It’s also rather specific and hard-coded, so maybe what would be better is to generate the XML on the fly. We can do this fairly easily in JS. Let’s add a new function to our main.js file that will encapsulate this alert in to a simpler alert with just an OK button.

function alert(str) {
  var alertXMLString = `<?xml version="1.0" encoding="UTF-8" ?>
  <document>
    <alertTemplate>
      <title>Hey Listen!</title>
      <description>${str}</description>
      <button>
        <text>OK</text>
      </button>
    </alertTemplate>
  </document>`
  var parser = new DOMParser();
  var alertDOMElement = parser.parseFromString(alertXMLString, "application/xml");
  navigationDocument.presentModal(alertDOMElement);
}

What we’re doing here is creating a string called alertXMLString that contains the TVML contents of a simple alert template with just 1 button. However for the description we’re using the TVJS string concatenation ${variable} to inject the value of str.

Next, we create a new DOMParser object that converts this string in to an actual XML DOM element.

Finally, we can present the DOM element modally using the presentModal method of the navigationDocument, a globally defined variable that will always contain the root navigation document.

Now, in our onLaunch function, let’s remove the code we had from before and simply call an alert…

App.onLaunch = function(options) {
    alert("Hello!");
}

tvOS Alert View

Run the app and you’ll see a cool tvOS alert saying “Hello!” However, clicking OK does nothing. So how do we handle event’s such as touch?

Basically, if you want to stay in JavaScript and TVML, you will want to add an event listener to the DOM element. So for example, we could add a second argument to the alert function, so that we can pass in a function that is called when the OK button sends the event for being “selected”. We’ll add this argument and call it doneCallback. To add the event listener, we can simply do so like this:

alertDOMElement.addEventListener("select", function() { doneCallback }, false);

So after updating the entire function, it should look like this:

function alert(str, doneCallback) {
  var alertXMLString = `<?xml version="1.0" encoding="UTF-8" ?>
  <document>
    <alertTemplate>
      <title>Hey Listen!</title>
      <description>${str}</description>
      <button>
        <text>OK</text>
      </button>
    </alertTemplate>
  </document>`
  var parser = new DOMParser();
  var alertDOMElement = parser.parseFromString(alertXMLString, "application/xml");
  alertDOMElement.addEventListener("select", doneCallback, false);
  navigationDocument.presentModal(alertDOMElement);
}

Now, we can modify our original onLaunch function to add a callback function that will present a TVML screen. Before we do that, let’s also add a function called getDocumentContents which similarly implements a callback function when loading is done, which passes in the XMLHttpRequest object’s response to the callbacks only argument. This allows us to more easily load in various types of TVML files.

function getDocumentContents(url, loadCallback) {
  var templateXHR = new XMLHttpRequest();
  templateXHR.responseType = "document";
  templateXHR.addEventListener("load", function() { loadCallback(templateXHR) }, false);
  templateXHR.open("GET", url, true);
  templateXHR.send();
  return templateXHR;
}

This is nearly the same code as the getDocument method we defined earlier, except this is asynchronous and does not actually push anything on to the view.

Now, with this function we can perform this call and then do a screen replace of the alert when the OK button is clicked.

App.onLaunch = function(options) {
    alert("Hello!", function() {
      var helloDocument = getDocumentContents("http://localhost:8000/hello.tvml", function(xhr) {
        navigationDocument.dismissModal();
        navigationDocument.pushDocument(xhr.responseXML);
      });
    });
}

Let’s also change our hello.tvml file to use the stackTemplate, since it’s more interesting to look at. A stackTemplate is a nice way to lay out a list of content with title’s and images. Here’s the example I’ll be using in my demo:

<document>
    <stackTemplate>
        <banner>
            <title>Which Artist Do You Prefer?</title>
        </banner>
        <collectionList>
            <shelf>
                <section>
                    <lockup>
                        <img src="http://localhost:8000/nina.png" width="256" height="256" />
                        <title>Nina Simone</title>
                    </lockup>
                    <lockup>
                        <img src="http://localhost:8000/coltrane.png" width="256" height="256" />
                        <title>John Coltrane</title>
                    </lockup>
                </section>
            </shelf>
        </collectionList>
    </stackTemplate>
</document>

So basically this is just the way a stackTemplate is laid out, the banner is the top banner, the collectionList is just something that contains many shelf objects, which is just something that contains section objects, which contains lockup objects. The lockup object is what actually contains our image and title. In my case I added some images to work with to my directory called nina.png and coltrane.png

tvOS stackTemplate Screenshot

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS

Developing tvOS Apps for Apple TV [Part 1]

tvOS - Apple TV SDK Released

Here’s what you’ll have at the end of part 2 of this tutorial if you follow along:

Looking for some help building your Apple TV tvOS App? I’m available for consulting and development, contact me.

Apple TV Developer Tools

Before we get started you’ll need the Xcode 7.1 beta, which you can download here:
Download Xcode 7.1 Beta
Note that this does require an Apple Developer account, and is currently pre-release, so some things may change!

One thing to note when installing is that there is a known issue if you rename the Xcode 7.1 app, apparently people do this. So just a forewarning… don’t do that or you’ll get a crashy tvOS Simulator.

Also note that while Yosemite is supported, the capabilities are limited. It is recommend that for tvOS development you use OS X 10.11 El Capitan or newer. The El Capitan beta can be downloaded here.

Let’s start with some simple definitions…

Becoming an Apple TV Developer – Vocabulary

TVMLKit

TVMLKit is Apple’s new framework designed for working with Javascript and XML to provide a rich user-interface on tvOS, while allowing your apps logic to be written in Swift or Objective-C.

TVML

TVML is short for “TV Markup Language” and is basically just some XML that describes how items are laid out in a client-server based tvOS app. When laying out an interface, we want to use some TVML templates that Apple provides to create our UI, and then we can script the interactions in TVJS.

TVJS

As far as I can tell, TVJS is just JavaScript like you might already be familiar with.

Hello World

Let’s start out by making a basic hello world. In terms of Apple TV, we could just log “Hello World” to the console and that would be a decent start, but let’s start digging in to some of the Apple TV’s TVMLKit elements and put up a template on-screen.

First, open Xcode 7.1 and create a new project. You’ll see a list of templates appear. On the left-hand side we want to select **CHANGE tvOS, and then select Single View Application* template.

This will create a number of files from the tvOS template which are going to be useful in setting up our UI, and gives us a simple entry point to start coding Swift as well.

Setting up the main TVJS file

So basically the “server” in client-server tvOS apps is the TVML and JavaScript files, and whatever associated data they need to operate. The JavaScript file will be what loads in the TVML, and adds the page to the view stack. Another way to think of this is that the JavaScript file acts as a router or controller for the TVML files which are basically the views.

Kicking things off

First, we want to make some modifications to the AppDelegate.swift file that launches the app. For one, we need our application to adhere to the TVApplicationControllerDelegate protocol. This is defined in the TVMLKit framework, so we’ll need to import that as well. So you’ll want to update your AppDelegate.swift to include those two items:

import TVMLKit
 
class AppDelegate: UIResponder,
                   UIApplicationDelegate,
                   TVApplicationControllerDelegate {
 
....

This interface has four functions that tvOS calls upon the implementing AppDelegate to notify our app of the lifecycle of the tvOS application. We don’t need to worry about these just yet, but we’ll dig in to these in later tutorials. For now, just add the protocol as shown in the code snippet above, and that’s enough to get us started.

Next, we need to add some code to actually launch our JS file. This is a beta, and actually requires we do this work ourselves, I’m sure in future Xcode version’s this will simply be a template.

In the application didFinishLaunchingWithOptions function we need to perform a few steps. These steps are unlikely to change for each individual app so you can simply copy this snippet here:

// We'll hold a reference to the app controller in an optional property
var appController: TVApplicationController?
 
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
  self.window = UIWindow(frame:UIScreen.mainScreen().bounds)
 
  let appControllerContext = TVApplicationControllerContext()
 
  let jsFilePath = NSURL(string: "http://localhost:8000/main.js")
  let javascriptURL = jsFilePath!
 
  appControllerContext.javaScriptApplicationURL = javascriptURL
  if let options = launchOptions
  {
    for (kind, value) in options
    {
      if let kindStr = kind as? String
      {
        appControllerContext.launchOptions[kindStr] = value
      }
    }
  }
 
  self.appController = TVApplicationController(context: appControllerContext, window: self.window, delegate: self)
 
  return true
}

In short, what this code does is it gets a reference to an TVApplicationControllerContext, a class that simply provides some launch data to our AppDelegate class, and gives us an interface to make adjustments to the launch process. Then it passes the URL of the main.js file we’re going to execute, and sets the path for the app controller to be that path.

We need to now add our javascript file, so click File > New, and then under the iOS tab you can select an Other > Empty file. Name this file main.js.

Do the same thing for a new file called hello.tvml.

In your main.js file we’ll add some relatively simple JavaScript to load in the hello.tvml file:

function getDocument(url) {
  var templateXHR = new XMLHttpRequest();
  templateXHR.responseType = "document";
  templateXHR.addEventListener("load", function() {pushDoc(templateXHR.responseXML);}, false);
  templateXHR.open("GET", url, true);
  templateXHR.send();
  return templateXHR;
}
 
function pushDoc(document) {
  navigationDocument.pushDocument(document);
}
 
App.onLaunch = function(options) {
  var templateURL = 'http://localhost:8000/hello.tvml';
  getDocument(templateURL);
}
 
App.onExit = function() {
  console.log('App finished');
}

Now in the hello.tvml file:

<document>
  <alertTemplate>
      <title>Hello tvOS!</title>
  </alertTemplate>
</document>

This TVML file is the meat of the UI. Documents must use templates or you’ll get crashes at runtime with the code we’re using. It just contains a simple template and a single title element.

One issue I found with setting things up is that I was unable to locally refer to these files, and instead they have to be on a web server. So, what is easiest to do is navigate to the location of your tvml and js files we just created and type the following command in the command line:

Setting up the server to host the files

python -m SimpleHTTPServer 8000

This just uses Mac OS’s built-in python interpreter to start a web server on port 8000 serving up the local directory. If you copied my code shown above you should now be able to boot in to your tvOS simulator by pressing the play button in Xcode! There is one more issue you should be aware of, which is that this is an http unsecured request. In iOS 9 this is blocked by default by App Transport Security. In order to allow localhost to be used in this way, we need to add a key to the Info.plist file.

Allowing Arbitrary Loads

Select your Info.plist file and add a new record by pressing one of the (+) buttons. In the list find “App Transport Security Settings” and press return. This will create a new Dictionary row, expand this and press the (+) button on this row to add a child row. For this one find, “Allows Arbitrary Loads” and set it to true. With this in place we can run the app and preview using the simulator.

Adding Buttons

What you’re seeing in this example is actually a template from Apple called the alertTemplate. You can also embed a few basic controls, such as text and buttons within the template. Try adding a few buttons to select from:

<document>
    <alertTemplate>
        <title>Hello tvOS!</title>
        <button>
            <text>A Button</text>
        </button>
        <button>
            <text>A Second Button</text>
        </button>
    </alertTemplate>
</document>

Here we just are adding child button elements, each with it’s own child text element. This in turn displays the fullscreen alert and the two buttons on the tvOS simulator. The official Apple documentation has a list of each template, and control you can use if you want to hit the ground running. Otherwise stay tight, subscribe, and I’ll be bringing you a full tutorial of a fully developed app very soon.

Next Steps

Proceed to PART 2 » of this tutorial to learn how to add interactivity to the app.

If you want me to email you letting you know when new posts are up, be sure to subscribe to my newsletter.

These are still early days, so expect issues. If you get stuck don’t hesitate to contact me on twitter @jquave

GO TO PART 2 »

Looking for an Apple TV Developer for your tvOS App? I’m available for consulting and development, contact me for more info.

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS

Swift 2 – What’s new

Monday Apple announced Swift 2, and just about everything announced is an extremely welcome change. This post is a summary of Chris Lattner’s discussion in the WWDC video “What’s new in Swift”. Now, let’s run through them…

Fundamentals

enums can now be printed to the console and show the actually value instead of just (enum value). Additionally, println is now just print. If you have ever used log debugging to figure out a problem involving enum values, rejoice!

I tested this and saw that not only is the enum value is printed, but so is the entire context of when and where the print() function is called.

enums can now support multiple types, so long to Box!

enum<T1, T2> {
  case First(T1)
  case Second(T2)
}

The do keyword:

do {
  //...
} while(someVar<5)

// Can now be represented as:
repeat {
  //...
}

Option Sets can now use a standard Set type in Swift 2.0

viewOptions = .Repeat | .CurveEaseIn
viewOptions = nil

// Can now be represented as a set:

viewOptions = [.Repeat, .CurveEaseIn]
viewOptions = []

We can also create our own set types

(Default implementations in protocols)

struct MyStyle : OptionSetType {
  let rawValue: Int
  static let Bold     = MyStyle(rawValue: 1)
  static let Italic   = MyStyle(rawValue: 2)
}

iStyle.style = []
iStyle.style = [.Bold, .Italic]

if iStyle.style.contains(.Bold) {
  //...
}

Function arguments now behave the same way, regardless of if they are global functions, or methods on a data structure.

So now, you provide arguments labels on functions by default:

func myFunc(name: String, age: Int) { ... }

myFunc("John", age: 35)

The # syntax is now gone, previously used to make the external argument name the same as the internal argument name.

In tests, public and internal are visible, via running a special run mode.

Pattern Matching
Added the guard statement which exits the scope in the else statement.

guard let name = json["name"] as? String else {
  return .Second("missing name")
}

// You can also combine this is to complex guards
guard let name = json["name"] as? String,
      let year = json["year"] as? Int else
    return .Second("bad input")
}
// name and year are now String and Int types, not optionals!
return .First(name, year)

Switch/case can now be used with inline if statements

switch bar() {
case .SomeCase(let value) where value != 42:
  doThing(value)

default: break
}

// Can now be represented as
if case .SomeCase(let value) = bar() where value != 42 {
  doSomething(value)
}

for in loops now support boolean filters and full pattern matching

for value in mySequence where value != "" {
  doThing(value)
}

for case .MyEnumCase(let value) in enumValues {
  doThing(value)
}

BONUS: Example of try-catch for JSON vs invalid JSON

let jsonString = "{\"name\":\"Fred\", \"age\":40}"
let badJsonString = "This really isn't valid JSON at all"

let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)!
let badJsonData = badJsonString.dataUsingEncoding(NSUTF8StringEncoding)!

do {
    // Try parsing some valid JSON
    let parsed = try NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.AllowFragments)
    print(parsed)
    
    // Try parsing some invalid JSON
    let otherParsed = try NSJSONSerialization.JSONObjectWithData(badJsonData, options: NSJSONReadingOptions.AllowFragments)
    print(otherParsed)
}
catch let error as NSError {
    // Catch fires here, with an NSErrro being thrown from the JSONObjectWithData method
    print("A JSON parsing error occurred, here are the details:\n \(error)")
}

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS