SlideShare uma empresa Scribd logo
1 de 113
Baixar para ler offline
REST/JSON/CoreData
      Example Code
               github.com/carlbrown/
               SeismicJSON
               Carl Brown
               Twitter: @CarlBrwn
               Email: CarlB@PDAgent.com

                                          1
Turn on Camera and ScreenFlow!!
Asynchronous iOS Programming Rules

    • Threads are bad

    • Use Queues instead

    • Apple's Concurrency Programming Guide*:
         • Page 10: "The Move Away from Threads"

         • Page 74: "Migrating Away from Threads"

         • Page 74: "Replacing Threads with Dispatch Queues"
* http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html



                                                                                                                                        2
Some things aren't thread-safe
•Any UIAnything in iOS
•CoreData




                                 3
UI tasks Must be on the Main
Thread


Often called the "UI" thread for that reason
                                               4
CoreData Contexts and Objects are tied to a
thread

• Never share a CoreData object between threads

• Never share a CoreData object between contexts

• Pass objects only by ID and fetch them again

• Always notify other contexts when you've made changes




                                                          5
Don't cross the threads
•Use the same serial queue to stay on the
 same thread

•Use dispatch_get_main_queue() or
 [NSOperationQueue mainQueue] to get to the
 Main Thread.




                                              6
SeismicJSON
The only project we'll be
working with today

Feel free to run it and play
with it for a couple of minutes

Also on github so you can
see how it was written.

                                  7
First Exercise:
  Follow the Code



                                                              8


This is a vital skill to have. We don't expect people to
write in English without reading a lot of English first, but
programmers consistently spend more time writing code
than reading it while learning a new language.
Please Open
  MasterView
  Controller.m
  This is pretty much a
  tableViewController like
  you've seen before.

  Ignore the #if conditionals
  this run-through



                                               9


Ignore the #if conditionals this run-through
MasterViewController
               should be mostly familiar
• Cell labels filled in from Model Object

• custom XIB for TableViewCell

• dateFormatter in viewDidLoad

• segmentedController sets FRC sorting

• actionSheet for adding rows (you can figure out)

• some iPad stuff (not Rocket Science)

• #if conditional stuff (for later)

                                                    10
DetailViewController
•Really simple

•simpler than the tableViewCell

•Just a bunch of labels

•nothing to discuss here



                                  11
Activity
Indicating
ImageView
This has a
UIActivityIndicatorView,
implements a new
custom @protocol and
refers to a
NetworkManager.



                           12
So open
ActivityIndicating
ImageView.m




                     13
-(void) awakeFromNib {
    _activityIndicator = [[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyle
Gray];
    [_activityIndicator setFrame:self.frame];
    [_activityIndicator setHidesWhenStopped:YES];
    [self addSubview:_activityIndicator];
    [_activityIndicator startAnimating];
}




     Start Spinner upon
                 waking
                                                              14
-(void) setImage:(UIImage *)image {
    [super setImage:image];
    if (image) {
        [self.activityIndicator stopAnimating];
    } else {
        [self.activityIndicator startAnimating];
    }
}




   Stop it when we get
                image
                                                   15
-(void) setImageFileName:(NSString *)imageFileName {

   _imageFileName = imageFileName;

   if (_imageFileName==nil) {
       [self setImage:nil];
       return;
   }




setImageFileName 1/3
                                                       16
//If the file already exists, don't bother to fetch it again
  NSString *fullFilePath = [[[NetworkManager sharedManager]
          cachedImageDirectory]
          stringByAppendingPathComponent:_imageFileName];
  if ([[NSFileManager defaultManager]
                  fileExistsAtPath:fullFilePath]) {
      [self imageDidBecomeAvailableAtPath:fullFilePath];
      return;
  }




setImageFileName 2/3
                                                                 17
[[NetworkManager sharedManager]
           fetchImagewithFilename:
                 imageFileName andNotifyTarget:self];

}




setImageFileName 3/3
                                                        18
-(void) fetchImagewithFilename:(NSString *) filename
andNotifyTarget:(NSObject <ImageFetchDelegate> *) target
{
    //Stuff we've seen before

    ImageFetchOperation *imageFetchOperation =
              [[ImageFetchOperation alloc] init];

    [imageFetchOperation setUrlToFetch:
             [self imageURLForImageFileName:filename]];

    [imageFetchOperation setNotificationTarget:target];

    [self.fetchQueue addOperation:imageFetchOperation];

}


OK, now we're getting
         somewhere
                                                           19
ImageFetch
Operation.m




              20
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    if (self.response.statusCode==200) {
        NSError *error=nil;
        NSString *filename = [self.urlToFetch lastPathComponent];
        NSString *fullFilePath = [
           [[NetworkManager sharedManager] cachedImageDirectory]
                      stringByAppendingPathComponent:filename];
        NSLog(@"About to write file: %@",fullFilePath);
        if (![self.fetchedData writeToFile:fullFilePath
                   options:NSDataWritingAtomic error:&error]) {
            NSLog(@"error occurred writing file: %@",
                   [error localizedDescription]);
        }
        if (self.notificationTarget) {
            [self.notificationTarget
                      imageDidBecomeAvailableAtPath:fullFilePath];
        }
    }
}




    connectionDidFinish
     Loading (abridged)
                                                                     21
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    if (self.response.statusCode==200) {
        NSError *error=nil;
        NSString *filename = [self.urlToFetch lastPathComponent];
        NSString *fullFilePath = [
           [[NetworkManager sharedManager] cachedImageDirectory]
                      stringByAppendingPathComponent:filename];
        NSLog(@"About to write file: %@",fullFilePath);
        if (![self.fetchedData writeToFile:fullFilePath
                   options:NSDataWritingAtomic error:&error]) {
            NSLog(@"error occurred writing file: %@",
                   [error localizedDescription]);
        }
        if (self.notificationTarget) {
            [self.notificationTarget
                      imageDidBecomeAvailableAtPath:fullFilePath];
        }
    }
}




Only save if no error
                                                                     22
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    if (self.response.statusCode==200) {
        NSError *error=nil;
        NSString *filename = [self.urlToFetch lastPathComponent];
        NSString *fullFilePath = [
           [[NetworkManager sharedManager] cachedImageDirectory]
                      stringByAppendingPathComponent:filename];
        NSLog(@"About to write file: %@",fullFilePath);
        if (![self.fetchedData writeToFile:fullFilePath
                   options:NSDataWritingAtomic error:&error]) {
            NSLog(@"error occurred writing file: %@",
                   [error localizedDescription]);
        }
        if (self.notificationTarget) {
            [self.notificationTarget
                      imageDidBecomeAvailableAtPath:fullFilePath];
        }
    }
}




  Get filename/path
                                                                     23
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    if (self.response.statusCode==200) {
        NSError *error=nil;
        NSString *filename = [self.urlToFetch lastPathComponent];
        NSString *fullFilePath = [
           [[NetworkManager sharedManager] cachedImageDirectory]
                      stringByAppendingPathComponent:filename];
        NSLog(@"About to write file: %@",fullFilePath);
        if (![self.fetchedData writeToFile:fullFilePath
                   options:NSDataWritingAtomic error:&error]) {
            NSLog(@"error occurred writing file: %@",
                   [error localizedDescription]);
        }
        if (self.notificationTarget) {
            [self.notificationTarget
                      imageDidBecomeAvailableAtPath:fullFilePath];
        }
    }
}




                    Save File
                                                                     24
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    if (self.response.statusCode==200) {
        NSError *error=nil;
        NSString *filename = [self.urlToFetch lastPathComponent];
        NSString *fullFilePath = [
           [[NetworkManager sharedManager] cachedImageDirectory]
                      stringByAppendingPathComponent:filename];
        NSLog(@"About to write file: %@",fullFilePath);
        if (![self.fetchedData writeToFile:fullFilePath
                   options:NSDataWritingAtomic error:&error]) {
            NSLog(@"error occurred writing file: %@",
                   [error localizedDescription]);
        }
        if (self.notificationTarget) {
            [self.notificationTarget
                      imageDidBecomeAvailableAtPath:fullFilePath];
        }
    }
}




Let the View Know
                                                                     25
[control]-[⌘]-J
back to ActivityIndicatingImageView
                                        26
-(void) imageDidBecomeAvailableAtPath:(NSString *) path {
    if (![[path lastPathComponent]
            isEqualToString:self.imageFileName]) {
        NSLog(@"Warning: notified of incorrect file:
                 %@, should have been %@",[path
                 lastPathComponent],self.imageFileName);
        //try again
        [self setImageFileName:self.imageFileName];
        return;
    }




 Only load the file we're expecting (race condition checking)


imageDidBecomeAvailableAtPath
                          1/2
                                                               27
//load image off the main queue
    UIImage *imageToLoad=[UIImage imageWithContentsOfFile:path];
    dispatch_async(dispatch_get_main_queue(), ^{
        [self setImage:imageToLoad];
        [self setNeedsDisplay];
    });
}




            Set our image with the file now on disk


imageDidBecomeAvailableAtPath
                          2/2
                                                                   28
Summary of ActivityIndicatingImageView

• Start the view with a spinner telling the user we are working on
  something

• See if the file is already on disk, and use it if so.

• If not, we ask the Network Manager to get the file for us

• The Network Manager creates an operation to get our file
  (presumably from the network) and write it to disk

• The Network Manager tells us the file is ready

• We load the file into our image property

• Now that we have an image, the spinner hides

                                                                     29
Recommended
   Networking Strategy
   •Always* load the UI from local storage

      •Core Data or local file or something

   •Always* put network data in local storage

   •Then tell the UI to refresh itself

   •Put up a placeholder if no data
 *Except with live web pages or HTTP streaming
                                                                          30

Some people argue with me about this, but it's served me well for years
Why do it that way?
•Separates network code from UI code

•Easier to test

•Much faster response if previous data

•Much better user experience offline



                                         31
Why wouldn't you?
    •Pointless if the network is infinitely fast
     and infinitely reliable*

    •More effort than "Unbreakable Glass"
     loading screens




*c.f. http://en.wikipedia.org/wiki/Fallacies_of_Distributed_Computing
                                                                        32
NSOperations and GCD



                       33
NSOperation
• Been around since the first iPhone OS SDK

• Way to encapsulate the pieces of a task in one
  place

• Can be queried, suspended or canceled

• Simple selector call or block variants

• NSOperations are placed in NSOperationQueues


                                                   34
NSOperationQueue
• Long-lived (presumably) queue that can contain
  numerous operations

• Can be serial or concurrent

• Can be suspended or canceled

• Nice (but verbose) Objective-C syntax

• Will stay on the same thread, if serial

• [NSOperationQueue mainQueue] is always on the Main
  Thread

                                                       35
Dispatch Queues
•C-style (concise) syntax

•quicker to use in-place

•much less typing than declaring an
 NSOperation and adding to Queue

•Harder to manage or cancel


                                      36
Which to use?
• No hard-and-fast rules, but...

• I tend to use NSOperations for:

  • things I'm going to do several times

  • things that have non-trivial complexity

• I tend to use dispatch_async() for things:

  • with less than 10 or so lines of code

  • done only once in the App

  • that won't need to change when spec changes

                                                  37
Waiting in Cocoa
•Don't Sleep

•Don't use locks

•Yield to the RunLoop

•See the FetchOperation for example

•Sleeping or Locking Freezes the Thread

                                          38
Be Nice to Threads
• POSIX Threads are a finite resource

• The system will spin up more if tasks are
  waiting

• But when no more can start, things will hang

• See: WWDC2012 Session Session 712 -
  Asynchronous Design Patterns with Blocks,
  GCD, and XPC

                                                 39
Back to our Application


                          40
Please Open
NotificationOr
ParentContext.h




                  41
//Make this a 1 to show notifications, and a 0 to show parent contexts
#define kUSE_NSNOTIFICATIONS_FOR_CONTEXT_MERGE 0
//if using notifications, set this to 1 to have them in the App
Delegate
#define kNSNOTIFICATIONS_HANDLED_IN_APPDELEGATE 0




Note: I'm not usually a fan of this kind of conditional
 compilation, but for this case, I thought it would
   let you play with project in the debugger in a
        cleaner way than with traditional if's.




   Project Variations
                                                                         42
OK, to the
AppDelegate.
m




               43
Open Source
Control View




               44
Click the TimeMachine
                        45
Scroll in the center of
Pick the Initial Commit   the screen

                                                    46
Got rid of some Xcode
              4.4-isms
                         47
Removed Observer
                   48
applicationWillResignActive:
!(UIApplication *) application

•Happens when user gets Texts,
 notifications, Alerts, phone calls or hits
 the home button

•Here I'm removing the notification
 observer so we won't try to get
 notifications while not Active



                                             49
Added Observer
                 50
Kicked off Network
             Fetch
                     51
applicationDidBecomeActive:
(UIApplication *)application

• Happens when App becomes full-focus

• After launch

• Or after returning from dealing with alert

• Or after dealing with "most recently used
  apps" along bottom of screen

• Here I'm adding a notification observer

                                               52
[[NSNotificationCenter defaultCenter] addObserver:self
      selector:@selector(changesSaved:)
      name:NSManagedObjectContextDidSaveNotification
                                           object:nil];




          This Runs
    "changesSaved:"
                                                          53
- (void)changesSaved:(NSNotification *)notification {
    if (![NSThread isMainThread]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self changesSaved:notification];
        });
        return;
    }
    if ([notification object] != self.managedObjectContext) {
        [self.managedObjectContext
       mergeChangesFromContextDidSaveNotification:notification];
    }
}




         Handler Code
                                                                   54
- (void)changesSaved:(NSNotification *)notification {
    if (![NSThread isMainThread]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self changesSaved:notification];
        });
        return;
    }
    if ([notification object] != self.managedObjectContext) {
        [self.managedObjectContext
       mergeChangesFromContextDidSaveNotification:notification];
    }
}




If not on Main, go there
                                                                   55
- (void)changesSaved:(NSNotification *)notification {
    if (![NSThread isMainThread]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self changesSaved:notification];
        });
        return;
    }
    if ([notification object] != self.managedObjectContext) {
        [self.managedObjectContext
       mergeChangesFromContextDidSaveNotification:notification];
    }
}




      Merge changes
                                                                   56
Queue Concurrency
             Type
                    57
Reset DB each run
                    58
Back to
normal view
now




              59
Network
Manager.m




            60
+ (NetworkManager *)sharedManager {
    static dispatch_once_t pred; dispatch_once(&pred, ^{
        sharedManager = [[self alloc] init];

        //Initialization Stuff

    });
    return sharedManager;
}




    Singleton Pattern
                                                           61
-(void) startMainPageFetch {
    [self setHostReach:[Reachability
          reachabilityWithHostName:[self.baseURL host]]];
    [self.hostReach startNotifier];

    [self queuePageFetchForRelativePath:
            @"/earthquakes/feed/geojson/significant/month"];
}




            Kicked off from
              AppDelegate
                                                               62
-(void) startMainPageFetch {
    [self setHostReach:[Reachability
          reachabilityWithHostName:[self.baseURL host]]];
    [self.hostReach startNotifier];

    [self queuePageFetchForRelativePath:
            @"/earthquakes/feed/geojson/significant/month"];
}




Inform users of network status
                or be Rejected
                                                               63
Just do it. If you want to
understand it, read Apple's writeup
                                      64
-(void) startMainPageFetch {
    [self setHostReach:[Reachability
          reachabilityWithHostName:[self.baseURL host]]];
    [self.hostReach startNotifier];

    [self queuePageFetchForRelativePath:
            @"/earthquakes/feed/geojson/significant/month"];
}




Start fetch of first batch
                                                               65
-(void) queuePageFetchForRelativePath:(NSString *) relativePath {
    EarthquakeFetchOperation *earthquakeFetchOperation =
                 [[EarthquakeFetchOperation alloc] init];
    [earthquakeFetchOperation setUrlToFetch:
                    [self urlForRelativePath:relativePath]];
    [earthquakeFetchOperation setMainContext:self.mainContext];
    [earthquakeFetchOperation setDelegate:self];
    [self.fetchQueue addOperation:earthquakeFetchOperation];
}




Make NSOp & Queue it
                                                                    66
#import <Foundation/Foundation.h>
#import "BaseFetchOperation.h"

@interface EarthquakeFetchOperation : BaseFetchOperation
@property (nonatomic, weak) NSManagedObjectContext *mainContext;

@end




EarthquakeFetchOperation.h
                                                                   67
@interface BaseFetchOperation : NSOperation
                       <NSURLConnectionDataDelegate>

@property   (nonatomic,   strong)   NSURL *urlToFetch;
@property   (nonatomic,   strong)   NSMutableData *fetchedData;
@property   (nonatomic,   assign,   getter=isDone) BOOL done;
@property   (nonatomic,   assign)   NSURLConnection *connection;
@property   (nonatomic,   retain)   NSHTTPURLResponse *response;

@property (nonatomic, weak) NSObject<FetchNotifierDelegate> *delegate;

-(void) finish;

@end

@protocol FetchNotifierDelegate <NSObject>
-(void) fetchDidFailWithError:(NSError *) error;
-(void) incrementActiveFetches;
-(void) decrementActiveFetches;
@end




BaseFetchOperation.h
                                                                         68
Methods needed for
      URL fetching
                     69
BaseFetchOp
eration.m




              70
- (void)main {
    if ([self isCancelled]) {
        return;
    }
    if (!_urlForJSONData) {
        NSLog(@"Cannot start without a URL");
        return;
    }

    [self setFetchedData:[NSMutableData data]]; //Initialize
    NSURLRequest *request =
                [NSURLRequest requestWithURL:[self urlToFetch]];

    if (self.delegate) { [self.delegate incrementActiveFetches]; }

    [self setConnection:[NSURLConnection
                    connectionWithRequest:request delegate:self]];
    CFRunLoopRun();
}




                 Entry Point
                                                                     71
- (void)main {
    if ([self isCancelled]) {
        return;
    }
    if (!_urlForJSONData) {
        NSLog(@"Cannot start without a URL");
        return;
    }

    [self setFetchedData:[NSMutableData data]]; //Initialize
    NSURLRequest *request =
                [NSURLRequest requestWithURL:[self urlToFetch]];

    if (self.delegate) { [self.delegate incrementActiveFetches]; }

    [self setConnection:[NSURLConnection
                    connectionWithRequest:request delegate:self]];
    CFRunLoopRun();
}




           Sanity Check
                                                                     72
- (void)main {
    if ([self isCancelled]) {
        return;
    }
    if (!_urlForJSONData) {
        NSLog(@"Cannot start without a URL");
        return;
    }

    [self setFetchedData:[NSMutableData data]]; //Initialize
    NSURLRequest *request =
                [NSURLRequest requestWithURL:[self urlToFetch]];

    if (self.delegate) { [self.delegate incrementActiveFetches]; }

    [self setConnection:[NSURLConnection
                    connectionWithRequest:request delegate:self]];
    CFRunLoopRun();
}




          Make request
                                                                     73
- (void)main {
    if ([self isCancelled]) {
        return;
    }
    if (!_urlForJSONData) {
        NSLog(@"Cannot start without a URL");
        return;
    }

    [self setFetchedData:[NSMutableData data]]; //Initialize
    NSURLRequest *request =
                [NSURLRequest requestWithURL:[self urlToFetch]];

    if (self.delegate) { [self.delegate incrementActiveFetches]; }

    [self setConnection:[NSURLConnection
                    connectionWithRequest:request delegate:self]];
    CFRunLoopRun();
}




         Inform user we're
                    active
                                                                     74
- (void)main {
    if ([self isCancelled]) {
        return;
    }
    if (!_urlForJSONData) {
        NSLog(@"Cannot start without a URL");
        return;
    }

    [self setFetchedData:[NSMutableData data]]; //Initialize
    NSURLRequest *request =
                [NSURLRequest requestWithURL:[self urlToFetch]];

    if (self.delegate) { [self.delegate incrementActiveFetches]; }

    [self setConnection:[NSURLConnection
                    connectionWithRequest:request delegate:self]];
    CFRunLoopRun();
}




    Start Connection
                                                                     75
- (void)main {
    if ([self isCancelled]) {
        return;
    }
    if (!_urlForJSONData) {
        NSLog(@"Cannot start without a URL");
        return;
    }

    [self setFetchedData:[NSMutableData data]]; //Initialize
    NSURLRequest *request =
                [NSURLRequest requestWithURL:[self urlToFetch]];

    if (self.delegate) { [self.delegate incrementActiveFetches]; }

    [self setConnection:[NSURLConnection
                    connectionWithRequest:request delegate:self]];
    CFRunLoopRun();
}




       Give Up Control of
                 thread
                                                                     76
-(void) finish {
    [self setDone:YES];
    if (self.delegate) {
        [self.delegate decrementActiveFetches];
    }
    CFRunLoopStop(CFRunLoopGetCurrent());
}




                         Finish
                                                  77
-(void) finish {
    [self setDone:YES];
    if (self.delegate) {
        [self.delegate decrementActiveFetches];
    }
    CFRunLoopStop(CFRunLoopGetCurrent());
}




Inform user we're done
                                                  78
-(void) finish {
    [self setDone:YES];
    if (self.delegate) {
        [self.delegate decrementActiveFetches];
    }
    CFRunLoopStop(CFRunLoopGetCurrent());
}




Stop the runloop & get
                    off
                                                  79
Other methods there
• didReceiveResponse

  • remember response

  • truncate data

  • (can get more than one response)

• didReceiveData

  • append data

• didFailWithError

  • report error to our delegate
                                       80
Earthquake
FetchOperation
                 81
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    if ([self isCancelled]) {
        [self finish];
        return;
    }

   if (self.response.statusCode==200) {




connectionDidFinishLoading
                       1/n
                                                                   82
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    if ([self isCancelled]) {
        [self finish];
        return;
    }

   if (self.response.statusCode==200) {




             Sanity Check/
             Housekeeping
                                                                   83
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    if ([self isCancelled]) {
        [self finish];
        return;
    }

   if (self.response.statusCode==200) {




          Don't parse bad
                 response
                                                                   84
id objectFromJSON = [NSJSONSerialization
  JSONObjectWithData:self.fetchedData options:0 error:&error];

if (objectFromJSON) {
#if kUSE_NSNOTIFICATIONS_FOR_CONTEXT_MERGE
   NSManagedObjectContext *context =
        [[NSManagedObjectContext alloc] init];
   [context setPersistentStoreCoordinator:
       self.mainContext.persistentStoreCoordinator];
#else
  NSManagedObjectContext *context = [[NSManagedObjectContext
  alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

  [context setParentContext:[self mainContext]];
#endif




    connectionDidFinish
           Loading 2/n
                                                                 85
id objectFromJSON = [NSJSONSerialization
  JSONObjectWithData:self.fetchedData options:0 error:&error];

if (objectFromJSON) {
#if kUSE_NSNOTIFICATIONS_FOR_CONTEXT_MERGE
   NSManagedObjectContext *context =
        [[NSManagedObjectContext alloc] init];
   [context setPersistentStoreCoordinator:
       self.mainContext.persistentStoreCoordinator];
#else
  NSManagedObjectContext *context = [[NSManagedObjectContext
  alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

  [context setParentContext:[self mainContext]];
#endif




            Parse JSON
                                                                 86
id objectFromJSON = [NSJSONSerialization
  JSONObjectWithData:self.fetchedData options:0 error:&error];

if (objectFromJSON) {
#if kUSE_NSNOTIFICATIONS_FOR_CONTEXT_MERGE
   NSManagedObjectContext *context =
        [[NSManagedObjectContext alloc] init];
   [context setPersistentStoreCoordinator:
       self.mainContext.persistentStoreCoordinator];
#else
  NSManagedObjectContext *context = [[NSManagedObjectContext
  alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

  [context setParentContext:[self mainContext]];
#endif




If the JSON was good
                                                                 87
id objectFromJSON = [NSJSONSerialization
  JSONObjectWithData:self.fetchedData options:0 error:&error];

if (objectFromJSON) {
#if kUSE_NSNOTIFICATIONS_FOR_CONTEXT_MERGE
   NSManagedObjectContext *context =
        [[NSManagedObjectContext alloc] init];
   [context setPersistentStoreCoordinator:
       self.mainContext.persistentStoreCoordinator];
#else
  NSManagedObjectContext *context = [[NSManagedObjectContext
  alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

  [context setParentContext:[self mainContext]];
#endif




           Make new
ManagedObjectContext
                                                                 88
NSDictionary *jsonDict = (NSDictionary *) objectFromJSON;

if (jsonDict) {

   NSArray *events = [jsonDict objectForKey:@"features"];

   if (events) {

       for (NSDictionary *eventDict in events) {




    connectionDidFinish
           Loading 3/n
                                                            89
NSDictionary *jsonDict = (NSDictionary *) objectFromJSON;

if (jsonDict) {

   NSArray *events = [jsonDict objectForKey:@"features"];

   if (events) {

       for (NSDictionary *eventDict in events) {




If we got a dictionary
                                                            90
NSDictionary *jsonDict = (NSDictionary *) objectFromJSON;

if (jsonDict) {

   NSArray *events = [jsonDict objectForKey:@"features"];

   if (events) {

       for (NSDictionary *eventDict in events) {




                  Get Array of
                  Earthquakes
                                                            91
NSDictionary *jsonDict = (NSDictionary *) objectFromJSON;

if (jsonDict) {

   NSArray *events = [jsonDict objectForKey:@"features"];

   if (events) {

       for (NSDictionary *eventDict in events) {




If Array/JSON is valid
                                                            92
NSDictionary *jsonDict = (NSDictionary *) objectFromJSON;

if (jsonDict) {

   NSArray *events = [jsonDict objectForKey:@"features"];

   if (events) {

       for (NSDictionary *eventDict in events) {




           Iterate over it
                                                            93
NSString *eventLocation = [eventDict
                    valueForKeyPath:@"properties.place"];
NSDate *eventDate = [NSDate dateWithTimeIntervalSince1970:
  [[eventDict valueForKeyPath:@"properties.time"] doubleValue]];
NSNumber *eventLong = [NSNumber numberWithDouble:[[[eventDict
  valueForKeyPath:@"geometry.coordinates"] objectAtIndex:0]
     doubleValue]];
NSNumber *eventLat =[NSNumber numberWithDouble:[[[eventDict
  valueForKeyPath:@"geometry.coordinates"] objectAtIndex:1]
     doubleValue]];
NSNumber *eventMagnitude = [NSNumber numberWithFloat:[
  [eventDict valueForKeyPath:@"properties.mag"] floatValue]];
NSString *eventWebPath = [@"http://earthquake.usgs.gov"
          stringByAppendingPathComponent:[eventDict
          valueForKeyPath:@"properties.url"]];




    connectionDidFinish
           Loading 4/n
                                                                   94
NSString *eventLocation = [eventDict
                    valueForKeyPath:@"properties.place"];
NSDate *eventDate = [NSDate dateWithTimeIntervalSince1970:
  [[eventDict valueForKeyPath:@"properties.time"] doubleValue]];
NSNumber *eventLong = [NSNumber numberWithDouble:[[[eventDict
  valueForKeyPath:@"geometry.coordinates"] objectAtIndex:0]
     doubleValue]];
NSNumber *eventLat =[NSNumber numberWithDouble:[[[eventDict
  valueForKeyPath:@"geometry.coordinates"] objectAtIndex:1]
     doubleValue]];
NSNumber *eventMagnitude = [NSNumber numberWithFloat:[
  [eventDict valueForKeyPath:@"properties.mag"] floatValue]];
NSString *eventWebPath = [@"http://earthquake.usgs.gov"
          stringByAppendingPathComponent:[eventDict
          valueForKeyPath:@"properties.url"]];




     Extract values from
               eventDict
                                                                   95
NSString *eventLocation = [eventDict
                    valueForKeyPath:@"properties.place"];
NSDate *eventDate = [NSDate dateWithTimeIntervalSince1970:
  [[eventDict valueForKeyPath:@"properties.time"] doubleValue]];
NSNumber *eventLong = [NSNumber numberWithDouble:[[[eventDict
  valueForKeyPath:@"geometry.coordinates"] objectAtIndex:0]
     doubleValue]];
NSNumber *eventLat =[NSNumber numberWithDouble:[[[eventDict
  valueForKeyPath:@"geometry.coordinates"] objectAtIndex:1]
     doubleValue]];
NSNumber *eventMagnitude = [NSNumber numberWithFloat:[
  [eventDict valueForKeyPath:@"properties.mag"] floatValue]];
NSString *eventWebPath = [@"http://earthquake.usgs.gov"
          stringByAppendingPathComponent:[eventDict
          valueForKeyPath:@"properties.url"]];




      Using keyPaths
                                                                   96
NSString *eventLocation = [eventDict
                    valueForKeyPath:@"properties.place"];
NSDate *eventDate = [NSDate dateWithTimeIntervalSince1970:
  [[eventDict valueForKeyPath:@"properties.time"] doubleValue]];
NSNumber *eventLong = [NSNumber numberWithDouble:[[[eventDict
  valueForKeyPath:@"geometry.coordinates"] objectAtIndex:0]
     doubleValue]];
NSNumber *eventLat =[NSNumber numberWithDouble:[[[eventDict
  valueForKeyPath:@"geometry.coordinates"] objectAtIndex:1]
     doubleValue]];
NSNumber *eventMagnitude = [NSNumber numberWithFloat:[
  [eventDict valueForKeyPath:@"properties.mag"] floatValue]];
NSString *eventWebPath = [@"http://earthquake.usgs.gov"
          stringByAppendingPathComponent:[eventDict
          valueForKeyPath:@"properties.url"]];




and/or Array elements
                                                                   97
NSFetchRequest *fetchRequest =
  [NSFetchRequest fetchRequestWithEntityName:
          NSStringFromClass([Earthquake class])];
[fetchRequest setFetchLimit:1];
NSPredicate *eventInfo =
  [NSPredicate predicateWithFormat:
          @"location = %@ AND date = %@",
                          eventLocation,
                          eventDate];
[fetchRequest setPredicate:eventInfo];
NSError *fetchError=nil;
NSArray *existingEventsMatchingThisOne =
  [context executeFetchRequest:fetchRequest error:&fetchError];




    connectionDidFinish
           Loading 5/n
                                                                  98
NSFetchRequest *fetchRequest =
  [NSFetchRequest fetchRequestWithEntityName:
          NSStringFromClass([Earthquake class])];
[fetchRequest setFetchLimit:1];
NSPredicate *eventInfo =
  [NSPredicate predicateWithFormat:
          @"location = %@ AND date = %@",
                          eventLocation,
                          eventDate];
[fetchRequest setPredicate:eventInfo];
NSError *fetchError=nil;
NSArray *existingEventsMatchingThisOne =
  [context executeFetchRequest:fetchRequest error:&fetchError];




Make a fetch request
                                                                  99
NSFetchRequest *fetchRequest =
  [NSFetchRequest fetchRequestWithEntityName:
          NSStringFromClass([Earthquake class])];
[fetchRequest setFetchLimit:1];
NSPredicate *eventInfo =
  [NSPredicate predicateWithFormat:
          @"location = %@ AND date = %@",
                          eventLocation,
                          eventDate];
[fetchRequest setPredicate:eventInfo];
NSError *fetchError=nil;
NSArray *existingEventsMatchingThisOne =
  [context executeFetchRequest:fetchRequest error:&fetchError];




matching our event
                                                                  100
NSFetchRequest *fetchRequest =
  [NSFetchRequest fetchRequestWithEntityName:
          NSStringFromClass([Earthquake class])];
[fetchRequest setFetchLimit:1];
NSPredicate *eventInfo =
  [NSPredicate predicateWithFormat:
          @"location = %@ AND date = %@",
                          eventLocation,
                          eventDate];
[fetchRequest setPredicate:eventInfo];
NSError *fetchError=nil;
NSArray *existingEventsMatchingThisOne =
  [context executeFetchRequest:fetchRequest error:&fetchError];




                 And run it
                                                                  101
if ([existingEventsMatchingThisOne count]==0) {

    //Didn't find one already, make a new one
    NSManagedObject *newManagedObject =
        [NSEntityDescription insertNewObjectForEntityForName:
                NSStringFromClass([Earthquake class])
                    inManagedObjectContext:context];

    [newManagedObject   setValue:eventLocation forKey:@"location"];
    [newManagedObject   setValue:eventDate forKey:@"date"];
    [newManagedObject   setValue:eventLat forKey:@"latitude"];
    [newManagedObject   setValue:eventLong forKey:@"longitude"];
    [newManagedObject   setValue:eventMagnitude forKey:@"magnitude"];
    [newManagedObject   setValue:eventWebPath forKey:@"webLinkToUSGS"];
}




    connectionDidFinish
           Loading 6/n
                                                                          102
if ([existingEventsMatchingThisOne count]==0) {

    //Didn't find one already, make a new one
    NSManagedObject *newManagedObject =
        [NSEntityDescription insertNewObjectForEntityForName:
                NSStringFromClass([Earthquake class])
                    inManagedObjectContext:context];

    [newManagedObject   setValue:eventLocation forKey:@"location"];
    [newManagedObject   setValue:eventDate forKey:@"date"];
    [newManagedObject   setValue:eventLat forKey:@"latitude"];
    [newManagedObject   setValue:eventLong forKey:@"longitude"];
    [newManagedObject   setValue:eventMagnitude forKey:@"magnitude"];
    [newManagedObject   setValue:eventWebPath forKey:@"webLinkToUSGS"];
}




     If there isn't already
                        one
                                                                          103
if ([existingEventsMatchingThisOne count]==0) {

    //Didn't find one already, make a new one
    NSManagedObject *newManagedObject =
        [NSEntityDescription insertNewObjectForEntityForName:
                NSStringFromClass([Earthquake class])
                    inManagedObjectContext:context];

    [newManagedObject   setValue:eventLocation forKey:@"location"];
    [newManagedObject   setValue:eventDate forKey:@"date"];
    [newManagedObject   setValue:eventLat forKey:@"latitude"];
    [newManagedObject   setValue:eventLong forKey:@"longitude"];
    [newManagedObject   setValue:eventMagnitude forKey:@"magnitude"];
    [newManagedObject   setValue:eventWebPath forKey:@"webLinkToUSGS"];
}




Make a new Object
                                                                          104
if ([existingEventsMatchingThisOne count]==0) {

    //Didn't find one already, make a new one
    NSManagedObject *newManagedObject =
        [NSEntityDescription insertNewObjectForEntityForName:
                NSStringFromClass([Earthquake class])
                    inManagedObjectContext:context];

    [newManagedObject   setValue:eventLocation forKey:@"location"];
    [newManagedObject   setValue:eventDate forKey:@"date"];
    [newManagedObject   setValue:eventLat forKey:@"latitude"];
    [newManagedObject   setValue:eventLong forKey:@"longitude"];
    [newManagedObject   setValue:eventMagnitude forKey:@"magnitude"];
    [newManagedObject   setValue:eventWebPath forKey:@"webLinkToUSGS"];
}




Set all its attributes
                                                                          105
// Save the context.
error = nil;
if (![context save:&error]) {
    // stuff
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}




    connectionDidFinish
           Loading 7/n
                                                                  106
// Save the context.
error = nil;
if (![context save:&error]) {
    // stuff
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}




                Save and
           check for errors
                                                                  107
#if kUSE_PARENT_CONTEXTS_FOR_CONTEXT_MERGE
  dispatch_sync(dispatch_get_main_queue(), ^{
    NSError *error = nil;
    if (![self.mainContext save:&error]) {
        // Stuff
        NSLog(@"Unresolved error %@, %@", error,
                 [error userInfo]);
        abort();
    }
  });
#endif




    connectionDidFinish
           Loading 8/n
                                                   108
#if kUSE_PARENT_CONTEXTS_FOR_CONTEXT_MERGE
  dispatch_sync(dispatch_get_main_queue(), ^{
    NSError *error = nil;
    if (![self.mainContext save:&error]) {
        // Stuff
        NSLog(@"Unresolved error %@, %@", error,
                 [error userInfo]);
        abort();
    }
  });
#endif




     If we're merging via
                  Parent
                                                   109
#if kUSE_PARENT_CONTEXTS_FOR_CONTEXT_MERGE
  dispatch_sync(dispatch_get_main_queue(), ^{
    NSError *error = nil;
    if (![self.mainContext save:&error]) {
        // Stuff
        NSLog(@"Unresolved error %@, %@", error,
                 [error userInfo]);
        abort();
    }
  });
#endif




On the Main Thread
                                                   110
#if kUSE_PARENT_CONTEXTS_FOR_CONTEXT_MERGE
  dispatch_sync(dispatch_get_main_queue(), ^{
    NSError *error = nil;
    if (![self.mainContext save:&error]) {
        // Stuff
        NSLog(@"Unresolved error %@, %@", error,
                 [error userInfo]);
        abort();
    }
  });
#endif




Tell the main context to
                   save
                                                   111
Review
 • Asynchronous programming

 • NSOperations

 • Grand Central Dispatch (GCD)

 • More Blocks

 • Notifications

 • App Lifecycle

 • Network I/O (HTTP/REST)

 • JSON parsing
                                  112
Questions?
 Now, Or Later:
 CarlB@PDAgent.com
 @CarlBrwn (Twitter/App.net)

 Today's App was:
  https://github.com/carlbrown/SeismicJSON

                                             113

Mais conteúdo relacionado

Mais procurados

iOS 2 - The practical Stuff
iOS 2 - The practical StuffiOS 2 - The practical Stuff
iOS 2 - The practical Stuff
Petr Dvorak
 
Database madness with_mongoengine_and_sql_alchemy
Database madness with_mongoengine_and_sql_alchemyDatabase madness with_mongoengine_and_sql_alchemy
Database madness with_mongoengine_and_sql_alchemy
Jaime Buelta
 

Mais procurados (20)

Nodejs functions & modules
Nodejs functions & modulesNodejs functions & modules
Nodejs functions & modules
 
iOSDevCamp 2011 Core Data
iOSDevCamp 2011 Core DataiOSDevCamp 2011 Core Data
iOSDevCamp 2011 Core Data
 
Apache Utilities At Work V5
Apache Utilities At Work   V5Apache Utilities At Work   V5
Apache Utilities At Work V5
 
Intsllation & 1st program nodejs
Intsllation & 1st program nodejsIntsllation & 1st program nodejs
Intsllation & 1st program nodejs
 
Django - sql alchemy - jquery
Django - sql alchemy - jqueryDjango - sql alchemy - jquery
Django - sql alchemy - jquery
 
iOS 7 SDK特訓班
iOS 7 SDK特訓班iOS 7 SDK特訓班
iOS 7 SDK特訓班
 
How to write easy-to-test JavaScript
How to write easy-to-test JavaScriptHow to write easy-to-test JavaScript
How to write easy-to-test JavaScript
 
Building node.js applications with Database Jones
Building node.js applications with Database JonesBuilding node.js applications with Database Jones
Building node.js applications with Database Jones
 
iOS5 NewStuff
iOS5 NewStuffiOS5 NewStuff
iOS5 NewStuff
 
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and MingRapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
 
Bonjour, iCloud
Bonjour, iCloudBonjour, iCloud
Bonjour, iCloud
 
Testing Web Applications with GEB
Testing Web Applications with GEBTesting Web Applications with GEB
Testing Web Applications with GEB
 
iOS 2 - The practical Stuff
iOS 2 - The practical StuffiOS 2 - The practical Stuff
iOS 2 - The practical Stuff
 
Scalable JavaScript
Scalable JavaScriptScalable JavaScript
Scalable JavaScript
 
Javascript Application Architecture with Backbone.JS
Javascript Application Architecture with Backbone.JSJavascript Application Architecture with Backbone.JS
Javascript Application Architecture with Backbone.JS
 
Database madness with_mongoengine_and_sql_alchemy
Database madness with_mongoengine_and_sql_alchemyDatabase madness with_mongoengine_and_sql_alchemy
Database madness with_mongoengine_and_sql_alchemy
 
Developing application for Windows Phone 7 in TDD
Developing application for Windows Phone 7 in TDDDeveloping application for Windows Phone 7 in TDD
Developing application for Windows Phone 7 in TDD
 
Simpler Core Data with RubyMotion
Simpler Core Data with RubyMotionSimpler Core Data with RubyMotion
Simpler Core Data with RubyMotion
 
jQuery and_drupal
jQuery and_drupaljQuery and_drupal
jQuery and_drupal
 
Sequelize
SequelizeSequelize
Sequelize
 

Semelhante a REST/JSON/CoreData Example Code - A Tour

Hızlı Cocoa Geliştirme (Develop your next cocoa app faster!)
Hızlı Cocoa Geliştirme (Develop your next cocoa app faster!)Hızlı Cocoa Geliştirme (Develop your next cocoa app faster!)
Hızlı Cocoa Geliştirme (Develop your next cocoa app faster!)
Sarp Erdag
 
iOS App with Parse.com as RESTful Backend
iOS App with Parse.com as RESTful BackendiOS App with Parse.com as RESTful Backend
iOS App with Parse.com as RESTful Backend
Stefano Zanetti
 
! Modernizr v2.0.6 httpwww.modernizr.com Copyri.docx
!  Modernizr v2.0.6  httpwww.modernizr.com   Copyri.docx!  Modernizr v2.0.6  httpwww.modernizr.com   Copyri.docx
! Modernizr v2.0.6 httpwww.modernizr.com Copyri.docx
MARRY7
 
Cocoa Heads Tricity - Design Patterns
Cocoa Heads Tricity - Design PatternsCocoa Heads Tricity - Design Patterns
Cocoa Heads Tricity - Design Patterns
Maciej Burda
 
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
SPTechCon
 
把鐵路開進視窗裡
把鐵路開進視窗裡把鐵路開進視窗裡
把鐵路開進視窗裡
Wei Jen Lu
 
MobileCity:Core Data
MobileCity:Core DataMobileCity:Core Data
MobileCity:Core Data
Allan Davis
 

Semelhante a REST/JSON/CoreData Example Code - A Tour (20)

Hızlı Cocoa Geliştirme (Develop your next cocoa app faster!)
Hızlı Cocoa Geliştirme (Develop your next cocoa app faster!)Hızlı Cocoa Geliştirme (Develop your next cocoa app faster!)
Hızlı Cocoa Geliştirme (Develop your next cocoa app faster!)
 
iOS App with Parse.com as RESTful Backend
iOS App with Parse.com as RESTful BackendiOS App with Parse.com as RESTful Backend
iOS App with Parse.com as RESTful Backend
 
Webエンジニアから見たiOS5
Webエンジニアから見たiOS5Webエンジニアから見たiOS5
Webエンジニアから見たiOS5
 
Slickdemo
SlickdemoSlickdemo
Slickdemo
 
! Modernizr v2.0.6 httpwww.modernizr.com Copyri.docx
!  Modernizr v2.0.6  httpwww.modernizr.com   Copyri.docx!  Modernizr v2.0.6  httpwww.modernizr.com   Copyri.docx
! Modernizr v2.0.6 httpwww.modernizr.com Copyri.docx
 
Core Data with multiple managed object contexts
Core Data with multiple managed object contextsCore Data with multiple managed object contexts
Core Data with multiple managed object contexts
 
Cocoa Heads Tricity - Design Patterns
Cocoa Heads Tricity - Design PatternsCocoa Heads Tricity - Design Patterns
Cocoa Heads Tricity - Design Patterns
 
Android workshop
Android workshopAndroid workshop
Android workshop
 
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
 
MFF UK - Introduction to iOS
MFF UK - Introduction to iOSMFF UK - Introduction to iOS
MFF UK - Introduction to iOS
 
Having Fun with Play
Having Fun with PlayHaving Fun with Play
Having Fun with Play
 
Intorduction of Playframework
Intorduction of PlayframeworkIntorduction of Playframework
Intorduction of Playframework
 
Implementing New Web
Implementing New WebImplementing New Web
Implementing New Web
 
Implementing new WebAPIs
Implementing new WebAPIsImplementing new WebAPIs
Implementing new WebAPIs
 
把鐵路開進視窗裡
把鐵路開進視窗裡把鐵路開進視窗裡
把鐵路開進視窗裡
 
Porting legacy apps to Griffon
Porting legacy apps to GriffonPorting legacy apps to Griffon
Porting legacy apps to Griffon
 
Android and the Seven Dwarfs from Devox'15
Android and the Seven Dwarfs from Devox'15Android and the Seven Dwarfs from Devox'15
Android and the Seven Dwarfs from Devox'15
 
MobileCity:Core Data
MobileCity:Core DataMobileCity:Core Data
MobileCity:Core Data
 
Synchronizing without internet - Multipeer Connectivity (iOS)
Synchronizing without internet - Multipeer Connectivity (iOS)Synchronizing without internet - Multipeer Connectivity (iOS)
Synchronizing without internet - Multipeer Connectivity (iOS)
 
Core Data with Swift 3.0
Core Data with Swift 3.0Core Data with Swift 3.0
Core Data with Swift 3.0
 

Mais de Carl Brown

Cocoa coders 141113-watch
Cocoa coders 141113-watchCocoa coders 141113-watch
Cocoa coders 141113-watch
Carl Brown
 

Mais de Carl Brown (20)

GDPR, User Data, Privacy, and Your Apps
GDPR, User Data, Privacy, and Your AppsGDPR, User Data, Privacy, and Your Apps
GDPR, User Data, Privacy, and Your Apps
 
New in iOS 11.3b4 and Xcode 9.3b4
New in iOS 11.3b4 and Xcode 9.3b4New in iOS 11.3b4 and Xcode 9.3b4
New in iOS 11.3b4 and Xcode 9.3b4
 
Managing Memory in Swift (Yes, that's a thing)
Managing Memory in Swift (Yes, that's a thing)Managing Memory in Swift (Yes, that's a thing)
Managing Memory in Swift (Yes, that's a thing)
 
Better Swift from the Foundation up #tryswiftnyc17 09-06
Better Swift from the Foundation up #tryswiftnyc17 09-06Better Swift from the Foundation up #tryswiftnyc17 09-06
Better Swift from the Foundation up #tryswiftnyc17 09-06
 
Generics, the Swift ABI and you
Generics, the Swift ABI and youGenerics, the Swift ABI and you
Generics, the Swift ABI and you
 
Swift GUI Development without Xcode
Swift GUI Development without XcodeSwift GUI Development without Xcode
Swift GUI Development without Xcode
 
what's new in iOS10 2016-06-23
what's new in iOS10 2016-06-23what's new in iOS10 2016-06-23
what's new in iOS10 2016-06-23
 
Open Source Swift: Up and Running
Open Source Swift: Up and RunningOpen Source Swift: Up and Running
Open Source Swift: Up and Running
 
Parse migration CocoaCoders April 28th, 2016
Parse migration CocoaCoders April 28th, 2016Parse migration CocoaCoders April 28th, 2016
Parse migration CocoaCoders April 28th, 2016
 
Swift 2.2 Design Patterns CocoaConf Austin 2016
Swift 2.2 Design Patterns CocoaConf Austin 2016Swift 2.2 Design Patterns CocoaConf Austin 2016
Swift 2.2 Design Patterns CocoaConf Austin 2016
 
Advanced, Composable Collection Views, From CocoaCoders meetup Austin Feb 12,...
Advanced, Composable Collection Views, From CocoaCoders meetup Austin Feb 12,...Advanced, Composable Collection Views, From CocoaCoders meetup Austin Feb 12,...
Advanced, Composable Collection Views, From CocoaCoders meetup Austin Feb 12,...
 
Gcd cc-150205
Gcd cc-150205Gcd cc-150205
Gcd cc-150205
 
Cocoa coders 141113-watch
Cocoa coders 141113-watchCocoa coders 141113-watch
Cocoa coders 141113-watch
 
iOS8 and the new App Store
iOS8 and the new App Store   iOS8 and the new App Store
iOS8 and the new App Store
 
Dark Art of Software Estimation 360iDev2014
Dark Art of Software Estimation 360iDev2014Dark Art of Software Estimation 360iDev2014
Dark Art of Software Estimation 360iDev2014
 
Intro to cloud kit Cocoader.org 24 July 2014
Intro to cloud kit   Cocoader.org 24 July 2014Intro to cloud kit   Cocoader.org 24 July 2014
Intro to cloud kit Cocoader.org 24 July 2014
 
Welcome to Swift (CocoaCoder 6/12/14)
Welcome to Swift (CocoaCoder 6/12/14)Welcome to Swift (CocoaCoder 6/12/14)
Welcome to Swift (CocoaCoder 6/12/14)
 
Writing Apps that Can See: Getting Data from CoreImage to Computer Vision - ...
Writing Apps that Can See: Getting Data from CoreImage to Computer  Vision - ...Writing Apps that Can See: Getting Data from CoreImage to Computer  Vision - ...
Writing Apps that Can See: Getting Data from CoreImage to Computer Vision - ...
 
Introduction to Git Commands and Concepts
Introduction to Git Commands and ConceptsIntroduction to Git Commands and Concepts
Introduction to Git Commands and Concepts
 
360iDev iOS AntiPatterns
360iDev iOS AntiPatterns360iDev iOS AntiPatterns
360iDev iOS AntiPatterns
 

Último

Último (20)

Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 

REST/JSON/CoreData Example Code - A Tour

  • 1. REST/JSON/CoreData Example Code github.com/carlbrown/ SeismicJSON Carl Brown Twitter: @CarlBrwn Email: CarlB@PDAgent.com 1 Turn on Camera and ScreenFlow!!
  • 2. Asynchronous iOS Programming Rules • Threads are bad • Use Queues instead • Apple's Concurrency Programming Guide*: • Page 10: "The Move Away from Threads" • Page 74: "Migrating Away from Threads" • Page 74: "Replacing Threads with Dispatch Queues" * http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html 2
  • 3. Some things aren't thread-safe •Any UIAnything in iOS •CoreData 3
  • 4. UI tasks Must be on the Main Thread Often called the "UI" thread for that reason 4
  • 5. CoreData Contexts and Objects are tied to a thread • Never share a CoreData object between threads • Never share a CoreData object between contexts • Pass objects only by ID and fetch them again • Always notify other contexts when you've made changes 5
  • 6. Don't cross the threads •Use the same serial queue to stay on the same thread •Use dispatch_get_main_queue() or [NSOperationQueue mainQueue] to get to the Main Thread. 6
  • 7. SeismicJSON The only project we'll be working with today Feel free to run it and play with it for a couple of minutes Also on github so you can see how it was written. 7
  • 8. First Exercise: Follow the Code 8 This is a vital skill to have. We don't expect people to write in English without reading a lot of English first, but programmers consistently spend more time writing code than reading it while learning a new language.
  • 9. Please Open MasterView Controller.m This is pretty much a tableViewController like you've seen before. Ignore the #if conditionals this run-through 9 Ignore the #if conditionals this run-through
  • 10. MasterViewController should be mostly familiar • Cell labels filled in from Model Object • custom XIB for TableViewCell • dateFormatter in viewDidLoad • segmentedController sets FRC sorting • actionSheet for adding rows (you can figure out) • some iPad stuff (not Rocket Science) • #if conditional stuff (for later) 10
  • 11. DetailViewController •Really simple •simpler than the tableViewCell •Just a bunch of labels •nothing to discuss here 11
  • 12. Activity Indicating ImageView This has a UIActivityIndicatorView, implements a new custom @protocol and refers to a NetworkManager. 12
  • 14. -(void) awakeFromNib { _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyle Gray]; [_activityIndicator setFrame:self.frame]; [_activityIndicator setHidesWhenStopped:YES]; [self addSubview:_activityIndicator]; [_activityIndicator startAnimating]; } Start Spinner upon waking 14
  • 15. -(void) setImage:(UIImage *)image { [super setImage:image]; if (image) { [self.activityIndicator stopAnimating]; } else { [self.activityIndicator startAnimating]; } } Stop it when we get image 15
  • 16. -(void) setImageFileName:(NSString *)imageFileName { _imageFileName = imageFileName; if (_imageFileName==nil) { [self setImage:nil]; return; } setImageFileName 1/3 16
  • 17. //If the file already exists, don't bother to fetch it again NSString *fullFilePath = [[[NetworkManager sharedManager] cachedImageDirectory] stringByAppendingPathComponent:_imageFileName]; if ([[NSFileManager defaultManager] fileExistsAtPath:fullFilePath]) { [self imageDidBecomeAvailableAtPath:fullFilePath]; return; } setImageFileName 2/3 17
  • 18. [[NetworkManager sharedManager] fetchImagewithFilename: imageFileName andNotifyTarget:self]; } setImageFileName 3/3 18
  • 19. -(void) fetchImagewithFilename:(NSString *) filename andNotifyTarget:(NSObject <ImageFetchDelegate> *) target { //Stuff we've seen before ImageFetchOperation *imageFetchOperation = [[ImageFetchOperation alloc] init]; [imageFetchOperation setUrlToFetch: [self imageURLForImageFileName:filename]]; [imageFetchOperation setNotificationTarget:target]; [self.fetchQueue addOperation:imageFetchOperation]; } OK, now we're getting somewhere 19
  • 21. - (void)connectionDidFinishLoading:(NSURLConnection *)connection { if (self.response.statusCode==200) { NSError *error=nil; NSString *filename = [self.urlToFetch lastPathComponent]; NSString *fullFilePath = [ [[NetworkManager sharedManager] cachedImageDirectory] stringByAppendingPathComponent:filename]; NSLog(@"About to write file: %@",fullFilePath); if (![self.fetchedData writeToFile:fullFilePath options:NSDataWritingAtomic error:&error]) { NSLog(@"error occurred writing file: %@", [error localizedDescription]); } if (self.notificationTarget) { [self.notificationTarget imageDidBecomeAvailableAtPath:fullFilePath]; } } } connectionDidFinish Loading (abridged) 21
  • 22. - (void)connectionDidFinishLoading:(NSURLConnection *)connection { if (self.response.statusCode==200) { NSError *error=nil; NSString *filename = [self.urlToFetch lastPathComponent]; NSString *fullFilePath = [ [[NetworkManager sharedManager] cachedImageDirectory] stringByAppendingPathComponent:filename]; NSLog(@"About to write file: %@",fullFilePath); if (![self.fetchedData writeToFile:fullFilePath options:NSDataWritingAtomic error:&error]) { NSLog(@"error occurred writing file: %@", [error localizedDescription]); } if (self.notificationTarget) { [self.notificationTarget imageDidBecomeAvailableAtPath:fullFilePath]; } } } Only save if no error 22
  • 23. - (void)connectionDidFinishLoading:(NSURLConnection *)connection { if (self.response.statusCode==200) { NSError *error=nil; NSString *filename = [self.urlToFetch lastPathComponent]; NSString *fullFilePath = [ [[NetworkManager sharedManager] cachedImageDirectory] stringByAppendingPathComponent:filename]; NSLog(@"About to write file: %@",fullFilePath); if (![self.fetchedData writeToFile:fullFilePath options:NSDataWritingAtomic error:&error]) { NSLog(@"error occurred writing file: %@", [error localizedDescription]); } if (self.notificationTarget) { [self.notificationTarget imageDidBecomeAvailableAtPath:fullFilePath]; } } } Get filename/path 23
  • 24. - (void)connectionDidFinishLoading:(NSURLConnection *)connection { if (self.response.statusCode==200) { NSError *error=nil; NSString *filename = [self.urlToFetch lastPathComponent]; NSString *fullFilePath = [ [[NetworkManager sharedManager] cachedImageDirectory] stringByAppendingPathComponent:filename]; NSLog(@"About to write file: %@",fullFilePath); if (![self.fetchedData writeToFile:fullFilePath options:NSDataWritingAtomic error:&error]) { NSLog(@"error occurred writing file: %@", [error localizedDescription]); } if (self.notificationTarget) { [self.notificationTarget imageDidBecomeAvailableAtPath:fullFilePath]; } } } Save File 24
  • 25. - (void)connectionDidFinishLoading:(NSURLConnection *)connection { if (self.response.statusCode==200) { NSError *error=nil; NSString *filename = [self.urlToFetch lastPathComponent]; NSString *fullFilePath = [ [[NetworkManager sharedManager] cachedImageDirectory] stringByAppendingPathComponent:filename]; NSLog(@"About to write file: %@",fullFilePath); if (![self.fetchedData writeToFile:fullFilePath options:NSDataWritingAtomic error:&error]) { NSLog(@"error occurred writing file: %@", [error localizedDescription]); } if (self.notificationTarget) { [self.notificationTarget imageDidBecomeAvailableAtPath:fullFilePath]; } } } Let the View Know 25
  • 27. -(void) imageDidBecomeAvailableAtPath:(NSString *) path { if (![[path lastPathComponent] isEqualToString:self.imageFileName]) { NSLog(@"Warning: notified of incorrect file: %@, should have been %@",[path lastPathComponent],self.imageFileName); //try again [self setImageFileName:self.imageFileName]; return; } Only load the file we're expecting (race condition checking) imageDidBecomeAvailableAtPath 1/2 27
  • 28. //load image off the main queue UIImage *imageToLoad=[UIImage imageWithContentsOfFile:path]; dispatch_async(dispatch_get_main_queue(), ^{ [self setImage:imageToLoad]; [self setNeedsDisplay]; }); } Set our image with the file now on disk imageDidBecomeAvailableAtPath 2/2 28
  • 29. Summary of ActivityIndicatingImageView • Start the view with a spinner telling the user we are working on something • See if the file is already on disk, and use it if so. • If not, we ask the Network Manager to get the file for us • The Network Manager creates an operation to get our file (presumably from the network) and write it to disk • The Network Manager tells us the file is ready • We load the file into our image property • Now that we have an image, the spinner hides 29
  • 30. Recommended Networking Strategy •Always* load the UI from local storage •Core Data or local file or something •Always* put network data in local storage •Then tell the UI to refresh itself •Put up a placeholder if no data *Except with live web pages or HTTP streaming 30 Some people argue with me about this, but it's served me well for years
  • 31. Why do it that way? •Separates network code from UI code •Easier to test •Much faster response if previous data •Much better user experience offline 31
  • 32. Why wouldn't you? •Pointless if the network is infinitely fast and infinitely reliable* •More effort than "Unbreakable Glass" loading screens *c.f. http://en.wikipedia.org/wiki/Fallacies_of_Distributed_Computing 32
  • 34. NSOperation • Been around since the first iPhone OS SDK • Way to encapsulate the pieces of a task in one place • Can be queried, suspended or canceled • Simple selector call or block variants • NSOperations are placed in NSOperationQueues 34
  • 35. NSOperationQueue • Long-lived (presumably) queue that can contain numerous operations • Can be serial or concurrent • Can be suspended or canceled • Nice (but verbose) Objective-C syntax • Will stay on the same thread, if serial • [NSOperationQueue mainQueue] is always on the Main Thread 35
  • 36. Dispatch Queues •C-style (concise) syntax •quicker to use in-place •much less typing than declaring an NSOperation and adding to Queue •Harder to manage or cancel 36
  • 37. Which to use? • No hard-and-fast rules, but... • I tend to use NSOperations for: • things I'm going to do several times • things that have non-trivial complexity • I tend to use dispatch_async() for things: • with less than 10 or so lines of code • done only once in the App • that won't need to change when spec changes 37
  • 38. Waiting in Cocoa •Don't Sleep •Don't use locks •Yield to the RunLoop •See the FetchOperation for example •Sleeping or Locking Freezes the Thread 38
  • 39. Be Nice to Threads • POSIX Threads are a finite resource • The system will spin up more if tasks are waiting • But when no more can start, things will hang • See: WWDC2012 Session Session 712 - Asynchronous Design Patterns with Blocks, GCD, and XPC 39
  • 40. Back to our Application 40
  • 42. //Make this a 1 to show notifications, and a 0 to show parent contexts #define kUSE_NSNOTIFICATIONS_FOR_CONTEXT_MERGE 0 //if using notifications, set this to 1 to have them in the App Delegate #define kNSNOTIFICATIONS_HANDLED_IN_APPDELEGATE 0 Note: I'm not usually a fan of this kind of conditional compilation, but for this case, I thought it would let you play with project in the debugger in a cleaner way than with traditional if's. Project Variations 42
  • 46. Scroll in the center of Pick the Initial Commit the screen 46
  • 47. Got rid of some Xcode 4.4-isms 47
  • 49. applicationWillResignActive: !(UIApplication *) application •Happens when user gets Texts, notifications, Alerts, phone calls or hits the home button •Here I'm removing the notification observer so we won't try to get notifications while not Active 49
  • 51. Kicked off Network Fetch 51
  • 52. applicationDidBecomeActive: (UIApplication *)application • Happens when App becomes full-focus • After launch • Or after returning from dealing with alert • Or after dealing with "most recently used apps" along bottom of screen • Here I'm adding a notification observer 52
  • 53. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changesSaved:) name:NSManagedObjectContextDidSaveNotification object:nil]; This Runs "changesSaved:" 53
  • 54. - (void)changesSaved:(NSNotification *)notification { if (![NSThread isMainThread]) { dispatch_async(dispatch_get_main_queue(), ^{ [self changesSaved:notification]; }); return; } if ([notification object] != self.managedObjectContext) { [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification]; } } Handler Code 54
  • 55. - (void)changesSaved:(NSNotification *)notification { if (![NSThread isMainThread]) { dispatch_async(dispatch_get_main_queue(), ^{ [self changesSaved:notification]; }); return; } if ([notification object] != self.managedObjectContext) { [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification]; } } If not on Main, go there 55
  • 56. - (void)changesSaved:(NSNotification *)notification { if (![NSThread isMainThread]) { dispatch_async(dispatch_get_main_queue(), ^{ [self changesSaved:notification]; }); return; } if ([notification object] != self.managedObjectContext) { [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification]; } } Merge changes 56
  • 57. Queue Concurrency Type 57
  • 58. Reset DB each run 58
  • 61. + (NetworkManager *)sharedManager { static dispatch_once_t pred; dispatch_once(&pred, ^{ sharedManager = [[self alloc] init]; //Initialization Stuff }); return sharedManager; } Singleton Pattern 61
  • 62. -(void) startMainPageFetch { [self setHostReach:[Reachability reachabilityWithHostName:[self.baseURL host]]]; [self.hostReach startNotifier]; [self queuePageFetchForRelativePath: @"/earthquakes/feed/geojson/significant/month"]; } Kicked off from AppDelegate 62
  • 63. -(void) startMainPageFetch { [self setHostReach:[Reachability reachabilityWithHostName:[self.baseURL host]]]; [self.hostReach startNotifier]; [self queuePageFetchForRelativePath: @"/earthquakes/feed/geojson/significant/month"]; } Inform users of network status or be Rejected 63
  • 64. Just do it. If you want to understand it, read Apple's writeup 64
  • 65. -(void) startMainPageFetch { [self setHostReach:[Reachability reachabilityWithHostName:[self.baseURL host]]]; [self.hostReach startNotifier]; [self queuePageFetchForRelativePath: @"/earthquakes/feed/geojson/significant/month"]; } Start fetch of first batch 65
  • 66. -(void) queuePageFetchForRelativePath:(NSString *) relativePath { EarthquakeFetchOperation *earthquakeFetchOperation = [[EarthquakeFetchOperation alloc] init]; [earthquakeFetchOperation setUrlToFetch: [self urlForRelativePath:relativePath]]; [earthquakeFetchOperation setMainContext:self.mainContext]; [earthquakeFetchOperation setDelegate:self]; [self.fetchQueue addOperation:earthquakeFetchOperation]; } Make NSOp & Queue it 66
  • 67. #import <Foundation/Foundation.h> #import "BaseFetchOperation.h" @interface EarthquakeFetchOperation : BaseFetchOperation @property (nonatomic, weak) NSManagedObjectContext *mainContext; @end EarthquakeFetchOperation.h 67
  • 68. @interface BaseFetchOperation : NSOperation <NSURLConnectionDataDelegate> @property (nonatomic, strong) NSURL *urlToFetch; @property (nonatomic, strong) NSMutableData *fetchedData; @property (nonatomic, assign, getter=isDone) BOOL done; @property (nonatomic, assign) NSURLConnection *connection; @property (nonatomic, retain) NSHTTPURLResponse *response; @property (nonatomic, weak) NSObject<FetchNotifierDelegate> *delegate; -(void) finish; @end @protocol FetchNotifierDelegate <NSObject> -(void) fetchDidFailWithError:(NSError *) error; -(void) incrementActiveFetches; -(void) decrementActiveFetches; @end BaseFetchOperation.h 68
  • 69. Methods needed for URL fetching 69
  • 71. - (void)main { if ([self isCancelled]) { return; } if (!_urlForJSONData) { NSLog(@"Cannot start without a URL"); return; } [self setFetchedData:[NSMutableData data]]; //Initialize NSURLRequest *request = [NSURLRequest requestWithURL:[self urlToFetch]]; if (self.delegate) { [self.delegate incrementActiveFetches]; } [self setConnection:[NSURLConnection connectionWithRequest:request delegate:self]]; CFRunLoopRun(); } Entry Point 71
  • 72. - (void)main { if ([self isCancelled]) { return; } if (!_urlForJSONData) { NSLog(@"Cannot start without a URL"); return; } [self setFetchedData:[NSMutableData data]]; //Initialize NSURLRequest *request = [NSURLRequest requestWithURL:[self urlToFetch]]; if (self.delegate) { [self.delegate incrementActiveFetches]; } [self setConnection:[NSURLConnection connectionWithRequest:request delegate:self]]; CFRunLoopRun(); } Sanity Check 72
  • 73. - (void)main { if ([self isCancelled]) { return; } if (!_urlForJSONData) { NSLog(@"Cannot start without a URL"); return; } [self setFetchedData:[NSMutableData data]]; //Initialize NSURLRequest *request = [NSURLRequest requestWithURL:[self urlToFetch]]; if (self.delegate) { [self.delegate incrementActiveFetches]; } [self setConnection:[NSURLConnection connectionWithRequest:request delegate:self]]; CFRunLoopRun(); } Make request 73
  • 74. - (void)main { if ([self isCancelled]) { return; } if (!_urlForJSONData) { NSLog(@"Cannot start without a URL"); return; } [self setFetchedData:[NSMutableData data]]; //Initialize NSURLRequest *request = [NSURLRequest requestWithURL:[self urlToFetch]]; if (self.delegate) { [self.delegate incrementActiveFetches]; } [self setConnection:[NSURLConnection connectionWithRequest:request delegate:self]]; CFRunLoopRun(); } Inform user we're active 74
  • 75. - (void)main { if ([self isCancelled]) { return; } if (!_urlForJSONData) { NSLog(@"Cannot start without a URL"); return; } [self setFetchedData:[NSMutableData data]]; //Initialize NSURLRequest *request = [NSURLRequest requestWithURL:[self urlToFetch]]; if (self.delegate) { [self.delegate incrementActiveFetches]; } [self setConnection:[NSURLConnection connectionWithRequest:request delegate:self]]; CFRunLoopRun(); } Start Connection 75
  • 76. - (void)main { if ([self isCancelled]) { return; } if (!_urlForJSONData) { NSLog(@"Cannot start without a URL"); return; } [self setFetchedData:[NSMutableData data]]; //Initialize NSURLRequest *request = [NSURLRequest requestWithURL:[self urlToFetch]]; if (self.delegate) { [self.delegate incrementActiveFetches]; } [self setConnection:[NSURLConnection connectionWithRequest:request delegate:self]]; CFRunLoopRun(); } Give Up Control of thread 76
  • 77. -(void) finish { [self setDone:YES]; if (self.delegate) { [self.delegate decrementActiveFetches]; } CFRunLoopStop(CFRunLoopGetCurrent()); } Finish 77
  • 78. -(void) finish { [self setDone:YES]; if (self.delegate) { [self.delegate decrementActiveFetches]; } CFRunLoopStop(CFRunLoopGetCurrent()); } Inform user we're done 78
  • 79. -(void) finish { [self setDone:YES]; if (self.delegate) { [self.delegate decrementActiveFetches]; } CFRunLoopStop(CFRunLoopGetCurrent()); } Stop the runloop & get off 79
  • 80. Other methods there • didReceiveResponse • remember response • truncate data • (can get more than one response) • didReceiveData • append data • didFailWithError • report error to our delegate 80
  • 82. - (void)connectionDidFinishLoading:(NSURLConnection *)connection { if ([self isCancelled]) { [self finish]; return; } if (self.response.statusCode==200) { connectionDidFinishLoading 1/n 82
  • 83. - (void)connectionDidFinishLoading:(NSURLConnection *)connection { if ([self isCancelled]) { [self finish]; return; } if (self.response.statusCode==200) { Sanity Check/ Housekeeping 83
  • 84. - (void)connectionDidFinishLoading:(NSURLConnection *)connection { if ([self isCancelled]) { [self finish]; return; } if (self.response.statusCode==200) { Don't parse bad response 84
  • 85. id objectFromJSON = [NSJSONSerialization JSONObjectWithData:self.fetchedData options:0 error:&error]; if (objectFromJSON) { #if kUSE_NSNOTIFICATIONS_FOR_CONTEXT_MERGE NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; [context setPersistentStoreCoordinator: self.mainContext.persistentStoreCoordinator]; #else NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; [context setParentContext:[self mainContext]]; #endif connectionDidFinish Loading 2/n 85
  • 86. id objectFromJSON = [NSJSONSerialization JSONObjectWithData:self.fetchedData options:0 error:&error]; if (objectFromJSON) { #if kUSE_NSNOTIFICATIONS_FOR_CONTEXT_MERGE NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; [context setPersistentStoreCoordinator: self.mainContext.persistentStoreCoordinator]; #else NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; [context setParentContext:[self mainContext]]; #endif Parse JSON 86
  • 87. id objectFromJSON = [NSJSONSerialization JSONObjectWithData:self.fetchedData options:0 error:&error]; if (objectFromJSON) { #if kUSE_NSNOTIFICATIONS_FOR_CONTEXT_MERGE NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; [context setPersistentStoreCoordinator: self.mainContext.persistentStoreCoordinator]; #else NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; [context setParentContext:[self mainContext]]; #endif If the JSON was good 87
  • 88. id objectFromJSON = [NSJSONSerialization JSONObjectWithData:self.fetchedData options:0 error:&error]; if (objectFromJSON) { #if kUSE_NSNOTIFICATIONS_FOR_CONTEXT_MERGE NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; [context setPersistentStoreCoordinator: self.mainContext.persistentStoreCoordinator]; #else NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; [context setParentContext:[self mainContext]]; #endif Make new ManagedObjectContext 88
  • 89. NSDictionary *jsonDict = (NSDictionary *) objectFromJSON; if (jsonDict) { NSArray *events = [jsonDict objectForKey:@"features"]; if (events) { for (NSDictionary *eventDict in events) { connectionDidFinish Loading 3/n 89
  • 90. NSDictionary *jsonDict = (NSDictionary *) objectFromJSON; if (jsonDict) { NSArray *events = [jsonDict objectForKey:@"features"]; if (events) { for (NSDictionary *eventDict in events) { If we got a dictionary 90
  • 91. NSDictionary *jsonDict = (NSDictionary *) objectFromJSON; if (jsonDict) { NSArray *events = [jsonDict objectForKey:@"features"]; if (events) { for (NSDictionary *eventDict in events) { Get Array of Earthquakes 91
  • 92. NSDictionary *jsonDict = (NSDictionary *) objectFromJSON; if (jsonDict) { NSArray *events = [jsonDict objectForKey:@"features"]; if (events) { for (NSDictionary *eventDict in events) { If Array/JSON is valid 92
  • 93. NSDictionary *jsonDict = (NSDictionary *) objectFromJSON; if (jsonDict) { NSArray *events = [jsonDict objectForKey:@"features"]; if (events) { for (NSDictionary *eventDict in events) { Iterate over it 93
  • 94. NSString *eventLocation = [eventDict valueForKeyPath:@"properties.place"]; NSDate *eventDate = [NSDate dateWithTimeIntervalSince1970: [[eventDict valueForKeyPath:@"properties.time"] doubleValue]]; NSNumber *eventLong = [NSNumber numberWithDouble:[[[eventDict valueForKeyPath:@"geometry.coordinates"] objectAtIndex:0] doubleValue]]; NSNumber *eventLat =[NSNumber numberWithDouble:[[[eventDict valueForKeyPath:@"geometry.coordinates"] objectAtIndex:1] doubleValue]]; NSNumber *eventMagnitude = [NSNumber numberWithFloat:[ [eventDict valueForKeyPath:@"properties.mag"] floatValue]]; NSString *eventWebPath = [@"http://earthquake.usgs.gov" stringByAppendingPathComponent:[eventDict valueForKeyPath:@"properties.url"]]; connectionDidFinish Loading 4/n 94
  • 95. NSString *eventLocation = [eventDict valueForKeyPath:@"properties.place"]; NSDate *eventDate = [NSDate dateWithTimeIntervalSince1970: [[eventDict valueForKeyPath:@"properties.time"] doubleValue]]; NSNumber *eventLong = [NSNumber numberWithDouble:[[[eventDict valueForKeyPath:@"geometry.coordinates"] objectAtIndex:0] doubleValue]]; NSNumber *eventLat =[NSNumber numberWithDouble:[[[eventDict valueForKeyPath:@"geometry.coordinates"] objectAtIndex:1] doubleValue]]; NSNumber *eventMagnitude = [NSNumber numberWithFloat:[ [eventDict valueForKeyPath:@"properties.mag"] floatValue]]; NSString *eventWebPath = [@"http://earthquake.usgs.gov" stringByAppendingPathComponent:[eventDict valueForKeyPath:@"properties.url"]]; Extract values from eventDict 95
  • 96. NSString *eventLocation = [eventDict valueForKeyPath:@"properties.place"]; NSDate *eventDate = [NSDate dateWithTimeIntervalSince1970: [[eventDict valueForKeyPath:@"properties.time"] doubleValue]]; NSNumber *eventLong = [NSNumber numberWithDouble:[[[eventDict valueForKeyPath:@"geometry.coordinates"] objectAtIndex:0] doubleValue]]; NSNumber *eventLat =[NSNumber numberWithDouble:[[[eventDict valueForKeyPath:@"geometry.coordinates"] objectAtIndex:1] doubleValue]]; NSNumber *eventMagnitude = [NSNumber numberWithFloat:[ [eventDict valueForKeyPath:@"properties.mag"] floatValue]]; NSString *eventWebPath = [@"http://earthquake.usgs.gov" stringByAppendingPathComponent:[eventDict valueForKeyPath:@"properties.url"]]; Using keyPaths 96
  • 97. NSString *eventLocation = [eventDict valueForKeyPath:@"properties.place"]; NSDate *eventDate = [NSDate dateWithTimeIntervalSince1970: [[eventDict valueForKeyPath:@"properties.time"] doubleValue]]; NSNumber *eventLong = [NSNumber numberWithDouble:[[[eventDict valueForKeyPath:@"geometry.coordinates"] objectAtIndex:0] doubleValue]]; NSNumber *eventLat =[NSNumber numberWithDouble:[[[eventDict valueForKeyPath:@"geometry.coordinates"] objectAtIndex:1] doubleValue]]; NSNumber *eventMagnitude = [NSNumber numberWithFloat:[ [eventDict valueForKeyPath:@"properties.mag"] floatValue]]; NSString *eventWebPath = [@"http://earthquake.usgs.gov" stringByAppendingPathComponent:[eventDict valueForKeyPath:@"properties.url"]]; and/or Array elements 97
  • 98. NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName: NSStringFromClass([Earthquake class])]; [fetchRequest setFetchLimit:1]; NSPredicate *eventInfo = [NSPredicate predicateWithFormat: @"location = %@ AND date = %@", eventLocation, eventDate]; [fetchRequest setPredicate:eventInfo]; NSError *fetchError=nil; NSArray *existingEventsMatchingThisOne = [context executeFetchRequest:fetchRequest error:&fetchError]; connectionDidFinish Loading 5/n 98
  • 99. NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName: NSStringFromClass([Earthquake class])]; [fetchRequest setFetchLimit:1]; NSPredicate *eventInfo = [NSPredicate predicateWithFormat: @"location = %@ AND date = %@", eventLocation, eventDate]; [fetchRequest setPredicate:eventInfo]; NSError *fetchError=nil; NSArray *existingEventsMatchingThisOne = [context executeFetchRequest:fetchRequest error:&fetchError]; Make a fetch request 99
  • 100. NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName: NSStringFromClass([Earthquake class])]; [fetchRequest setFetchLimit:1]; NSPredicate *eventInfo = [NSPredicate predicateWithFormat: @"location = %@ AND date = %@", eventLocation, eventDate]; [fetchRequest setPredicate:eventInfo]; NSError *fetchError=nil; NSArray *existingEventsMatchingThisOne = [context executeFetchRequest:fetchRequest error:&fetchError]; matching our event 100
  • 101. NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName: NSStringFromClass([Earthquake class])]; [fetchRequest setFetchLimit:1]; NSPredicate *eventInfo = [NSPredicate predicateWithFormat: @"location = %@ AND date = %@", eventLocation, eventDate]; [fetchRequest setPredicate:eventInfo]; NSError *fetchError=nil; NSArray *existingEventsMatchingThisOne = [context executeFetchRequest:fetchRequest error:&fetchError]; And run it 101
  • 102. if ([existingEventsMatchingThisOne count]==0) { //Didn't find one already, make a new one NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName: NSStringFromClass([Earthquake class]) inManagedObjectContext:context]; [newManagedObject setValue:eventLocation forKey:@"location"]; [newManagedObject setValue:eventDate forKey:@"date"]; [newManagedObject setValue:eventLat forKey:@"latitude"]; [newManagedObject setValue:eventLong forKey:@"longitude"]; [newManagedObject setValue:eventMagnitude forKey:@"magnitude"]; [newManagedObject setValue:eventWebPath forKey:@"webLinkToUSGS"]; } connectionDidFinish Loading 6/n 102
  • 103. if ([existingEventsMatchingThisOne count]==0) { //Didn't find one already, make a new one NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName: NSStringFromClass([Earthquake class]) inManagedObjectContext:context]; [newManagedObject setValue:eventLocation forKey:@"location"]; [newManagedObject setValue:eventDate forKey:@"date"]; [newManagedObject setValue:eventLat forKey:@"latitude"]; [newManagedObject setValue:eventLong forKey:@"longitude"]; [newManagedObject setValue:eventMagnitude forKey:@"magnitude"]; [newManagedObject setValue:eventWebPath forKey:@"webLinkToUSGS"]; } If there isn't already one 103
  • 104. if ([existingEventsMatchingThisOne count]==0) { //Didn't find one already, make a new one NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName: NSStringFromClass([Earthquake class]) inManagedObjectContext:context]; [newManagedObject setValue:eventLocation forKey:@"location"]; [newManagedObject setValue:eventDate forKey:@"date"]; [newManagedObject setValue:eventLat forKey:@"latitude"]; [newManagedObject setValue:eventLong forKey:@"longitude"]; [newManagedObject setValue:eventMagnitude forKey:@"magnitude"]; [newManagedObject setValue:eventWebPath forKey:@"webLinkToUSGS"]; } Make a new Object 104
  • 105. if ([existingEventsMatchingThisOne count]==0) { //Didn't find one already, make a new one NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName: NSStringFromClass([Earthquake class]) inManagedObjectContext:context]; [newManagedObject setValue:eventLocation forKey:@"location"]; [newManagedObject setValue:eventDate forKey:@"date"]; [newManagedObject setValue:eventLat forKey:@"latitude"]; [newManagedObject setValue:eventLong forKey:@"longitude"]; [newManagedObject setValue:eventMagnitude forKey:@"magnitude"]; [newManagedObject setValue:eventWebPath forKey:@"webLinkToUSGS"]; } Set all its attributes 105
  • 106. // Save the context. error = nil; if (![context save:&error]) { // stuff NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } connectionDidFinish Loading 7/n 106
  • 107. // Save the context. error = nil; if (![context save:&error]) { // stuff NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } Save and check for errors 107
  • 108. #if kUSE_PARENT_CONTEXTS_FOR_CONTEXT_MERGE dispatch_sync(dispatch_get_main_queue(), ^{ NSError *error = nil; if (![self.mainContext save:&error]) { // Stuff NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } }); #endif connectionDidFinish Loading 8/n 108
  • 109. #if kUSE_PARENT_CONTEXTS_FOR_CONTEXT_MERGE dispatch_sync(dispatch_get_main_queue(), ^{ NSError *error = nil; if (![self.mainContext save:&error]) { // Stuff NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } }); #endif If we're merging via Parent 109
  • 110. #if kUSE_PARENT_CONTEXTS_FOR_CONTEXT_MERGE dispatch_sync(dispatch_get_main_queue(), ^{ NSError *error = nil; if (![self.mainContext save:&error]) { // Stuff NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } }); #endif On the Main Thread 110
  • 111. #if kUSE_PARENT_CONTEXTS_FOR_CONTEXT_MERGE dispatch_sync(dispatch_get_main_queue(), ^{ NSError *error = nil; if (![self.mainContext save:&error]) { // Stuff NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } }); #endif Tell the main context to save 111
  • 112. Review • Asynchronous programming • NSOperations • Grand Central Dispatch (GCD) • More Blocks • Notifications • App Lifecycle • Network I/O (HTTP/REST) • JSON parsing 112
  • 113. Questions? Now, Or Later: CarlB@PDAgent.com @CarlBrwn (Twitter/App.net) Today's App was: https://github.com/carlbrown/SeismicJSON 113