How to use Bolts Framework[Facebook+Parse]
Asked Answered
F

3

9

Just Now I see this announcement from Facebook about Bolts Framework for IOS.

I can see this as main concept:

The first component in Bolts is “tasks”, which make organization of complex asynchronous code more manageable

But I didn't get this. I got confused about Bolts framework. How to use it(whether its related to web service or to JSON response parsing).

They provided examples with ParseObject with parse SDK but I don't know about it and they didn't provide any example with Xcode project.

Facebook provided explanation about this. But I can't figure out how to integrate with my project.

Code they provided is very confusing:

[[object saveAsync:obj] continueWithBlock:^id(BFTask *task) {
  if (task.isCancelled) {
    // the save was cancelled.
  } else if (task.error) {
    // the save failed.
  } else {
    // the object was saved successfully.
    SaveResult *saveResult = task.result;
  }
  return nil;
}];

We can download bolts-framework here. Can anyone please explain more about this?

Update: Here I received some answer about new question of bolts.

Examples: Assume you want to load all images from AssertLibrary and resize all images to standard size while loading, so it will struck if do with main thread. In this place, If you go with async operation means, How to use BFTask with it? Another Ex. In one time, you are trying to call 10 webservice parallel with async operation, how could you use GCD with BFTask?

Feme answered 3/2, 2014 at 7:8 Comment(1)
Sorry, I haven't been able to find much info myself, and I haven't yet tried integrating it with my own projects.Alaska
S
11

Bolts is great. Agree that the documentation is a little unfocussed right now. Here's a quick example, though:

// the completion source is the *source* of the task...
BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
[self asynchronousMethodWithCompletion:^(id response, NSError *error) {
   // your task completed; inform the completion source, which handles
   // the *completion* of the task
   error ? [source setError:error] : [source setResult:entity];
}];

[source.task continueWithBlock:^id(BFTask *task) {
   // this will be executed after the asynchronous task completes...
}];

I've found it especially useful for group completions (semi pseudo-code):

NSMutableArray *tasks = @[].mutableCopy;
loop {
  BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];

  // ...make a task similar to above...

  [tasks addObject:source.task];
}

// now the group completion:
BFTask *groupTask = [BFTask taskForCompletionOfAllTasks:tasks.copy];
[source.task continueWithBlock:^id(BFTask *task) {
   // this will be executed after *all* the group tasks have completed
}];

This is a neater way of doing what you might otherwise do with dispatch groups. There's plenty more to it, though, in terms of sequencing your tasks in both serial and parallel, etc. etc.

Hope that helps you get started.

Stunt answered 18/2, 2014 at 12:46 Comment(7)
thanks @itsthejb see this link #21599672. Your answer is same as like this. Please look at those comment. and please explain How it is related to Async operation?Feme
Bolts is intended to wrap/encapsulate a asynchronous task of any kind. All that you need to do is message the task completion source when your operation completes. The idea is to wrap any of your operations in this way.Stunt
Yes I know man. But the task above where could you find asynchronous task? You've handled with BFTask after completion of asynchronous task instead of during async task? AM I right? I want to know, how to use BFTask with Asynchronous task, especially where to lock and unlock resources with BFTask?Feme
Any way I'll give you +1 for your approach? If you didn't get my point, give comments. Or give more research with this and help me. Thank you :)Feme
I think you're misunderstanding the purpose of the framework. Bolts does nothing to help you write asynchronous operations. It's intended to encapsulate and sequence operations using a common interfaceStunt
You can see this line ` BFTask is not a replacement for NSOperation or GCD. In fact, they play well together.` in bolts-github. What does it mean?Feme
Mani, see my example. I use Bolts in conjunction with GCD and create my own async BFTasks. It's all how you use the executor.Anselme
A
0

Here's a full example of an application that could support multiple actors changing data while processing networking requests and user interaction.

I've set a guideline for myself to use the BFExecutor linked to the serial dispatch_queue_t when touching anything service related for thread safety.

Pointing out other best practices would be useful, thanks!

#import <Bolts/Bolts.h>

dispatch_queue_t serialQueue;
BFExecutor *serialExecutor;
BFTask *maintask;
int32_t key = 0;
BOOL initialized = FALSE;

@implementation yAppDelegate

- (void)setKey:(int32_t)key_ {
    // Don't worry about success, just appending to outstanding tasks
    maintask = [maintask continueWithExecutor:serialExecutor withBlock:^id(BFTask *task) {
        BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];

        key = key_;
        NSLog(@"set key to %d", key);
        [source setResult:nil];

        if (initialized) {
            [self initialize];
        }

        return source.task;
    }];
}

- (BFTask *)downloadConfig {
    BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
    dispatch_async(serialQueue, ^{
        NSLog(@"working on init config, key = %d...", key);

        // Trigger from a different queue
        double delayInSeconds = 1.0;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            [source setResult:nil];
        });
    });
    return source.task;
}

- (BFTask *)initializeService {
    BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
    dispatch_async(serialQueue, ^{
        NSLog(@"working on init service, key = %d...", key);

        // Trigger from a different queue
        double delayInSeconds = 1.0;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            [source setResult:nil];
        });

        // Set final state
        initialized = TRUE;
    });
    return source.task;
}

- (void)initialize {
    int32_t oldKey = key;
    __block bool reinit = false;

    // Start by chaining it to whatever task is in flight without regard to success
    // Everything should use the serialQueue or serialExecutor for thread safety
    maintask = [[[maintask continueWithExecutor:serialExecutor withBlock:^id(BFTask *task) {
        if (oldKey != key) {
            NSLog(@"key out of date (%d != %d).  reinitializing...", oldKey, key);
            reinit = true;
            return [BFTask cancelledTask];
        }
        return [self downloadConfig];
    }] continueWithExecutor:serialExecutor withSuccessBlock:^id(BFTask *task) {
        if (oldKey != key) {
            NSLog(@"key out of date (%d != %d).  reinitializing...", oldKey, key);
            reinit = true;
            return [BFTask cancelledTask];
        }
        return [self initializeService];
    }] continueWithExecutor:serialExecutor withBlock:^id(BFTask *task) {
        if (oldKey != key) {
            NSLog(@"key out of date (%d != %d).  reinitializing...", oldKey, key);
            reinit = true;
        }

        if (task.error || task.exception || task.isCancelled) {
            if (reinit) {
                [self initialize];
            }
            return nil;
        } else {
            NSLog(@"initService completed = %d", key);
            return nil;
        }
    }];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    serialQueue = dispatch_queue_create("serial", NULL);
    serialExecutor = [BFExecutor executorWithDispatchQueue:serialQueue];

    // Start with an empty task so all tasks can be strung together without need to initialize
    maintask = [BFTask taskWithResult:nil];

    // Everything related to changing service state should be contained with the serialQueue dispatch queue
    [self setKey:1];
    [self initialize];
    [self setKey:2];
    [self setKey:3];

    dispatch_async(dispatch_get_main_queue(), ^(void){
        [self setKey:4];
    });

    dispatch_async(serialQueue, ^(void){
        [self setKey:5];
    });

    [self setKey:6];

    // Override point for customization after application launch.
    return YES;
}

@end

The results are as expected. Initialization is attempted with the key = 1, but is differed until it stops changing. The service is then initialized on its queue with key = 5 and then re-initialized with key = 4.

Results:

set key to 1
key out of date (0 != 1).  reinitializing...
key out of date (0 != 1).  reinitializing...
set key to 2
set key to 3
set key to 6
set key to 5
key out of date (1 != 5).  reinitializing...
key out of date (1 != 5).  reinitializing...
working on init config, key = 5...
working on init service, key = 5...
initService completed = 5
set key to 4
working on init config, key = 4...
working on init service, key = 4...
initService completed = 4
Anselme answered 8/4, 2014 at 21:47 Comment(0)
P
0

Refer to the test files (i.e TaskTests.m) for examples. The examples on Github were specific to Parse.

Generally looking at the tests written for a software would be sufficient in learning how to use the code.

Pollinosis answered 19/9, 2015 at 1:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.