(713) Watch Connectivity

Watch Connectivity (OS2) session talks about the techniques to share data between 2 apps: iPhone host app and Watch app. 

  1. How to get data over to the Watch? Firstly, we need to setup Session on both sides.
    1. Setup on both sides: let session = WCSession.defaultSession()
    2. session.delegate = self
    3. session.activateSession()
    4. iPhone app: Session state:
    5. session.paired
    6. session.watchAppInstalled
    7. session.watchDirectoryURL
  2. Categories: there are 3 types of background transfers, and 1 live transfer of data.
    1. Background transfers: 
      1. content not needed immediately (queued up)
      2. OS intelligently transfers for us
      3. sending side can exit
      4. applicationContext -> receivedApplicationContext // Override old data
      5. Sending: try { updateApplicationContext } catch {}
      6. Receiving: didReceiveApplicationContext
      7. context = Dictionary (plist types) // work well for Glances
      8. User-Info-Transfer: WCSession.defaultSession().transferUserInfo()
      9. userInfo = Dictionary (plist types) – access to queue, used for f.e level in games
      10. Receiving: didReceiveUserInfo
      11. File-Transfer: metaData (Dictionary) transferFile(: metaData:)
      12. Receiving: ~/Documents/Inbox
    2. Interactive messaging
      1. Live (f.e. game when both screens are up)
      2. Requires other app available: WCSession.defaultSession().reachable
      3. Reachable: from iPhone, asking if the Watch app is reachable.
        1. devices connected over BlueTooth or WIFI
        2. Watch app foreground (on the Watch)
      4. Reachable: from Watch, asking if the iPhone app is reachable.
        1. devices must be connected
        2. WatchKit extension foreground (? WatchKit extension, not app ?)
        3. You don’t need the iPhone app to be running, you can sendMessage to launch the iPhone app in the background.
      5. Message: Dictionary (plist types)
        1. sendMessage (message:, replyHandler:, errorHandler:) // optional handler
          1. replyHandler – recommended (confirmation)
          2. delegateCallbacks -> supply a replyHandler
        2. sendMessageData (data:, replyHandler:, errorHandler:)
  3. NSURLSession:
    1. existing Foundation class, on watchOS2 (tetherless to known Wi-Fi) 38.20
    2. Use: new content (tailored to Watch)
    3. F.e. iOS app got new content -> deliver applicationContext to Watch app!
    4. When Watch app launches -> use iOS data, while loading new content
  4. Complications: How to update Complications timeline entries!
    1. Updating clock face // using ClockKit framework
      1. let server = CLKComplicationServer.sharedInstance()
      2. server.extendTimelineForComplication(aComplication)
      3. getCurrentTimelineEntryForComplication //
      4. getTimelineEntriesForComplication //
      5. getNextRequestedUpdateDateWithHandler // suggest time to update again. Budgeted
    2. Get content:
      1. special update when reachable: use sendMessage to launch host app
      2. content can be pushed (f.e. sport score)
      3. requested interval fetch (direct from Watch to Cloud) – use NSURLSession background session
      4. keep runtime as short as possible
    3. Pushed: PushKit // use the iOS app to register the device with ApplePush server, get a PushToken, save that token with the server so
      1. pushRegistry = PKPushRegistry(queue:dispatch_get_main_queue())
      2. pushRegistry.delegate = self
      3. pushRegistry.desiredPushTypes = [PKPushTypeComplication]
      4. pushRegistry(registry: PKPushRegistry!, didUpdatePushCredentials)
      5. pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload)
      6. session.transferUserInfo(timeLineEntry1)
      7. session.transferUserInfo(timeLineEntry2)
      8. session.transferCurrentComplicationUserInfo(presentTimeLineEntry) Budgeted
      9. Receiving: didReceiveUserInfo:
      10. PushTypeComplication has 4K payload -> use it to send all data!

(209) Creating Complications with Clock Kit

  1. Complication
    1. Small pieces of info on Clock Face. OS2 – can create own Complications.
    2. ForceTouch Face to customize – use Crown to switch Complication.
    3. Complication contain not only labels, but images !
    4. Data has to be loaded in advance – in timeline entries format !
    5. Time Travel: (OS2)
    6. Families: 5 different families
      1. Modular small: square
      2. Modular large: center rectangle
      3. Utilitarian small: small corner rectangle
      4. Utilitarian large: bottom rectangle
      5. Circular small: 4 corner squares
    7. Layout: 
      1. Modular Small: 7 templates
      2. Modular Large: 5 templates
      3. Util small: 4 templates
      4. Util large: 1 template
      5. Circular small: 6 templates
    8. CLKComplicationTemplate:
      1. Header image : CLKImageProvider?
      2. Header text : CLKTextProvider
      3. Body 1 text : CLKTextProvider
      4. Body 2 text : CLKTextProvider?
    9. CLKImageProvider
      1. backgroundImage: UIImage
      2. backgroundColor:
      3. foregroundImage: UIImage
      4. foregroundColor:  CLKImageProviderForegroundColor.White
    10. CLKTextProvider
      1. Space is very constrained
      2. Declare intentions -> system formats for us
      3. Wednesday, September 23 -> Wed, Sep 23 -> Sep 23 -> 23
        1. let sep32: NSDate =
        2. let units = [NSCalendarUnit.Weekday, NSCalendarUnit.Month, NSCalendarUnit.Day]
        3. textProv = CLKDateTextProvider (sep23, units)
      4. SimpleTextProvider, TimeTextProvider, TimeIntervalTextProvider, RelativeDateTextProvider
        1. choose style: Natural, Offset, Timer 
        2. create textProvider (date: style: units:)
        3. template.body2TextProvider = txtProv
  2. Timeline:
    1. Calendar: put event-entries when the previous event ends! -> never empty data, predictive
    2. CLKComplicationDataSource protocol
    3. getCurrentTimelineEntryForComplication (complication: withHandler) // handler takes the entries (CLKComplicationTimelineEntry) array as param
    4. getTimelineEntriesForComplication (complication: afterDate: limit: withHandler)
    5. Time Travel Direction: Stock (past = backward), Weather (future = forward)
    6. getSupportedTimeTravelDirectionsForComplications (complication: withHandler)
    7. Timeline boundariesgetTimelineEndDateForComplication & getTimelineStartDateForComplication
    8. Placeholder Template: getPlaceholderTemplateForComplication
  3. Show: Actual coding stuffs
(?) Is the NSURLSession can be done only over tetherless WIFI or the NSURL Session can utilise 3G as well?

On http://asciiwwdc.com/2015/sessions/713, it says that “If the Apple Watch does connect to known Wi-Fi networks, you can use NSURLSession to go over that Wi-Fi to connect to your servers and fetch content.” 

Another one http://asciiwwdc.com/2015/sessions/711, says that “if the Apple Watch is ” In most circumstances, if the users’ watch is nearby their paired iPhone device, then we will actually leverage the Bluetooth connection between them, perform the HTTP loads on the phone itself, and deliver the results back to the watch over Bluetooth.

If the user happens to be out and away from their phone, but with their watch, and the watch is connected to a known Wi-Fi network, then we can actually use that Wi-Fi network directly.” – Quoted

 So basically “in most circumstances“, the Watch uses it’s paired iPhone network over it’s own WiFi connection!

(?) If there is no WIFI, use WatchConnectivity:

“WatchConnectivity allows device-to-device communication between your apps”. In this case, you have to launch the iPhone app (on background) to connect to you. 

So – if you forget the phone at home and have no known WIFI -> no internet.

(?) If you have the phone with you, and you have known WIFI as well, which way you would better use, WatchConnectivity or WIFI to get new data on the Watch?

In this “http://asciiwwdc.com/2014/sessions/707

Another benefit is that in a background session, we monitor the network and power environment for you. This means that we cover things like network reachability and connectivity for you, so you don’t have to use the reachability APIs at all. We won’t attempt to establish a connection until we know that the server is reachable.

Actually just use NSURLSession and it will make sure to connect the Watch to internet for you. No need to talk to the iPhone app to do it.

(?) Is the NSURLSession is a background session only? 

(711) NSURLSession

Setup Configuration
NSURLSessionConfiguration *sessionConfig =
[NSURLSessionConfiguration defaultSessionConfiguration];
 
// restricts network operations to Wifi
sessionConfig.allowsCellularAccess = NO;
 
// sets all requests to only accept JSON responses
[sessionConfig setHTTPAdditionalHeaders:
          @{@"Accept": @"application/json"}];
 
// configures timeouts & restricts app to only have one network connection to a host
sessionConfig.timeoutIntervalForRequest = 30.0;
sessionConfig.timeoutIntervalForResource = 60.0;
sessionConfig.HTTPMaximumConnectionsPerHost = 1;
Creating Session:
// set the image URL
NSString *imageUrl = @"http://......png";
 
// create the default session configuration
NSURLSessionConfiguration *sessionConfig =
  [NSURLSessionConfiguration defaultSessionConfiguration];
 
// create a session using the current class as a delegate
NSURLSession *session =
  [NSURLSession sessionWithConfiguration:sessionConfig
                                delegate:self
                           delegateQueue:nil];

Download File

// the task is created from a session
NSURLSessionDownloadTask *getImageTask =
[session downloadTaskWithURL:[NSURL URLWithString:imageUrl]
 
    completionHandler:^(NSURL *location,
                        NSURLResponse *response,
                        NSError *error) {
        // the image is uploaded as NSData
        UIImage *downloadedImage =
          [UIImage imageWithData:
              [NSData dataWithContentsOfURL:location]];
      // update UIImageView image to show the new file
      dispatch_async(dispatch_get_main_queue(), ^{
        // do stuff with image
        _imageWithBlock.image = downloadedImage;
      });
}];
 
// start up the task
[getImageTask resume];

Get JSON

NSURLSessionDataTask *jsonData = [session dataTaskWithURL:yourNSURL
      completionHandler:^(NSData *data,
                          NSURLResponse *response,
                          NSError *error) {
        // handle NSData
}];

Other Sample Code

Screen Shot 2015-06-24 at 3.59.11 pm Screen Shot 2015-06-24 at 5.07.47 pm Screen Shot 2015-06-25 at 10.35.15 am Screen Shot 2015-06-25 at 10.36.56 am