How to write an Objective-C Completion Block
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/
Custom completion block for my own method
1) Define your own completion block,
typedef void(^myCompletion)(BOOL);
2) Create a method which takes your completion block as a parameter,
-(void) myMethod:(myCompletion) compblock{
//do stuff
compblock(YES);
}
3)This is how you use it,
[self myMethod:^(BOOL finished) {
if(finished){
NSLog(@"success");
}
}];
How to write a completion block in objective-C?
check out http://fuckingblocksyntax.com for syntax.
For personal choice I like to return value and error in the completion block (similar to iOS framework pattern)
As an example;
declaration
- (void)fetchStuff:(void (^)(id value,NSError *error))completion;
calling the function
// async fetch
[object fetchStuff:^(id value, NSError *error) {
// do stuff with value
}];
Objective-C: How to give a completion block to a method that uses a result from the method execution
Yes, what you've shown is the typical way of doing this. Look at Apple's own APIs to see that they do it the same way. For example, -[NSURLSession dataTaskWithURL:completionHandler:]
, whose declaration is:
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url
completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
Using Obj-C completion block in Swift
Given that your closure doesn't specify nullability qualifiers (where they almost certainly are optional), one can safely assume that your Objective-C API has not been audited for nullability. Thus, Swift will treat pointers as implicitly unwrapped optionals. Furthermore, nowadays the NSDictionary
is mapped to a [NSObject : AnyObject]
Swift dictionary.
Thus, it would be:
obj.objcMethod(param) { (success: Bool, result: [NSObject : AnyObject]!, error: NSError!) in
if success {
// do something
}
}
Or, as Kobi points out, you can let the compiler infer the types:
obj.objcMethod(param) { success, result, error in
if success {
// do something
}
}
Note, you don't have to remember this yourself. You can leverage Xcode's code completion as you enter the code. So, type enough to match the method name and when it matches objcMethod
, then hit enter:
When you get to MYCompletionBlock
, hit enter again, and it will show you the correct signature:
If this Objective-C method was my own class, I would audit it for nullability. So, for example, let's assume the param
is optional, the closure is required, and the result
and error
were optional, you might define it like so:
NS_ASSUME_NONNULL_BEGIN
typedef void (^MYCompletionBlock)(BOOL success, NSDictionary * _Nullable result, NSError * _Nullable error);
@interface MyObject : NSObject
- (void)objcMethod:(NSDictionary * _Nullable)param1 withCompletionHandler:(MYCompletionBlock)completionHandler;
@end
NS_ASSUME_NONNULL_END
And, if that was the case, your Swift code would call it like so:
obj.objcMethod(param) { (success: Bool, result: [NSObject : AnyObject]?, error: NSError?) in
if success {
// do something
}
}
Or, again, just let the compiler infer the types for you (but this time they'd be inferred as optionals that are not implicitly unwrapped):
obj.objcMethod(param) { success, result, error in
if success {
// do something
}
}
Objective C: Is there a way to call a completion block for a method in another method?
You can pass completion blocks around and then call them from your final method which handles all the get requests. I usually make completion blocks that are going to be reused typedefs for brevity. Here's an example of what I mean (I added a second example method that also passes through to the center getRequestWithURLString:onSuccess:onFailure:
method):
LLFakeManager.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void (^_Nullable SuccessCompletionBlock)(id responseObject);
typedef void (^_Nullable FailureCompletionBlock)(NSError *error);
@interface LLFakeManager : NSObject
- (void)getUserInfoOnSuccess:(SuccessCompletionBlock)successBlock onFailure:(FailureCompletionBlock)failureBlock;
- (void)getBooksCheckedOutOnSuccess:(SuccessCompletionBlock)successBlock onFailure:(FailureCompletionBlock)failureBlock;
@end
NS_ASSUME_NONNULL_END
LLFakeManager.m
#import "LLFakeManager.h"
@interface LLFakeManager()
- (void)getRequestWithURLString:(NSString *)urlString
onSuccess:(SuccessCompletionBlock)successBlock
onFailure:(FailureCompletionBlock)failureBlock;
@end
@implementation LLFakeManager
- (void)getUserInfoOnSuccess:(SuccessCompletionBlock)successBlock onFailure:(FailureCompletionBlock)failureBlock {
NSString *urlStr = @"FakeUserUrlPath";
[self getRequestWithURLString:urlStr onSuccess:successBlock onFailure:failureBlock];
}
- (void)getBooksCheckedOutOnSuccess:(SuccessCompletionBlock)successBlock onFailure:(FailureCompletionBlock)failureBlock {
NSString *urlString = @"FakeBooksUrlPath";
[self getRequestWithURLString:urlString onSuccess:successBlock onFailure:failureBlock];
}
// central method that will handle all the get requests
- (void)getRequestWithURLString:(NSString *)urlString
onSuccess:(SuccessCompletionBlock)successBlock
onFailure:(FailureCompletionBlock)failureBlock {
// some fake implementation here to do your request, then use the completion block passed in from whatever other method
if (successBlock) {
successBlock(@"responseObjectPassedBackHere");
}
}
@end
And an example of calling it:
LLFakeManager *manager = [[LLFakeManager alloc] init];
[manager getUserInfoOnSuccess:^(id _Nonnull responseObject) {
NSLog(@"Here's my response object = %@", responseObject);
} onFailure:^(NSError * _Nonnull error) {
// no implementation but same idea
}];
Would produce this log:
Here's my response object = responseObjectPassedBackHere
This site: http://goshdarnblocksyntax.com is a handy list of block syntax which may also be helpful for you.
completion block with parameters
If you want to pass a NSString*
to the method:
-(void)testingFunction:(void(^)(NSMutableArray* result))handler andString:(NSString*) yourString;
Or if you want to pass a NSString*
to the completion block:
-(void)testingFunction:(void(^)(NSMutableArray* result, NSString* yourString))handler;
Edit:
You can call the method like this:
NSString* yourString = @"Some Text";
testingFunction:^(NSMutableArray* result) {
//Do whatever you want here
} andString:yourString;
Go read this: http://www.tutorialspoint.com/objective_c/objective_c_functions.htm
EDIT2:
As trojanfoe said, if your string is supposed to be an url you should use NSURL
instead of NSString
How to create c++ equivalent of Objective-C completion block as param
In C++ you would usually use std::function
for this, which is a class template that can hold any kind of callable function/object including a lambda (caveat: as long as it's copyable):
void mySetter(Thing thing, std::function<void(bool)> completion) {
// ...
completion(true);
}
mySetter(thing, [](bool success) {
// ...
});
Note that in Objective-C++ (.mm files) you can generally mix C++ and Obj-C types freely, so you could for example pass a block as a parameter to a C++ function — you just need to be aware of the proper block pointer syntax.
void mySetter(Thing thing, void (^completion)(BOOL)) {
// ...
completion(YES);
}
mySetter(thing, ^(BOOL success) {
// ...
});
objective c completion handler
RequestManager.h
@interface RequestManager : NSObject
//Create a block property
typedef void(^postRequestBlock)(BOOL status);
-(void) getContentInBackgroundWithMemberId: (int) memberId completed:(postRequestBlock)completed;
@end
RequestManager.m
-(void) getContentInBackgroundWithMemberId: (int) memberId completed:(postRequestBlock)completed{
NSDictionary * params = [NSDictionary dictionary];
params = @ {
@ "member_id": [NSNumber numberWithInt: memberId]
};
[self postRequestWithParams: params completed:^(BOOL status){
completed(status);
}];
}
//Add completion block.
-(void) postRequestWithParams: (NSDictionary * ) params completed:(postRequestBlock)completed{
[NSURLConnection sendAsynchronousRequest: request queue: [NSOperationQueue mainQueue] completionHandler: ^ (NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
if (!connectionError) {
NSDictionary * serverData = [NSJSONSerialization JSONObjectWithData: data options: 0 error: nil];
NSArray * result = [NSArray array];
result = [serverData objectForKey: @ "result"];
completed(YES);
} else {
completed(NO);
}
}];
}
There are already tons of Q&A's for blocks. Here's one that might help further explain
How to write an Objective-C Completion Block
Related Topics
Cocoa Touch: How to Change Uiview's Border Color and Thickness
How to Xcodebuild a Static Library with Bitcode Enabled
Xcode6: Run Two Instances of the Simulator
Using Core Data, Icloud and Cloudkit for Syncing and Backup and How It Works Together
Uibutton Touch Is Delayed When in Uiscrollview
Swift Modal View Controller with Transparent Background
How to Open Maps App Programmatically with Coordinates in Swift
iOS Convert Large Numbers to Smaller Format
Xcode 6 with Swift Super Slow Typing and Autocompletion
iPhone Simulator Suddenly Started Running Very Slow
Disable Autolayout Constraint Error Messages in Debug Console Output in Xcode
Add Swipe to Delete Uitableviewcell
Paging Uiscrollview in Increments Smaller Than Frame Size
iPhone Sdk:How to Play Video Inside a View? Rather Than Fullscreen
Xcode Changes Unmodified Storyboard and Xib Files
How to Create Otp Verification Screen and Detect Delete Backward on Multiple Uitextfield Is Swift