Monday, 23 June 2014

Tutorial: Asynchronous HTTP Client Using NSOperationQueue

Introduction
Recently here at ELC, we’ve been working on an app that requires a lot of server interaction, which has been a learning experience for managing threading, server load and connectivity. In order to keep the app performant and responsive while sending a large number of requests and aggregating a large set of data, our team had to intelligently manage and prioritize network interactions.
This is where NSOperationQueue helps out. This class relies heavily on Grand Central Dispatch (GCD) to execute NSOperations (in this case, HTTP requests and JSONserializations). These operations can be executed in various configurations, including concurrently and asynchronously.
In this tutorial, I’ll show you how to set up a block-based operation queue that takes advantage of NSBlockOperation, a concrete subclass of NSOperation. You can use this server client class to manage all interactions with your external server. To demonstrate this, our sample code will query Twitter for a set of search tags and return the results asynchronously. The sample code is available on Drive. Let’s get started.
Server Client Setup
First, create a MediaServer class with an operation queue property. This server class is a singleton because we want to route all network requests through a single operation queue.
@interface MediaServer : NSObject
 
@property (strong) NSOperationQueue *operationQueue;
 
+ (id)sharedMediaServer;
 
@end
Our server singleton is instantiated as follows. Note that using dispatch_once takes advantage of GCD and is recommended by Apple for thread safety.
+ (id)sharedMediaServer;
{
    static dispatch_once_t onceToken;
    static id sharedMediaServer = nil;
 
    dispatch_once( &onceToken, ^{
        sharedMediaServer = [[[self class] alloc] init];
    });
 
    return sharedMediaServer;
}
Next, in MediaServer’s init method, initialize the operation queue and set the concurrent operation count. The maxConcurrentOperationCount property can be changed later, too.
- (id)init;
{
    if ( ( self = [super init] ) )
    {        
        _operationQueue = [[NSOperationQueue alloc] init];
        _operationQueue.maxConcurrentOperationCount = 2;
    }
 
    return self;
}
Search Tags Management
In the project files, you’ll notice SearchTagsViewController. I’ve set this up to handle adding, removing and editing Twitter search tags. You’ll find a pretty straightforward implementation using NSUserDefaults to persist your search tags. The main purpose of this view controller is to prepare a series of server requests.
Server Calls Using NSBlockOperation
Now, we’re ready to start using our operation queue. For our example, we’ll be searching for tweets containing various keywords, so we only need one fetch method in our server class.
Note: because blocks are a bit syntactically difficult to read, it can be convenient to typedef assign a block’s input and return parameters. This also makes methods much more readable when passing in blocks. For our example, we are expecting an array of tweet objects, and we’ll check for errors in the HTTP request and JSONserialization. In MediaServer.h, add:
typedef void (^FetchBlock)(NSArray *items, NSError *error);
Now, we’re ready to add our fetch tweets method. This method accepts a search string and a return block (FetchBlock). This method will create an NSBlockOperation instance using blockOperationWithBlock: and dispatch it to the Media Server’s dispatch queue. Within that block, we’ll asynchronously send an NSURLRequest, serialize the response and synchronously return the tweets using our FetchBlock. Let’s take a look at the code.
- (void)fetchTweetsForSearch:(NSString *)searchString block:(FetchBlock)block;
{
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
 
        NSMutableArray *tweetObjects = [[NSMutableArray alloc] init];
        NSError *error = nil;
        NSHTTPURLResponse *response = nil;
 
        NSString *encodedSearchString = [searchString stringWithURLEncoding];
        NSString *URLString = [NSString stringWithFormat:@"http://search.twitter.com/search.json?q=%@&rpp=%i&include_entities=true&result_type=mixed", encodedSearchString, SEARCH_RESULTS_PER_TAG];
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:URLString] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:DEFAULT_TIMEOUT];
        NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
 
        NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
        NSArray *tweets = [JSON objectForKey:@"results"];
 
        // Serialize JSON response into lightweight Tweet objects for convenience.
 
        for ( NSDictionary *tweetDictionary in tweets )
        {
            Tweet *tweet = [[Tweet alloc] initWithJSON:tweetDictionary];
            [tweetObjects addObject:tweet];
        }        
 
        NSLog(@"Search for '%@' returned %i results.", searchString, [tweetObjects count]);
 
        // Return to the main queue once the request has been processed.
 
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
 
            if ( error )
                block( nil, error );
            else
                block( tweetObjects, nil );
        }];
 
    }];
 
    // Optionally, set the operation priority. This is useful when flooding
    // the operation queue with different requests.
 
    [operation setQueuePriority:NSOperationQueuePriorityVeryHigh];
    [self.operationQueue addOperation:operation];
}
Dispatching Tweet Searches
Let’s look at how we’ll use this server method. In TweetsViewController’s viewDidLoad: method, we’ll want to loop through our search tags and fetch each set of tweets. Because each operation is dispatched to our operation queue, we don’t have to worry about swamping the server or causing timeouts due to limited network bandwidth. To do so, in viewDidLoad:, add:
MediaServer *server = [MediaServer sharedMediaServer];
 
    for (NSString *tag in self.tags) 
    {
        [server fetchTweetsForSearch:tag block:^(NSArray *items, NSError *error) {
 
            if ( items && error == nil )
            {
                [self.tweets addObjectsFromArray:items];
 
                NSArray *sortDescriptorsArray = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"createdAtDate" ascending:NO]];
                [self.tweets sortUsingDescriptors:sortDescriptorsArray];
 
                [self.tableView reloadData];
                [self.activity stopAnimating];
            }
        }];
    }
Canceling Tweet Searches
In certain cases, you might want to cancel operations in your queue, for example, when the user navigates away from a view displaying content from several serverrequests. In this example, when the user taps the back ‘Tags’ button, we want to prevent the remaining search requests from going through. This is as easy as:
- (void)viewDidDisappear:(BOOL)animated;
{
    MediaServer *server = [MediaServer sharedMediaServer];
    [[server operationQueue] cancelAllOperations];
}
Note that this doesn’t immediately remove operations from the queue, instead it notifies the queue to abort the operation as soon as possible. If you need more specific control of which operations to keep running, NSOperationQueue also exposes an operations property to manually cancel specific operations.
Concurrency
The only thing fetchTweetsForSearch:block: does is create an operation and submit it to the queue, so it returns almost immediately. The main benefit of this approach is that all of the work within the block operation occurs on a background queue, leaving the main thread free and ensuring that the UI remains responsive. To confirm this is working properly, you can open up the Time Profiler in Instruments (an extremely useful tool for improving UX) and check which queue the block is executed on.
Profiled NSOperation
In the profiler, you’ll see that initWithJSON:, JSONObjectWithData:options:error:and sendSynchronousRequest:returningResponse:error: are all executed on a dispatched worker thread, not the main thread. That’s exactly what we want.
Conclusion
There you have it. As a developer, you’ll glean the most benefit from this server paradigm when sending out a large number of URL requests or when your user is on a slow network. If you do encounter situations where your queue is filling up with requests, remember that you can prioritize your operations, e.g.,
[operation setQueuePriority:NSOperationQueuePriorityVeryHigh];
Another benefit of this approach is the ability to return cached data using our FetchBlock, while updating from the server in the background. Look for more on that in a later blog post.
Happy iCoding!

Getting the user’s location using CoreLocation

In this iPhone application development tutorial we are going to create an app which will present the user’s location info such as; geographical coordinates, altitude and speed using the CoreLocation Framework.

The CoreLocation Framework

From the early beginning of iOS, Apple gave to developers the ability to get user’s location using the CoreLocation Framework. However, the user must first allow an app to use location services in order to retrieve the user’s location.

Here is an app using location services which requests user’s permission in order to retrieve location.

Let’s get started!

First of all you need to create a new Xcode Single View Application:
xcode single view app
Then, click Next and you will be prompted to another screen asking you for the project settings. Type in the first field (“Product name”) the name of your project and click Next.

Importing the CoreLocation Framework

In order to use CoreLocation Framework you need to include the framework
  • Click on the Target
  • Click the “+” button on “Linked Frameworks and Libraries” section in order to access frameworks.
  • Finally click “Add”.
Import CoreLocation Framework
xcode choose framework
In order to use the CoreLocation framework you need to import it in the Xcode project application header file (in our case “ViewController.h”) as shown below:


#import <CoreLocation/CoreLocation.h>
@interface ViewController : UIViewController <CLLocationManagerDelegate>



In our header file we should also declare some variables as shown below.


IBOutlet UILabel *latitude;
IBOutlet UILabel *longitude;
IBOutlet UILabel *altitude;
IBOutlet UILabel *speed;
CLLocationManager *locationManager;
locationManager is the object responsible for the location data. The UILabels are the controls on which we will show the location data.

Designing the screen

Now click on the “Main.storyboard” file and drag 8 UILabels on the view. Arrange the labels as shown in the picture below.
xcode storyboard view
When you arrange the UILabels properly, link each UILabel declared with the correspondent UILabel on the view as shown in the picture below.
xcode link file

Let’s get serious.

It is time to edit the implementation file (in our case “ViewController.m”).
Add the following code in the viewDidLoad method in order to instantiate and set locationManager properties.


- (void)viewDidLoad
{
    [super viewDidLoad];
    locationManager = [[CLLocationManager alloc]init]; // initializing locationManager
    locationManager.delegate = self; // we set the delegate of locationManager to self.
    locationManager.desiredAccuracy = kCLLocationAccuracyBest; // setting the accuracy

    [locationManager startUpdatingLocation];  //requesting location updates
}



On the 4th line we instantiate the locationManager object which will be responsible for the location data.
On the 5th line we set the delegate of locationManager to ViewController (self) .
This means that every location related event will be sent to ViewController.
On the 6th line we set the desiredAccuracy to kCLLocationAccuracyBest which is the best provided. Keep in mind that higher accuracies need more device resources (power etc) and you should be careful on how you set the desiredAccuracy property.
Do not set the desiredAccuracy to more than you really need.
On the 8th line we call the startUpdating method of the locationManager.
By calling this method we request location updates.
In order to capture the location updates we have to implement the delegate methods of the locationManager properties.
Add the following code to ViewController.m file.


-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
    UIAlertView *errorAlert = [[UIAlertView alloc]initWithTitle:@"Error" message:@"There was an error retrieving your location" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
    [errorAlert show];
    NSLog(@"Error: %@",error.description);
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation *crnLoc = [locations lastObject];
    latitude.text = [NSString stringWithFormat:@"%.8f",crnLoc.coordinate.latitude];
    longitude.text = [NSString stringWithFormat:@"%.8f",crnLoc.coordinate.longitude];
    altitude.text = [NSString stringWithFormat:@"%.0f m",crnLoc.altitude];
    speed.text = [NSString stringWithFormat:@"%.1f m/s", crnLoc.speed];
}
The first method implemented (locationManager didFailWithError) will be called if the location manager face an error when it tries to retrieve location.
The second method implemented (and the most important one) will be called when the device has new location data. locationManage didUpdateLocations method provides an NSArray (locations) which contains the most recent locations (object of CLLocation type). So, the most recent location is the last object of the NSArray. We assign the value of the last location retrieved to a new CLLocation object called crnLoc and we set the text property of the UILabels to the location data we want.

Let’s run it!

Now run the application in the simulator. The app will first request your permission to use your current location then it will show you some fake location data on the UILabels.
You should see something like this:
xcode simulator app

Final Notes

I hope you find this tutorial helpful. If you have any question do not hesitate to ping me on Twitter or leave a comment below.

Enjoyed this post?

Sunday, 22 June 2014

How to create rounded avatars in your iOS Application

Rounded avatars seem to be very fashionable these days. Even Apple adopted the rounded images for contacts. If you’re wondering how to achieve this in your app here is the answer.
All we need to do is adjust the CALayer for the image view representing the avatar:

self.avatarImageView.layer.cornerRadius = 150.0f;
self.avatarImageView.layer.borderWidth = 2.0f;
self.avatarImageView.layer.borderColor = [UIColor blackColor].CGColor;
self.avatarImageView.clipsToBounds = YES;


The value for the corner radius is exactly half the width of the image. We’ll assume that we have a square image of size 300×300 otherwise we won’t get a perfect circle. We’ll add a nice black border and we’ll set the property clipsToBounds to YES. The clipsToBounds property needs to be YES for this to work. And there you have it, a big round image.

Enjoyed this post?


Integrating Social Media in your iOS Applications

In this iPhone development tutorial we are going tο show how you can post on Facebook and Twitter via your app using the Social framework.

Let’s get started!

First of all you need to create a new Xcode Single View Application:

xcode single view app

Then, click Next and you will be prompted to another screen asking you the project settings. Type in the first field (“Product name”) the name of your project and click Next.

Importing the Social Framework

In order to use Social Framework you need to include the framework
  • Click on the Target
  • Click the “+” button on “Linked Frameworks and Libraries” section in order to access frameworks.
  • Finally click “Add”.

Import Social Framework
xcode choose framework

In order to use the Core Location framework you need to import it in the header file (in our case “ViewController.h”) as shown below:


#import "Social/Social.h"
@interface ViewController : UIViewController

In our header file we should also declare some variables as shown above and two actions.


@interface ViewController : UIViewController
{
 IBOutlet UIButton *Tweet;
 IBOutlet UIButton *FBPost;
}
-(IBAction)TweetPressed;
-(IBAction)FBPressed;
@end


Designing the screen

Now click on the “Main.storyboard” file and drag 2 UIButtons on the view. When you arrange the 2 UIButtons , link each UIButton and each IBAction declared with the correspondent UIButton on the view as shown in the picture below. For the IBActions select the “Touch Up Inside” event.
xcode link
xcode link

Let’s get serious

It is time to edit the implementation file (in our case “ViewController.m”) .
Add the following code in the ViewController.m file in order to be able to post on facebook by tapping the “Post on Facebook” button.

-(IBAction)FBPressed{
    if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook])
    {
        SLComposeViewController *fbPostSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
        [fbPostSheet setInitialText:@"This is a Facebook post!"];
        [self presentViewController:fbPostSheet animated:YES completion:nil];
    } else
    {
        UIAlertView *alertView = [[UIAlertView alloc]
                                  initWithTitle:@"Sorry"
                                  message:@"You can't post right now, make sure your device has an internet connection and you have at least one facebook account setup"
                                  delegate:self
                                  cancelButtonTitle:@"OK"
                                  otherButtonTitles:nil];
        [alertView show];
    }
}


In the second line we check if facebook is available on the iOS device that our app runs.
Then we instantiate the SLComposeViewController and we set service type to SLServiceTypeFacebook. Then, we present the compose view controller. If facebook is not available on the device on which our apps run, then we present a UIAlert informing the user that he has not set any facebook account on his device.
And for twitter:

-(IBAction)TweetPressed{
    if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter])
    {
        SLComposeViewController *tweetSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
        [tweetSheet setInitialText:@"This is a tweet!"];
        [self presentViewController:tweetSheet animated:YES completion:nil];
   
    }
    else
    {
        UIAlertView *alertView = [[UIAlertView alloc]
                                  initWithTitle:@"Sorry"
                                  message:@"You can't send a tweet right now, make sure your device has an internet connection and you have at least one Twitter account setup"
                                  delegate:self
                                  cancelButtonTitle:@"OK"
                                  otherButtonTitles:nil];
        [alertView show];
    }

}

As you can see implementation for twitter is pretty much the same with the implementation for facebook.

Let’s run it!

Now run the application in the simulator. Try to press the buttons to tweet and post on facebook from the app that you created.
Post on facebook
Post on facebook

Final Notes

I hope you find this tutorial helpful. If you have any question do not hesitate to ping me on Twitter or leave a comment below.

Enjoyed this post?

Read values of the accelerometer

All modern iOS devices includes a accelerometer, which can detect the motion of the device. The accelerometer provides motion values over the x,y and z-axis. In this tutorial we will read this values and display them on the screen.
Open Xcode and create a new Single View Application. For product name, use AccelerometerDemo and then fill out the Organization Name, Company Identifier and Class Prefix fields with your customary values. Make sure only iPhone is selected in Devices, and that the Use Storyboards checkbox is deselected and Use Automatic Reference Counting checkbox is selected.
In ViewController.m in the interface section, declare the following properties.


@interface ViewController ()

@property (nonatomic, strong) CMMotionManager *motionManager; 
@property (nonatomic, strong) IBOutlet UILabel *xAxis; 
@property (nonatomic, strong) IBOutlet UILabel *yAxis; 
@property (nonatomic, strong) IBOutlet UILabel *zAxis; 

@end



The CMMotionManager object acts as a gateway to the motion services provided by iOS. The other properties are needed to update the Labels with the axes values. Go to ViewController.xib and add 3 UILabels to the main View.

Ctrl + Drag from the Label to the File's Owner and make the connection with the corresponding properties.
Change the viewDidLoad method to 
- (void)viewDidLoad
{ 
  [super viewDidLoad];

  self.motionManager = [[CMMotionManager alloc] init]; 
  self.motionManager.accelerometerUpdateInterval = 1;

  if ([self.motionManager isAccelerometerAvailable]) 
  { 
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [self.motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{ 
          self.xAxis.text = [NSString stringWithFormat:@"%.2f",accelerometerData.acceleration.x]; 
          self.yAxis.text = [NSString stringWithFormat:@"%.2f",accelerometerData.acceleration.y]; 
          self.zAxis.text = [NSString stringWithFormat:@"%.2f",accelerometerData.acceleration.z];
        });
      }]; 
  } else 
  NSLog(@"not active"); 
}


First we initialize the CMMotionManager object, then we check if the accelerometer is available on the iOS device. We use the startAccelerometerUpdatesToQueue:withHandler method to get updates every secondd of the accelerometer. Then we update our UILables with the coresponding values on the main queue.
Because the iOS Simulator doesn't have a simulated accelerometer, you can run this app only on a device. Build and Run, and move the Device into the different axes. The values should be between -1.00 and 1.00



Friday, 27 December 2013

Objective C Programming Tutorials (playlist)

Objective C Programming Tutorials (playlist)


https://www.youtube.com/playlist?list=PL640F44F1C97BA581