Thursday, May 30, 2013

About NSManagedObjectContext PerformBlock Method.

Recently working on my to be announced project "***", I need to create core data file and import the data into my database for the user when and only when it's the first time to run the app. The whole process of importing data to my database is
1, read data from variety property list.
2, convert the data to the usable data my program can use.
3, store these data in my database via Core Data.
To implement this, I borrowed a piece of code from Stanford's CS193p Online Course Core Data Demo. The key part I borrowed is followed.

Code:

// Whenever the table is about to appear, if we have not yet opened/created or demo document, do so.

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    if (!self.managedObjectContext) [self useDemoDocument];
}

- (void)useDemoDocument
{
    NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    url = [url URLByAppendingPathComponent:@"Demo Document"];
    UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:url];
    
    if (![[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
        [document saveToURL:url
           forSaveOperation:UIDocumentSaveForCreating
          completionHandler:^(BOOL success) {
              if (success) {
                  self.managedObjectContext = document.managedObjectContext;
                  [self refresh];
              }
          }];
    } else if (document.documentState == UIDocumentStateClosed) {
        [document openWithCompletionHandler:^(BOOL success) {
            if (success) {
                self.managedObjectContext = document.managedObjectContext;
            }
        }];
    }
}

#pragma mark - Refreshing

// Fires off a block on a queue to fetch data from Flickr.
// When the data comes back, it is loaded into Core Data by posting a block to do so on
//   self.managedObjectContext's proper queue (using performBlock:).
// Data is loaded into Core Data by calling photoWithFlickrInfo:inManagedObjectContext: category method.

- (IBAction)refresh
{
    [self.refreshControl beginRefreshing];
    dispatch_queue_t fetchQ = dispatch_queue_create("Flickr Fetch", NULL);
    dispatch_async(fetchQ, ^{
        NSArray *photos = [FlickrFetcher latestGeoreferencedPhotos];
        // put the photos in Core Data
        [self.managedObjectContext performBlock:^{
            for (NSDictionary *photo in photos) {
                [Photo photoWithFlickrInfo:photo inManagedObjectContext:self.managedObjectContext];
            }
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.refreshControl endRefreshing];
            });
        }];
    });
}

This code shows, every time the view is opened, NSManagedObjectContext State gets examined. If it's not assigned (is nil), then we go deep to see if we can do anything includes: "Create", "Open"... maybe more if we have more "else if" statement. Mostly the code did in "useDemoDocument" is assign one valid value to NSManagedObjectContext object, except it will initiate the database through refresh function if the database is not existed.

In refresh function. (KEY)

It starts the refreshControl first, refreshControl is added since iOS6.0, only available for UITableViewController. Developer could enable it through Xcode Inspection for the UITableViewController. (BTW, you can add action to it by just drag and drop through Xcode as many developers do for segue things, you can only make it functional through code.)

Then, it creates a queue, in which we could do some tasks that would not block our UIView. This is used when you have some time consuming task needs to be done without much attention from the user, like download files, core data operation (insert, delete, update) and post content to internet without user's notice.

Then, assign tasks to this queue with dispatch_async. In the queue, the code fetches data from the internet first. When it's done, we call
performBlock:
- (void)performBlock:(void (^)())blockParametersblock
The block to perform.
Discussion
Availability
  • Available in iOS 5.0 and later.
Declared InNSManagedObjectContext.h

This function was added with his brother performBlockAndWait: coming with iOS5.0. The best part of this is the developer knows who is associated with making the most operations. Because NSManagedObjectContext is the handler here to insert all of the data into our database, no one can ask it to do any conflict job until it's all done with the job I am asking it to be doing it now. Which means this NSManagedObjectContext is blocked until this task is done. Otherwise, your data can't be successfully added to your database here. The data will be lost every time.

At last, the function asks the main queue (which is also queue responsible for main UI related operations) to dismiss the refresh control it started at the start this function is called.

So for this function, three key parts.
1st, use dispatch_queue to do any time consuming task.
2nd, use performBlock to import the large data at the background for NSManagedObjectContext, or conflict will be showed up.
3rd, any NSManagedObjectContext related operations must be placed inside performBlock's block. Otherwise, it's equally like not using performBlock. So I can't make a loop out to call performBlock for every property list, since one will block the other when it's called. That's what's wrong I initially did, after the first time I opened the app, it seems app could be saved and loaded in my UITableView, the second time I opened my app, nothing shows up.

More detail, please check this.

Wednesday, May 15, 2013

How to Implement Pair and Unpair with other Bluetooth Devices Programmable on Android.

Recently, based on a project's requirement, the application has to repair with device if this android device has paired with that device recently. This really brought me bunch of challenges I had never experienced. Anyway, "There is always a way with such a strong will.". After a few days' research and experience, the door is open now.

Here I made a app to demonstrate how I handle each challenge and maybe other related problem. The link to the source code is at the bottom of this article.




This demo firstly have a list view to display all of the available devices around your android device. The paired devices is labeled with pink color while the unpaired is labeled with brown color. The paired device is always listed at the top. For each item in the list literally describes the device's name, 16 hex digits MAC address and the status of it's paired or not.

The device will pop on the list view one by one after user clicks on "Scan Device" button at the bottom. With the time Bluetooth adapter found new devices, it will check if it's paired, if it's paired, then it's already in the list, otherwise, add it to the list as a new device.








If user clicks on a new device, the app will pair it for user, while if the user clicks on the paired device, the app will unpair the device first, after the device is unpaired, the app will pair with device again with a request from Bluetooth adapter asking for the pass code to get paired. Once the device is newly paired, the app will connect with it.






Then, it will works like a SPP app on the Android device.