WatchKit 开发注意事项

501 查看

从一月中旬开始,我就开始全职开发我新的 WhereNotes App 的 Apple Watch 部分。我非常幸运地被邀请到位于库比提诺的 Apple Watch 实验室。 在过去的三个半月里,我收集了很多小贴士和技巧,这篇文章中会提到很多。我希望之中的有些东西能够在 Apple Watch 开发上帮到你们。

  • 也许你熟悉 UIApplication 中诸如 applicationWillEnterForeground:applicationDidEnterBackground: 的方法(这两个方法分别在对应的 UIApplicationWillEnterForegroundNotificationUIApplicationDidEnterBackgroundNotification 前被调用),那么在 WatchKit 中鲜为人知的等效的 NSExtensionContext 通知是:

  • 我个人经验和苹果开发者论坛的帖子都表明:如果你的 Apple Watch 处于充电状态,测试和 debug 会更容易进行,同时也更佳稳定。
  • 不像在 iOS 中你在任何时候都能更新用户界面,在 Apple Watch 里,只有属于目前处于活动状态或者可见的视图控制器的元素才可以被更新。在 didDeactivate 被调用之前对界面的更新是安全的(didDeactivate 中不能更新界面),这意味着如果你想要更新一个目前处于隐藏状态的视图(比如被一个最上面的视图挡住了),你需要在处于最上面的视图被关闭的时候,也就是下面的视图控制器的 willActivate 被调用的时候再更新界面。
  • 除了预置在 WatckKit 程序包中的素材,每一个 app 可以额外加入最多为 5MB 的图片素材,这些素材可以通过 app 扩展中的 WKInterfaceDevice 的一些方法来生成和管理。从 app 扩展发送图片到 Apple Watch 耗费时间和电力,如果你打算重复使用一张图片(哪怕是一次),你都值得去把它缓存起来。如果你用 addCachedImage:name: 来发送图片,无论 PNG 是不是最好的选择,由于以 PNG 发送是最保险的,这张图片会自动转换为 PNG 编码然后发送到缓存中。如果你的图片可以用 JPG 编码,我强烈推荐你使用 addCachedImageWithData:name:,同时把你的图片用 JPG 编码,并尝试设置图片质量。这不仅能够让图片传输得更快,还能节省很多储存空间,这样你就能放下更多图片。
  • 接着上一条建议,记住你可以用后台进程来缓存图片的(苹果的职员在开发者论坛这么说的)。在我的 Watch app 中我用这个技巧在图片需要之前就把它缓存进去。
  • 如果你要用到上面提到的图片缓存,没有什么内置的方法可以帮你找出哪些是最老的图片从而让你删除掉。如果你的 app 涉及到大量的图片,你也许会考虑再封装一层来管理图片缓存。
  • 要测试通知的话在 iPhone 上的 Apple Watch app 中先把通用设置中的手腕检测关掉。
  • 长按侧面的按钮(译者注:直到电源关闭界面出现松手),然后再长按一次,这样就可以强制关闭你的 app (这样并不会把你的扩展关掉)。
  • 减少 willActivate 中的处理量来节省加载时间。
  • 想想假如用户先启动 Watch 上的 app 再启动 iPhone 上对应的 app 会发生什么,设计的时候考虑到这点,否则就让苹果审核人员来发现问题。
  • 记住你的 Watch app 是作为一个扩展来运行的,它的内存限制比 iPhone 大得多。如果你要处理大型的图片,最好还是通过 openParentApplication:reply: 交给 iPhone 来处理。另外你要知道在模拟器中并没有这些内存的限制,所以你必须在 Apple Watch 上真机测试。
  • 如果你想检查你的 app 是不是和 Watch 配对了,在共享NSUserDefaults(用 shared app group) 里设置一个 BOOL 值这样在 iPhone 上也能够访问到这个值。
  • 在 iPhone 和 Watch 中同步数据,要么通过 openParentApplication:reply: 来呼叫 iPhone,要么用 Darwin 通知 功能来 Watch 和 iPhone 之间传递信息。Darwin 通知功能不支持装载数据,如果你想用这个通知来传递数据,去看看 MMWormhole 项目。
  • 当你使用计时器(包括在 willActivate 中)来更新用户界面的时候,你要知道如果你的数据源支持的话你也可以使用 KVO 来完成。我就是这么做的。用这个方法界面元素只在被更改的时候才更新,剩下了不必要的通信也延长了电池使用时间。
  • 如果你要追踪视图控制器的话,考虑一下在 awakeWithContext: 中把引用传递给self,从而建立联系。另外通过 JBInterfaceController 的子类 把这个方法沿用到了我自己的 app 中。这样的技巧让你让你能够像 UIViewController 一样来设置 delegate(委托)。
  • 一个 WatchKit 的扩展被看成是前台的扩展[参考],所以如果你需要访问 地理位置的权限 的话你只需要 请求『使用期间』权限
  • 除非迫不得已,想想自己是不是真的必须要保持 iPhone 和 Watch 的实时更新同步。用户通常不会同时使用两个设备,所以你可以当下次 iphone 或 watch 的 app 被激活后刷新数据,从而避免很多这样的频繁同步。然而不幸的是,看到两个模拟器在一个屏幕上难免会诱导你去写那些复杂的同步逻辑。嗯,也许我被诱导过,谁知道呢。
  • 当你不能用代码创建视图控制器和控件的时候,你可以聪明地通过显示和隐藏界面元素来达到目的。在 WatchKit 的开发中这样做很常见,例如在一个页面中,重要事情发生的时候一个标签可以从隐藏变为不隐藏从而被显示出来。或者,如果你有两个布局你想通过程序来选择要哪个,你可以把它们放在根部的容器中,然后显示或者隐藏他们。
  • 记住每一次触摸屏幕和更新内容都会涉及到 Watch 和 iPhone 之间的来回通信。按照这一点来开发程序。由于 WatchKit 中的界面元素只能够写不能读(因为只有 setter ),所以追踪你给它设置的变量的值是很有用的,这样可以避免重复设置。WatchKit 会试图合并在这个运行周期中你设置的所有值,然后发送最后一次的值的方法来帮助解决这个问题,但是你仍然可以通过追踪那些变量来更好地实现这个。
  • 由于没有内置的活动指示器空间,当耗时较长的一些任务(发送图片或者下载任务)在执行的过程中,你可以显示一系列的动图。任务完成后只用简单地把动图隐藏即可。2015年3月5日补充:我在 GitHub 上建立了 JBWatchActivityIndicator 项目,让创建活动指示器动图变得简单。里面也包括了一些渲染好的苹果风格动图。
  • 请确保你下载并阅读了 Apple Watch 设计资料。除了一些有用的颜色以及大小推荐,里面还有高质量的底座图片(bezel images)素材(Apple Watch 底座),你可以用它加上截屏来做市场宣传。回到这篇文章的主题,你提交的截图是不能带有底座背景的。很多开发者感到很失望,因为他们发现图片在模拟器上显示得好好的在真机上却不这么样。事实上这也是为什么很多 app 通过不了审核的原因。这个问题和图像的命名和『松散』的图片文件有关。最保险的方法是把所有的图片都放在 Watch App 的资源库中(不是扩展的资源库)。我就是这么做的,我也推荐你这么做。
  • 虽然普遍都这么请求,但是没有办法在 Apple Watch 上来程序性启动 iPhone 上的 app 使之运行在前台(就算在模拟器中有些方法能这么做)。考虑使用『连续互通(Handoff)』这个特性。
  • 如果你需要让两个视图控制器互相传递信息,而且你无法使用 awakeWithContext: 来完成它,试试发送 NSNotifications 吧,这在扩展中能够达到目的。或者你可以试试我的 JBInterfaceController 子类 配上delegate(委托)来完成。
  • 本地通知需要设置 soundName 属性 来生成 Taptic 反馈和语音报时。
  • 模拟器是个好的开始,但是在真机上测试程序是非常重要的(相比开发 iPhone 和 iPad app 重要得多)。
  • Good luck with your Watch app!
  • 祝你开发 Watch app 好运。