Quick viewing(Text Mode)

Watchos: - Principles - Practices

Watchos: - Principles - Practices

Independent watchOS: - principles - practices

Vadim Drobinin | @valzevul Independent apps

@valzevul / drobinin.com 2 Independent apps. Just put a checkmark?

@valzevul / drobinin.com 3 Independent apps

@valzevul / drobinin.com 4 Questions?

drobinin.com | @valzevul Independent apps. Just put a checkmark? Think different.

@valzevul / drobinin.com 6 Agenda

@valzevul / drobinin.com 7 Agenda

— Back to the roots

@valzevul / drobinin.com 7 Agenda

— Back to the roots — watchOS 1..<6

@valzevul / drobinin.com 7 Agenda

— Back to the roots — watchOS 1..<6 — watchOS 6

@valzevul / drobinin.com 7 Agenda

— Back to the roots — watchOS 1..<6 — watchOS 6 — Principles

@valzevul / drobinin.com 7 Agenda

— Back to the roots — watchOS 1..<6 — watchOS 6 — Principles — Best practices

@valzevul / drobinin.com 7 Agenda

— Back to the roots — watchOS 1..<6 — watchOS 6 — Principles — Best practices — Watch App Store

@valzevul / drobinin.com 7 Agenda

— Back to the roots — watchOS 1..<6 — watchOS 6 — Principles — Best practices — Watch App Store — Summary

@valzevul / drobinin.com 7 https://paleotronic.com/2019/09/28/from-pocket-computers-to-palmtops-an-early-history-of-mobile-telecomputing/ 8 Back to the roots

@valzevul / drobinin.com 9 Back to the roots

— 13th century: eyeglasses

@valzevul / drobinin.com 9 Back to the roots

— 13th century: eyeglasses — 16th century: wearable clocks

@valzevul / drobinin.com 9 Back to the roots

— 13th century: eyeglasses — 16th century: wearable clocks — 1960s: "Beat the Dealer"

@valzevul / drobinin.com 9 Back to the roots

— 13th century: eyeglasses — 16th century: wearable clocks — 1960s: "Beat the Dealer" clock — 1975: wristwatch

@valzevul / drobinin.com 9 Back to the roots

— 13th century: eyeglasses — 16th century: wearable clocks — 1960s: "Beat the Dealer" clock — 1975: calculator wristwatch — 1984: The Organiser

@valzevul / drobinin.com 9 Back to the roots

— 13th century: eyeglasses — 16th century: wearable clocks — 1960s: "Beat the Dealer" clock — 1975: calculator wristwatch — 1984: The Organiser — 2002: bluetooth headset

@valzevul / drobinin.com 9 Back to the roots

— 13th century: eyeglasses — 16th century: wearable clocks — 1960s: "Beat the Dealer" clock — 1975: calculator wristwatch — 1984: The Organiser — 2002: bluetooth headset — 2006-2013: Nike+, Fitbit, Google Glass

@valzevul / drobinin.com 9 Back to the roots

— 13th century: eyeglasses — 16th century: wearable clocks — 1960s: "Beat the Dealer" clock — 1975: calculator wristwatch — 1984: The Organiser — 2002: bluetooth headset — 2006-2013: Nike+, Fitbit, Google Glass — 2014:

@valzevul / drobinin.com 9 Back to the roots

10 Back to the roots

— PDAs

10 Back to the roots

— PDAs — Smartphones

10 Back to the roots

— PDAs — Smartphones — Wearables

10 watchOS 1..<6

Name Apple Watch

Purpose and/or Tell time, track Function fitness

Beneficial Promote Effect(s) awareness

Harmful Potentially Effect(s) distracting

@valzevul / drobinin.com 11 © apple.com 12 watchOS 6

@valzevul / drobinin.com 13 watchOS 6

— Dedicated App Store

@valzevul / drobinin.com 13 watchOS 6

— Dedicated App Store — New APIs for developers

@valzevul / drobinin.com 13 watchOS 6

— Dedicated App Store — New APIs for developers — Software Updates without iPhone

@valzevul / drobinin.com 13 watchOS 6

— Dedicated App Store — New APIs for developers — Software Updates without iPhone — New watch faces, Noise app

@valzevul / drobinin.com 13 watchOS 6

— Dedicated App Store — New APIs for developers — Software Updates without iPhone — New watch faces, Noise app — Cycle Tracking app, Activity Trends

@valzevul / drobinin.com 13 New APIs for developers

@valzevul / drobinin.com 14 New APIs for developers

— Independent apps

@valzevul / drobinin.com 14 New APIs for developers

— Independent apps — Extended Runtime

@valzevul / drobinin.com 14 New APIs for developers

— Independent apps — Extended Runtime — Streaming audio API

@valzevul / drobinin.com 14 New APIs for developers

— Independent apps — Extended Runtime — Streaming audio API — Sign-in with Apple

@valzevul / drobinin.com 14 New APIs for developers

— Independent apps — Extended Runtime — Streaming audio API — Sign-in with Apple — Core ML and the Neural Engine

@valzevul / drobinin.com 14

Extended Runtime

@valzevul / drobinin.com 16 Extended Runtime

— Provide runtime to complete targeted tasks

@valzevul / drobinin.com 16 Extended Runtime

— Provide runtime to complete targeted tasks — Self Care

@valzevul / drobinin.com 16 Extended Runtime

— Provide runtime to complete targeted tasks — Self Care —

@valzevul / drobinin.com 16 Extended Runtime

— Provide runtime to complete targeted tasks — Self Care — Mindfulness — Physical Therapy

@valzevul / drobinin.com 16 Extended Runtime

— Provide runtime to complete targeted tasks — Self Care — Mindfulness — Physical Therapy — Health Monitoring

@valzevul / drobinin.com 16 Extended Runtime

— Provide runtime to complete targeted tasks — Self Care — Mindfulness — Physical Therapy — Health Monitoring — Alarm

@valzevul / drobinin.com 16 Extended Runtime

— Provide runtime to complete targeted tasks — Self Care — Mindfulness — Physical Therapy — Health Monitoring — Alarm — Preserve battery

@valzevul / drobinin.com 16 Extended Runtime

@valzevul / drobinin.com 17 Extended Runtime

— For non-workout activities

@valzevul / drobinin.com 17 Extended Runtime

— For non-workout activities — Move and Exercise rings not affected

@valzevul / drobinin.com 17 Extended Runtime

— For non-workout activities — Move and Exercise rings not affected — Heart rate sensor off by default

@valzevul / drobinin.com 17 Extended Runtime let runtimeSession = WKExtendedRuntimeSession()

// Must be called while app is active runtimeSession.delegate = self runtimeSession.start() runtimeSession.invalidate()

@valzevul / drobinin.com 18 Extended Runtime let runtimeSession = WKExtendedRuntimeSession()

// Must be called while app is active runtimeSession.delegate = self runtimeSession.start() runtimeSession.invalidate()

@valzevul / drobinin.com 18 Extended Runtime let runtimeSession = WKExtendedRuntimeSession()

// Must be called while app is active runtimeSession.delegate = self runtimeSession.start() runtimeSession.invalidate()

@valzevul / drobinin.com 18 Streaming audio API

@valzevul / drobinin.com 19 Streaming audio API

— HLS (via AVQueuePlayer)

@valzevul / drobinin.com 19 Streaming audio API

— HLS (via AVQueuePlayer) — Custom audio protocols

@valzevul / drobinin.com 19 Streaming audio API

— HLS (via AVQueuePlayer) — Custom audio protocols — Network.framework

@valzevul / drobinin.com 19 Streaming audio API

— HLS (via AVQueuePlayer) — Custom audio protocols — Network.framework — URLSessionStreamingTask from URLSession

@valzevul / drobinin.com 19 Streaming audio API

— HLS (via AVQueuePlayer) — Custom audio protocols — Network.framework — URLSessionStreamingTask from URLSession — URLSessionWebSocketTask (new in iOS 13 and watchOS 6)

@valzevul / drobinin.com 19 Streaming audio API

— HLS (via AVQueuePlayer) — Custom audio protocols — Network.framework — URLSessionStreamingTask from URLSession — URLSessionWebSocketTask (new in iOS 13 and watchOS 6) — AVPlayer and AVQueuePlayer

@valzevul / drobinin.com 19 Sign-in with Apple

@valzevul / drobinin.com 20 Sign-in with Apple

— Use Sign-in with Apple:

@valzevul / drobinin.com 20 Sign-in with Apple

— Use Sign-in with Apple: — WKInterfaceAuthorizationAppleIDButton

@valzevul / drobinin.com 20 Sign-in with Apple

— Use Sign-in with Apple: — WKInterfaceAuthorizationAppleIDButton — AuthenticationServices.framework

@valzevul / drobinin.com 20 Sign-in with Apple

— Use Sign-in with Apple: — WKInterfaceAuthorizationAppleIDButton — AuthenticationServices.framework — Create an app without user accounts

@valzevul / drobinin.com 20 Sign-in with Apple

— Use Sign-in with Apple: — WKInterfaceAuthorizationAppleIDButton — AuthenticationServices.framework — Create an app without user accounts — Create custom form

@valzevul / drobinin.com 20 Core ML and the Neural Engine

@valzevul / drobinin.com 21 Core ML and the Neural Engine

— SoundAnalysis.framework "Use the SoundAnalysis framework to analyze audio and recognize it as a particular type, such as laughter or applause"

@valzevul / drobinin.com 21 Core ML and the Neural Engine

— SoundAnalysis.framework "Use the SoundAnalysis framework to analyze audio and recognize it as a particular type, such as laughter or applause" — MLUpdateTask et al "A task that updates a model with additional training data"

@valzevul / drobinin.com 21 22 Principles

@valzevul / drobinin.com 23 ! Lightweight interactions

@valzevul / drobinin.com 24 ! Holistic design

@valzevul / drobinin.com 25 ! Personal communication

@valzevul / drobinin.com 26 Glanceable

@valzevul / drobinin.com 27 Glanceable — readiliy available information

@valzevul / drobinin.com 27 Glanceable — readiliy available information — no distractions

@valzevul / drobinin.com 27 Glanceable — readiliy available information — no distractions — up-to-date snapshots

@valzevul / drobinin.com 27 Glanceable — readiliy available information — no distractions — up-to-date snapshots — provides a complication

@valzevul / drobinin.com 27 Glanceable — readiliy available information — no distractions — up-to-date snapshots — provides a complication — a custom notification interface

@valzevul / drobinin.com 27 Actionable

@valzevul / drobinin.com 28 Actionable — mindful of the information

@valzevul / drobinin.com 28 Actionable — mindful of the information — what’s onscreen is always current and relevant

@valzevul / drobinin.com 28 Actionable — mindful of the information — what’s onscreen is always current and relevant — refreshes in background

@valzevul / drobinin.com 28 Actionable — mindful of the information — what’s onscreen is always current and relevant — refreshes in background — custom actions in notifications

@valzevul / drobinin.com 28 Responsive

@valzevul / drobinin.com 29 Responsive — interactions are quick

@valzevul / drobinin.com 29 Responsive — interactions are quick — minimizes the launch time

@valzevul / drobinin.com 29 Responsive — interactions are quick — minimizes the launch time — responds with immediate feedback

@valzevul / drobinin.com 29 Best practices

@valzevul / drobinin.com 30 Best practices

@valzevul / drobinin.com 31 Best practices

— Create account / Sign in

@valzevul / drobinin.com 31 Best practices

— Create account / Sign in — Request proper permissions (i.e HealthKit)

@valzevul / drobinin.com 31 Best practices

— Create account / Sign in — Request proper permissions (i.e HealthKit) — Download data directly to the watch

@valzevul / drobinin.com 31 Best practices

— Create account / Sign in — Request proper permissions (i.e HealthKit) — Download data directly to the watch — Send Push Notifications directly to the watch

@valzevul / drobinin.com 31 Best practices

The independent app can’t use WatchConnectivity as its main source of data, and must be capable of accessing information on its own a

a Ensure Your App Runs Independently, developer.apple.com

@valzevul / drobinin.com 32 Extended Runtime (self-care)

@valzevul / drobinin.com 33 Extended Runtime (self-care)

— Focused on health or emotional well-being

@valzevul / drobinin.com 33 Extended Runtime (self-care)

— Focused on health or emotional well-being — Sessions run up to 10 minutes

@valzevul / drobinin.com 33 Extended Runtime (self-care)

— Focused on health or emotional well-being — Sessions run up to 10 minutes — Exiting app ends session

@valzevul / drobinin.com 33 Extended Runtime (mindfulness)

@valzevul / drobinin.com 34 Extended Runtime (mindfulness)

— Silent or near-silent meditation

@valzevul / drobinin.com 34 Extended Runtime (mindfulness)

— Silent or near-silent meditation — Sessions run up to 1 hour

@valzevul / drobinin.com 34 Extended Runtime (mindfulness)

— Silent or near-silent meditation — Sessions run up to 1 hour — Exiting app ends session

@valzevul / drobinin.com 34 Extended Runtime (physical therapy)

@valzevul / drobinin.com 35 Extended Runtime (physical therapy)

— Stretching, strengthening, or range-of-motion exercises

@valzevul / drobinin.com 35 Extended Runtime (physical therapy)

— Stretching, strengthening, or range-of-motion exercises — Continues to run in the background

@valzevul / drobinin.com 35 Extended Runtime (physical therapy)

— Stretching, strengthening, or range-of-motion exercises — Continues to run in the background — Sessions run up to 1 hour

@valzevul / drobinin.com 35 Extended Runtime (physical therapy)

— Stretching, strengthening, or range-of-motion exercises — Continues to run in the background — Sessions run up to 1 hour — Move and Exercise rings not affected

@valzevul / drobinin.com 35 Extended Runtime (health monitoring)

@valzevul / drobinin.com 36 Extended Runtime (health monitoring)

— Requires entitlement

@valzevul / drobinin.com 36 Extended Runtime (health monitoring)

— Requires entitlement — Use respectfully

@valzevul / drobinin.com 36 Extended Runtime (health monitoring)

— Requires entitlement — Use respectfully — Continues in the background

@valzevul / drobinin.com 36 Extended Runtime (alarm)

@valzevul / drobinin.com 37 Extended Runtime (alarm)

— Monitor for the optimal time to play an alarm

@valzevul / drobinin.com 37 Extended Runtime (alarm)

— Monitor for the optimal time to play an alarm — Can be scheduled up to 36 hours in advance

@valzevul / drobinin.com 37 Extended Runtime (alarm)

— Monitor for the optimal time to play an alarm — Can be scheduled up to 36 hours in advance — Sessions run up to 30 minutes

@valzevul / drobinin.com 37 Extended Runtime (alarm)

— Monitor for the optimal time to play an alarm — Can be scheduled up to 36 hours in advance — Sessions run up to 30 minutes — Continues to run in the background

@valzevul / drobinin.com 37 Extended Runtime (alarm)

— Monitor for the optimal time to play an alarm — Can be scheduled up to 36 hours in advance — Sessions run up to 30 minutes — Continues to run in the background — Must call WKInterfaceDevice.play()

@valzevul / drobinin.com 37 Extended Runtime (alarm)

— Monitor for the optimal time to play an alarm — Can be scheduled up to 36 hours in advance — Sessions run up to 30 minutes — Continues to run in the background — Must call WKInterfaceDevice.play() — Supports haptics

@valzevul / drobinin.com 37 Streaming audio API

@valzevul / drobinin.com 38 Streaming audio API

— WKInterfaceDevice.current().supportsAudioStreaming

@valzevul / drobinin.com 38 Streaming audio API

— WKInterfaceDevice.current().supportsAudioStreaming — Caching

@valzevul / drobinin.com 38 Streaming audio API

— WKInterfaceDevice.current().supportsAudioStreaming — Caching — Reachability

@valzevul / drobinin.com 38 Streaming audio API

— WKInterfaceDevice.current().supportsAudioStreaming — Caching — Reachability — Network transitions

@valzevul / drobinin.com 38 Streaming audio API

— WKInterfaceDevice.current().supportsAudioStreaming — Caching — Reachability — Network transitions — Audio Quality

@valzevul / drobinin.com 38 Sign-in

@valzevul / drobinin.com 39 Sign-in

— Support Password Autofill:

@valzevul / drobinin.com 39 Sign-in

— Support Password Autofill: — WKTextContentType (username, password, newPassword, oneTimeCode)

@valzevul / drobinin.com 39 Sign-in

— Support Password Autofill: — WKTextContentType (username, password, newPassword, oneTimeCode) — Set up your app’s associated domains

@valzevul / drobinin.com 39 Sign-in

— Support Password Autofill: — WKTextContentType (username, password, newPassword, oneTimeCode) — Set up your app’s associated domains — Add placeholders

@valzevul / drobinin.com 39 Sign-in

@valzevul / drobinin.com 40 Sign-in

— WKAlertAction for Terms & Conditions

@valzevul / drobinin.com 40 Sign-in

— WKAlertAction for Terms & Conditions — Embed TextField

@valzevul / drobinin.com 40 Sign-in

— WKAlertAction for Terms & Conditions — Embed TextField — Use textContentType

@valzevul / drobinin.com 40 Push Notifications import WatchKit import UserNotifications class ExtensionDelegate: NSObject, WKExtensionDelegate { func applicationDidFinishLaunching() { let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in if granted { WKExtension.shared().registerForRemoteNotifications() } else { ... } } } }

@valzevul / drobinin.com 41 Push Notifications import WatchKit import UserNotifications class ExtensionDelegate: NSObject, WKExtensionDelegate { func applicationDidFinishLaunching() { let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in if granted { WKExtension.shared().registerForRemoteNotifications() } else { ... } } } }

@valzevul / drobinin.com 41 Push Notifications import WatchKit import UserNotifications class ExtensionDelegate: NSObject, WKExtensionDelegate { func applicationDidFinishLaunching() { let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in if granted { WKExtension.shared().registerForRemoteNotifications() } else { ... } } } }

@valzevul / drobinin.com 41 Push Notifications import WatchKit import UserNotifications class ExtensionDelegate: NSObject, WKExtensionDelegate { func applicationDidFinishLaunching() { let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in if granted { WKExtension.shared().registerForRemoteNotifications() } else { ... } } } }

@valzevul / drobinin.com 41 Push Notifications import WatchKit import UserNotifications class ExtensionDelegate: NSObject, WKExtensionDelegate { func applicationDidFinishLaunching() { let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in if granted { WKExtension.shared().registerForRemoteNotifications() } else { ... } } } }

@valzevul / drobinin.com 41 Push Notifications import WatchKit import UserNotifications class ExtensionDelegate: NSObject, WKExtensionDelegate { func applicationDidFinishLaunching() { let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in if granted { WKExtension.shared().registerForRemoteNotifications() } else { ... } } } }

@valzevul / drobinin.com 41 Push Notifications import WatchKit import UserNotifications class ExtensionDelegate: NSObject, WKExtensionDelegate { func applicationDidFinishLaunching() { let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in if granted { WKExtension.shared().registerForRemoteNotifications() } else { ... } } } }

@valzevul / drobinin.com 41 Push Notifications import WatchKit import UserNotifications class ExtensionDelegate: NSObject, WKExtensionDelegate { func applicationDidFinishLaunching() { let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in if granted { WKExtension.shared().registerForRemoteNotifications() } else { ... } } } }

@valzevul / drobinin.com 41 Push Notifications func didRegisterForRemoteNotifications(withDeviceToken deviceToken: Data) { /* Forward the token to your provider, using a custom method. */ } func didFailToRegisterForRemoteNotificationsWithError(_ error: Error) { /* Disable remote notification features */ }

@valzevul / drobinin.com 42 Push Notifications func didRegisterForRemoteNotifications(withDeviceToken deviceToken: Data) { /* Forward the token to your provider, using a custom method. */ } func didFailToRegisterForRemoteNotificationsWithError(_ error: Error) { /* Disable remote notification features */ }

@valzevul / drobinin.com 42 Push Notifications func didRegisterForRemoteNotifications(withDeviceToken deviceToken: Data) { /* Forward the token to your provider, using a custom method. */ } func didFailToRegisterForRemoteNotificationsWithError(_ error: Error) { /* Disable remote notification features */ }

@valzevul / drobinin.com 42 Push Notifications func didReceiveRemoteNotification(_ userInfo: [AnyHashable : Any], fetchCompletionHandler: @escaping (WKBackgroundFetchResult) -> Void) { // handle it // ... fetchCompletionHandler(.newData) }

@valzevul / drobinin.com 43 Push Notifications func didReceiveRemoteNotification(_ userInfo: [AnyHashable : Any], fetchCompletionHandler: @escaping (WKBackgroundFetchResult) -> Void) { // handle it // ... fetchCompletionHandler(.newData) }

@valzevul / drobinin.com 43 Push Notifications func didReceiveRemoteNotification(_ userInfo: [AnyHashable : Any], fetchCompletionHandler: @escaping (WKBackgroundFetchResult) -> Void) { // handle it // ... fetchCompletionHandler(.newData) }

@valzevul / drobinin.com 43 Push Notifications func didReceiveRemoteNotification(_ userInfo: [AnyHashable : Any], fetchCompletionHandler: @escaping (WKBackgroundFetchResult) -> Void) { // handle it // ... fetchCompletionHandler(.newData) }

@valzevul / drobinin.com 43 Push Notifications func didReceiveRemoteNotification(_ userInfo: [AnyHashable : Any], fetchCompletionHandler: @escaping (WKBackgroundFetchResult) -> Void) { // handle it // ... fetchCompletionHandler(.newData) }

@valzevul / drobinin.com 43 Watch App Store

@valzevul / drobinin.com 44 ✍ Text Optimization

@valzevul / drobinin.com 45 ! Graphics Optimization

@valzevul / drobinin.com 46 ⭐ Rating and Reviews

@valzevul / drobinin.com 47 ! Ge!ing Featured

@valzevul / drobinin.com 48 Summary

@valzevul / drobinin.com 49 Summary

— Apple Watch ≠ iPhone's extension

@valzevul / drobinin.com 49 Summary

— Apple Watch ≠ iPhone's extension — Check out the new APIs

@valzevul / drobinin.com 49 Summary

— Apple Watch ≠ iPhone's extension — Check out the new APIs — Extended Runtime is very powerful

@valzevul / drobinin.com 49 Summary

— Apple Watch ≠ iPhone's extension — Check out the new APIs — Extended Runtime is very powerful — Think about ASO

@valzevul / drobinin.com 49 Not enough?

@valzevul / drobinin.com 50 Not enough?

— Streaming Audio on watchOS 6, session 716

@valzevul / drobinin.com 50 Not enough?

— Streaming Audio on watchOS 6, session 716 — Extended Runtime for watchOS Apps, session 251

@valzevul / drobinin.com 50 Not enough?

— Streaming Audio on watchOS 6, session 716 — Extended Runtime for watchOS Apps, session 251 — Creating Independent Watch Apps, session 208

@valzevul / drobinin.com 50 Questions?

drobinin.com | @valzevul