Swift 2 examples – #12 Extensions. Show Loading Indicators and Alerts

This post is part of my collection: Swift 2 – For Beginners.

Swift extensions add new functionality to an existing class, structure, enumeration, or protocol type.

In this example we will see how to write a UIViewController extension to show loading indicators and alerts.

To add a class extensions we have to write something like:

extension UIViewController {
// write your extensions here
}

Show a loading indicator

Our first extension method will be a method that should show a loading indicator and disable the application so that the user cannot interact with it. To help the user understand that the application is disabled it should be grayed out.

To use this method we don’t need any parameter but we need a reference to the created indicator to hide it later:

var spinner = self.showModalSpinner()

To do this we can use the class UIActivityIndicatorView.

/**
Shows a loading indicator and disables user interaction with the app until it is hidden
*/
func showModalSpinner() ->UIActivityIndicatorView{

// user cannot interact with the app while the spinner is visible
UIApplication.sharedApplication().beginIgnoringInteractionEvents()

var indicator = UIActivityIndicatorView()

indicator = UIActivityIndicatorView(frame: self.view.frame)
indicator.center = self.view.center
indicator.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
indicator.hidesWhenStopped = true
indicator.startAnimating()

self.view.addSubview(indicator)

return indicator
}

Now we need a method to hide the spinner when our task was completed:

self.hideModalSpinner(self.spinnerView)
/**
Hides the loading indicator and enables user interaction with the app
*/
func hideModalSpinner(indicator: UIActivityIndicatorView){

indicator.stopAnimating()
indicator.hidden = true

// user can interact again with the app
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}

If we put these two methods within a UIViewController extension we can use them like a normal method of the class.

Show an alert

Let’s add now a method to our extension to show an alert to the user, it should have an ‘ok’ button that will close the alert when the user taps it.

alert ios

I would like to have a very simple method that has only two parameters to indicate the title and message:

self.showAlert("Error", message: "Invalid user name / password.")

We can use the class UIAlertController to do that:

/**
Shows an alert with a title and a message
*/
func showAlert(title: String, message:String) {

let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)

// show the alert with a "ok" button that will close the alert
let okAction = UIAlertAction(title: "ok", style: UIAlertActionStyle.Default) { (action) -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
}
alertController.addAction(okAction)

// show alert controller
self.presentViewController(alertController, animated: true, completion: nil)
}

Complete example:

import UIKit
extension UIViewController{

/**
Shows an alert with a title and a message
*/
func showAlert(title: String, message:String) {

let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)

// show the alert with a "ok" button that will close the alert
let okAction = UIAlertAction(title: "ok", style: UIAlertActionStyle.Default) { (action)-> Void in
self.dismissViewControllerAnimated(true, completion: nil)
}
alertController.addAction(okAction)

// show alert controller
self.presentViewController(alertController, animated: true, completion: nil)
}

/**
Shows a loading indicator and disables user interaction with the app until it is hidden
*/
func showModalSpinner()->UIActivityIndicatorView{

// user cannot interact with the app while the spinner is visible
UIApplication.sharedApplication().beginIgnoringInteractionEvents()

var indicator = UIActivityIndicatorView()

indicator = UIActivityIndicatorView(frame: self.view.frame)
indicator.center = self.view.center
indicator.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
indicator.hidesWhenStopped = true
indicator.startAnimating()

self.view.addSubview(indicator)

return indicator
}

/**
Hides the loading indicator and enables user interaction with the app
*/
func hideModalSpinner(indicator: UIActivityIndicatorView){

indicator.stopAnimating()
indicator.hidden = true

// user can interact again with the app
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}
}

Swift 2 examples – #11 Loading images from the Photo Library and the Camera

This post is part of my collection: Swift 2 – For Beginners.

In this example we will see how to load photos in our application from the Photo Library and from the camera.

To open the photo library to pickup a photo we need a UIImagePickerController. Our controller can be the delegate. To do that we have to implement the protocols: UINavigationControllerDelegate and UIImagePickerControllerDelegate.

This code creates UIImagePickerController and use the method presentViewController to open the Photo Library:

  func selectImage(){
        let imagePickerController = UIImagePickerController()
        imagePickerController.delegate = self

        imagePickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
        imagePickerController.allowsEditing = true

        self.presentViewController(imagePickerController, animated: true, completion: nil)
    }

Once the user selects a photo our controller will be called back. Let’s implement a function that will load the selected image into a imageView:

   // Pick image finished, show selected image in a imageView
    func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
        // Show image
        imageView.image = image
        self.dismissViewControllerAnimated(true, completion: nil)
    }

To take a new photo using the camera we just change the value of imagePickerController.sourceType. Instead of PhotoLibrary we use Camera.

We can add a parameter to the previous function to specify the source of our image:

    func selectImage(from source: UIImagePickerControllerSourceType){
        let imagePickerController = UIImagePickerController()
        imagePickerController.delegate = self

        imagePickerController.sourceType = source
        imagePickerController.allowsEditing = true

        self.presentViewController(imagePickerController, animated: true, completion: nil)
    }

Complete example:

import UIKit

class ViewController: UIViewController,

    // 1 Allows navigation to camera or photo library to pick up a photo
    // used to set this controller as delegate of UIImagePickerController
    UINavigationControllerDelegate, UIImagePickerControllerDelegate
{

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBOutlet weak var imageView: UIImageView!

    @IBAction func addFromCamera(sender: AnyObject) {
        selectImage(from: UIImagePickerControllerSourceType.Camera)
    }

    @IBAction func addFromLibrary(sender: AnyObject) {
        selectImage(from: UIImagePickerControllerSourceType.PhotoLibrary)
    }

    // 2 Show image picker or camera app
    func selectImage(from source: UIImagePickerControllerSourceType){
        let imagePickerController = UIImagePickerController()
        imagePickerController.delegate = self

        imagePickerController.sourceType = source
        imagePickerController.allowsEditing = true

        self.presentViewController(imagePickerController, animated: true, completion: nil)
    }

    // 3 Pick image finished, show selected image
    func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
        // Show image
        imageView.image = image
        self.dismissViewControllerAnimated(true, completion: nil)
    }
}

Swift 2 examples – #10 Serialization to/from JSON using ObjectMapper

This post is part of my collection: Swift 2 – For Beginners.

JSON (JavaScript Object Notation) is a lightweight, human readable, open source, data-interchange format. In this example we will see how to serialize and deserialize objects to/from JSON using ObjectMapper in our iOS application.

For example, this would be the JSON representation of an object with two properties
name: string = “Juan Carlos”
age: Int = 29
JuanCarlosJSON

ObjectMapper is an open source project to make JSON Object mapping written in Swift. It is available in github: ObjectMapper.

Reference ObjectMapper using CocoaPods

CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects. We will use it to reference ObjectMapper in our project. (If would also be possible to add it as a submodule. Check the project in GitHub for more information)

To add ObjectMapper to our project we need to execute the following commands:
Install cocoapods

sudo gem install cocoapods

Create Podfile in our project root

touch Podfile

To reference ObjectMapper add this content to the PodFile

use_frameworks!
pod 'ObjectMapper', '~> 1.0'

Select XCode path

sudo xcode-select --switch /Applications/Xcode.app

Add ObjectMapper repository

pod repo add ObjectMapper https://github.com/Hearst-DD/ObjectMapper.git

Install the project locally

pod install

Close XCode and open the created file *.xcworkspace. It contains our project and a reference to a new project called Pods. All our referenced projects will be contained in this new project.

Now we are ready to import and use ObjectMapper:

import ObjectMapper

Define our objects and implement the protocol “Mappeable”

To serialize and deserialize objects we must implement the protocol Mappeable.

// Mappeable source code
public protocol Mappable {
	init?(_ map: Map)
	mutating func mapping(map: Map)
}

Let’s write our User class, it will have two properties, name and age. The class will also implement the protocol Mappeable.

// Define our class and implement the Mappable protocol
class User: Mappable {
    var name: String?
    var age: Int = 0
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    // MARK: Mappable
    required init?(_ map: Map) {
        // subClasses must call the constructor of the base class
        // super.init(map)
    }

    func mapping(map: Map) {
        name <- map["name"]
        age  <- map["age"]
    }
}

Serialize and Deserialize objects

ObjectMapper is extremely easy to use:

        // Object to be serialized to JSON
        let myUser = User(name: "Juan Carlos", age: 29)

        // Convert Object to JSON
        let serializedUser = Mapper().toJSONString(myUser)
        print(serializedUser)
        
        // Convert JSON to Object
        if let deserializedUser = Mapper<User>().map(serializedUser){
            print(deserializedUser.name)
        }
        
        // Output:
        //  Optional("{\"age\":29,\"name\":\"Juan Carlos\"}")
        //  Optional("Juan Carlos")

Swift 2 examples – #9 Core Data. Create, get, update and remove entities

This post is part of my collection: Swift 2 – For Beginners.

Core Data is a persistence framework provided by Apple. In this example we will see how to use it in our iOS application. First we will see how to create our data model and then how to perform the following basic operations:

  • Create
  • Get by Id
  • Get all and Get applying a predicate
  • Update
  • Remove

How to create our data model

A data model consist of a group of entities. The concept of entity in Core Data is similar to the concept of table in SQL. Each entity can have attributes in the same way that a table can have columns.

To create a new entity you need a project with Core Data included. Then go to your xdatamodel file and click “Add Entity”, “Add Attribute” to configure it.

To keep the example simple our data model will have one entity with two attributes. A Person with name and age:

Person
-name: string
-age: integer

createEntity

To create a class that represent our data model click on “Editor” > “Create NSManagedObject Subclass…”.
Create NSManagedObject

It should create a class and an extension representing our entity “Person”. To that class we will only add a static field with the entity name, we will use it later to avoid magic strings:

extension Person {
    @NSManaged var name: String?
    @NSManaged var age: NSNumber?
}
class Person: NSManagedObject {
    // Add this line, the string must be equal to your class name
    static let entityName = "Person"
}

managedObjectContext

Before writing code to make our basic operations it is important to know what the managedObjectContext is.

When the project has Core Data Xcode add a bunch of code to our AppDelegate.swift. A piece of that code is the managedObjectContext. This is the object that we need to access the data model. We can get it in our controller with this line of code:

let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

To save our changes we always have to call context.save(). It can throw an exception so we have to make the call in a do.. catch.. block:

    // Saves all changes
    func saveChanges(){
        do{
            try context.save()
        } catch let error as NSError {
            // failure
            print(error)
        }
    }

Now we are ready to write our basic functions to access the data model:

Create

    // Creates a new Person
    func create(name: String, age: NSNumber) -> Person {
        
        let newItem = NSEntityDescription.insertNewObjectForEntityForName(Person.entityName, inManagedObjectContext: context) as! Person
        
        newItem.name = name
        newItem.age = age
        
        return newItem
    }

GetById

    // Gets a person by id
    func getById(id: NSManagedObjectID) -> Person? {
        return context.objectWithID(id) as? Person
    }

Get

Now we will see how to get all entities and how to get all entities applying a NSPredicate to our query.

    // Gets all with an specified predicate.
    // Predicates examples:
    // - NSPredicate(format: "name == %@", "Juan Carlos")
    // - NSPredicate(format: "name contains %@", "Juan")
    func get(withPredicate queryPredicate: NSPredicate) -> [Person]{
        let fetchRequest = NSFetchRequest(entityName: Person.entityName)
        
        fetchRequest.predicate = queryPredicate
        
        do {
            let response = try context.executeFetchRequest(fetchRequest)
            return response as! [Person]
            
        } catch let error as NSError {
            // failure
            print(error)
            return [Person]()
        }
    }

Our get all function will be a call to the previous function. The predicate will be a true predicate, to return all objects in our data model.

    // Gets all.
    func getAll() -> [Person]{
        return get(withPredicate: NSPredicate(value:true))
    }

Update

    // Updates a person
    func update(updatedPerson: Person){
        if let person = getById(updatedPerson.objectID){
            person.name = updatedPerson.name
            person.age = updatedPerson.age
        }
    }

Remove

    // Deletes a person
    func delete(id: NSManagedObjectID){
        if let personToDelete = getById(id){
            context.deleteObject(personToDelete)
        }
    }

Complete example:

This class puts all the functions that we have seen before together, we can call it PersonService.

import CoreData

class PersonService{
    
    var context: NSManagedObjectContext
    
    init(context: NSManagedObjectContext){
        self.context = context
    }
    
    // Creates a new Person
    func create(name: String, age: NSNumber) -> Person {
        
        let newItem = NSEntityDescription.insertNewObjectForEntityForName(Person.entityName, inManagedObjectContext: context) as! Person
        
        newItem.name = name
        newItem.age = age
        
        return newItem
    }
    
    // Gets a person by id
    func getById(id: NSManagedObjectID) -> Person? {
        return context.objectWithID(id) as? Person
    }
    
    // Gets all.
    func getAll() -> [Person]{
        return get(withPredicate: NSPredicate(value:true))
    }
    
    // Gets all that fulfill the specified predicate.
    // Predicates examples:
    // - NSPredicate(format: "name == %@", "Juan Carlos")
    // - NSPredicate(format: "name contains %@", "Juan")
    func get(withPredicate queryPredicate: NSPredicate) -> [Person]{
        let fetchRequest = NSFetchRequest(entityName: Person.entityName)
        
        fetchRequest.predicate = queryPredicate
        
        do {
            let response = try context.executeFetchRequest(fetchRequest)
            return response as! [Person]
            
        } catch let error as NSError {
            // failure
            print(error)
            return [Person]()
        }
    }
    
    // Updates a person
    func update(updatedPerson: Person){
        if let person = getById(updatedPerson.objectID){
            person.name = updatedPerson.name
            person.age = updatedPerson.age
        }
    }
    
    // Deletes a person
    func delete(id: NSManagedObjectID){
        if let personToDelete = getById(id){
            context.deleteObject(personToDelete)
        }
    }
    
    // Saves all changes
    func saveChanges(){
        do{
            try context.save()
        } catch let error as NSError {
            // failure
            print(error)
        }
    }
}

How to use the service:

        // Create an instance of the service.
        let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
        let personService = PersonService(context: context)
        
        // Create
        let juanCarlos = personService.create("Juan Carlos", age: 52)

        // Read all
        var people : [Person] = personService.getAll()

        // Read by id
        let firstPerson = personService.getById(people[0].objectID)!

        // Update
        firstPerson.name = "Juan Carlos Sanchez"
        personService.update(firstPerson)

        // Delete
        personService.delete(firstPerson.objectID)

Swift 2 examples – #8 Gestures Recognizers. Swipe, Tap, Rotate, Panning, etc.

This post is part of my collection: Swift 2 – For Beginners.

This example shows how gesture recognizers works.

Adding gestures programmatically

It is possible to add gesture recognizers programmatically to a view using the method addGestureRecognizer. To do that we can use these lines of code:

static func addSwipeGestureRecognizer(targetView: UIView, swipeDirection: UISwipeGestureRecognizerDirection, actionTarget: AnyObject?, action: Selector){
        // Create gesture recognizer
        let swipeRecognizer = UISwipeGestureRecognizer(target: actionTarget, action: action)
        
        // Set swipe direction
        swipeRecognizer.direction = swipeDirection
        
        // Add recognizer to target view
        targetView.addGestureRecognizer(swipeRecognizer)
}

Swipe

However you can also add gestures using “drag and drop” in your storyboard from the object library:
gestures

It is also possible to configure some parameters using XCode, i.e the swipe direction.
swipeDirection

Once you have your gesture in the storyboard you can select it and drag it to your ViewController to create the action that should be executed when the gesture was recognized.

    @IBAction func onViewSwipe(recognizer: UISwipeGestureRecognizer) {
        print("swipe down")
    }

Rotate

We can use the rotation gesture recognizer to rotate a view:

    @IBOutlet weak var ghostImage: UIImageView!

    // Rotate
    @IBAction func onGhostRotate(recognizer: UIRotationGestureRecognizer) {
        print("onGhostRotate \(recognizer.rotation)")

        // Apply gesture rotation to ghost view
        ghostImage.transform = CGAffineTransformRotate(ghostImage.transform, recognizer.rotation)

        // Reset recognizer rotation
        recognizer.rotation = 0
    }

rotatedGhost

Tap

Using a tap gesture recognizer and what we learnt before when can play a sound when the user tap our image:

@IBAction func onGhostTapped(sender: UITapGestureRecognizer) {
   let audioPath = NSBundle.mainBundle().pathForResource("punch", ofType: "mp3")!
   do  {
        // Initialize the player
        player = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: audioPath))
        // Play
        player.play()   
 
    } catch{
        print("Error creating audio player.")
    }    
}

Panning

The pan gesture recognizer allows us to drag and drop views in our application:

 @IBAction func onGhostPanning(recognizer: UIPanGestureRecognizer) {
        GestureHelper.applyGestureTranslationToGestureView(recognizer, parentView: self.view)
    }
static func applyGestureTranslationToGestureView(recognizer: UIPanGestureRecognizer, parentView: UIView){
        // Apply translation recognizer view
        if let recognizerView = recognizer.view {
            
            // Get recognized translation
            let translation = recognizer.translationInView(parentView)
            
            // Calculate new center
            let originalCenter = recognizerView.center
            recognizerView.center = CGPoint(x:originalCenter.x + translation.x, y:originalCenter.y + translation.y)
        }
        
        // Reset recognizer translation
        recognizer.setTranslation(CGPointZero, inView: parentView)
    }

Shake

There is not shake gesture recognizer, to do something when the user shake the phone we have to override the method motionEnded. This code resets the position and rotation of our ghost when the user shakes the phone:

    override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?) {
        
        if event?.subtype == UIEventSubtype.MotionShake{
            // Reset initial position
            ghostImage.center = ghostInitialPosition
            // Reset rotation and scale
            ghostImage.transform = CGAffineTransform()
        }
    }

Complete example:

GestureHelper

import UIKit

public class GestureHelper {
    
    // this adds a helper in "code behind" normally you would do it adding the gesture using the storyboard and then the gesture action to the controller...
     static func addSwipeGestureRecognizer(targetView: UIView, swipeDirection: UISwipeGestureRecognizerDirection, actionTarget: AnyObject?, action: Selector){
        
        // Create gesture recognizer
        let swipeRecognizer = UISwipeGestureRecognizer(target: actionTarget, action: action)
        
        // Set swipe direction
        swipeRecognizer.direction = swipeDirection
        
        // Add recognizer to target view
        targetView.addGestureRecognizer(swipeRecognizer)
    }
    
    static func applyGestureTranslationToGestureView(recognizer: UIPanGestureRecognizer, parentView: UIView, inertia: Bool = true){
       
        // Apply translation recognizer view
        if let recognizerView = recognizer.view {
            
            // Get recognized translation
            let translation = recognizer.translationInView(parentView)
            
            // Calculate new center
            let originalCenter = recognizerView.center
            recognizerView.center = CGPoint(x:originalCenter.x + translation.x, y:originalCenter.y + translation.y)
            
            // Inertia
            if inertia && recognizer.state == UIGestureRecognizerState.Ended {
                
                // Get speed
                let velocity = recognizer.velocityInView(parentView)
                let magnitude = min(sqrt((velocity.x * velocity.x) + (velocity.y * velocity.y)) / 10000, 0.1)
                
                // Calculate final point
                var finalPoint = CGPoint(
                    x:recognizerView.center.x + (velocity.x * magnitude),
                    y:recognizerView.center.y + (velocity.y * magnitude))
                
                // Consider bounds
                finalPoint.x = min(max(finalPoint.x, 0), parentView.bounds.size.width)
                finalPoint.y = min(max(finalPoint.y, 0), parentView.bounds.size.height)
                
                // Apply inertia animation
                UIView.animateWithDuration(0.5,
                    delay: 0,
                    options: UIViewAnimationOptions.CurveEaseOut,
                    animations: {recognizerView.center = finalPoint },
                    completion: nil)
            }
        }
        
        // Reset recognizer translation
        recognizer.setTranslation(CGPointZero, inView: parentView)
    }
}

ViewController

import UIKit
import AVFoundation

class ViewController: UIViewController {

    var ghostInitialPosition: CGPoint = CGPoint();
    var player = AVAudioPlayer()

    @IBOutlet weak var ghostImage: UIImageView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        ghostInitialPosition = ghostImage.center
    }
    
    // Swipe
    @IBAction func onViewSwipe(recognizer: UISwipeGestureRecognizer) {
        print("swipe down")
    }
    
    // Tap
    @IBAction func onGhostTapped(sender: UITapGestureRecognizer) {
        playPunchSound()
    }
    
    // Rotate
    @IBAction func onGhostRotate(recognizer: UIRotationGestureRecognizer) {
        print("onGhostRotate \(recognizer.rotation)")
        
        // Apply gesture rotation to ghost view
        ghostImage.transform = CGAffineTransformRotate(ghostImage.transform, recognizer.rotation)

        // Reset recognizer rotation
        recognizer.rotation = 0
    }
    
    // Panning
    @IBAction func onGhostPanning(recognizer: UIPanGestureRecognizer) {
        GestureHelper.applyGestureTranslationToGestureView(recognizer, parentView: self.view)
    }
    
    // Shake
    override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?) {
        
        if event?.subtype == UIEventSubtype.MotionShake{
            // Reset initial position
            ghostImage.center = ghostInitialPosition
            // Reset rotation and scale
            ghostImage.transform = CGAffineTransform()
        }
    }
    
    func playPunchSound(){
        let audioPath = NSBundle.mainBundle().pathForResource("punch", ofType: "mp3")!
        
        do  {
            // Initialize the player
            player = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: audioPath))
            
            // Play
            player.play()
            
        } catch{
            print("Error creating audio player.")
        }
    }
}

Swift 2 examples – #7 Reproduce an audio file

This post is part of my collection: Swift 2 – For Beginners.

In this example we will see how to play and pause an audio file.

We can use the class AVAudioPlayer of AVFoundation to easily reproduce audio files, let’s start importing it.

import AVFoundation

We need the path to the audio file. E.g. To get a file named “allegro.mp3” in our project we can use the following line:

let audioPath = NSBundle.mainBundle().pathForResource("allegro", ofType: "mp3")!

That’s all, we are ready to create the AVAudioPlayer. The creation of the player could fail if for example the file would be corrupted. That’s why we need to create it in a “do try”:

do  {
   // Initialize the player with the audio file
   player = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: audioPath))
           
   // Play
   player.play()
            
} catch{
   print("Error creating audio player.")
}

To pause the audio we just call:

player.pause()

Complete example:

import UIKit
// 1. Import audio video foundation
import AVFoundation

class ViewController: UIViewController {
    
    // 2. Create player
    var player = AVAudioPlayer()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 3. Get path to the audio file inside the application bundle
        let audioPath = NSBundle.mainBundle().pathForResource("allegro", ofType: "mp3")!
        
        do  {
            // 4. Initialize the player with the audio file inside a "do try cath"
            player = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: audioPath))
            
            // 5. Play
            player.play()
        } catch{
            print("Error creating audio player.")
        }
    }
}

Swift 2 examples – #6 Take a Snapshot of a MKMapView

This post is part of my collection: Swift 2 – For Beginners.

This is an example that shows how to take a snapshot of a MKMapView using MKMapSnapshotter. The annotations are not included.

First, we write a generic method that takes a snapshot and execute a callback, it allows us to do what we want with the generated UIImage:

    // Takes a snapshot and calls back with the generated UIImage
    static func takeSnapshot(mapView: MKMapView, withCallback: (UIImage?, NSError?) -> ()) {
        let options = MKMapSnapshotOptions()
        options.region = mapView.region
        options.size = mapView.frame.size
        options.scale = UIScreen.mainScreen().scale
        
        let snapshotter = MKMapSnapshotter(options: options)
        snapshotter.startWithCompletionHandler() { snapshot, error in
            guard snapshot != nil else {
                withCallback(nil, error)
                return
            }
            
            withCallback(snapshot!.image, nil)
        }
    }

We can use the previous method to write a method that directly saves the map snapshot as a png.

    // Takes a snapshot and saves the image locally in the DocumentDirectory
    static func takeSnapshot(mapView: MKMapView, filename: String) {
        
        MapHelper.takeSnapshot(mapView) { (image, error) -> () in
            guard image != nil else {
                print(error)
                return
            }
            
            // Save file in DocumentDirectory
            if let data = UIImagePNGRepresentation(image!) {
                let filename = getDocumentsDirectory().stringByAppendingPathComponent("\(filename).png")
                data.writeToFile(filename, atomically: true)
            }
        }
    }

To get the path to the documents directory we can use this method:

    // Gets the path to the current directory
    static func getDocumentsDirectory() -> NSString {
        let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
        let documentsDirectory = paths[0]
        return documentsDirectory
    }