Create Custom Delegate and Protocol iOS | Swift & Objective-C

One of the most powerful tools an iOS developer has is the ability to create protocols and implement delegates that let your classes notify when an event occurs. I use them all the time in my projects and wanted to show how easy it is to implement your iOS Project. I will demonstrate them in both Objective-C and Swift so you can see a side by side comparison. In this example we will be implementing timer that fires a timerFinished method to let the UIViewController know the timer finished and start the timer over.

If you want to follow allow you can download the projects from GitHub here:

Objective-C  Project
Swift Project

Protocols

A protocol is a list of methods that specify an interface that your delegate will implement. There are two kinds of delegates we can use: Option and Required. They are pretty self explanatory but the difference is Required will throw an error letting you know your class is not conforming to the protocol. Also protocol methods are required by default so if you want it optional don’t forget that optional keyword. If you are using swift you will also need to add the @objc prefix if you want an optional method.

Swift
//
//  MyTimer.swift
//  SwiftProtocol
//
//  Created by Barrett Breshears on 10/11/14.
//  Copyright (c) 2014 Sledge Dev. All rights reserved.
//

import UIKit

// set up the MyTimerDelegate protocol with a single option timer finished function
@objc protocol MyTimerDelegate{
    optional func timerFinished()
}


class MyTimer: UIViewController {
    
    // this is where we declare our protocol
    var delegate:MyTimerDelegate?
    
    // set up timer variables and labels
    var timer:NSTimer! = NSTimer()
    var labelTimer:NSTimer! = NSTimer()
    var timerLabel:UILabel! = UILabel()
    var timerCount = 0
    var duration = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Do any additional setup after loading the view.
        timerLabel = UILabel(frame: self.view.frame)
        timerLabel.textAlignment = NSTextAlignment.Center
        self.view.addSubview(timerLabel)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    func startTimer(timerDuration:Double){
        self.duration = Int(timerDuration)
        timerLabel.text = String(format: "%d", duration)
        
        timer = NSTimer.scheduledTimerWithTimeInterval(timerDuration, target: self, selector: Selector("timerFired:"), userInfo: nil, repeats: false)
        
        labelTimer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("updateLabel:"), userInfo: nil, repeats: true)
        
        
    }
    
    func timerFired(timer:NSTimer){
        if(timer.valid){
            timer.invalidate()
        }
        if(labelTimer.valid){
            labelTimer.invalidate()
        }
        // ************************************** \\
        // ************************************** \\
        // This is the important part right here
        // we want to call our protocol method
        // so the class implementing this delegate will know
        // when the timer has finished
        // ************************************** \\
        // ************************************** \\
        delegate?.timerFinished!()
        
    }
    
    func updateLabel(timer:NSTimer){
        duration = duration - 1
        timerLabel.text = String(format: "%d", duration)
    }
    
    
    /*
    // MARK: - Navigation
    
    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
    }
    */
    
}

This is a pretty simple example but here is what is going on. The UIViewController has a start timer method that sets up the two timers: one that will fire when the overall time is complete and one that fires every second to update the timer label. When the total duration timer is finished the timerFired method is called, and thats where we run the delegate’s timerFinished method. Now lets do the same thing but with Objective-C.

Objective-C
//
//  MyTimer.h
//  ObjectIveCProtocol
//
//  Created by Barrett Breshears on 10/11/14.
//  Copyright (c) 2014 Sledge Dev. All rights reserved.
//

#import 

// set up the MyTimerDelegate protocol with a single option timer finished function
@protocol MyTimerDelegate 

@optional
-(void)timerFinished;

@end

@interface MyTimer : UIViewController
// this is where we declare our protocol
@property (nonatomic, strong) id delegate;
// set up timer variables and labels
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, strong) NSTimer *labelTimer;
@property (nonatomic, strong) UILabel *timerLabel;
@property (nonatomic, assign) int timerCount;
@property (nonatomic, assign) int duration;

- (void)startTimer:(float)duration;

@end

//
//  MyTimer.m
//  ObjectIveCProtocol
//
//  Created by Barrett Breshears on 10/11/14.
//  Copyright (c) 2014 Sledge Dev. All rights reserved.
//

#import "MyTimer.h"



@interface MyTimer ()

@end

@implementation MyTimer

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _timer = [[NSTimer alloc] init];
    _labelTimer = [[NSTimer alloc] init];
    _timerCount = 0;
    _duration = 0;
    
    
     _timerLabel = [[UILabel alloc] initWithFrame:self.view.frame];
    [self.view addSubview:_timerLabel];
    [_timerLabel setTextAlignment:NSTextAlignmentCenter];
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)startTimer:(float)duration{
    _duration = (int)duration;
    _timerLabel.text = [NSString stringWithFormat:@"%d", _duration];
    
    _timer = [NSTimer scheduledTimerWithTimeInterval:duration
                                              target:self
                                            selector:@selector(timerFired:)
                                            userInfo:nil
                                             repeats:NO];
    
    _labelTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                                   target:self
                                                 selector:@selector(updateLabel:)
                                                 userInfo:nil
                                                  repeats:YES];
    
    
}

- (void)timerFired:(NSTimer *)timer {
    if ([_timer isValid]) {
        [_timer invalidate];
    }
    _timer = nil;
    if ([_labelTimer isValid]) {
        [_labelTimer invalidate];
    }
    _labelTimer = nil;
    // ************************************** \\
    // ************************************** \\
    // This is the important part right here
    // we want to call our protocol method here
    // so the class implementing this delegate will know
    // when the timer has finished
    // ************************************** \\
    // ************************************** \\
    [_delegate timerFinished];
}

- (void)updateLabel:(NSTimer *)timer{
    _duration = _duration - 1;
    _timerLabel.text = [NSString stringWithFormat:@"%d", _duration];
}

@end

So same thing just different syntax. The UIViewController has a start timer method that sets up the two timers: one that will fire when the overall time is complete and one that fires every second to update the timer label. When the total duration timer is finished the timerFired method is called, and thats where we run the delegate’s timerFinished method.

Delegates

Now that we have our protocols all set up all we have to do is implement them. We are going to do this by creating a delegate. A delegate is a variable that complies to a protocol, which a class typically uses to notify of events, in this case the timer finishing. To do this we add our protocol to our class declaration, to let our class know it must comply with the delegate. Then we add our delegate method to our class.

Swift
//
//  ViewController.swift
//  Swift-Protocol
//
//  Created by Barrett Breshears on 10/11/14.
//  Copyright (c) 2014 Sledge Dev. All rights reserved.
//

import UIKit

// add our MyTimerDelegate to our class
class ViewController: UIViewController, MyTimerDelegate {
    
    var timer:MyTimer = MyTimer()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        timer.view.frame = self.view.frame
        // ************************ \\
        // This is where we let the delegate know 
        // we are listening for the timerFinished method
        // ************************ \\
        timer.delegate = self
        self.view.addSubview(timer.view)
        timer.startTimer(10.0)
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // ************************ \\
    // This is where our delegate method is fired
    // ************************ \\
    func timerFinished(){
        timer.startTimer(10.0)
        println("Hey my delegate is working")
    }

}

So the important thing here is we set the timer.delegate to self so the ViewController’s method timerFinished() class is called.

Objective-C
//
//  ViewController.h
//  ObjectIveCProtocol
//
//  Created by Barrett Breshears on 10/10/14.
//  Copyright (c) 2014 Sledge Dev. All rights reserved.
//

#import 
#import "MyTimer.h"

// add our MyTimerDelegate to our class
@interface ViewController : UIViewController 

@property (nonatomic, strong) MyTimer *timer;

@end
//
//  ViewController.m
//  ObjectIveCProtocol
//
//  Created by Barrett Breshears on 10/10/14.
//  Copyright (c) 2014 Sledge Dev. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _timer = [[MyTimer alloc] init];
    _timer.view.frame = self.view.frame;
    _timer.delegate = self;
    [self.view addSubview:_timer.view];
    [_timer startTimer:10.0];
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)timerFinished{
    [_timer startTimer:10.0];
    NSLog(@"Hey my delegate is working!");
}

@end

When we run the code we see the timer label is added and set to a 10 second timer. It counts down, and when it reaches 0 it notifies the viewcontroller, restarts the timer and prints the “Hey my delegate is working in the console”.

If you have any questions about the code or found this tutorial helpful let me know in the comments below! I appreciate feedback. Also don’t forget to follow me on twitter. I am always looking for iOS developers to tweet with.

CoreLocation iOS 8 and Swift

I started my first swift app with iOS 8 and needed to use core location, something that I haven’t used in a really long time. After writing all the code and testing out the app I couldn’t get the dang thing to use my location! After a frustrating 30 min of debugging I figured out what the issue is and decided to write this post to hopefully help you avoid that frustration. So here is what you need to do to get core location working with iOS 8 and swift.

CoreLocation Project setup

Add the CoreLocation.framework to your project

First step is to select your project in the project navigator, and select your project target. Once selected expand the link binary and click the add items button. Choose the CoreLocation.framework from the pop up window.
Screen Shot 2014-10-09 at 7.14.55 AM

Editing Info.plist

This is where I screwed up in my attempt, I forgot to add the property key in my plist. This caused the app not to get my location, and it didn’t give any warning or error to let me know there was even a problem. So add NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription key in your Info.plist file to indicate the level of authorization you require. NSLocationWhenInUseUsageDescription will allow you to get the device location when you are using the app and NSLocationAlwaysUsageDescription will allow you to get the device location when you are using the app as well as in the background when the app isn’t running. The key you set is what will be displayed in the pop up asking for permission. Also as a side note if you need to localize your plist here is apple’s docs on how to do that Localizing Property List Values. I only need to use location while the app is in use so I used NSLocationWhenInUseUsageDescription and only wanted apple’s default message so I left the value blank.
Screen Shot 2014-10-09 at 7.37.09 AM

Create your view controller and add the CLLocationManagerDelegate

Now that we have all that project setup done we can get on to the code. So first step is to add the CLLocationManagerDelegate to your view controller, implement these delegate methods. Then init your CLLocationManager and start receiving your location data.

//
//  MapViewController.swift
//
//  Created by Barrett Breshears on 10/6/14.
//  Copyright (c) 2014 Sledge Dev. All rights reserved.
//

import UIKit
import CoreLocation

class MapViewController: UIViewController, CLLocationManagerDelegate {

    let locationManager = CLLocationManager()
    
    override func viewDidLoad() {
        if (CLLocationManager.locationServicesEnabled()) {
            locationManager.delegate = self
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.requestWhenInUseAuthorization()
            locationManager.startUpdatingLocation()
        } else {
            println("Location services are not enabled");
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    
    // MARK: - CoreLocation Delegate Methods
    func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
        locationManager.stopUpdatingLocation()
        removeLoadingView()
        if ((error) != nil) {
            print(error)
        }
    }
    
    func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
        var locationArray = locations as NSArray
        var locationObj = locationArray.lastObject as CLLocation
        var coord = locationObj.coordinate
        println(coord.latitude)
        println(coord.longitude)
    }
}

Let me know if there are any questions or comments about the code or the project setup. Also don’t forget to follow me on twitter! I’m always looking for new internet friends.