Wednesday 19 November 2014

How to start developing apps for Apple Watch using WatchKit framework iOS 8.2 and Xcode 6.2

It was a wonderful day yesterday (Nov-18th, 2014) for iOS developers, finally Apple  released Xcode 6.2 Beta with WatchKit framework to start developing apps for Apple Watches. It started a new chapter in iOS developers  life to play with Watchkit framework and make relationship with Apple watches.

I am not sure about What the NDA restrictions are, but I am just having thing on the basis of publicly information the  Apple WatchKit
But its not so limited information, its too much to learn with Beta release.

Software and tools required to start developing apps for Apple Watch using WatchKit framework are.

1- Xcode 6.2 or latter
2- Mac OSx 10.10
3- iOS 8.2 or latter

Lets first go through some basic but interesting things about Apple Watch Architecture and Watchkit Layout.

* iPhone Device:  It works like a "Model" and "Controller" of the  app. iPhone Device contains all the code of the application and responds to all the events like app launch, button taps etc.

* Apple Watch: It works like a "View" of the  app. All the Static images and Storyboard are contains in it. It is an user interface of the app. It process user inputs but did not run any code.

Communication between Apple Watch and  iPhone get automatically. Wow interesting! WatchKit SDK handles this communication via Bluetooth behind the scenes.
One issue for developers is that Animations are not supported in WatcKit. It means Developers will try for third party API (if any) or They will use set of .GIF images to achieve animations.

Two new words are seen to represent Watchkit, Glances and Notifications.

1-Glances: Provides a Quick overview of the contents within app

2-Notifications: Let us receive notifications on our watch.


Sunday 9 November 2014

How to Scan Barcode using iOS native framework

Prior to iOS 7, It was a big problem for iOS developer to add a native QR code scanner. So some third party code library were used to achieve barcode scanning. But iOS 7 brings a bunch of new features. The AVFoundation framework is  one of them where some new API/classes are added, as a result AVFoundation provided the ability to discover and read bar codes in real time.

In this tutorial we are going to scan a QR Code, its known as Quick Response Code. QR Code is 2D barcode. Its design can have horizontal and vertical lines. So you can scan 1D (vertical lines) and 2D (Vertical and Horizontal lines).

What can be store in QR code ?
QR Code can contain information of like web URL, Phone number, SMS text and other Simple text.


Lets start how to Implement QR Code Scanning with Native iOS framework.

1- Open Xcode and create a new project  (I pick single view application  ), name it "BarcodeScanner".

2-Open Storyboard and Create a UIbutton to Start/Stop scanning, a UILabel to print scanning status/scanned information, And a UIView inside that a camera will be open to scan QR code.


QR Code scan and read using native iOS framework

3- Open ViewController.h and  define some properties

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>

@interface ViewController : UIViewController<AVCaptureMetadataOutputObjectsDelegate>

@property (nonatomic, strong) AVCaptureSession *captureSession;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *videoPreviewLayer;
@property(nonatomic,assign) BOOL IsScanning;

@property(weak, nonatomic)IBOutlet UILabel *lblScanStatus_info;
@property(weak, nonatomic)IBOutlet UIButton *btnStartStop;
@property(weak, nonatomic)IBOutlet UIView *vwCameraPreview;

-(IBAction)StartStop_Scan:(id)sender;
@end



4- Connect IBOutlet from starboard, Connect "StartStop_Scan" method to Button.
Open ViewController.m
Define two private method one for Start Scan and Second one for Stop scan

@interface ViewController ()
-(void)StartScanning;
-(void)StopScanning;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _IsScanning = NO; //
    
}

#pragma mark IBAction

-(IBAction)StartStop_Scan:(id)sender
{
    NSLog(@"Inside startstopmethod");
    //If start button  pressed
    if(!_IsScanning)
    {
        [self StartScanning];
        
    }
    ////If Stop button  pressed
    else
    {
        [self StopScanning];
        
    }
    
    //set the flag opposit to earlier ;
    _IsScanning = !_IsScanning;
}

Don't forget to check camera support in device, Simulator don't support camera so we need a device to run this tutorial.


-(void)StartScanning
{
    AVCaptureDevice *captureDevice = nil;
    AVCaptureDeviceInput *input = nil;
    //Check for device camera existance
    NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    if ( [videoDevices count] > 0 ) // This device has one or more cameras
    {
        NSError *error;
        captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        
        // Get an instance of the AVCaptureDeviceInput class
        input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
        
        if (!input) {
            // If any error occurs, simply log the description
            NSLog(@"error desc:%@", [error localizedDescription]);
            
        }
        else
        {
            // init the captureSession .
            _captureSession = [[AVCaptureSession alloc] init];
            // Set  input device at the capture session.
            [_captureSession addInput:input];
            
            
            // Initialization of a AVCaptureMetadataOutput object and set it to output sessioncapture
            AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
            [_captureSession addOutput:captureMetadataOutput];
            
            // make a serial dispatch queue.
            dispatch_queue_t dispatchQueue;
            dispatchQueue = dispatch_queue_create("QRCodeScanQueue", NULL);
            [captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatchQueue];
            [captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]];
            
            //  video preview layer and adding at subview _vwCameraPreview
            _videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
            [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
            [_videoPreviewLayer setFrame:_vwCameraPreview.layer.bounds];
            [_vwCameraPreview.layer addSublayer:_videoPreviewLayer];
            
            // Start video capturing.
            [_captureSession startRunning];
            
            [_btnStartStop setTitle:@"Stop Scan" forState:UIControlStateNormal];
            [_lblScanStatus_info setText:@"Scanning for QR Code:-----"];

        }
    }
    else
    {
        NSLog(@"Camera is not supported here");
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"Current device does not supporting camera " delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        
        [alert show];
        
    }
    
}

-(void)StopScanning
{
    // Stop video capture
    [_captureSession stopRunning];
    _captureSession = nil;
    
    // Remove video preview layer.
    [_videoPreviewLayer removeFromSuperlayer];
    
    [_btnStartStop setTitle:@"Start Scan" forState:UIControlStateNormal];
    [_lblScanStatus_info setText:@"QR Code Scan is not started"];
}

#pragma mark Implememntaion of AVCaptureMetadataOutputObjectsDelegate method

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
    
    if (metadataObjects != nil && [metadataObjects count] > 0) {
        // Get the metadata object.
        AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];
        //Check QR type meta data object
        if ([[metadataObj type] isEqualToString:AVMetadataObjectTypeQRCode]) {
            // If the found metadata is equal to the QR code metadata then update the label status info text,
            
            // Proecees of main thread done.
            [_lblScanStatus_info performSelectorOnMainThread:@selector(setText:) withObject:[metadataObj stringValue] waitUntilDone:NO];
            //and stop reading
            [self performSelectorOnMainThread:@selector(StopScanning) withObject:nil waitUntilDone:NO];
            
            [_btnStartStop performSelectorOnMainThread:@selector(setTitle:) withObject:@"Start Scan" waitUntilDone:NO];
            
            _IsScanning = NO;
            
        }
    }
}

To test QR code scan you may find some images from Google and or use the image shown above for sample QR code.

Hope you will enjoyed this code. If you have any concern please share your comments.

Sunday 2 November 2014

How to fix CLLocationManager location updates issue in iOS 8

It was  time consuming issue for me to  fix CLLocationManager location update issue in iOS 8.x, I was too much worried, why my code is not working in iOS 8 neither asking for user permission nor the location update delegate. 

CLLocationManager fails to work with  [self.locationManager startUpdatingLocation]. CLLocationManager not working for updating location updates. Its neither giving any error nor warning so it is more complex to figure out why this is not working.
Even Application did not ask for location related permission. So you might get irritate, don’t worry here the solution.

What are extra updates for iOS 8:
There are two more things need to be add to working with location.
1- Add one or both below keys to (app-name)info .plist file:

* NSLocationAlwaysUsageDescription
* NSLocationWhenInUseUsageDescription

Above keys take a string as description of why your app need location services. You may enter a string like “To Find out your location”
Note: iOS will not ask an user for permission to use their location until you have given a reason as why you are requesting it.

2- Request authorization for related location method, i.e WhenInUSe or Always. Use one of below method calls.

[self.locationManager requestWhenInUseAuthorization];
[self.locationManager requestAlwaysAuthorization];

WhenInUseAuthorization:  It allows the application to get location updates when the application is in foreground state and not in other state.
requestAlwaysAuthorization: It allows the application to get location updates in both when the application  is in the foreground and or in the background.
Below is the sample code for help.

Before using this code make sure you already added NSLocationWhenInUseUsageDescription in the  (app-name)info .plist  file.

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    // Check for iOS 8 Vs earlier version like iOS7.Otherwise code will
   // crash on ios 7
    if ([self.locationManager respondsToSelector:@selector
                       (requestWhenInUseAuthorization)]) {
        [self.locationManager requestWhenInUseAuthorization];
    }
    [self.locationManager startUpdatingLocation];
}

// CLLocationManager Delegate Methods
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:
(NSArray *)locations
{
    NSLog(@"location info object=%@", [locations lastObject]);
}
 
 
I hope You will enjoyed this post and if you any suggestion or feedback
 your most welcome