iOS Development

Apple Watch Glance and Inter Device Communications…

As a continuation to the series of articles I have been writing about Apple Watch and WatchKit (which you can find here and here), today I intend to discuss about the “Glance” feature of Apple Watch. But as Christmas is drawing near, and Mr. Clause would like me to do so, in addition to Glance, I’ll give you something extra. Very recently, Apple has revealed a new feature to WatchKit. The feature is inter device communication, which bridges the information gap between the iPhone device and Apple Watch.  I’ll cover that too!

So, as an aggregation measure and in the urge to know everything right now and here, which is an instance of an insatiable inquisitiveness inherent in invariably all individuals such as I, this article enjoys the daylight.

Glances

Glances are a summary view of the actual application running in the Apple Watch. You can very well compare them with the live tiles in Windows Phone. In Apple’s own words —

Glances are a browsable collection of timely and contextually relevant moments from the wearer’s favorite apps. Individually, a Glance is a quick view of your app’s most important content.

Keeping that intention in mind, Apple has put severe restrictions to the design of Glances. The first of them is —

  1. Template Based: they are template based. So, there is no way other than following where Apple wants you to put your views.
  2. Single Action: Another one is, they only can host one single action. If the user taps on the Glance, the Watch App launches.

Inter Device Communication

It would have been really good if the communication was really two way, in an impartial manner. Unfortunately, iPhone turned out to be too shy to start the conversation with Apple Watch. And in such a scenario, Apple Watch does just what a guy/girl does when trying his/her luck with their shy counterpart, it takes the initiative and approaches. Hopefully, if s/he wishes to do so, iPhone can reply back. Romantic, isn’t it?

How to Glance (and not watch!)

Today, I will not delve deep into step by step tutorials. Because, making an Apple Watch app with Glances is really easy and only takes a heartfelt tick on the tick box that says “Include Glance Scene”.

GlanceHowTo

I will rather explain what I want to present to you in terms of source code, which as usual is uploaded in Github (MIT license).

Application Overview (my big idea)

My idea of utilising both Glance and Inter Device Communication is as follows:

Let me tell you a secret, I love loans. There is no other thing in the world that has such a tremendous power to provide you endless sleepless nights (for two) and at an extreme, even the unique opportunity to be homeless again, only at the nominal cost of a little temporary happiness! That’s why, I would like to make a banking app which lets you view your loan balance, and unlike other selfish banks, encourages you to pay back the loan and be out of that debt soon (so that you can borrow an even larger amount soon enough!)

The original iPhone application shows your loan account number and a pie chart that depicts how much you have paid back and how much outstanding amount you have to pay further.

MainScreen

This information will also be available in my Apple Watch app. In the Glances view, the user can see the graph in the iPhone app which urges her to make the whole green.

MainWatch

And here will be the Glance view for the data.

Glance

The graph is generated using the famed CorePlot. Apple Watch unfortunately does not have the guts to use CorePlot yet, so it will have to suffice with a PNG representation of the graph view which will be thrown to the Watch App upon request.

iPhone App – with all her beauty, waits for her knight in shining armour

Our iPhone application has a JSON file, which contains the following data. Of course In the real life scenario all these data would be coming from the server locked in encryptions with keys thrown in the water.

   { 
      "LoanAmount": "70000",
      "Outstanding": "20000",
      "Paid": "50000",
      "AccountNo" : "3423847289",
      "NextInstallment": "01/01/2015"
    }

The iPhone app reads the data and generates the pie chart using core plot API. Finally the graph is converted into png image and is saved in the document directory.

What Apple (Knight) Watch does

The Apple Watch have the ability to invoke the parent app. So, when the user taps on the “Refresh” button, the iPhone app launches and generates the graph.


- (IBAction)refreshGlance {
    [self openParentAppToRefreshGraph];
}

-(void) openParentAppToRefreshGraph {
    [WKInterfaceController openParentApplication:[NSDictionary dictionaryWithObjectsAndKeys:@"ImageName", @"chartImage.png", nil] reply:^(NSDictionary *replyInfo, NSError *error) {
        NSData *pngData = replyInfo[@"Image"];
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
        NSString *filePath = [documentsPath stringByAppendingPathComponent:@"chartImage.png"]; //Add the file name
        [pngData writeToFile:filePath atomically:YES];
    }];
}

Her silent reply…

In the callback to the Apple Watch, the image generated from the graph is sent back to the Apple Watch for display.

    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

-(void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply {
    
    [[NSNotificationCenter defaultCenter] postNotificationName:@"WatchKitNotification" object:nil];
    
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
    NSString *filePath = [documentsPath stringByAppendingPathComponent:@"chartImage.png"];
    NSData *pngData = [NSData dataWithContentsOfFile:filePath];
    
    NSDictionary *response = @{@"Image" : pngData};
    reply(response);
 
}

Phew!… It’s a Yes !!

Once the data is received, Apple Watch then saves the image in the documents directory. So, when user goes to glance, the new updated graph is ready.

- (void)awakeWithContext:(id)context {
    [super awakeWithContext:context];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
    NSString *filePath = [documentsPath stringByAppendingPathComponent:@"chartImage.png"];
    NSData *pngData = [NSData dataWithContentsOfFile:filePath];
    [self.glanceImage setImageData:pngData];  
}

Conclusion

And the Apple Watch and iPhone lived happily ever after (for many many years).

Hope you liked the story. The code is uploaded in Github, you can grab it here. You’re welcome! 🙂

iOS Development

Apple Watch Notifications

Remote and local notifications are not at all a new thing. From the very day – the 17th of June, 2009, they were introduced, they have always been, arguably, the most favourite mechanism to deliver messages to the user. The messages has ranged from proximity alert to a store, new update to an app, directional instructions to illegal promotions.

 But in Apple watch, notifications take a new dimension. From the way the notifications are designed for  Watch, it appears quite evident that Apple has spent considerable amount of energy to make them better and more meaningful. Following is the flow showing how a notification is presented to an  Watch user.

  1. According to the  Watch Human Interface Guidelines and WatchKit Programming Guide, when an app running in the iPhone receives a notification, iOS decides where to show the notification. Though this is kind of a vague statement, but as of now there seems to be no control over this even if we specifically need the notification to be shown in  the  Watch. Also, I could not find any information on how iOS decides to show the notification where it decides to show. Guess we’ll have to wait a bit for to know that.
  2. If the notification is sent to the  Watch, the user feels a subtle vibration on his wrist or a mild audio cue based on the notification’s payload.
  3. Alarmed, as the user raises his hand, a very minimalistic screen is presented which is called Short Look interface. This is an immutable, non-scrollable screen that conveys the user the most important information about the notification. iOS designs them based on a predetermined template and present on the screen. This is  Watch’s interpretation of your everyday notification, with just a title provided by you in the notification payload.
  4. All work and no play makes Jack a dull boy. Who understands this better than Apple? So, here is the shining playful part. The customisable, scrollable, actionable notification screen. After the Short Look notification is displayed, if the user continues to keep his hand raised (in the hope that something else will happen…soon…well..anytime now…), or taps on the Short Look interface the Long Look interface is displayed.

Apple has given you freedom within the aesthetic boundary to design the Long Look interface. You can add buttons and graphics and customise the behaviour when the user taps on it.

But what happens if you don’t provide the Long Look interface? Well, Apple has a backup plan. iOS displays a default interface with the app icon, title string and alert message. Tapping on it launches the app.

OK, so let’s not allow Apple to have all the fun and design our own Long Look interface!

A Long Look interface has three major parts —

  • The Sash at the top of the screen — this includes the title string and the icon of the app
  • The content — this is your playground. Add any graphics and buttons
  • The dismiss button — this is always present, added by the iOS, and dismisses the notification once tapped

 In the Sash section, as a third party developer, you have basic freedom. You can change the tint color and title of the notification.

In the content, you have much more liberty. You can modify and design the label to show your notification message. You can add buttons, but not directly. All the buttons should come in a specific format through a JSON payload that will invoke the notification. The SDK already generates one  such JSON payload file while creating  notification scene for testing purpose.

Screen Shot 2014-12-04 at 02.29.48Changing the alert, title and category controls what the notification screen will display.

As you see above, in the “WatchKit Simulator Actions” array is holding a collection of buttons in the form of dictionary which can be used to add/remove/modify buttons in the notification.

To create a notification, create a new project and add a new Watch Target as discussed in my previous post. This time keep the “Include Notification Scene” checkbox selected to include our notification interfaces.

Screen Shot 2014-12-04 at 02.35.44

Include all the necessary app icon images. Apple could not think of any more, so they want only the following few dimensions :

  • 29 X 29
  • 36 X 36
  • 58 X 58
  • 80 X 80
  • 88 X 88
  • 196 X 196
  • 172 X 172

Xcode will generate two extra interfaces for you in the interface.storyboard (other than the usual screen for your watch app) inside your project. They are —

  • Static Interface — A notification interface that can be configured design time. It is mandatory to keep this interface in your app bundle.
  • Dynamic Interface — A notification interface that can be decorated with dynamic data in runtime. This is not mandatory.

When running the app, iOS first looks for Dynamic Interface. If not found it falls back to Static Interface. If the static interface suffices your n0tification requirement, it is safe to delete the dynamic interface. Also, it can be explicitly instructed not to show the dynamic interface.

For the time being, lets change the Static Interface. What we are trying to do here is —

  •  show a notification stating that Japan is already enjoying the new year with an action button.
  • Tapping on the “View” button the app will launch and
  • display the current time in Tokyo

Now create a new scheme for notification and map the notification executable to that scheme to view the  Watch notification in iOS simulator.

Screen Shot 2014-12-04 at 03.07.07

Screen Shot 2014-12-04 at 03.07.28

Screen Shot 2014-12-04 at 03.15.01

If you build and run your app now, the default static notification screen will show. Take into notice that the message text, button text everything is being pulled from the JSON file that is included in the bundle.  you can try changing them to see if the change takes place in the notification.

Before we do some action, lets modify the JSON file to suit our needs by changing title, message and category. (Screenshot above) Lets name them as follows:

"aps": {
        "alert": "Japan is already celebrating new year!",
        "title": "Happy New Year!",
        "category": "NewYear"
    }

Also, in the Interface.storyboard file, select the “Category” under Static Notification Interface Controller and in the attributes inspector, change it to “NewYear”. Make sure that the category names are matching in the JSON as well as in the storyboard. Otherwise, the app will not build at all.

Now we want the user to tap on the button and make something happen. Let’s add a date label to the interface of the  Watch app which will display the date based on the timezone set. Hook it to the InterfaceController as dateLabel.

Inside the Interface Controller, we can handle the notification like so:

@IBOutlet weak var dateLabel: WKInterfaceDate!
override func handleActionWithIdentifier(identifier: String?, forRemoteNotification remoteNotification: [NSObject : AnyObject]) {

        if let id = identifier {
            if id == "firstButtonAction" {

                var plistKeys: NSDictionary?
                var timeZones: NSDictionary?

                if let path = NSBundle.mainBundle().pathForResource("Timezones", ofType: "plist") {
                    plistKeys = NSDictionary(contentsOfFile: path)!
                    timeZones = plistKeys!["TimeZones"] as NSDictionary?
                }

                if let dict = timeZones {
                    NSLog("%@", dict.valueForKey("Tokyo") as String)
                    dateLabel.setTimeZone(NSTimeZone(name: dict.valueForKey("Tokyo") as String))
                }

            }
        }

    }

Now build and run the app. At first it will show your designed screen. Tapping on the dismiss button dismisses the notification screen. Tapping on “View” button shows Tokyo’s current time.

Right now, the app is showing the static notification screen. If you want to show a custom dynamic notification with data that can only dynamically be passed into the interface, we need to modify the “Dynamic Interface”. The Dynamic Interface is controlled by the NotificationController.swift. If you navigate there, you will find two functions commented out

override func didReceiveRemoteNotification(remoteNotification: [NSObject : AnyObject], withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void))

override func didReceiveLocalNotification(localNotification: UILocalNotification, withCompletion completionHandler: (WKUserNotificationInterfaceType) -> Void)

Uncomment the

didReceiveRemoteNotification(remoteNotification: [NSObject : AnyObject], withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void))

and make sure that the completionHandler is set to be .Custom

override func didReceiveRemoteNotification(remoteNotification: [NSObject : AnyObject], withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void)) {
        
        // Tell WatchKit to display the custom interface.
        completionHandler(.Custom);
    }

Now if we make any modification to the dynamic interface, you will see that the dynamic interface with its changes is being shown as the notification screen. This is because, as I mentioned earlier, iOS searches for the custom dynamic interface first. If it can not find one, then only it loads the static one.

Try changing the .Custom to .Default to see your static interface.

You can download the whole project from Github — https://github.com/an-indya/WatchNotifications

Hope you will enjoy building for  Watch as much as I did. I will try putting in something more as I learn. Please do leave a reply and feel free to share if you like!

Hope this helps!