Saturday, April 29, 2017

iOS Design Patterns: MVC and MVVM

11:15 AM Posted by CHANDAN MAKHIJA No comments
iOS was first released in 2007, since then it has implemented Model-View-Controller (MVC) development design pattern. This design pattern is a common way to organize code so that it is reusable and more easily extensible. 
In this post, we are going to look closer at the existing iOS MVC pattern variation and offer an alternative, and perhaps better, pattern for iOS development.

Introducing MVVM
One issue facing iOS developers is how to deal with major iOS updates for existing projects. More specifically, how to implement UI/UX changes as iOS evolves. Because iOS uses a combined view-controller design, this task can require a greater level of effort than should be necessary. Here’s why: because the view and controller are coupled, an iOS view-controller class will usually contain both UI logic and business logic. This means that changes in the way the view is presented (UI logic) will usually also require changes to business logic within the same view controller class.
Further, as view controller classes implement increasingly complex UI requirements, the amount of business-logic code also tends to grow within the same view controller class. This, is turn, typically results in large, unwieldy, and difficult-to-read view controller classes.
Wouldn’t it be better to have thin, flexible, easy-to-read view controller classes in iOS?

The MVVM Design Pattern
The “Model-View ViewModel” design pattern, or “MVVM”, is similar to the MVC as implemented in iOS, but provides better decoupling of the UI and business logic. This decoupling results in thin, flexible, and easy-to-read view controller classes in iOS
Business Logic and workflows are contained in ViewModel and all UI related logic is there in View-Controllers, and knows nothing about business logic.
MVVM is built around three fundamental parts: data model, view/view-controller, and viewModel:

1) Data Model
Just like in the MVC design pattern, the MVVM data model is a class that declares properties for managing business data.
2) ViewModel
The viewModel is at the heart of the MVVM design pattern and provides the connection between the business logic and the view/view controller. The view (UI) responds to user input by passing input data (defined by the model) to the viewModel. In turn, the viewModel evaluates the input data and responds with an appropriate UI presentation according business logic workflow.
3) View/View Controller
The view/view controller is the context (i.e. the view controller class) that presents user interface elements. It contains no business logic


Advantages of MVVM Design Pattern

Thin view controllers that are easily debugged
In MVVM, view controllers are concerned only with the UI. MVVM UI encapsulation means finding and fixing UI issues is usually a straightforward process that no longer involves digging through business logic trying to locate the root cause of a particular bug.
Flexible business logic and work flows
Adding and modifying workflows (e.g. which view shows when) is done very quickly in the MVVM design pattern. MVVM UI/business logic decoupling means changes can usually be done with simple one or two line changes in the viewModel.
Code reuse
Most init, setup, and view presentation methods are implemented in the viewModel class. As such, each new view controller shares those methods, which means no redundant view controller code.

Sunday, April 16, 2017

Scan QR Code / Bar Code Using AVFoundation Framework (Objective c)

10:54 PM Posted by CHANDAN MAKHIJA No comments
AV Foundation is the full featured framework for working with time-based audiovisual media on iOS, macOS, watchOS and tvOS. Using AV Foundation, you can easily play, create, and edit QuickTime movies and MPEG-4 files, play HLS streams, and build powerful media functionality into your apps.

AVFoundation incorporates,  the ability to discover and read bar codes in real-time. Any barcode scanning, including QR codes, is totally based on video capturing, that’s why AVFoundation framework supports the barcode reading feature. Keep this info on mind, as it’s the starting point for the entire application.

First of all add UIView view, which is going to be the container for the preview layer of the video captured by the device.

Here is how the demo application is going to work:

Add the camera button on the UIViewController. When you will click on this button a UIView will be loaded that will contain the preview of recording. When you will start recording any Barcode , it will capture it and display the information captured on UITextField.

Also, a sound effect (a beep sound) is being played every time a QR code has been successfully read to make our app more vivid and interactive.

Note that this application cannot be tested on the Simulator, neither to a device without a camera, as everything is based on real-time video capturing. Therefore, you’ll need to connect your device and run it there if you want to see the application live.

First of all make XIB that contains a UIView with Black BackGround, as shown below





 Make a class named "QRScanView"

Add following code in "QRScanView.h"


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

@interface QRScanView : UIView <AVCaptureMetadataOutputObjectsDelegate>

// connect your Xib here
@property (weak, nonatomic) IBOutlet UIView *scanView;

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

@property  BOOL isReading;

@property void(^barCodeScanned)(NSString*);

+ (instancetype) loadScanner;

- (void)showScanner:(UIViewController*) vc;
- (void) getScannedValue:(void(^)(NSString* message))callback;


@end


Add following code in "QRScanView.m"


#import "QRScanView.h"

static QRScanView* sSharedInstance;
static QRScanView* _scanner ;

@interface QRScanView()
{
    NSString* _serviceTag;
}

@end

@implementation QRScanView

+ (instancetype) loadScanner
{
    if(!sSharedInstance)
    {
        // load your xib on UIView here
        sSharedInstance = [[[NSBundle mainBundle] loadNibNamed:@"QRScanView" owner:sSharedInstance options:nil] lastObject];
    }
    
    return  sSharedInstance;
}

- (void)showScanner:(UIViewController*) vc
{
    sSharedInstance.isReading = NO;
    sSharedInstance.captureSession = nil;
    sSharedInstance.center = CGPointMake(vc.view.frame.size.width  / 2,
                                  vc.view.frame.size.height / 2);
    sSharedInstance.layer.borderWidth = 1.0f;
    sSharedInstance.layer.borderColor = [UIColor lightGrayColor].CGColor;
    
    [sSharedInstance startReading];
    
    [vc.view addSubview:sSharedInstance];
}

- (void) startReading
{
    NSError *error;
    
    AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
    if (!input)
    {
        NSLog(@"%@", [error localizedDescription]);
    
    }
    
    _captureSession = [[AVCaptureSession alloc] init];
    [_captureSession addInput:input];
    
    AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
    [_captureSession addOutput:captureMetadataOutput];
    
    dispatch_queue_t dispatchQueue;
    dispatchQueue = dispatch_queue_create("myQueue", NULL);
    
    [captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatchQueue];
    
     // include all types of barcode here
    [captureMetadataOutput setMetadataObjectTypes:[NSArray                 arrayWithObjects:AVMetadataObjectTypeQRCode, AVMetadataObjectTypeFace, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeUPCECode,AVMetadataObjectTypeAztecCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeITF14Code,AVMetadataObjectTypeCode39Code, AVMetadataObjectTypeCode93Code, AVMetadataObjectTypePDF417Code, AVMetadataObjectTypeCode128Code, AVMetadataObjectTypeDataMatrixCode,AVMetadataObjectTypeCode39Mod43Code,AVMetadataObjectTypeInterleaved2of5Code, nil]];
    
    _videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
    [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    [_videoPreviewLayer setFrame:self.scanView.layer.bounds];
    [self.scanView.layer addSublayer:_videoPreviewLayer];
    
    [_captureSession startRunning];
    
}

- (void) stopReading
{
    [_captureSession stopRunning];
    _captureSession = nil;
    
    [_videoPreviewLayer removeFromSuperlayer];
    
    // remove Xib from view here
    [self removeFromSuperview];
    
    self.barCodeScanned(_serviceTag);
}

// get scanned value here
- (void) getScannedValue:(void(^)(NSString* message))callback
{
    self.barCodeScanned = callback;
}

// Delegate methods for AVCaptureMetadataOutputObjects
- (void) captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    if (metadataObjects != nil && [metadataObjects count] > 0)
    {
        AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];
        
        _serviceTag = [metadataObj stringValue];
        
        [self performSelectorOnMainThread:@selector(stopReading) withObject:nil waitUntilDone:NO];
       
        self.isReading = NO;
    }
}


@end



Now import "BarcodeScan.h" file in you ViewController

On IBAction of  button click, call the method of "QRScanView" class like this


- (IBAction)scanCodeBtnPressed:(id)sender
{
    QRScanView* scanner = [QRScanView loadScanner];

    self.scanCodeBtn.selected = !self.scanCodeBtn.isSelected;
    
    if(self.scanCodeBtn.selected)
        [scanner showScanner:self];
    
    [scanner getScannedValue:^(NSString *message) {
        //display scanner message on UITextField
        UITextField* textField = (UITextField*)[self.view viewWithTag:EServiceTag];
        textField.text = message;
    }];

}