{"id":1943,"date":"2015-04-21T21:28:33","date_gmt":"2015-04-22T01:28:33","guid":{"rendered":"http:\/\/www.rexfeng.com\/blog\/?p=1943"},"modified":"2015-04-21T21:28:33","modified_gmt":"2015-04-22T01:28:33","slug":"pomodoro-pro-watchkit-architecture","status":"publish","type":"post","link":"https:\/\/www.rexfeng.com\/blog\/2015\/04\/pomodoro-pro-watchkit-architecture\/","title":{"rendered":"Pomodoro Pro WatchKit Architecture"},"content":{"rendered":"<p><em>My iOS app, <a href=\"https:\/\/itunes.apple.com\/us\/app\/pomodoro-pro\/id966415847?ls=1&amp;mt=8\">Pomodoro Pro<\/a>, is a constant work in progress. This post discusses how Apple Watch support was added to v1.1.0. Pomodoro Pro is a free, easy to use app\u00a0for getting work done in continuous work &amp; break cycles.<\/em><\/p>\n<p>If you&#8217;ve looked into Apple Watch development, you&#8217;ve learned that the watch portion is an extension of your phone app. Your phone app is the brains of the operation, and your\u00a0watch app relies on the phone app to do anything.<\/p>\n<p><strong>2 Way Event Binding Demo<\/strong><\/p>\n<p>From watch to phone<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-1946 size-full\" src=\"http:\/\/www.rexfeng.com\/blog\/wp-content\/uploads\/2015\/04\/watch-final.gif\" alt=\"watch-final\" width=\"688\" height=\"458\" \/><\/p>\n<p>From phone to watch<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-1960 size-full\" src=\"http:\/\/www.rexfeng.com\/blog\/wp-content\/uploads\/2015\/04\/phone_8.gif\" alt=\"phone_8\" width=\"686\" height=\"474\" \/><\/p>\n<p><strong>App Lifecycle<\/strong><\/p>\n<p>With mobile development, it&#8217;s necessary to understand when a user will come into contact with your application. If\u00a0the user comes across your application and starts using it when the visuals are incorrect, the user will lose confidence and not trust your application.<\/p>\n<p>Working with Xcode and the watch simulator, my app has the following key lifecycle events:<\/p>\n<ul>\n<li>watch\u00a0starts up<\/li>\n<li>watch resumes<\/li>\n<li>watch actions (button presses) are reflected on both the watch &amp; phone<\/li>\n<li>phone actions (button presses) are reflected on both the phone &amp; watch<\/li>\n<\/ul>\n<p>Each of these events are\u00a0essential for maintaining a consistent user experience that does not surprise the user.<\/p>\n<p><strong>Implementation<\/strong><\/p>\n<p>My implementation used <a href=\"https:\/\/github.com\/mutualmobile\/MMWormhole\">MMWormhole<\/a> for 2 way event\u00a0binding. This means that the phone should know when a button was pressed on the watch and vice versa. <a href=\"http:\/\/blog.curtisherbert.com\/data-synchronization-with-watchkit\/\">Curtis Herbert has a great blog post on sharing data and events<\/a>.<\/p>\n<p>In order to have the phone app be responsible for the correct timer state,\u00a0my phone app is responsible for returning a NSDictionary of the current timer properties. My watch app is able to get\u00a0the NSDictionary and update the watch screen accordingly. It is important to note that\u00a0my phone app is the ONLY source of the canonical current timer state. Trying to sync state between the phone and watch app would be a nightmare and a lot of work.<\/p>\n<p>When the watch app starts, the watch app asks the phone for the current timer state (a NSDictionary) in order to set itself up correctly.\u00a0When the watch app resumes from inactivity, it also asks the phone for the current timer state (a NSDictionary).<\/p>\n<p>Using\u00a0MMWormhole, I&#8217;m able to listen on events from the watch or the phone. When the user presses a button on the watch, the watch app passes a message to notify the phone,\u00a0and the phone updates the timer state. Similarly, when the user presses a button on the phone, the phone app updates the timer state and notifies the watch app with the latest state.<\/p>\n<p><strong>Lessons Learned<\/strong><\/p>\n<ol>\n<li>The phone app (not the watch app) is responsible for the correct state<\/li>\n<li>Use a Framework or shared classes (in File Inspector, add Target Membership) to DRY (don&#8217;t repeat yourself) out your codebase<\/li>\n<\/ol>\n<p><strong>Summary<\/strong><\/p>\n<p>With Pomodoro Pro, it was essential for a user to be able to start \/ pause \/ resume \/ stop the timer from either the phone app or the watch app. This required a way to manage the timer state (phone app) and have user actions occur on all screens (phone &amp; watch).<\/p>\n<p>In order to build a user friendly watch app, you should anticipate &amp; identify where your app&#8217;s state and events\u00a0come from. Make sure to account for\u00a0those scenarios.<\/p>\n<p><em><a href=\"https:\/\/itunes.apple.com\/us\/app\/pomodoro-pro\/id966415847?ls=1&amp;mt=8\">Pomodoro Pro v1.1.0<\/a> went live on 4\/14\/2015. Please let me know what you think <a href=\"https:\/\/twitter.com\/rexfeng\">@rexfeng<\/a><\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>My iOS app, Pomodoro Pro, is a constant work in progress. This post discusses how Apple Watch support was added to v1.1.0. Pomodoro Pro is a free, easy to use app\u00a0for getting work done in continuous work &amp; break cycles. If you&#8217;ve looked into Apple Watch development, you&#8217;ve learned that the watch portion is an [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1029],"tags":[127,982,619,966,1213,1214,1212],"class_list":["post-1943","post","type-post","status-publish","format-standard","hentry","category-programming","tag-architecture","tag-demo","tag-design","tag-ios","tag-pomodoro","tag-pro","tag-watchkit"],"_links":{"self":[{"href":"https:\/\/www.rexfeng.com\/blog\/wp-json\/wp\/v2\/posts\/1943","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rexfeng.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rexfeng.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rexfeng.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rexfeng.com\/blog\/wp-json\/wp\/v2\/comments?post=1943"}],"version-history":[{"count":9,"href":"https:\/\/www.rexfeng.com\/blog\/wp-json\/wp\/v2\/posts\/1943\/revisions"}],"predecessor-version":[{"id":1967,"href":"https:\/\/www.rexfeng.com\/blog\/wp-json\/wp\/v2\/posts\/1943\/revisions\/1967"}],"wp:attachment":[{"href":"https:\/\/www.rexfeng.com\/blog\/wp-json\/wp\/v2\/media?parent=1943"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rexfeng.com\/blog\/wp-json\/wp\/v2\/categories?post=1943"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rexfeng.com\/blog\/wp-json\/wp\/v2\/tags?post=1943"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}