📜 ⬆️ ⬇️

Write under that, I don’t know what: development features for Apple Watch using Mail.Ru Mail as an example



Hi, Habr! We recently updated the Mail.Ru Mail app for iOS . Now it supports Apple Watch. Today I want to tell you what we encountered when developing an application for the hours that had not yet been released at that time, and talk about how to cope with the minimalism of the SDK and the inability to test the application on a “live” device.

Features of the development interface for hours


Writing code for the clock is not much different from writing code for iOS - after all, all the logic is performed on the phone. However, when it comes to the development of the user interface, everything becomes more complicated: there are new classes for watches that are used only in them. At the moment there are only 11 of these classes - but here you can remember the first version of iOS, which also had few features. We all know how their number has grown in subsequent versions, and we can hope that the SDK for watches has a similar fate. In the meantime, the realities of developing UI for watches are as follows:

Use only components for hours


The very first disappointment that you encounter when developing an interface for a watch is that you have to forget about all the beautiful buttons, animations, screens that you have previously developed for your phone. They can not be applied again, because the interface uses only visual components for watches.
')

New model layout


Relatively recently, Apple introduced a new layout for its phones. Previously, it was possible to set specific coordinates of various elements and the rules by which elements were stretched when the screen was stretched. But starting with iOS 6, a new model called Auto Layout has appeared, allowing you to set all these rules more flexibly.

One would expect that Auto Layout could also be used on the clock - however, a third, completely new layout model appeared in the clock SDK. It resembles the one used in the Android SDK (the entire interface consists of containers that are filled with elements vertically or horizontally).

Rebuilding while hiding


An interesting feature that was not in the iOS SDK: when you hide an element, it does not just disappear from the screen - all other elements take its place, and there is no empty space on the screen. Thanks to this, it is possible to place several on one screen at once, with the possibility of dynamic switching between them.

In general, in the clock SDK there are several possibilities for switching between screens: the screen animatedly pops up from the bottom or leaves the side animatedly (multi-page interface). Switching emulation when hiding elements is an unofficial, undocumented, but rather interesting alternative, which allows you to change the screen without animation.

Context instead of controller interface


If in the iOS SDK we could initialize the ViewController ourselves and assign any properties to it, then the SDK for the clock creates the interface controller (WKInterfaceController) done by the system and cannot be accessed from the code. Instead, the developers received a new parameter called context, which can be passed to the controller when it is displayed. Inside the controller, it will be available in the awakeWithContext method. Through context, you can pass the same parameters that we used to pass when creating: for example, the blocks that the child controller will have to call to pass information to the parent, the message ID to open, etc.

Animation


The clock greatly reduced the possibility of creating animations. There are no CoreAnimation and UIKit frameworks with which animations can be created programmatically. The only possibility to animate something on the clock is to create a sequence of pictures, something like an animated gif-ki.

On the one hand, this method loses its flexibility, because we cannot dynamically set animation parameters in the code. On the other hand, it may be more convenient for programmers, because designers fully develop animation and provide it in a ready-made form - as a sequence of pictures that can only be reproduced (in a circle, one-time or even not completely, that is, only some frame sequence).



Glance


Glance (aka “Preview”) is a small screen that any application can create to display some information on it. For example, we show on it the number of unread letters and the name of the sender of the last unread letter. Creating an interface for Glance is limited to a set of layouts. Options in this set is enough, but still have to choose from the proposed.

Recommended Font Styles


A set of recommended styles has appeared in the interface editor when defining a text font. Previously, it was possible to select the system font, its size and style (Italic, Bold, etc.). Now there are templates, such as Body, Title, etc. The use of styles is optional, but the opportunity itself, we hope, will contribute to the fact that developers will use it, and the interfaces will look more uniform.

Overlay elements


Unlike iOS, watches do not allow imposing elements on each other. The only way to get around this limitation is to set the background of the group in the form of a picture and insert an element into the contents of the group. In the Mail.Ru Mail application, this workaround is used to display the notification screen of a new letter (sender's avatar, on top of which text is displayed — the subject of the letter and the sender; so that the text can be seen, a gradient is under it).



Attributed String Support


Of the nice things that we were surprised to find in the clock interface, it is worth mentioning the support of Attributed String in labels. Because of this, we have the opportunity to draw text in which the individual fragments will be formatted in different ways. As an example, we can cite the same Glance: we select a part of the text with a different color, and another part with a larger font.



Force Touch and Digital Crown


A new gesture called Force Touch appeared in the watch, that is, a “hard press”. The Apple Watch also inherited the Digital Crown wheel from the usual watches. I combined these elements into one point, because both the Digital Crown and Force Touch are interface controls with which there is no complete clarity. It seems that Apple is not sure how they will be used. So, the Digital Crown is used somewhere to scroll the list, somewhere to scroll, and somewhere to move between Storyboard elements.

If the behavior of Digital Crown is beyond the control of developers and it remains to be content with “hanging up” on it at Apple, then with Force Touch we decided to wait and did not include its support in the first version of the Mail application.

Image cache


The mechanism of caching images on the clock is rather peculiar, but it is quite logical, if we recall what exactly is the application for the clock. In short, it consists of several parts: the main “host” application, WatchKit Extension — an extension for a watch that runs on the phone — and a watch application that runs directly on the watch.

When you add a picture to the clock screen, the following happens. The main logic is launched on the phone, which forms a picture - for example, downloads it from the Internet. The picture is given via Bluetooth or Wi-Fi on the clock. Further features of the watch begin. In normal caching, we simply upload the image, show it to the user and put it in the file until the next time. With the clock, we can’t just put the image into a file on the phone, because then we’ll have to transfer it to the watch every time. In this case, the clock has its own cache of 20MB.

It is interesting that you can put an image in the clock cache, but only the standard control can get it from there. And, as far as we know, there are only indirect signs by which we can understand whether there is a picture in the cache. For example, using API methods that let you know the size of the cached image. This is done as follows: we ask what are the sizes of the file with the name of the supposedly cached image. Depending on whether the dimensions return to us or not, we can conclude whether this image is in the cache, or it will be necessary to upload it first.

No applicationDidFinishLaunching and applicationDidResignActive


An iOS application has its own life cycle. It starts, goes to the background, unloads from memory, and so on. When these events occur, the application receives the appropriate alerts. There are no alerts on the clock, which somewhat complicates the creation of objects at the stage of launching the application. In addition, there may be problems with third-party libraries whose work is tied to these alerts. For example, we are faced with the fact that the Flurry Statistics Library does not send events to the statistics to the server precisely because it does not receive notifications.

The problem is due to the fact that, unlike an iOS application with a single entry point, where you can initialize everything once, there are three input options in the clock application: Glance, Notification and Main App. However, in all three cases, the launch of the clock application is associated with the display of the first controller. This allows us to use the awakeWithContext method.

@implementation MRWKContactsInterfaceController - (void)awakeWithContext:(id)context { [super awakeWithContext:context]; [MRWKInitialization extensionDidFinishLaunching]; ... } @implementation MRWKInitialization + (void)extensionDidFinishLaunching { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self internalExtensionDidFinishLaunching]; }); } 

In addition, you can use the initialize method in one of the extension classes that will be executed when you first access this class.

You can also use "lazy" initialization, when objects are created only when accessing them. But keep in mind that in the case of statistics this does not help - for example, Flurry needs to know when exactly the application was launched.

Interaction with the main application


The capabilities of the application for hours are limited, and to perform actions that are not supported in it, we can call the main application. Consider ways to do this.

OpenParentApplication


A new API has appeared - +[WKInterfaceController openParentApplication:reply:] , which allows you to call the main application from the clock application, which then starts in the background, performs the requested actions and returns the result.

Example:

 @implementation WKInterfaceController (MRWKParentApplicationActions) + (void)mrwk_markMessageIdAsRead:(NSString *)messageId accountName:(NSString *)accountName completionBlock:(MRWKParentApplicationActionsCompletionBlock)completionBlock { MRWKParentApplicationAction *action = [MRWKParentApplicationAction markAsReadActionWithMessageId:messageId accountName:accountName]; [self openParentApplication:action.dictionary reply:^(NSDictionary *replyInfo, NSError *error) { if (completionBlock) { MRWKParentApplicationReplyInfo *info = [[MRWKParentApplicationReplyInfo alloc] initWithDictionary:replyInfo]; completionBlock(info.success, error ?: info.error); } }]; } @end 

Application groups


The second way of interaction is Application Groups. On iOS, all applications and extensions are executed in separate sandboxes - limited parts of the file system that only they themselves have access to. To allow applications to use shared resources or share files, there are Application Groups. If you assign a common Application Group to the application and the extension for the clock, a folder will appear in the file system, to which both will have access. We use this feature, in particular, so that the Mail.Ru Mail application for watches has access to the database of the main application (all emails, contacts, etc.) and to the NSUserDefaults object (various user settings).

Darwin Notifications


Darwin notifications are a way to exchange messages between processes in iOS. This possibility has existed for a long time, but it has become relevant with the advent of extensions - in particular, the extensions for watches. With the help of Darwin notifications, you can send a notification to your watch if something changes in the main application - for example, if the contact list is open on the watch and a new contact is added in the main application. Darwin notifications allow you to send an event from the main application so that the clock updates the list of contacts from the database.



Handoff


The latest version of iOS has the Handoff feature, which Apple is actively promoting. Handoff can also be considered as a way to interact with the main application. Here, the interaction will not be direct data exchange, for example, the creation of activations from the clock, so that when you open the main application, a certain screen is immediately loaded. Suppose if a user opened Mail.Ru Mail and started writing a letter on the clock, and then launched the application on the iPhone - Handoff allows you to automatically automatically open the letter creation screen on the phone.

Development without device


The most important reef of development for Apple Watch is the impossibility to understand how what we write will work on a real device. We see only a simulator, which is only some of its approximation. Even in official documentation there are inconsistencies with reality.

Receive multiple notifications


Some aspects in the simulator are not reflected in principle. For example, the following case is described in the documentation: when several notifications come, a round application icon and the text “N new notifications” appear on the screen. However, the simulator does not allow to see how it will look. It would seem a trifle, but in practice this may turn out to be unexpected: the name of the application will not be the same as we expect, the icon will be displayed incorrectly - and it is almost impossible to predict. The only thing that can be done to minimize risks is to follow the documentation in absolutely everything.

Emoji


At the presentation, Apple said that the clock will have beautiful animated emoticons that can be customized. Our application supports them - smiles can be added through the standard screen of writing a letter. At the same time, it is not known how they will look in the letter, in what format they will come into the program and how they should be converted in order to be inserted into the letter. There is an assumption that these will be ordinary emoji characters, but it is also possible that they will come in the form of animated gif-ki or something else not previously known. The format is also not specified in the documentation. We decided in the first version of the Mail application to proceed from the assumption that smiles will come in the form of text.

Voice dialing


Another ambiguity associated with writing a letter - the work of voice dialing. In general, everything is clear: click on the microphone, we say, we get the entered text. But even here nuances are possible - for example, not one line of text can be returned, but several variants of what the user dictated.

Unauthorized user


The following ambiguous case: suppose a person installed the Mail.Ru Mail application on the phone, but never launched it and, as a result, did not log in. Will there be an application available on the clock in this case, will the user be able to start it from the Apple Watch display? We will know the unequivocal answer only after the clock is released, and yet we decided to make sure in case the launch is possible: the message “Open the application on the phone to log in” will be displayed to an unlocked user.

Handoff


It was announced that the Handoff will be maintained on the clock, and the documentation shows the same thing. However, it is not yet possible to check how this works, since the simulator does not support Handoff. And again we have to wait for the real device.

Static / dynamic notification


When push notifications arrive, the clock first tries to display a dynamic notification, that is, a more complex interface. If the download is delayed, a simpler interface appears - static notification. Unfortunately, using a simulator to see how this happens is impossible.



Notifications: "division of labor" between the phone and the clock


Some actions are connected with notifications: we can reply to the letter, delete it, mark it as read. Some of these actions are handled by the clock extension, others by the main application. We can not test on the simulator actions that are processed by the main application. You can call these actions directly from the phone, but if we try to call them from the clock, the simulator will try to process them in the extension for the clock, while in real life they will be processed by the application. This behavior is due to the fact that the simulator does not support push-notifications in principle - neither for the phone, nor for the clock. However, in the clock SDK, it was possible, when launching the clock application from the development environment, to “slip” the contents of the push notification and check how it will be processed. The problem is that there is no way to test all possible cases. For example, it is impossible to specify whether the notifications will be processed by the main application or by the extension for the clock. I had to restrict myself to checking those actions that are processed by the application, through the call of actions from the notification on the phone (in a real device), and hope that on the clock it will work the same way.

Xcode Development


Everyone who works with Xcode knows that there are errors in it - and in every new version there are fresh instances. The release of WatchKit SDK is no exception.

Certificates for distribution


Recently, Xcode headed for automatic code signing and provisioning. In theory, Xcode creates for you all the profiles, the necessary certificates, Bundle ID. In practice, she does this in some cases, but not in all. In particular, we are faced with the fact that distribution-profiles for watches are not automatically created, they have to be created independently on the Apple portal for developers.

Setting individual rounding radii for 38/42 mm


In the interface builder, it became possible to set individual properties of elements for clocks of 38 and 42 mm in size, but in some cases this does not work. For example, if you set your own rounding radius for a group for different versions of clocks, nothing is rounded at all. We bypassed this bug by setting the rounding through the code, and not through the graphical interface editor.

RemoteInterfacePrincipalClass


In the plist of the clock application, new properties have appeared, in particular, the RemoteInterfacePrincipalClass property of an incomprehensible purpose. On the one hand, it seems to be set, on the other hand, judging by experience, the value of this property does not affect anything. We entered there the correct class name in case it actually “pops up” on a real clock.

SPErrorGizmoInstallNeverFinishedErrorMessage


There are problems in the simulator. Sometimes, if you start one application for a clock, work with it, stop, and then try to start another, the simulator refuses to install it and gives an error with a strange name from the subtitle. It is treated only by removing the simulator, resetting the entire contents and a full clean project.

Code separation between application and extension


Finally a couple of words about process optimization. The Mail.Ru Mail application and extension for watches perform similar functions: both allow you to see a list of contacts, send a letter, etc. Therefore, it makes sense to reuse part of the code from the main application in hours. Below is a list of actions that allow to increase the share of the total code.

Conclusion


If we summarize our experience, we can say that we have identified for ourselves the following (rather obvious) basic rules of development without a device:

I think, here, on Habré, there are quite a few people who have already faced the need to develop a device that, for one reason or another, cannot be held in their hands. In the comments I propose to share experiences on how to make your life easier in such cases.

Source: https://habr.com/ru/post/256503/


All Articles