29 Mar

WatchKit tutorial: Maps on the Apple Watch

We finally have a release date for the Apple Watch! I’m looking forward to building apps with the actual device, but for now the simulator will just have to do. There’s an interesting new watch simulator called Watch Sim that will let you evaluate your Apple Watch app in real size on an iPhone or iPad, but it’s a bit pricey. If you’re not planning to get a watch though, $9.99 is a great bargain (but would you really be making Watch apps without owning an actual watch?). I find that Bezel is a pretty good substitute, but there’s no physical interaction (it’s still only on your computer screen).

Onto the tutorial!

Apple Watch Maps

If you’ve been following along, this next tutorial should be pretty simple compared to the last couple that I’ve made. We’ll be looking at displaying a map on the Apple Watch and setting both native and custom markers on it.

Map Interface
The downside is that that’s pretty much all you can do at this moment (as of the iOS 8.2 release). Users can’t even interact with these static maps – once the user taps on it, it will open up the default Apple Maps app. You can read more about map capabilities on the Apple Human User Interface Guidelines.

As always, I’ve provided all of the project code on GitHub. You’ll also need some image files – download the zip file here. Let’s get started.

Watch Interface

Create a new iPhone project and name it WatchKitMap. If you’ve never created a Watch project before, check out the beginning of this post. Remember to create a new target for the Watch app since it won’t be created for you automatically. Expand the WatchKitMap WatchKit App folder and open the Interface.storyboard. Grab a map from the sidebar and drag it into the default Interface Controller scene. Drag the sides so that it takes up the entire space of the frame.

Map IB

Click on the Map box in the storyboard and then click on the Attributes Inspector icon on the sidebar (looks like a bookmark). In the size area, change the width and height to be “Relative to Container.” That way, the map will fill all the available space no matter what size Watch the user has.

Map Attributes Inspector

The last thing you want to do is open up the assistant editor (split view, the icon with the two overlapping circles) between the watch storyboard and InterfaceController.m. Drag an outlet from the storyboard map to InterfaceController.m and name it map. That’s it for the interface.

WatchKit Extension

Since this will be a static map, the entire thing will only take 3-4 lines of code to set up the map and display the markers. We’ll be using some APIs from MapKit and CoreLocation to set up the location and zoom factor. Replace your awakeWithContext method with the following:

- (void)awakeWithContext:(id)context {
    [super awakeWithContext:context];
    
    // Determine a location to display - Apple headquarters
    CLLocationCoordinate2D mapLocation = CLLocationCoordinate2DMake(37, -122);
    //
    MKCoordinateSpan coordinateSpan = MKCoordinateSpanMake(1, 1);
    
    // Other colors include red and green pins
    [self.map addAnnotation:mapLocation withPinColor: WKInterfaceMapPinColorPurple];
    
    [self.map setRegion:(MKCoordinateRegionMake(mapLocation, coordinateSpan))];
}

CLLocationCoordinate2D allows us to define a map location by latitudinal and longitudinal values (in degrees). (37, -122) happens to be the current Apple headquarters location =).

Next, we create a coordinate span. This basically just defines the area spanned by the map region – the (1,1) corresponds to the zoom level (with smaller values = higher zoom).

The next line

[self.map addAnnotation:mapLocation withPinColor: WKInterfaceMapPinColorPurple];

adds a pin to the mapLocation we set earlier. Notice there are multiple color options here.

Purple – show a particular point
Red – show an ending location (for use in directions)
Green – show a starting location (for use in directions)

Make sure to use the correct color pin depending on your circumstance.

Lastly, we’ll set the region we just created for the map view. Save and run the watch app. You should now have something that looks like this:

Single pin watchkit map

Notice the pin actually appears in the center of the map. You can move the location of the pin by a basic offset if you wish. I’ll show you how to do that in the next step with a custom pin marker image.

Open up the zip file you downloaded earlier. In it, there should be a couple of pin marker images – go ahead and pick one. I’m using the pink flag. Drag your desired image into the Images.xcassets folder of your WatchKit extension (this will not work if you drag it into the wrong Images.xcassets folder, so make sure it’s the one for your WatchKit extension). Ideally, you should have an image for all of the sizes, but these markers are only for 2x so make sure you drag the image over to its appropriate place in Images.xcassets, otherwise it might appear too large and pixelated.

Images.xcassets

Add the following line of code in between the last two lines, substituting the name appropriately if you went with a different image:

<span class="s1">[</span><span class="s2">self</span><span class="s1">.</span><span class="s3">map</span> <span class="s4">addAnnotation</span><span class="s1">:mapLocation </span><span class="s4">withImage</span><span class="s1">:[</span><span class="s4">UIImage</span> <span class="s4">imageNamed</span><span class="s1">:</span><span class="s5">@"pink_flag"</span><span class="s1">] </span><span class="s4">centerOffset</span><span class="s1">:</span><span class="s4">CGPointMake</span><span class="s1">(</span><span class="s6">20</span><span class="s1">, -</span><span class="s6">20</span><span class="s1">)];</span>

Here you can see we were able to specify a custom image and a center offset. Run your program again at this point and you’ll see both the map markers on the screen. That’s it!

Leave any questions and thoughts in the comments and I’ll get back to you =)

19 Jan

WatchKit Tutorial: Communicate from Parent App Back to Watch via Reply

Note: this will be a continuation from my last tutorial on WatchKit – Send Data to your Parent iOS App. If you haven’t done it already, I suggest you start there first.

WatchKit Updates

It’s been about a month since my last WatchKit tutorial and we’ve had 2 new beta releases for iOS 8.2, including a few changes for WatchKit. Here’s the release notes for iOS 8.2 beta 4:

watchkit iOS 8.4 beta 4 release notes

WatchKit still has pretty limited functionality and there haven’t been many new changes – it’ll be interesting to see what kinds of apps people create once it’s live. Looking back on the Apple keynote where they announced the Apple Watch and showed all the cool tricks like being able to send your heartbeat to someone else, I feel a bit cheated as a third-party developer. So far, we don’t have access to any of these interesting features. It’s like when a movie trailer shows all the cool parts of the movie, and the movie itself is pretty lame.

Reply Back to Watch app

Counter Reply

Now, to get down to business – I’ve added on to my original tutorial that allowed you to send data to your parent iOS app from the watch. We’ll be exploring replying back to the Watch app from the parent iOS app. This is useful for cases where you may want to update the UI of your Watch app or execute some code after your iOS app receives data from the Watch. In this case, we’ll be showing the user a “Saved” notification indicating that their save to the iOS app was successful.

You can see the finished project for this tutorial here on Github.

Modify Watch Storyboard

To get started, bring up the finished counter project from the last tutorial. If you haven’t gone through it yet, you can grab it here from Github.

Let’s start with the Watch interface first. We’ll be changing our original interface:

WatchKit App Storyboard File

To this:

Screen Shot 2015-01-14 at 2.29.31 PM First drag in another label to go underneath the counter value and change the color of the text to differentiate it from the counter. I set the text to “Saved N,” but it doesn’t really matter what you put here since you’ll be modifying this value later. This “Saved N” label is going to be our notification to the user, so it should be hidden until a user saves a count value. You can hide this by going to the Attributes inspector in the sidebar (it looks like a bookmark ribbon) and changing the Alpha value to 0.

Attributes inspector

Note that I’ve left the Hidden box unchecked. You could technically use the Hidden option also to hide the label, but there’s a subtle difference between changing the Alpha and setting the Hidden option.

Hidden – if turned on, hides the element and does NOT preserve element spacing
Alpha – if set to 0, hides the element and preserves element spacing

That is, the extra space taken up by the label would disappear if we turned the Hidden attribute on, but stays the same if you just turn the alpha to 0. It’s up to you which one you prefer, but the buttons will keep moving when the Saved notification label toggles visibility so I preferred just preserving the space so the buttons would stay in the same spot.

Next, to get the buttons to appear side-by-side, you’ll need to drag in a Group from the sidebar to your watch interface. Drag it in above the Hit button and you should see a gray dashed outline with the word Group in it. Resize the Hit and Clear buttons so that they’re about half the horizontal size of the screen and drag them into the group. The dashed line should disappear once you drag the first button in.

Screen Shot 2015-01-14 at 3.41.32 PM

That’s it for the watch interface! One last thing you should do is drag an IBOutlet from the (now hidden) Saved notification label into the InterfaceController.h file. I named it this:

@property (weak, nonatomic) IBOutlet WKInterfaceLabel *savedNotificationLabel;

AppDelegate.m – Change Reply Dictionary

There aren’t many code changes you need to make from the last tutorial to this one – in fact, we already had the reply working in the previous project, but it wasn’t very useful aside from being a good sanity check that the flow was correct.

See this line in the handleWatchKitExtensionRequest method? You can find this in the AppDelegate.m file in the WatchKitCounterDemo folder.

reply(@{@"insert counter value":@(1)});

In the original tutorial, we logged the key and value out of the dictionary that was sent through this reply. You can see this in the saveCounter method in the InterfaceController.m file:

NSLog(@"%@ %@",replyInfo, error);

We’re going to change this reply to include something that’s a bit more useful. Replace your current handleWatchKitExtentionRequest method with this one:

- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void(^)(NSDictionary *replyInfo))reply {
    
    NSString *counterValue = [userInfo objectForKey:@"counterValue"];
    NSDictionary *replyDict = @{@"response": counterValue};
    
    reply(replyDict);

    if (!self.tempCounterData) {
        self.tempCounterData = [[NSMutableArray alloc] init];
    }
    
    [self.tempCounterData addObject:counterValue];
    
    //Add the new counter value and reload the table view
    AppDelegate *tmpDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    ViewController *vc = (ViewController *)((UINavigationController*)tmpDelegate.window.rootViewController).visibleViewController;
    
    vc.counterData = self.tempCounterData;
    [vc.mainTableView reloadData];

}

Now we’re sending a dictionary with “response” as the key and the counter value as the value. It’s a little redundant, I admit, since the watch extension already has this value, but the point is to show how we can send useful information back to the extension.

Great, that’s done. Now let’s move onto your InterfaceController.m file.

Accept the response – InterfaceController.m

Replace your InterfaceController.m code with this:

//
//  InterfaceController.m
//  WatchKitCounterDemo WatchKit Extension
//
//  Created by Thai, Kristina on 12/10/14.
//  Copyright (c) 2014 Kristina Thai. All rights reserved.
//

#import "InterfaceController.h"


@interface InterfaceController()

@property (nonatomic, assign) int counter;
@end

@implementation InterfaceController

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

    // Configure interface objects here.
    NSLog(@"%@ awakeWithContext", self);
    self.counter = 0;
}

- (void)willActivate {
    // This method is called when watch view controller is about to be visible to user
    NSLog(@"%@ will activate", self);
}

- (void)didDeactivate {
    // This method is called when watch view controller is no longer visible
    NSLog(@"%@ did deactivate", self);
}

#pragma mark - Button actions

- (IBAction)incrementCounter {
    [self hideSaveNotificationLabel];
    
    self.counter++;
    [self setCounterLabelText];
}
- (IBAction)saveCounter {
    //Send count to parent application
    NSString *counterString = [NSString stringWithFormat:@"%d", self.counter];
    NSDictionary *applicationData = [[NSDictionary alloc] initWithObjects:@[counterString] forKeys:@[@"counterValue"]];
    
    //Handle reciever in app delegate of parent app
    [WKInterfaceController openParentApplication:applicationData reply:^(NSDictionary *replyInfo, NSError *error) {
        
        int reply = [[replyInfo objectForKey:@"response"] intValue];
        
        //Show and change text for hidden save notification label
        [self.savedNotificationLabel setText:[NSString stringWithFormat:@"Saved %d", reply]];
        [self showSaveNotificationLabel];
    }];

}
- (IBAction)clearCounter {
    [self hideSaveNotificationLabel];
    
    self.counter = 0;
    [self setCounterLabelText];
}

#pragma mark - Helper methods

- (void)setCounterLabelText {
    [self.counterLabel setText:[NSString stringWithFormat:@"%d", self.counter]];
}

- (void)hideSaveNotificationLabel {
    [self.savedNotificationLabel setAlpha:0];
}

-(void)showSaveNotificationLabel {
    [self.savedNotificationLabel setAlpha:1];
}

@end

I’ve added in some helper methods to show and hide the SavedNotificationLabel. The main piece you should pay attention to is in the saveCounter method.

- (IBAction)saveCounter {
    //Send count to parent application
    NSString *counterString = [NSString stringWithFormat:@"%d", self.counter];
    NSDictionary *applicationData = [[NSDictionary alloc] initWithObjects:@[counterString] forKeys:@[@"counterValue"]];
    
    //Handle reciever in app delegate of parent app
    [WKInterfaceController openParentApplication:applicationData reply:^(NSDictionary *replyInfo, NSError *error) {
        
        int reply = [[replyInfo objectForKey:@"response"] intValue];
        
        //Show and change text for hidden save notification label
        [self.savedNotificationLabel setText:[NSString stringWithFormat:@"Saved %d", reply]];
        [self showSaveNotificationLabel];
    }];

}

In this case, I know that my response will contain my counter value (normally, this is not the safest thing to do – if there’s not a “response” key in your dictionary, this will crash your app).  I grab this value out of the reply dictionary and I set the text for my Saved label to “Saved” + the counter value. This will un-hide the savedNotificationLabel and notify the user that their save went through successfully, along with the value that they’ve just saved.

That’s it! Run the app and you should see the notification come up every time you hit the Save button on the watch. The notification will hide when the user increments the counter or if they clear the counter.

One thing to note here, in the original tutorial written during iOS 8.2 beta 2, the iOS app launched when the user hit Save. This was apparently a bug and the iOS parent app will NOT launch anymore, but it will update in the background. You can click on the WatchKitCounterDemo app icon to launch the parent app and it will show all of your saved values. If the app is in the foreground, you can see the values updating like normal.

Expanding this to more variable situations

Now this tutorial was fairly simple in that it showed a reply with only one key/value pair in the dictionary from the iOS AppDelegate and only one path in the reply block back in the watch extension. For more variable purposes, you could imagine replying from handleWatchKitExtentionRequest with a large dictionary filled with many values. You could also use a switch/if & else statement in the reply block in openParentApplication that depends on the type of response you get back. The possibilities here are endless.

As always, feel free to ask any questions below. The inspiration for this post came from a comment from my last tutorial, so don’t be shy.

12 Dec

WatchKit Tutorial: Send data to parent iOS app

WatchKit beta 2 was just released as a part of the iOS 8.2 developer seed. One of the best new additions to the SDK is the ability to communicate back to the iOS parent app from the WatchKit extension. This allows us to essentially “close the loop” and pass information from the watch to the watch extension and to the iOS parent app that supports it.

I created a simple counter app for the watch which saves its count values back to a simple iOS table view app when the user hits a Save button. This tutorial will demonstrate how to transfer data to the parent iOS app from the watch extension. See the finished project on GitHub.

watchkitdemo

Now before you jump into this tutorial, I highly recommend that you read Natasha’s Hello World WatchKit tutorial first. This tutorial will assume that you’re already a little bit familiar with WatchKit (and iOS development in general).

Read More

10 Dec

Easy iOS Device Projector Demos

I recently did a talk where I live demoed some OCR functionality that I built. Essentially, I had to show what was happening on my device screen while I took a picture of a piece of paper. The main issue with this for screen reflector apps is that the refresh rate isn’t nearly as fast as the native iOS camera app – especially in web conference meetings, so you end up getting horizontal black lines all over your camera view.

Looks something like this:

Camera lines

Luckily, I found an simple and free solution for it that also makes iOS demos on a Mac computer screen much easier. You can now do demos with the new Quicktime Yosemite movie recording feature.

QuickTime shows your connected device’s screen live on your own computer screen. It makes it really easy to show whatever you are demoing your device in a much larger format. This can be very useful for presentations or meetings, rather than having a bunch of people crowd around your tiny screen or using third-party software like Reflector.

The best part is that it’s straightforward to set up. Here are the steps:

  1. Open QuickTime
  2. Connect your iOS mobile device to you computer using the lightning cable
  3. Right-click on the QuickTime icon and choose New Movie Recording
  4. At this point it’ll probably pop up with a live stream of your face since it connects to the web cam by default.
  5. Now, click on the down arrow button next to be big red recording button.
  6. Find your device name listed in the Camera section and click it. It should now show your device’s screen instead.

That’s it! At this point you can interact with your device like normal.

Just in case that wasn’t clear enough, here’s a quick video of how to do it:

The only thing that’s not as good as some of the third party apps is that this technique doesn’t show where the user is tapping on the screen. Most of the time, you’re able to just talk through where you’re tapping while you’re demoing so it shouldn’t pose too much of an issue.

26 Sep

It’s not my job to care what the product looks like, or is it?

Well it doesn’t look pretty, but we’re not designers anyway…it’s not our job.

-Software engineer intern

I overheard one of the interns earlier this summer talking about the project he was working on when he was updating his mentor on his progress. He was working as a client-side iOS engineer for the summer and didn’t seem to think it was his job to make things pretty, just functional.

While there’s a certain truth to the fact that engineers don’t have to come up with good UI/interaction designs for their products (at least at large software companies where there’s dedicated designers for this purpose), this doesn’t necessarily mean that they should turn a blind eye to their “ugly” but functional work.

For front-end/client-side engineers, there are two things that I’ve observed that the best engineers do:

Work hand in hand with their designers to come up with the best experience but DON’T hinder the designer to think big.

In general, the ideal workflow goes something like this:

  1. Based on the requirements, designer and engineering team brainstorm different ideas for the product
  2. Designer takes an initial stab at the designs (alone) with the brainstorming feedback from the team
  3. Designer sits down with engineering team to do a first pass over the user interface and user interaction mockups
  4. Designer takes team feedback and iterates on his mockups
  5. The engineering team does another pass over the new designs
  6. Steps 4 & 5 repeat until both sides are happy

As someone that’s taken a stab at being an interaction designer from an engineering background, step 2 is a particularly important step. This is what is necessary to allow a designer to dream big and not get their ideas immediately shut down by the engineering team. Engineers are generally limited by what they think is technically possible. However, I often see instances where an engineer will tell a designer “It can’t be done” and come back later and say “Wait, I think I figured it out.” They just needed a little bit of time to think (or Google around for an answer) and a bit of inspiration from the design itself.

That being said, the rest of the steps are to bring the designer back down to earth and find a common ground between the design and engineering teams. Sometimes things actually aren’t technically feasible, or there’s just not enough time to implement everything the designer wants.

Push back against designs that just aren’t good enough.

Even though your designer is probably great at his job, he doesn’t always have the right answers to everything. If there’s something that just doesn’t sit right with you as an engineer, say something, even if you don’t necessarily have a solution for the issue. Likely, others on the team will agree with you and then together, the team can find a better way of doing things than sticking with the original idea.

When both the engineers and the designers have a say in the UI, everyone will be much more invested in building a great product. Otherwise, if the product doesn’t turn out quite the way everyone envisioned it, you may have a case of the blame game on your hands – “The designer just didn’t do a great job with the interactions” or “The engineers didn’t understand me and built something completely different.”

Building great and beautiful products is definitely a team responsibility. While everyone has their own roles on the team, that doesn’t mean that as an engineer, you shouldn’t get any say in the design (if you want it). The best designs are not “thrown over the fence” and given to the engineers to implement. The best designs are iterated on, pushed and prodded, fiddled and tinkered with until it just works…for everyone.

25 Jul

How to be a speaker if you don’t have any experience

I recently did a presentation for the Intuit Girls Who Code class – it was the first of many presentations I hope to give throughout my career. Some people seem to feel that public speaking is one of those fears that is even greater than death. I’m not a big fan of being put on the spot either, but with enough preparation (and maybe a friendly starting audience), it can actually be a pretty enjoyable experience. Now, I don’t claim to know it all from just one presentation, but these were the things I used to get started.

The Start

In the beginning, the first two hurdles I saw were:

  1. Opportunity
  2. Topic

Opportunities are actually a lot easier to come by than most people think. Presentations aren’t necessarily shown to hundreds of people at a time. In fact, that’s probably the worst way to get started as a speaker. There are plenty of opportunities around in unexpected places. Here’s some examples:

  • You have a new coworker that doesn’t know how to use Git. Create a presentation and teach them all about the wonders of this versioning tool. (1 on 1)
  • You have a group of summer interns that need to come up to speed on the best coding standards for your team. Grab all that information and create a presentation that’s way more interesting than the boring wiki you normally tell new team members to read. (1 on 5)
  • Worked with some interesting new technology that not everyone on your team knows about? Present all your findings during a lunchtime/staff meeting. (1 on 10-20)
  • Go back to your alma mater and teach your 21 year old self how to do something that’s not pulling all-nighters and partying too hard. (1 on 50-100+)

You can scale your talks accordingly. Start small with friendly people that will give you great feedback…then work your way up to a larger and more intimidating audience. Once you master the few, working your way up won’t seem too bad.

Topics on the other hand…this is one that I struggled with more than finding opportunities. Especially when you’re early in your career, sometimes it’s hard to figure out what would be an interesting topic to teach others about. Topics can be too basic, too complex, too narrow, too generic…the list goes on and there’s extremes at either end.

This one is more personal – what works for some may not work for others. I took some advice from Zach Holman, who has an amazing resource on speaking. In particular, he states that anyone can be an expert on most things if they focus and narrow in on something that they’re passionate about. Even if you’re not a complete expert in that particular area, it’s really about “making your audience reconsider their own perspectives. You don’t have to be smarter than them or even be more correct than them to do that.”

Generally, chosen topics are about something that you have done or experienced personally. However,  another way to think about it is that the best way to learn something is to teach it. If there’s a particular area that you’re interested in learning more about, the best way to thoroughly understand that area is to teach it to someone else. You can’t teach what you don’t know.

Read More