How to write an Objective-C Completion Block

77,041

Solution 1

I always use this when I want to write a block:

http://fuckingblocksyntax.com

EDIT

If you are writing Swift then use this:

http://fuckingswiftblocksyntax.com

If the above links are not opening because of the obscene language, use this one.

http://goshdarnblocksyntax.com/

Solution 2

I wrote a completionBlock for a class which will return the values of a dice after they have been shaked:

  1. Define typedef with returnType (.h above @interface declaration)

    typedef void (^CompleteDiceRolling)(NSInteger diceValue);
    
  2. Define a @property for the block (.h)

    @property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
    
  3. Define a method with finishBlock (.h)

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
    
  4. Insert previous defined method in .m file and commit finishBlock to @property defined before

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
        self.completeDiceRolling = finishBlock;
    }
    
  5. To trigger completionBlock pass predefined variableType to it (Don't forget to check whether the completionBlock exists)

    if( self.completeDiceRolling ){
        self.completeDiceRolling(self.dieValue);
    }
    

Solution 3

You define the block as a custom type:

typedef void (^ButtonCompletionBlock)(int buttonIndex);

Then use it as an argument to a method:

+ (SomeButtonView*)buttonViewWithTitle:(NSString *)title 
                      cancelAction:(ButtonCompletionBlock)cancelBlock
                  completionAction:(ButtonCompletionBlock)completionBlock

When calling this in code it is just like any other block:

[SomeButtonView buttonViewWithTitle:@"Title"
                   cancelAction:^(int buttonIndex) {
                         NSLog(@"User cancelled");
               } 
                 completionAction:^(int buttonIndex) {
                         NSLog(@"User tapped index %i", buttonIndex);
               }];

If it comes time to trigger the block, simply call completionBlock() (where completionBlock is the name of your local copy of the block

Solution 4

Regarding to http://goshdarnblocksyntax.com/

As a local variable:

returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};

As a property:

@property (nonatomic, copy) returnType (^blockName)(parameterTypes);

As a method parameter:

- (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName;

As an argument to a method call:

[someObject someMethodThatTakesABlock:^returnType (parameters) {...}];

As a typedef:

typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^returnType(parameters) {...};

Solution 5

Simple Completion block

    // Completion block method
    -(void)myMethod:(void (^)(void))completion {
        NSLog(@"iPhone");
        completion();
        NSLog(@"iPod");
    }

   // Calling completion block method
    - (void)viewDidLoad {
        [super viewDidLoad];

        [self myMethod:^{
           NSLog(@"iPad");
        }];
    }

  // output
  iPhone
  iPad
  iPod
Share:
77,041

Related videos on Youtube

Andrew
Author by

Andrew

Updated on July 09, 2022

Comments

  • Andrew
    Andrew almost 2 years

    I'm in a situation where need to call a class method from my view controller, have it do it's thing, but then perform some actions ONLY AFTER the class method has completed.

    (I think what I need is a completion block, but please correct me if I'm wrong.)

    Here is the situation:

    I'm using Parse.com for my apps back end. When a user signs up for an account, they enter their name, company and some other info in a popup then click submit. The submit button is linked to a class method (shown below) which takes their PFUser object, and company name and creates some database objects. After the function completes, the popup is dismissed using a delegate.

    The issue is I need the creation of these objects to happen in a specific order because they depend on each others objectId's to exist. The problem is, the delegate method to dismiss the popup is called right away after clicking submit as it's the next on the stack.

    When saving a Parse object one calls a method that looks something like this: (This is kind of what I'm hoping to write and I think would solve my issue)

    [someParseObject saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
        // Code here runs AFTER the method completes.
        // This also happens on another thread which
        // I'd like to implement as well.
    }];
    

    So, What I need to figure out how to do something like the following: (Everything having to do with the block is completely wrong I'm sure)

    SignUpViewController.m
    
    myUserOrg *userOrg = [myUserOrg object]; // myUserOrg = Custom PFObject Subclass
    
    // My method that takes in a user object and a string, creates
    // the database objects in order.
    [userOrg registerNewUserOrgWithUser:(PFUser*) andCompanyName:(NSString*) companyName withBlock(somethingHere)block {
    
        if(error) {
            NSLog(@"Unable to create org!");
        } else {
            NSLog(@"Created Org!");
            [self.delegate dismissSignupView];
    }
    

    Please let me know if you need additional information or clarification.

    Thank you in advance!

    --------- EDIT ONE ----------

    Alright, So several sizeable units of time later, this is what I've come up with. The whole implementation could be better simplified and make far fewer api calls but will work on that. Several other glaring issues with it as well but is a first step.

    Method Call:

    [testOrg registerNewUserOrgWithUser:currentUser
             creatingOrgContactWithName:@"MyBigHappy Corp."
                              withBlock:^(BOOL succeeded, NSError *error) {
                                  if (error) {
                                      NSLog(@"Not working");
                                  } else {
                                      NSLog(@"Working!");
                                  }
                              }];
    

    Method Implementation:

    @implementation MYUserOrg
    
    @dynamic orgContact;
    @dynamic orgDisplayName;
    @dynamic members;
    @dynamic contacts;
    
    + (NSString *)parseClassName {
        return @"MYUserOrg";
    }
    
    dispatch_queue_t NewUserOrgRegistrationQueue;
    
    -(void)registerNewUserOrgWithUser:(MYUser*)user
           creatingOrgContactWithName:(NSString*) orgContactName
                            withBlock:(MYBooleanResultBlock) block {
    
        NewUserOrgRegistrationQueue = dispatch_queue_create("com.myapp.initialOrgCreationQueue", NULL);
    
        dispatch_async(NewUserOrgRegistrationQueue, ^{
    
            NSMutableArray *errors = [[NSMutableArray alloc] init];
    
            // Initial org save to generate objectId
            NSError *orgSaveError = nil;
            [self save:&orgSaveError];
    
            if (orgSaveError) {
                [errors addObject:@"Initial Org save Failed"];
            }
    
            // Create and Relate Org Contact
            NSError *saveOrgContactError = nil;
            MYontact *orgContact = [MYContact object];
            [orgContact setContactType:MYContactTypeUserOrganization];
            [orgContact setDisplayName:orgContactName];
            [orgContact setParentOrg:self];
            [orgContact save:&saveOrgContactError];
    
            if (saveOrgContactError) {
                [errors addObject:@"Saving Org Contact Failed"];
            } else {
                // If Org contact saved, set it;
                [self setOrgContact:orgContact];
            }
    
            // Create amd Relate User Contact
            NSError *saveUserContactError = nil;
            MYContact *userContact = [MYContact object];
            [userContact setFirstName:user.firstName];
            [userContact setLastName:user.lastName];
            [userContact setContactType:MYcontactTypeUser];
            [userContact setParentOrg:self];
            [userContact save:&saveUserContactError];
    
            if (saveUserContactError) {
                [errors addObject:@"Saving user contact failed"];
            }
    
            NSError *saveUserError = nil;
            [user setParentOrg:self];
            [user setUserContact:userContact];
            [user save:&saveUserError];
    
            if (saveUserError) {
                [errors addObject:@"Saving User failed"];
            }
    
            // Return if block succeeded and any errors.
            NSError *error = nil;
            BOOL succeeded;
            if (errors.count > 0) {
    
                NSDictionary *userInfo = @{@"error" : errors};
                errors = [NSError errorWithDomain:@"MyAppErrorDomain"
                                             code:1
                                         userInfo:userInfo];
                succeeded = NO;
            } else {
                succeeded = YES;
            }
            block(succeeded, error);
        });
    
    }
    
    @end
    
    • Wain
      Wain about 10 years
      Take a look at saveAllInBackground:block: to see if you can make only 1 save and dismiss from that block. You probably won't need to define your own block, just use the block called by Parse SDK.
    • Andrew
      Andrew about 10 years
      I actually had no idea about this method so thank you for pointing it out! The issue is that the objects need to be created in a specific order as some of their properties depend on each other. I can't just save them all at once as the property values wont exist in some cases when the save happens.
  • logixologist
    logixologist over 9 years
    awesome URL. I will have to open it when I get back home. I dont even want to try to have that in my history. LOL!
  • user1988
    user1988 about 8 years
    lmao awesome style to handle something you can't remember
  • AndaluZ
    AndaluZ about 6 years
    Hahahaha I really understand why they call it f... 😄
  • frakman1
    frakman1 about 5 years
    It seems to me that completion blocks are an awesome feature that C/C++ sorely needs. Is there a library for C/C++ that I could import which provides the completion block feature?
  • Billyobobo
    Billyobobo almost 3 years
    Thank you! Exactly what i needed