Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I create a wrapper to use blocks for a class that uses callbacks?

I'm diving into iOS programming and I'm learning how to use blocks. I have a sucky, over-engineered library that I'm using in my project and it uses a single callback method to handle all data requests...

@protocol SuckyClassDelegate <NSObject>
-(void)returnedSuckyData:(NSMutableDictionary*)data;
@end

@interface SuckyClass: NSObject
@property (nonatomic, weak) id<SuckyClassDelegate> delegate;
-(void)getSuckyData;
@end

@interface MyViewController: UIViewController <SuckyClassDelegate>
-(void)requestDataFromSuckyClass;
@end

I'd like to create a wrapper class for the SuckyClass that allows me to use blocks when I need to access data from the SuckyClass, but I don't know how to do this. I'd like to have something like this...

@interface SuckyClassWrapper
- (void)requestDataWithSuccessBlock:(void(^)((NSMutableDictionary*)data))successBlock;
@end

@implementation MyViewController
-(void)requestDataFromSuckyClass {
   SuckyClassWrapper *wrapper = [[SuckyClassWrapper alloc] init];
   [wrapper requestDataWithSuccessBlock:^(NSMutableDictionary *data) {
       NSLog(@"%@", data);
   }
}
@end

...but I can't figure out how to convert the callback process into blocks. Can anyhow give me some direction here?

Thanks in advance for your wisdom!

By the way, I just whipped up the code without testing it, so I apologize if there are any typos.

like image 217
BeachRunnerFred Avatar asked Feb 18 '23 05:02

BeachRunnerFred


2 Answers

The trick is to copy the completion block to a class iVar that you can then call later.

@property (nonatomic, copy) void (^errorHandler)(NSError *);
@property (nonatomic, copy) void (^successHandler)(NSString *);

Here is a method that saves two blocks for use later and then calls another class method:

- (void)methodWithErrorHandler:(void(^)(NSError *error))errorBlock successHandler: (void(^)(NSString *data))successBlock
{
    // Copy the blocks to use later
    self.successHandler = successBlock;
    self.errorHandler = errorBlock;

    // Run code
    [self doOtherThings];
}

Later - when what we want to do has completed, we have another method that we call to run the blocks. In this silly example code we check to see if a class property self.error is nil. If it is not nil, we send that error to our saved error block. If it is nil, we pass self.data to the success block.

- (void)finishThingsUp
{
    // Check to see if we should call the error block or the success block
    if (self.error) {
        self.errorHandler(self.error);
    } else {
        self.successHandler(self.data);
    }
    // Clean up the blocks
    self.errorHandler = nil;
    self.successHandler = nil;
}
like image 185
redlightbulb Avatar answered May 01 '23 01:05

redlightbulb


We could use like this:

typedef void (^SuccessDataBlock)(NSMutableDictionary *);
@interface SuckyClassWrapper : NSObject <SuckyClassDelegate>
    @property (nonatomic, retain) NSData *inputData;
    @property (nonatomic, copy) SuccessDataBlock completionHandler;

    + (id)requestData:(NSData *)data successBlock:(SuccessDataBlock)handler;
@end

@implementation SuckyClassWrapper

@synthesize inputData;
@synthesize completionHandler;

- (id)initWithData:(NSData *)data completionHandler:(SuccessDataBlock)handler

{
    self = [super init];
    if (self != nil)
    {
        inputData = [data retain];
        self.completionHandler = handler;
    }
    return self;
}

+ (id)requestData:(NSData *)data successBlock:(SuccessDataBlock)handler
{
    return [[[self alloc] initWithData:data completionHandler:handler] autorelease];
}

//implement SuckyClass delegate
- (void)returnedSuckyData:(NSMutableDictionary *)data
{
    self.completionHandler(data);
}

@end

Usage:

SuckyClassWrapper *wrapper = [SuckyClassWrapper requestData:data successBlock:^(NSMutableDictionary *successData) {
    //your code here
}];
like image 34
Ghien cafe Avatar answered May 01 '23 00:05

Ghien cafe