Pages

Friday 23 March 2012

Blocks Programming in iOS

Hello friends!!
In this post i am going to talk about "Blocks programming in iOS". iOS4 and latter supports blocks programming.

Blocks:
     Blocks are code snippet that can be use as a function or can be written as inline code at time of method invocation.

I think defining blocks is wasting of time because i can not define whole thing about blocks as it has lots of features , lots more than the above definition. You will get good understanding of blocks programming by examples of course! because examples are good way to explain anything.Let's start

Syntax to declare inline block:
     
^(parameter list){
   //you code
}

Inline block means blocks defined at time of function call.Caret (^) indicate the start of block literal and curly braces {} indicate the body of block.For example -
//following method use the block to print the square of given number 
-(void)printSquareOf:(NSInteger)number byExecutingBlock:(int(^)(int))block{

    NSLog(@"Square of %i = %i",number,block(number));
    //you can call block same as c functions 
    //syntax to call block using block name - blockName(commas separated parameter list)
    //like block(number); block1() and so on 
}

//call above method to print the square fo desire number
//block will square the given number and called method will print result in log

[self printSquareOf:4 byExecutingBlock:^(int n){return n*n;}];

OUTPUT:                                                                                                                             
   Square of 4 = 16                                                                                                                

We have define a inline block at line number 13 which take a integer as parameter and returns square of that number and "printSquareOf: byExecutingBlock: " prints the result.You can declare block as function argument like -

(return_type(^)(parameters list))block_name
For examples -
-(void)myLog:(void(^)())block;
-(void)printSquareOf:(NSInteger)number byExecutingBlock:(int(^)(int))block;
We need to writes inline blocks again and again, thats not good, now question is how can we writes once and use many times? of course we can do it by assigning blocks to a block variable, call blocks using block variable. 
Syntax to declare a block Variable -
  returnType (^blockVariableName)(parametersTypeList);
For example - 
  void(^now)(void);
  void(^doSquare)(int);
  void(^sayHelloTo)(NSString*);

Syntax to assign block literal to block variable -
  blockName = ^(parameterList){};

Following example shows that how to declare block variables and assign block literal in two different steps

    //declare block variable
    void(^sayHello)(void);//sayHello is block variable name
    //assign block literal to block variable sayHello
    sayHello = ^{NSLog(@"Hello,I am coding for bugs");};
    
    //call block using block variable
    sayHello();

In above example we have declare block variable at line number 2 which return type is void, name is "sayHello" and it does not accept any parameter so void in parameter list. We have assigned block literal at line number 4 which print "Hello,I am coding for bugs". We are calling block at line number 7.

In one shot we can declare block variable and assign block literals like this - 
 returnType (^blockVariableName)(parameterTypeList) = ^(parameterList){};

Following example shows that how to declare block variable and assign block literal in one shot -
    //this block calculate factorial of given number 
    NSInteger(^calculateFactorialOf)(NSInteger) = ^(NSInteger n){
        NSInteger fact = 1;
        if (n==0) {//factorial of 0 is 1 
            return fact;
        }
        for (int i=n; i>0; i--) {
            fact = fact*i;
        }
        
        return fact;
    };
    //call block, factorial will be assign to result 
    NSInteger result = calculateFactorialOf(4);
    NSLog(@"factorial = %i",result);//print 24
In above example you have seen how to use inline block and block variables.One main thing left about block is - it captures surrounding state that's why it is called as closures. Blocks are called closures because they are closed around variables that are in scope at the time of block declaration. Its means that a variable declare before of block declaration can be directly used in block.Block keeps a read only copy of in scope variables for example -
//calculate half of the given number using provided block and print the result 
-(void)calculateHalfOf:(NSInteger)number byExecutingBlock:(int(^)(int))block{
    NSLog(@"Half of %i = %i",number,block(number));
    //you can call block same as c functions 
    //syntax to call block using block name - blockName(commas separated parameter list)
    //like block(number); block1() and so on 
}

//cal the calculate method from viewDidLoad: method
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    NSInteger divider = 2;
    [self calculateHalfOf:4 byExecutingBlock:^(int n){return n/divider;}];
    //above block will carry a read only copy of divider variable 
}

OUTPUT:                                                                                                                            
     Half of 4 = 2                                                                                                                    

In above block we have not define the divider variable but we are using we can't modify the value of divider if we will do that it will give error as -


In above screenshot we can see that error - Variable is not assignable(missing __block type specifier).
Oh! Its not good, What will we do?if need to modify the divider variable. Don't worry take long breath and think what is __block? Yeah you are thinking right by using __block  storage type modifier we can make divider mutable.For example -
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    
   __block NSInteger divider = 1;//__block makes variable mutable
    //see the above example for calculateHalfOf: byExecutingBlock: definition 
    [self calculateHalfOf:4 byExecutingBlock:^(int n){
        //modify divider, now it is not read only 
        divider = divider*2;
        return n/divider;}];
}
OUTPUT:                                                                                                                            
     Half of 4 = 2                                                                                                                    

When we use __block storage type modifier, variables passed by reference into the block.
Note: We should not use __block causally because it moves variables to heap, so please do not use it unless you really need it.
Hey cheers! We have completed basic of block programming.I can't believe that still you guys are reading! I salute your enthusiasm.Now i am going to discuss last things.
Declare block as property:

BlockDemo.h

#import<uikit/uikit.h>
typedef int (^devideEquallyBlock)(int);
@interface BlockDemo : UIViewController{
     devideEquallyBlock callbackBlock;
}
@property (nonatomic, copy) devideEquallyBlock callbackBlock;
-(void)calculateHalfOf:(NSInteger)number byExecutingBlock:(devideEquallyBlock)block;
@end
BlockDemo.m
#import "BlockDemo.h"

@implementation BlockDemo

@synthesize callbackBlock;

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];

   __block NSInteger divider = 1;//__block makes variable mutable
    //assigning block to property 
    callbackBlock = ^(int n){
        divider = divider*2;
        return n/divider;};
    [self calculateHalfOf:4 byExecutingBlock:callbackBlock];
}
-(void)calculateHalfOf:(NSInteger)number byExecutingBlock:(devideEquallyBlock)block{
    NSLog(@"Half of %i = %i",number,block(number));
    //you can call block same as c functions 
    //syntax to call block using block name - blockName(commas separated parameter list)
    //like block(number); block1() and so on 
}
- (void)dealloc {
    [callbackBlock release];
    [super dealloc];
}
@end
In .h file i have used typedef, it is a c construct that assign a name to existing data type.For example i have given a new name devideEquallyBlock to block type "(int(^)(int))". Even Apple uses typedef to assign names to block because it keeps our code clean and readable.We should also use typedef.I have released block at line number 27 in .m file because i have called copy at time of property  declaration at line number 6 in .h file.

Thanks for reading, please leave your comments below,bye bye! 

Wednesday 21 March 2012

Sharing data among iOS applications using UIPasteboard

Hello Friends,
As iOS apps have their own sandbox and one app can't read data of other application, for security reason its look good and really its good.One day i got a requirement in which i need to share data of one app with other app.First i thought it is not possible in an environment like sandbox, i searched on google and got a luck on  stackoverflow saying use UIPasteboard but a programmer like me expect some code. That day i  did not get any thing more than a little luck saying don't worry it is possible using UIpasteboard so in this post i am going to talk about UIPasteboard and write some basic codes.

UIPasteboard:
   UIPasteboard is a class given in UIKit.framework.You can use it to share data within the application and with other applications.

I am using two application to demonstrate the use of UIPasteboard.
  1. PBWriter
  2. PBReader 
let's create first application named as PBWriter or you can name something else.This application take some input from user and save those in UIPasteboard.User Interface of this application look like this - 

Enter data in above fields, hit "Save in PB" button it will save the data in Pasteboard.Oh! i have not discussed any thing about code. let's write code -

You can use any of following methods to create/get Pasteboard object, i am using "pasteboardWithName:create:"
  • +generalPasteboar //use this to get system pasteboard
  • +pasteboardWithName:create: //use this to create your own pasteboard

//use reverse DNS notation to keep you Paste board unique 
NSString *PBNAME = @"com.codingForBugs.PBDemo.PBDemo";


 UIPasteboard *pb = [UIPasteboard pasteboardWithName:PBNAME create:YES];//create a new PB if does not exists 

By default custom pasteboards are not persistent, you need to make it manually.If not required in your case just ignore it.

//pass yes to make pasteboard persistent 
[pb setPersistent:YES];


If you are not setting Persistent  property to YES, remamber that pastboard will be removed when your application quit.Now we have pasteboard and we need to write user information in it.

Note:Pasteboard will be removed when application uninstalled not matter it is persistent or not. 

There are some predefine properties in which you can write your data like string, strings, url,image, items etc.For example you have only one string you can use "string" property of UIPasteboard. If you want to save more than one item than better to use items properties like - 
    NSMutableArray *pbValues = [[NSMutableArray alloc] init];
    NSDictionary *d1 = [NSDictionary dictionaryWithObject:name.text forKey:@"name"];
    NSDictionary *d2 = [NSDictionary dictionaryWithObject:s1Value.text forKey:@"dob"];
    NSDictionary *d3 = [NSDictionary dictionaryWithObject:s2Value.text forKey:@"age"];
    NSDictionary *d4 = [NSDictionary dictionaryWithObject:s3Value.text forKey:@"height"];
    [pbValues addObject:d1];
    [pbValues addObject:d2];
    [pbValues addObject:d3];
    [pbValues addObject:d4];
    
    //save all items in pasteboard
    [pb setItems:pbValues];
When you will hit "Save in PB" it call a method "saveButtonClicked:", this method look like this
- (IBAction)saveButtonClicked:(id)sender {
    //get paste board 
    
    UIPasteboard *pb = [UIPasteboard pasteboardWithName:PBNAME create:YES];//create a new PB if does not exists 
    
    //make PB persistent. PB will be deleted when its owner application uninstalled
    [pb setPersistent:YES];
    
    //save values in PB with unique key so thet other application can read it
    NSLog(@"name = %@",name.text);
    NSMutableArray *pbValues = [[NSMutableArray alloc] init];
    NSDictionary *d1 = [NSDictionary dictionaryWithObject:name.text forKey:@"name"];
    NSDictionary *d2 = [NSDictionary dictionaryWithObject:nameValue.text forKey:@"dob"];
    NSDictionary *d3 = [NSDictionary dictionaryWithObject:ageValue.text forKey:@"age"];
    NSDictionary *d4 = [NSDictionary dictionaryWithObject:heightValue.text forKey:@"height"];
    [pbValues addObject:d1];
    [pbValues addObject:d2];
    [pbValues addObject:d3];
    [pbValues addObject:d4];
    
    //save all items in pasteboard
    [pb setItems:pbValues];
    
}

If you calls setItems: again, it overwrites previous values to prevent it use addItems: as [pb addItems:newValues]
Now we have finish the writer part we need to write a reader application that will read the data save in pasteboard. Reader app ui look like this - 
When you will hit the "Read PB" button one pop-up will come displaying whatever information we have saved in pasteboard.To read data first we need to get the pasteboard, we will retrieve dat using any of following function - 
  • dataForPasteboardType:
  • string
  • strings
  • items and so on 


Data will be returned in form of NSData and of particular type same as properties. For example if you are assigning value to string property it will return string, when we access using other method, we need to convert it in string from NSData. When you will hit "Read PB" button "- (IBAction)readPB:(id)sender" called this method look like this -
- (IBAction)readPB:(id)sender {
    //get pasteboard with predefine name
    UIPasteboard *pb = [UIPasteboard pasteboardWithName:PBNAME create:NO];//no because we doesn't want to create a new one 
    if (pb) {
        NSArray *dataArray = [pb items];
        NSDictionary *name = [dataArray objectAtIndex:0];
        //valuesInPb use to create message text
        NSString *valuesInPb = [NSString stringWithFormat:@"Name = %@\n",
                                [[NSString alloc] initWithData:[name valueForKey:@"name"] encoding:NSUTF8StringEncoding]];
        NSDictionary *info = [dataArray objectAtIndex:1];
        valuesInPb = [NSString stringWithFormat:@"%@D.O.b = %@\n",valuesInPb,
                      [[NSString alloc] initWithData:[info valueForKey:@"dob"] encoding:NSUTF8StringEncoding]];
        NSDictionary *info1 = [dataArray objectAtIndex:2];
        valuesInPb = [NSString stringWithFormat:@"%@Age = %@\n",valuesInPb,
                      [[NSString alloc] initWithData:[info1 valueForKey:@"age"] encoding:NSUTF8StringEncoding]];
        NSDictionary *info2 = [dataArray objectAtIndex:3];
        valuesInPb = [NSString stringWithFormat:@"%@Height = %@\n",valuesInPb,
                      [[NSString alloc] initWithData:[info2 valueForKey:@"height"] encoding:NSUTF8StringEncoding]];
        [self showText:valuesInPb target:self];
        NSLog(@"%i",[dataArray count]);
    }else{
        [self showText:@"PB doesn't exists.\nYou should run PBWritter app first" target:self];
    }
}


//this method is use to display the alert vew
-(void)showText:(NSString*)textMessage target:(id)target{
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"pbReader" message:textMessage delegate:target cancelButtonTitle:@"OK" otherButtonTitles: nil];
    [alert show];
    [alert release];
}
I think it  may help you, plese leave you comments.bye bye!