Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii...

14

Transcript of Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii...

Page 1: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now
Page 2: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now

Geospatial Application Development withSwiftA guide to writing awesome location based apps for iOSand OS X

Dr. Kashif Rasul and Shoaib Burq

This book is for sale at http://leanpub.com/geoswift

This version was published on 2015-03-30

ISBN 978-0-9805032-0-3

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishingprocess. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools andmany iterations to get reader feedback, pivot until you have the right book and build traction onceyou do.

©2014 - 2015 Dr. Kashif Rasul and Shoaib Burq

Page 3: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now

Tweet This Book!Please help Dr. Kashif Rasul and Shoaib Burq by spreading the word about this book on Twitter!

The suggested hashtag for this book is #geoswift.

Find out what other people are saying about the book by clicking on this link to search for thishashtag on Twitter:

https://twitter.com/search?q=#geoswift

Page 4: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now

Contents

Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iPrerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iCocoaPods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iNew Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iiGit Submodules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ivTesting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vCarthage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vWhat is Covered? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . x

Page 5: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now

PrefaceIncreasingly developers are adding location context to their applications to distinguish their servicefrom those of their rivals. This is no where more true than for mobile applications on iOS whereeach new model of phone has seen more and more sensors added. The frameworks and APIs havealso now matured from the early days and today allow for some very sophisticated use cases.

This book is our own journey to learning these frameworks as well as a number of external locationbased services, and we aim to provide you all the knowledge you need to build a compelling andcaptivating location based application on iOS and OS X.

Prerequisites

To run the code and examples you will obviously need the latest Xcode 6.1.1 or higher, running onYosemite (10.10) or higher, and for field testing an iOS device running iOS 8.1 or higher. We hopeyou have some previous programming experience on this platform but if you are completely new,there are two books on getting started with Swift¹ programming from Apple which are worth goingover.

CocoaPods

We will use CocoaPods² as our dependency manager. This is a ruby gem so we will need a newishruby on our machine (even though an older ruby version comes with OS X). We will mange our rubyinstallations and versions via rbenv³. The easiest way to install this is via Homebrew⁴. Just type thefollowing into your Terminal to install Homebrew:

$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/maste\

r/install)"

and then install rbenv via the following command:

¹https://developer.apple.com/swift/²http://cocoapods.org/³https://github.com/sstephenson/rbenv⁴http://brew.sh

Page 6: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now

Preface ii

$ brew install rbenv

...

To use Homebrew's directories rather than ~/.rbenv add to your profile:

export RBENV_ROOT=/usr/local/var/rbenv

To enable shims and autocompletion add to your profile:

if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi

So do as instructed above by adding the environment variable and shims support to your .profilefile and then install ruby-build⁵, a rbenv plugin, that provides the install command so that we canfetch and compile a modern ruby version (2.2.0 at the time of writing) on our development machine:

$ brew install ruby-build

...

$ rbenv install 2.2.0

...

$ rbenv global 2.2.0

...

$ rbenv rehash

$ gem update --system

...

$ gem update

...

Ok so we are finally ready to install CocoaPods (0.36.0.beta.2 at the this time):

$ gem install cocoapods --pre

...

Successfully installed cocoapods-0.36.0.beta.2

New Project

We will use Xcode to setup a new Swift based application. To create a new project just choose File >New > Project and then the target and Application template of your choice. Next what is importantis to choose Swift in the language menu.

⁵https://github.com/sstephenson/ruby-build

Page 7: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now

Preface iii

Choose Swift language options for your new project.

And that is it the rest should be familiar to developers. You should now have a Swift project. Nowwould be a good time to add CocoaPods support to this project. Begin by creating a new Podfile inthe root directory of your project, for example:

source 'https://github.com/CocoaPods/Specs.git'

platform :ios, '8.1'

xcodeproj 'MyProject'

link_with 'MyProject', 'MyProjectTests'

pod 'AFNetworking', '~> 1.0'

This more or less specifies a dependency of the project, AFNetworking in this case. The ∼> 1.0

means version 1.0 and up to 2.0 but not including 2.0. Leave out the version number option to usethe latest version of a library or Pod. The xcodeproj specifies the Xcode project that contains thetarget that the Pods library should be linked with. You can get more information about the formatof the Podfile from the Podfile guide⁶.

Once you have saved a Podfile open up your Terminal and cd into your project folder and type:

⁶http://guides.cocoapods.org/syntax/podfile.html

Page 8: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now

Preface iv

$ pod install

...

From now on open your generated *.workspace instead of your project. Remember Swift has noheader files but if we are to use external Objective-C libraries or Pods via CocoaPods we will needan Objective-C bridging header to expose these APIs to Swift. It is always a good idea to add oneeven if it is empty. Simply add a dummy Objective-C file to your new Swift project and Xcode willprompt you to add a new bridging header. Say yes and then delete your dummy file. The bridgingheader file name is your project’s name appended with -Bridging-Header.h and contains yourimports from any public headers that Swift needs to know about, for example when using the Parseframework in Swift you need:

#import <Bolts/Bolts.h>

#import <Parse/Parse.h>

Git Submodules

brew install git is a given and we are ashamed to even write it here. Since CocoaPods’ supportof Swift libraries is still in beta at the time of writing, we can use (well no other option really) GitSubmodules. Git Submodules are great because:

• They track exactly which version of the library is being used;• It’s easy to update the library to the latest version (or any other version).

The Alamofire⁷ project’s installation⁸ documents it perfectly:

1. Add the library you want to include as a submodule by opening the Terminal, cd-ing intoyour top-level project directory, and entering the commands:

$ git submodule add https://github.com/X/Y.git Vendor/Y

...

$ git submodule update --init --recursive

2. Open the Y folder, and drag Y.xcodeproj into the file navigator of your app project.3. In Xcode, navigate to the target configuration window by clicking on the blue project icon,

and selecting the application target under the “Targets” heading in the sidebar.4. Ensure that the deployment target of Y.framework matches that of the application target.5. In the tab bar at the top of that window, open the Build Phases panel.

⁷https://github.com/Alamofire/Alamofire⁸https://github.com/Alamofire/Alamofire#installation

Page 9: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now

Preface v

6. Expand the “Link Binary with Libraries” group, and add Y.framework.7. Click on the + button at the top left of the panel and select “New Copy Files Phase”. Rename

this new phase to “Copy Frameworks”, set the “Destination” to “Frameworks”, and addY.framework.

To update the submodule to the latest version go to the directory and pull from the master andcommit the fact that you’ve updated the submodule:

$ git pull --rebase origin master

...

$ commit -m "Updated Y submodule"

By the time you read this, this section might be deprecated, but we decided to keep it in here justfor the sake of reference.

Testing

Testing has become a must for any developer and on Apple there is no exception. To test means toautomatically confirm that your code behaves as you think it should when it runs. There are tons ofreasons for testing, but we will not go through them here, but instead we present three frameworksfor testing on iOS and OS X.

Carthage

Carthage⁹ is a new and very simple way of adding different framework dependencies to your project.Just like CocoaPods, you create a Cartfile, you then add your frameworks in it, and run thecarthage command on this which fetches and builds these libraries. Once build you manually dragthe .framework binaries into your XCode. The tool does not change or create any XCode workspaceand does not mess with your build settings etc. On the other hand there is no central repository ofprojects, so you just need to find the library you want and add it yourself.

XCTest

Apple has replaced its OCUnit testing framework with XCTest which was first introduced in XCode5. These unit tests are contained inside an XCTestCase subclass and contain a particular set of featuresthat are being tested. The common pattern most unit test follow are arrange, act and assert and thisis what XCTest does too. The setUp and tearDownmethods are called before each test is run or afterit is finished respectively. Use these methods to create objects which each test case needs or cleanup objects after a test finishes. To create a test each method in your test class must have a namebeginning with test.

⁹https://github.com/Carthage/Carthage

Page 10: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now

Preface vi

import UIKit

import XCTest

class MyTests: XCTestCase {

override func setUp() {

super.setUp()

// Put setup code here. This method is called before the

// invocation of each test method in the class.

}

override func tearDown() {

// Put teardown code here. This method is called after the

// invocation of each test method in the class.

super.tearDown()

}

func testExample() {

// This is an example of a functional test case.

XCTAssert(true, "Fail")

}

func testPerformanceExample() {

// This is an example of a performance test case.

self.measureBlock() {

// Put the code you want to measure the time of here.

}

}

}

Above we see an example of an assertions XCTAssert which takes an expression and if it evaluatesto true the test passes, else it prints the message in it’s second argument. For Bool tests useXCTAssertTrue or XCTAssertFalse and to test if two values are or are not equal use XCTAssertEqualor XCTAssertNotEqual.

The measureBlock is new to XCode 6 and gives us a way to benchmark the performance ofa piece of code. Another great new feature is the ability to for asynchronous testing with theXCTestExpectation class. You canmake your tests wait a certain amount of time for some assertionsto be true or false.

Page 11: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now

Preface vii

func testWebPageDownload() {

let expectation = expectationWithDescription("Swift Expectations")

self.pageLoader.requestUrl("http://leanpub.com", completion: {

(page: String?) -> () in

if let downloadedPage = page {

XCTAssert(!downloadedPage.isEmpty, "The page is empty")

expectation.fulfill()

}

})

waitForExpectationsWithTimeout(5.0, handler:nil)

}

Behavior-Driven Development

If you know RSpec¹⁰, Specta¹¹ or Ginkgo¹² then you have perhaps heard of behavior-drivendevelopment or BDD and in Swift we will use Quick¹³ and Nimble¹⁴ frameworks to do the samefor iOS and OS X. Once you checkout these two frameworks via Submodules you can add them toyou Test targets and you should be ready to start writing specs.

Quick provides an it function which can be used to demonstrate how code should behave, just likethe the tests in XCTest. it takes two parameters: the name and a closure.

import Quick

import Nimble

class DolphinSpec: QuickSpec {

override func spec() {

it("is friendly") {

expect(Dolphin().isFriendly).to(beTruthy())

}

}

}

A bunch of specs can be grouped together using the describe function which makes the specs easierto read and it is also useful for sharing setup and teardown code (or arrange code) among the groupof specs. For example we use the beforeEach function in the code below to create a new instanceof a Dolphin to ensure we are in a fresh state for every spec. To execute code after each spec useafterEach.

¹⁰https://github.com/rspec/rspec¹¹https://github.com/specta/specta¹²https://github.com/onsi/ginkgo¹³https://github.com/quick/quick¹⁴https://github.com/quick/nimble

Page 12: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now

Preface viii

class DolphinSpec: QuickSpec {

override func spec() {

describe("a dolphin") {

var dolphin: Dolphin?

beforeEach { dolphin = Dolphin() }

describe("its click") {

var click: Click?

beforeEach { click = dolphin!.click() }

}

}

}

}

We use context to specify conditional behaviour. For example we create one context for the normalcase and one context for some special case:

...

var dolphin: Dolphin?

beforeEach { dolphin = Dolphin() }

describe("its click") {

context("when the dolphin is not near anything interesting") {

it("is only emitted once") {

expect(dolphin!.click().count).to(equal(1))

}

}

context("when the dolphin is near something interesting") {

...

}

}

...

The expect(...).to syntax in the examples above are provided by Nimble a simple language todefine expectations or assertions that code behaves a certain way and to show a failure if it doesnot. Some examples are:

Page 13: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now

Preface ix

import Nimble

expect(person.greeting).to(equal("Oh, hi."))

expect(person.greeting).notTo(equal("Hello!"))

expect(person.isHappy).toEventually(beTruthy())

Most importantly we can use Quick and Nimble to test UIKit interactions. For example in aTableViewController we want to test that the cell gets displayed when the view is loaded.Additionally, we would like to delete the row upon selecting it. An approach might be:

...

var viewController: DolphinTableViewController!

beforeEach { viewController = DolphinTableViewController() }

describe("viewDidLoad") {

beforeEach {

viewController.view

// Accessing the view property causes the UIKit framework to trigger

// the necessary methods to render the view.

}

it("loads the table view with one cell") {

let tv = viewController.tableView

var ip = NSIndexPath(forRow: 0, inSection: 0)

var cell = viewController.tableView(tv, cellForRowAtIndexPath: ip)

expect(cell.textLabel?.text).to(equal("Bottlenose"))

}

}

describe("didSelectRowAtIndexPath") {

beforeEach { viewController.view }

it("deletes the selected row and reloads the tableView's data") {

let tv = viewController.tableView

let ip = NSIndexPath(forRow: 0, inSection: 0)

viewController.tableView(tv, didSelectRowAtIndexPath: ip)

var cell = viewController.tableView(tv, cellForRowAtIndexPath: ip)

Page 14: Geospatial Application Development with Swiftsamples.leanpub.com/geoswift-sample.pdf · Preface iii ChooseSwiftlanguageoptionsforyournewproject. Andthatisittherestshouldbefamiliartodevelopers.YoushouldnowhaveaSwiftproject.Now

Preface x

expect(cell.textLabel?.text).to(beNil())

}

}

...

Functional Testing

Notice in the other frameworks we were always changing the state of some object and checking ifthe state changed or not. In functional testing one deals with immutable objects so we do not reallyneed to worry about state. And with this paradigm these imperative frameworks do not really fitthis view. The unit and spec frameworks are good though for checking state of objects. But typicallythe responsibility of checking edge cases of states falls onto the developer and in most cases thedeveloper does not think up the edge cases and it is typically some crash report that prompts thedeveloper to write specs or unit tests.

Functional languages like Haskel, Clojure and Erlang for example have solved this by Property-Based Testing tools that generate tests for you. These frameworks let you describe a property ofyour program that should always hold true and then generate code based on these requirementsand try to find causes of failures. Fox¹⁵ is a port of such a property based testing tool to Objective-Cand Swift by the Quick developers.

What is Covered?

First a bit of history of location: introduced in iOS 2, with iOS 3 there was the ability to get callbackson heading changes. In iOS 4 they added continuous background location updates, geofencingor geo triggers and significant location changes APIs. iOS 5 saw the addition of geocoding APIs.Battery efficient means of getting location updates, called deferred updates were introduced in iOS6. iBeacons were added in iOS 7 and finally iOS 8 added location authorization, visit monitoringand the ability to do indoor positioning. OS X 10.9 Mavericks also saw the addition of some of theseAPIs for Macs.

We will cover all these topics in this book:

Core LocationLocation based services can be divided into two topics, namely the core location servicesprovided by the Core Location framework and maps.

Map KitMap Kit provides the second of these two topics and this is used for displaying maps.

Additional SensorsAdditional sensors like iBeacon transmitters provide a low-energy way of to obtain locallocation information.

¹⁵https://github.com/jeffh/Fox