Brian Coleman

  • Home
  • ABOUT
  • SERVICES
  • PORTFOLIO
  • BLOG
  • Contact

Objective-c 16

Tutorial: Run a Block of Code After a Delay

LED Baseball Pitch
For the longest time I’ve wanted to know how to pause the app or wait a certain amount of time before running a block of code. This is often useful for when you’re doing simple animations or if you need to wait before updating the UI. Take the example to the right, in the LED Baseball game I needed to find a way to animate the pitches from the mound to home plate. The LEDs images are in set spots, and for the effect of a pitch they need to blink on and off in sequence, sometimes at different speeds depending on the pitch.

The best way to implement this code block is to implement a category that can be reused in any of your projects.
The following code is credited to Duncan C.

The header file: (filename = NSObject+performBlockAfterDelay.h)

//
//  NSObject+performBlockAfterDelay.h
//
//  Copyright (c) 2012 WareTo. May be used by anyone, free of license, as 
//  long as this copyright notice remains.
//

@interface NSObject (performBlockAfterDelay)

- (void) performBlock: (dispatch_block_t) block
           afterDelay: (NSTimeInterval) delay;

@end

The .m file: (filename = NSObject+performBlockAfterDelay.m)

//
//  NSObject+performBlockAfterDelay.m
//  ChromaKey
//
//  Copyright (c) 2012 WareTo. May be used by anyone, free of license, as 
//  long as this copyright notice remains.
//

#import "NSObject+performBlockAfterDelay.h"

@implementation NSObject (performBlockAfterDelay)

- (void) performBlock: (dispatch_block_t) block
           afterDelay: (NSTimeInterval) delay;
{
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC), dispatch_get_current_queue(), 
                 block);
}
@end

To use it, just add the .m and .h files above to your project.
Then add a #import at the top of any .m file that needs this method:

#import "NSObject+performBlockAfterDelay.h"

Here’s a sample of the code in action for a fastball in LED Baseball.

[self performBlock:^{led1.hidden = NO;} afterDelay: .75];
[self performBlock:^{led1.hidden = YES;led2.hidden = NO;} afterDelay: .15];
[self performBlock:^{led2.hidden = YES;led3.hidden = NO;} afterDelay: .225];
[self performBlock:^{led3.hidden = YES;led4.hidden = NO;} afterDelay: .3];
[self performBlock:^{led4.hidden = YES;led7.hidden = NO;} afterDelay: .375];
[self performBlock:^{led7.hidden = YES;led8.hidden = NO;} afterDelay: .45];
[self performBlock:^{led8.hidden = YES;led9.hidden = NO;} afterDelay: .525];

By running a series of blocks in a row, each LED is shown, then hidden after a set delay.

I hope this code helps you as much as it did me, I’ll be using this category anytime I need to “pause”.

June 26, 2013 Tutorialsios, objective-c, tutorial

Tutorial: Write a Review / Rate Us

User ratings play a huge role in where your app ranks on Apples Top Charts lists, and it influences new users who are considering downloading your app. Yet it’s often tough to get users to rate your app, because without reminding them and making it really simple they most likely will not review your app. The only exception to this is the user who only wants to criticize your app or the die hard fans of the app. The largest user segmentation you have is the users who like your app and these users are the ones we need to remind.

There is also a problem of misuse in regards to ratings. Often users who don’t know better will use the review along with a low rating in an attempt to get tech support from a developer. This is flawed for everyone involved; it lowers the rating of the app, scares away new users, and worst of all, as a developer we have no way to get in touch with users who rate our app.

Below is the code that belongs in your – (void)viewDidLoad or – (void)viewWillAppear methods. It checks to see how many times the user has launched the app and increments it by 1. If the launch counter is equal to 3, 9, 15, or 21 we’ll show the Rate App alert message unless “neverRate” equals to YES.

    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    //Ask for Rating
    BOOL neverRate = [prefs boolForKey:@"neverRate"];

    int launchCount = 0;
    //Get the number of launches
    launchCount = [prefs integerForKey:@"launchCount"];
    launchCount++;
    [[NSUserDefaults standardUserDefaults] setInteger:launchCount forKey:@"launchCount"];
    
    if (!neverRate)
    {
        if ( (launchCount == 3) || (launchCount == 9) || (launchCount == 15) || (launchCount == 21) )
        {
            [self rateApp];
        }
    }
    [prefs synchronize];

When we ask the user if they would like to rate the app it’s good to give them three options “Rate It Now”, “Remind Me Later” and “No, Thanks”. If the user selects one of the first or last then we’ll set NSUserDefaults to YES so the user will never see the alert again. If they select “Maybe Later” we’ll show it to them again in the future.

- (void)rateApp {   
    BOOL neverRate = [[NSUserDefaults standardUserDefaults] boolForKey:@"neverRate"];
    
    if (neverRate != YES) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Please rate !"
                                                        message:@"If you like it we'd like to know."
                                                       delegate:self
                                              cancelButtonTitle:nil
                                              otherButtonTitles:@"Rate It Now", @"Remind Me Later", @"No, Thanks", nil];
        alert.delegate = self;
        [alert show];
    }
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 0) {
        [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"neverRate"];
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=573753324"]]];
    }
    
    else if (buttonIndex == 1) {
        
    }

    else if (buttonIndex == 2) {
        [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"neverRate"];
    }
}

If the user selects “Rate” they will be redirected to your app within the App Store, and the Rates & Reviews section already opened. To make it go to your app, change “573753324” in the code above to point to your unique app id within iTunes Connect. If you leave it as is your users will be directed to my Travel Bomb app, I could use some reviews!
how_many_reviews_do_ios_developers_have

May 29, 2013 Tutorialsios, objective-c, tutorial

Tutorial: Create Your Own iOS Web API

Most of the top apps in the App Store communicate with a server for one reason or another. An app may need to send user data such as game scores or currency to a global database to track rankings or currency. It may serve up dynamic content or even ads that the owner wishes to change without making an app update. All of this and more require an API (application programming interface) to communicate between the app and the web server. In this tutorial we’ll walk-through how to setup a script on the web server using PHP and how to communicate with that script from within an app.

Setup the PHP Web API

To start you’ll need a web server that will host the PHP script. One of the best shared hosts in the US is 1 & 1. You can host as many URLs and MySQL databases as you wish for around $100 a year. Next you need to write your PHP script that will be sending data to your app.

The code below is PHP, you can cut and paste it into a text editor and save it as adAPI.php, then upload it to your server. Be sure to set the permissions of the file on the server to 0755 to make it executable. You can do that using any FTP program and right clicking on the file and selecting “Permissions”. If they are not set correctly the app will not be able to execute the PHP script on the server and it will return an error.


You’ll notice that we are passing a parameter into the script ‘AdID’, this is needed if you would like to return different ads or if you would like to use the same script for multiple apps. Most ad APIs setup ads by zones, meaning a zone is a specific place in your app where you would like the ad to show up. Above we have setup one ad. All this script does it return a single line string back to the app “1|http://images.apple.com/home/images/promo_iphone5.png|http://www.apple.com/iphone/”. This string is broken up into 3 variables that are separated by a pipe “|” character. The first variable “1” is used as a flag to turn the ad on and off. I’ve used this to turn an interstitial ad (loading screen ad) on and off. If it’s set to 1 we’ll show the ad in the app, if it’s set to 0 we’ll hide the ad. The second variable “http://images.apple.com/home/images/promo_iphone5.png” is the link to the ad image we’re going to display in the app. You’ll want to link to an image on your server, as an example I’m linking to an image on Apple.com. Lastly, the third variable “http://www.apple.com/iphone/” is where the ad links to when a user taps on it.

Communicating to the API From Your App

Now that the server side is setup we need to get the data into our app. The best way to do this is using a framework named ASIHTTPRequest. This framework allows us to send a HTTP request to the web server and listen for a response back which we’ll parse into variables.

Download the framework from the ASIHTTPRequest documentation site.

First, you’ll need to integrate the framework into your project.
1. Download the source code from the link above.
2. Add all of the header and implementation files into your project.
4. Include the following header to the top of your implementation class:
– #import “ASIHTTPRequest.h”

Here is the header file to our implementation where the ad link, button and image are defined so we can use them globally within the implementation.

// AdViewController.h
// Ad View

#import 

@interface AdViewController : UIViewController

@property (nonatomic, strong) NSString *adLink;
@property (nonatomic, strong) IBOutlet UIView *adView;
@property (nonatomic, strong) IBOutlet UIButton *adLinkButton;
@property (nonatomic, strong) IBOutlet UIImageView *adImageView;

- (void)callAdService;
- (IBAction)adButtonPushed:(id) sender;

@end

Below is the top of our implementation where we synthesize the properties and make the call to run the API in the viewDidLoad method.

// AdViewController.m
// Ad View

#import "AdViewController.h"
#import "ASIHTTPRequest.h"

@implementation AdViewController

@synthesize adLink, adLinkButton, adImageView;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    [self callAdService];
}

Next, include the following method into your class to post the request to your web server. Notice that we’re setting the ‘AdID’ to 1 to match the ad that we want to receive from the API that we defined above in the PHP script. The setDidFinishSelector: defines what is the name of the method that will handle the return message from the server and setDidFailSelector: the method if an error happened and we didn’t receive a response back.

-(void)callAdService{
    //this is a typical url for REST webservice, where you can specify the method that you want to call and the parameters directly with GET
    NSURL *url = [NSURL URLWithString:@"http://www.YOURWEBSERVER.com/api/adAPI.php?AdID=1"];
    
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    
    [request setDidFinishSelector:@selector(requestCompleted:)];
    [request setDidFailSelector:@selector(requestError:)];
    
    [request setDelegate:self];
    [request startAsynchronous];
}

Below is the method that is called when we receive the data back from the API. Remember we are expecting to receive a single string with three variables in it separated by a pipe. 1|http://images.apple.com/home/images/promo_iphone5.png|http://www.apple.com/iphone/
The string is parsed by the pipes and fed into an array. The first element being the flag to show or hide and ad, the second is the link to the hosted ad image and the third is the link once the ad is tapped. After the data is parsed into variables we check to see if the ad should be displayed, if it should be then we animate the ad up, grab the image from the URL to display it.

- (void)requestCompleted:(ASIHTTPRequest *)request
{
    NSString *responseString = [request responseString];
    NSLog(@"API Response: %@", responseString);
    
    NSArray *valueArray = [responseString componentsSeparatedByString:@"|"];
    NSString *interstitialAd = [valueArray objectAtIndex:0];
    NSString *adImage = [valueArray objectAtIndex:1];
    adLink = [valueArray objectAtIndex:2];
    
    if ([interstitialAd integerValue] == 1)
    {
        [self.view bringSubviewToFront:adView];
        adImageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:interstitialAdImage]]];
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:0.3];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
        [UIView setAnimationBeginsFromCurrentState:YES];
        
        // The transform matrix
        if (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPad && IS_IPHONE_5) {
            CGAffineTransform transform = CGAffineTransformMakeTranslation(0, -569);
            adView.transform = transform;
        }
        else {
            CGAffineTransform transform = CGAffineTransformMakeTranslation(0, -569);
            adView.transform = transform;
        }
    }
}

To finish up the process, we need to handle two events, the click-thru and the close buttons. In the “clickAdButtonPushed” we take the “adLink” variable and open the URL in the Users browser if they click on it. If you are using analytics tracking, this is where you’ll place your tag to track the conversion of your ad. The other method “closeAdButtonPushed” will animate the ad down off the screen so the user can continue to use your application.

-(IBAction) clickAdButtonPushed:(id) sender {
    NSString *str = adLink;
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];
    adView.alpha = 0;
}

-(IBAction) closeAdButtonPushed:(id) sender {
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
    [UIView setAnimationBeginsFromCurrentState:YES];
    
    // The transform matrix
    CGAffineTransform transform = CGAffineTransformMakeTranslation(0, 0);
    adView.transform = transform;
    // Commit the changes
    [UIView commitAnimations];
}
April 10, 2013 Tutorialsapi, ios, objective-c, tutorial

Tutorial: Collection View using Flow Layout

Note: Newer tutorial available. If you would like to learn how to implement Collection Views using Swift, read Tutorial: Collection View using Swift

Flow Layout
One of the best features for developers that came in the iOS 6 SDK is UICollectionView. It is very similar to UITableView but you can customize it a lot more and it can scroll horizontal, goodbye scroll views! Most recently I used a collection view for a bottom navigation scroller and it worked really well.

A UICollectionView view has three main components:
1. Cells: Display your content in cells that are de-queued as they leave the screen.
2. Supplementary Views: Add labels, section headers and footers to define your content areas.
3. Decoration Views: Decorate the collection view to look like a bookshelf or a background image.

Start by adding the delegates to your header file and define your collection view.

@interface ViewController : UIViewController 

@property (nonatomic, strong) IBOutlet UICollectionView *collectionView;
@property (nonatomic, strong) IBOutlet UICollectionViewFlowLayout *flowLayout;

The most common layout for a UICollectionView is UICollectionViewFlowLayout. Flow layout organizes items into a grid with optional header and footer views for each section. The items in the collection view flow from one row or column (depending on the scrolling direction) to the next, with each row comprising as many cells as will fit. Cells can be the same sizes or different sizes.

Define the UICollectionView & UICollectionViewFlowLayout properties in the viewDidLoad method.

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.collectionView registerClass:[CellClass class] forCellWithReuseIdentifier:@"classCell"];
    self.collectionView.backgroundColor = [UIColor clearColor];
    
    // Configure layout
    self.flowLayout = [[UICollectionViewFlowLayout alloc] init];
    [self.flowLayout setItemSize:CGSizeMake(191, 160)];
    [self.flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
    self.flowLayout.minimumInteritemSpacing = 0.0f;
    [self.collectionView setCollectionViewLayout:self.flowLayout];
    self.collectionView.bounces = YES;
    [self.collectionView setShowsHorizontalScrollIndicator:NO];
    [self.collectionView setShowsVerticalScrollIndicator:NO];
}

Most of the properties are self explanatory but the key is to define your cell class if you are using custom cells (just like table views) and setting the size of the cells, above they are set to 191×160. Choose the scroll direction (UICollectionViewScrollDirectionHorizontal or UICollectionViewScrollDirectionVertical) and the spacing in between each cell, I have it set to 0. Lastly define the layout, we’re using self.flowLayout that was defined in the header.

- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(0, 0, 0, 0);
}

The method above sets the spacing between sections within the collection view. You might breakup your data by sections if you have artists or albums in your data.

Setting up a UICollectionView is very similar to a UITableView, so you’ll recognized a lot of the methods below.

-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
   return [dataArray count];
}

Above we’re defining how many sections and how many items in the section for the collection view. If you have more than one section, then you’ll need to define the number of items in each section within the numberOfItemsInSection method.

Below is the real meat of the UICollectionView, defining the cells with your data. If you choose to use a custom cell you can add images or whatever you need to display.

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    Cell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"cellName" forIndexPath:indexPath];
    cell.label.text = [NSString stringWithFormat:@"%d",indexPath.item];
    return cell;
}

Collection View Data
Finally you can drag the UICollectionView object into your view within your NIB. Move and scale the collection view to where you want to display it and review all of the properties within the inspector. Be sure to hookup the collectionView reference outlet and dataSource and delegates so the collection view can provide all the functionality available. Do the same for the Collection View Flow Layout. (See image on the right).

April 3, 2013 Tutorialsios, objective-c, tutorial

Tutorial: Threads

Threads allows you to execute multiple code paths concurrently within your application. The reason you’ll use it is to speed up the performance of your application by running some code on another thread while other operations are happening on the main thread. This is helpful for when you need to pre-load data. I’ve used this before in a magazine app where I needed to pre-load the next couple of pages while the user is reading the current page, this made the application much smoother when swiping to the next page.

There are a couple of disadvantages to threads, one problem with creating threads yourself is that they add uncertainty to your code. Threads are a relatively low-level and complicated way to support concurrency in your application. If you do not fully understand the implications of your design choices, you could easily encounter synchronization or timing issues, the severity of which can range from subtle behavioral changes to the crashing of your application and the corruption of the user’s data. Another problem area with Cocoa and multithreading comes from user interface updates. All UI updates in Cocoa must be done on the main thread, or instability can result. If you have a background thread performing a calculation, make sure to wrap whatever method updates the UI in a -performSelectorOnMainThread:withObject:waitUntilDone: method call. The single biggest source of crashes with multithreaded Cocoa applications is simultaneous access to a shared resource.

Grand Central Dispatch (GCD)

Grand Central Dispatch is one of the most common ways to add threads. With GCD, you define the task you want to perform and add it to a work queue, which handles the scheduling of your task on an appropriate thread. Work queues take into account the number of available cores and the current load to execute your tasks more efficiently than you could do yourself using threads.

Adding a Task to a Queue

dispatch_async(myQueue, ^{
    // Insert code to be executed on another thread here
    [self methodName];
});
dispatch_release(myQueue);

To execute a task, you must dispatch it to an appropriate dispatch queue. You can dispatch tasks synchronously or asynchronously. Once in a queue, the queue becomes responsible for executing your tasks as soon as possible, given its constraints and the existing tasks already in the queue. Do not call the dispatch_sync function from a task that is executing on the same queue that you pass to your function call. Doing so will deadlock the queue. If you need to dispatch to the current queue, do so asynchronously using the dispatch_async function.

Performing Tasks on the Main Thread

You can use to execute tasks on your application’s main thread while you’re running code on another thread. You can get the dispatch queue for your application’s main thread by calling the dispatch_get_main_queue function. Tasks added to this queue are performed serially on the main thread itself. Therefore, you can use this queue as a synchronization point for work being done in other parts of your application.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
    // Insert code to be executed on another thread here
    [self methodName];
    dispatch_async(dispatch_get_main_queue(), ^{
        // Insert code to be executed on the main thread here
        [self methodName];
    });
});
dispatch_release(queue);

Suspending and Resuming Queues

It’s not possible to stop a queue after it has been called because it would cause your application to be unstable but you can pause and resume the queue. You suspend a dispatch queue using the dispatch_suspend function and resume it using the dispatch_resume function.

-(void) startRunning {
    // queue will need to be defined in your header since it's use globally in the class
    queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        // Insert code to be executed on another thread here
        [self methodName];
    });
}

-(void) userCancels {
    if (queue) {
       dispatch_suspend(queue);
    }
}

-(void) userResumes {
    if (queue) {
       dispatch_resume(queue);
    }
}

-(void) dealloc {
    if (queue) dispatch_release(queue);

    [super dealloc[;
}

March 27, 2013 Tutorialsios, objective-c, tutorial
Page 2 of 4«1234»
Recent Posts
  • Classix for iPhone, iPad & Apple TV
  • Tutorial: How to test your app for IPv6 compatibility
  • Tutorial: Testing SSL using Charles Proxy on an iOS Device
  • Tutorial: 3D Touch – Quick Actions in Swift
  • tvOS Tutorial: Top Shelf in Swift
Featured Apps
Classix
Sportsnet
TAGS
tutorialswiftios8iosobjective-cvideogamesstrategynewsframeworkappsmonitizefacebookwatchappleios7toolstvosios9apiprovisionsocialtutorialsbooksdesignbookiapIPv6iTunes Connect
Search
TAGS
tutorialswiftios8iosobjective-cvideogamesstrategynewsframeworkappsmonitizefacebookwatchappleios7toolstvosios9apiprovisionsocialtutorialsbooksdesignbookiapIPv6iTunes Connect
ABOUT
Brian is a Lead iOS/tvOS Developer from Toronto with over 18 years of multifaceted experience including development, design, business analysis and project management.

FOLLOW ME
    
Email Subscription
Sign up for my newsletter to receive the latest news and tutorials posted.

Enter your email address:

2023 © Brian Coleman