Let’s build an iOS app in Swift and own frameworks: WikiLocation

[Update July 23.] Apple added access control in Beta 4 which means that I have to adapt the new behavior (explicit exposing methods for public access) for my example frameworks. Right now I’m traveling around. This will be done later in August. [/update]
Enough theory, enough reading in Swift. Now it is time to build a real app. With this blog post I will demonstrate Swift. This is not a tutorial how to build iOS apps from scratch. I assume that you are familiar with building iOS apps in Objective C. The purpose of this article is to explain some Swift features, pitfalls and patterns with code snippets.
What is this app about? The app’s name is WikiLocation. As the name implies, it uses your current location and provides you nearby Wikipedia articles. (Buildings, lakes and so on). Straight forward. Not too much magic.
This mini app covers ViewControllers, Views & Models in Swift and custom iOS Frameworks (modules) :

  • ViewControllers in Swift
    • TableViewController with an UITableView: Show the Wiki articles nearby in a simple list
    • WebViewController with an UIWebView: showing the selected article’s website in a detail view
  • GeoLocation Manager
    • Retrieve the user’s location, stop after we got the coordinates
    • Stop update when going into background, refresh the location when coming into foreground
    • TableViewController needs to observe the location property to load new articles
  • Network Manager
    • Provides a model for the WikiArticles
    • Fetches the Wiki articles from the API
    • JSON parsing and mapping with model

In this demo project I will also demonstrate the usage of the new iOS frameworks. The GeoLocation and Network Manager are good examples for reusable frameworks.

Download the final project from github

Let’s start building the app.
As mentioned above I assume that you know how to create iOS apps. This won’t be a super detailed step by step tutorial. I will pick out some Swift specific things.
But here we go:

1. Setup the ViewController and Views

  1. We create a new project with Xcode 6 (‘Single View Application’ with Swift as language)
  2. Xcode created a ‘ViewController.swift file: Change the superclass for that class to
    ‘UITableViewController’
  3. Add a new Swift file named ‘WebViewController’
  4. Go to the Storyboard
    1. Remove the existing ViewController
    2. Add a new UINavigationController
    3. Set the new NavigationController to ‘Initial View Controller’
    4. The NavigationController’s RootViewController should be an UITableViewController with the custom class ‘ViewController’ (which should match your existing ‘ViewController.swift’ file)
      1. Add a TableView Cell Identifier ‘WikiTableViewCellID’
      2. Change the TableViewCell’s style to ‘Subtitle’
    5. Create a new ViewController ‘WebViewController’ (for the Wiki websites)
      1. Change the custom class ‘WebViewController’
      2. Drag an UIWebView to the ‘WebViewController’
      3. Connect a segue from the TableView’s cell to the created ‘WebViewController’
      4. Add the segue identifier ‘ShowWebDetails’

2. Add the iOS Frameworks GeoManager & NetworkManager

  1. Create a new Target in the project
    1. Select ‘Framework & Library’and ‘Cocoa Touch Framework’
    2. Name the target ‘GeoManager’
    3. Add a new Swift file (right click the ‘GeoManager’ folder in your project navigator) to the GeoManager framework target named ‘GeoManager’.
  2. Repeat the first step and name the new target ‘NetworkManager’ and also add a Swift file to the ‘NetworkManager’ framework target named ‘NetworkManager’.

That’s it! You just created your own two iOS frameworks / modules that can be reused in other projects.
Let’s add some real code to the GeoManager.  Open ‘GeoManager.swift’.
Add the following skeleton:

import Foundation
import CoreLocation

public class GeoManager : NSObject, CLLocationManagerDelegate {
    

    //MARK: - Properties
    public var locationManager:CLLocationManager = CLLocationManager()
    //private(set)  var location:CLLocation?
    public var location:CLLocation?
    var locationAuthorized = false
    public class var sharedInstance: GeoManager {
    struct SharedInstance {
        static let instance = GeoManager()
        }
        return SharedInstance.instance
    }
}

First we need to import Foundation and the CoreLocation modules. We inherit from the Foundation’s ‘NSObject’. Also we add the ‘CLLocationManagerDelegate’ protocol.
We add three properties. The first ‘locationManager’ will initialized immediately and is our CoreLocation’s location manager. The second is our location which we retrieve later. Because we cannot guarantee that the location will be determined, it can be nil. Therefore we must mark the location property as optional.
The third property is our Singleton shared instance to the GeoManager. The singleton pattern changes a bit in Swift. We could use the dispatch_once pattern. But a shorter way in Swift is the struct and a static var. At this time Swift does not really provide class variables. XCode 6 says “Class variables not yet supported”. Looks like that this will come later. But Swift provides type methods. Type methods are equivalent to the class methods + (void)method in Objective C. Structs and enumerations also provides type properties with the static keyword. Type properties can “store a value that is global to all instances of that type (like a static variable in C).” Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
So we can go the way with a struct and a read only type property. Swift provides the static keyword in structs which we can use to create to a constant type variable in the struct ‘SharedInstance’. The struct is little a helper to enable class typed variables like we know it from C with static. I think the type property and the pattern is still under little construction and will be changed in the near future.
Now we can add all the CoreLocation classes and the delegate methods. I won’t paste the whole code here. Basically there is no real magic and will be the same as long as we use the standard CoreLocation framework. But there is an important change in iOS 8 for CoreLocation: The app must request the permission to access the location:

self.locationManager.requestWhenInUseAuthorization() 

Also we need to add a text for the corresponding info.plist key ‘NSLocationWhenInUseUsageDescription’.
Now we can add the framework (module) ‘WikiManager’ to fetch the JSON and map to a proper model object. There is nothing really new except the JSON parsing type safe Dictionaries.

var articles = NSMutableOrderedSet()
if (data.length > 0) {
  var error:NSError?
  var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error:&error) as Dictionary<String,AnyObject>
                
 if let jsonarticles = jsonResult["articles"] as? NSArray {
   for item : AnyObject in jsonarticles {
     var article = WikiArticle(json: item as Dictionary<String, AnyObject>)
     articles.addObject(article)
   }
  }
}
completion(articles.array as [WikiArticle])

The json object will directly casted to a Swift Dictionary via ‘as Dictionary<String,AnyObject>’ and assigned to jsonResult.
We all model objects in an NSMutableOrderedSet to avoid dups but having a consistent order.
Because Swift is strict about types, we need to check and cast a lot. This is one of the biggest differences to Objective C in this example app.
We access the jsonResult via the subscript “articles” and check with if let for the NSArray type (NSJSONSerialization uses Foundation objects).
In the for loop we initialize the model object ‘WikiArticle’ with a casted Dictionary.
In Objective C the code would like this

NSError *error = nil;
id jsonResult = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSMutableOrderedSet *articles = [NSMutableOrderedSet orderedSet];
for (NSDictionary *item in jsonResult[@"articles"] ) {
    WikiArticle *article = [[WikiArticle alloc] initWithJson:item];
    [articles addObject:article];
}
completion([articles array]);

In Objective C we don’t need to care types. (but we could – mostly we don’t). That makes it easy and dynamic but what happens if we the JSON result and types change? Crash boom bang! Not directly here in the loop but maybe later in the model.
The model object in Swift looks like this:

public class WikiArticle : NSObject {
    public let title:String!
    public let title:String = &amp;amp;quot;&amp;amp;quot;
    init(json:Dictionary&amp;amp;lt;String,AnyObject&amp;amp;gt;) {
        super.init()

        if let title = json["title"] as? NSString {
            self.title = title
        }
        ...
    }
}

The model object ‘WikiArticle’ has implicit optional properties with the ‘!’ except for title because we want to avoid nil for the title in all cases. Because we add the value directly in the initializer we can use optional let here.
We inherit from NSObject to use the Foundation methods hash() and isEqual later. The initializer needs to call super, because we’re using the NSObject. As mentioned above we use type checks again. That makes the app safer than Objective C a implementation.
Actually that are most important patterns that differ from Objective C. Have a look in the ViewController classes and you will notice that there is nothing really magic. The tableView’s delegate methods should look familiar to you after a while.
An interesting thing and a big change in Swift is that we can initialize the properties directly with the declaration.

class ViewController: UITableViewController {

    lazy var geoManager = GeoManager.sharedInstance
    var dataSource = [WikiArticle]()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.geoManager.start()
        self.geoManager.addObserver(self, forKeyPath: &amp;quot;location&amp;quot;, options: NSKeyValueObservingOptions.New, context: nil)
    }
    .....
}

The geoManager will be initialized lazy. That means that the instance for the property will be created when we access the property the first time (in viewDidLoad). The dataSource property will be initialized directly. Initializing properties in the declaration is new to Objective C developers.

That’s it from my side. Any questions? Feel free to comment here or fork the project and send pull requests.

2 thoughts on “Let’s build an iOS app in Swift and own frameworks: WikiLocation

  1. This article was published when Swift has not provide public/private accessors, so for this tutorial to work just ensure you add public accessors to the class declarations and also method declararions.

  2. Hey there,
    Great article.

    I’m trying to create my own framework and having some trouble.

    When I have a working project and add a “Framework” target to it, that works fine.

    But what I want to do (And what I currently have working with Objective C) , is have one project with the framework code, where I compile and get a .framework file. Then, I take that MyFramework.framework file and import it in a different project.

    When I do that and type “import MyFramework” it doesn’t seem to recognise it (unless its a target in that same project).

    Is there anything I can do to solve this, if this was understandable to you? :)

    Thanks,
    Shai

Leave a Reply

Your email address will not be published. Required fields are marked *