Upgrade to Pro — share decks privately, control downloads, hide ads and more …

初代 SwiftUI 就用來寫 Watch App 吧!

61a2325aa2033a3d43c8edfb43718562?s=47 Ethan Huang
September 21, 2019

初代 SwiftUI 就用來寫 Watch App 吧!

許多人對 SwiftUI 抱持高度興趣,想找機會來使用與學習。但是實際玩過以後會發現它的成熟度還不足以取代 UIKit,作為開發 iOS app 的主要 UI 框架。開發者普遍的共識是,在 iOS 13 使用 SwiftUI 的風險很大,用在 side project 或小型專案還可以,但不適用於商業上的應用。

難道 SwiftUI 就無用武之地嗎?2019 年第一版它只能當玩具嗎?其實 Apple 規劃出一個很不錯的路線,讓 SwiftUI 可以用在商業應用上,只是很容易被開發者忽略——watchOS 6 才是 SwiftUI 在今年發揮作用的平台。

watchOS 6 在開發方面有幾個特色:
1. 因為 Swift ABI 穩定,app 體積小,安裝到實機開發節省相當多時間
2. 可以使用 SwiftUI,不再受限 WKInterface
3. 可以獨立打包上架,不一定要有 iOS app
4. WKExtendedRuntimeSession 增加了好幾個應用情境

過去 watch app 很少人開發,很大的原因就是上述幾點困難尚未被克服。既然開發者大多都沒有寫過、甚或移除了原有的 watch app,watchOS 6 + SwiftUI 或許就是很好的重新來一次的機會,也可以想出許多 side project 的題目。

61a2325aa2033a3d43c8edfb43718562?s=128

Ethan Huang

September 21, 2019
Tweet

Transcript

  1. 初代 SwiftUI 就⽤來寫 Watch App 吧! 13 @ iPlayground19

  2. 我是 13 • 任職於 CATCHPLAY • 獨立開發者,作品: NoteBox、Blahker、 Ladybug、Knil... •

    Twitter: @ethanhuang13
 #iplayground #102 • GitHub: ethanhuang13
  3. 先來個⼯商時間 最近 side projects 太多

  4. CATCHPLAY

  5. weak self https://weakself.dev 台灣唯⼀ iOS 開發 podcast

  6. «13的開發者週報» https://ethanhuang13.substack.com 減低你的資訊焦慮 Google Search “開發者週報”

  7. 本議程的 HackMD https://hackmd.io/@iPlayground/rJdZOKrSB

  8. 為什麼要講 watchOS

  9. 為什麼要講 watchOS 因為想來玩 SwiftUI,但是 iOS 13 才能⽤

  10. 為什麼要講 watchOS 之前沒有 Watch App,直接來⽤ watchOS 6

  11. 為什麼要講 watchOS 據說 SwiftUI ⼀開始是為了 watchOS ⽽做, 完成度應該比較⾼ 
 好天真

  12. 為什麼要講 watchOS 現在寫 watch app 比以前更容易, 對 iOS ⼯程師應該也不難吧 


    長⼤了還是這麼天真
  13. 為什麼要講 watchOS 我最喜歡去做沒⼈在研究的題⽬

  14. 需要先備知識 • 有 Apple Watch 使⽤經驗 • 這不是 SwiftUI 入⾨,


    ⾄少要玩過官⽅的 SwiftUI Tutorials
  15. 你會聽到什麼? ⽤ SwiftUI 做 Watch App 會遇到的常⾒問題

  16. 你會聽到什麼? 獨立的 Watch App 怎麼送審、上架

  17. 你會聽到什麼? 幾個實驗性的 Watch Apps

  18. 你不會聽到什麼? 畢竟我才玩三個禮拜

  19. 不會聽到什麼? • ⽤ WCSession 整合 iOS app • 多媒體 •

    串接網路或 iCloud • 怎麼靠寫 app 發⼤財
  20. 對⼯作的幫助 0% iPlayground 無⽤ session 系列

  21. 邊緣開發? tvOS

  22. 末梢開發! watchOS

  23. 如果不是你想聽的, 可以去聽別的

  24. 2019 獨立 vs 統⼀ 你要選哪⼀個

  25. 以前只能統⼀⽤ iOS + watchOS app

  26. 到最後往往變成 「先做好 iOS app」

  27. 然後就沒有然後了

  28. watchOS 6 可以做 獨立的 watch app

  29. watchOS 6 UI framework ⽅⾯,SwiftUI 比 WatchKit 強⼤ ⾃訂 UI

    元件、⽅便更新畫⾯
  30. watchOS 6 獨立上架到 Watch App Store
 雖然可能沒⼈會⽤

  31. watchOS 6 獨立的 target 可以跟 iOS 分開安裝或移除

  32. watchOS 歷年進化很⼤
 可⾒ iOSDC Japan 2019
 堤 修⼀ 的今こそwatchOS

  33. 基本架構 • iOS App Target: 可割可棄 • Watch App Target

    放 Storyboard • WatchKit Extension Target 放 code • Complication 顯⽰在錶⾯ • Notification 適時的個⼈化通知
  34. Storyboard Code, Complication, Notification

  35. Storyboard Code, Complication, Notification

  36. 怎樣建立獨立 Watch App 直接⽤官網的圖

  37. None
  38. None
  39. 獨立 Watch App 怎麼送審與上架?

  40. App Store Connect • ⼀樣在 Developer Account 開新的 Bundle ID

    • ⼀樣在 App Store Connect 開新的 app,選 iOS • ⽤ Xcode 11 上傳 binary • 附上 watch app 截圖,但不⽤ iOS 的 • 其他都⼀樣,定價、Pre-release、⾃動發布...
  41. iOS App 不在了嗎? • 獨立 watch app 其實藏了⼀個不會顯⽰在 iPhone 桌⾯跟系

    統設定的 iOS app • iOS app 與 watchOS 仍然是同⼀個 bundle ID prefix、同⼀ 個 binary • 如果將來要加上 iOS app,在 App Store 上是就會是同⼀個 app
  42. TestFlight? • 發送流程、安裝流程,都跟 iOS TestFlight 完全⼀樣。因為 Apple Watch 上沒有 TestFlight

    app,所以你也只能⽤ iOS 來 安裝 • 獨立 watch app 的圖⽰會顯⽰ 圓形的版本。也會有⼩字標 「Apple Watch 上」
  43. IAP? 妹有~

  44. Watch App 的 UX 設計

  45. 以秒為計的使⽤時間 ⼿會痠 ,不想等

  46. 能點兩下就不要點三下 螢幕太⼩不好按

  47. 按鈕跟資訊不能多 精簡、重點⼀眼就看懂

  48. 獨有的控制⽅式 • Force Touch Menu • 觸覺回饋 Haptic Feedback •

    數位錶冠 Digital Crown
  49. 不⽤⽀援 Dark Mode

  50. 其他我個⼈想法 • 不要把 iOS app 搬到 watchOS,從頭開始想 • 畫⾯能夠不要捲動就不要捲動 •

    但可以⽤ Digital Crown 來精準改變數值 • ⽤ Paged Navigation,主畫⾯⼀⾴、設定畫⾯⼀⾴
  51. #我有⼀個⼤膽的想法

  52. App 1

  53. 想記錄喝了幾杯飲料

  54. DrinkBobo ☕

  55. None
  56. None
  57. 在等珍珠奶茶的 Emoji ⽬前還不能上架

  58. ⼯程師是不是 想得太複雜了

  59. DrinkBobo https://github.com/ethanhuang13/DrinkBobo
 如果 404 的話表⽰還沒上傳

  60. App 2

  61. 想要快速啟動世界迷霧

  62. None
  63. 想要快速啟動世界迷霧 • 走在路上突然想要記錄軌跡 • ⼿機版:
 拿出⼿機 ➡ 解鎖 ➡ 找到

    app ➡ 點開 等載入 點⼀下 開始記錄 ➡ 上鎖 ➡ 收起⼿機 • ⼿錶版:
 抬⼿ ➡ 點⼀下錶⾯進入 app ➡ 點⼀下開始記錄 放⼿
  64. 概念 • 點⼀下開始記錄、再點⼀下結束 • ⽤ CoreLocation 追蹤⼀系列座標 • 儲存在 CloudKit(watchOS

    沒有 iCloud Drive 與 iCloud Key-Value Store) • 輸出前,把經緯度與時間轉成 GPX(XML) 格式
  65. 這個還沒做完 因為我懶得寫 iOS app...

  66. Defog 底法格 https://github.com/ethanhuang13/Defog 如果 404 的話表⽰還沒上傳

  67. App 3

  68. 番茄鐘計時器

  69. 番茄鐘⼯作法 • The Pomodoro Technique • 安排要作的事情 • 固定 25

    分鐘為專注的時間單位 • 設定倒數計時器,做事直到計時器響起 • 記錄完成事項,辨別內外在⼲擾因素 • 休息 5 分鐘後進⾏下⼀個...
  70. None
  71. 爛⼤街題⽬

  72. Pineapple Timer

  73. ⼩巧思:旋轉錶冠來模擬轉緊番茄鐘

  74. Public TestFlight 請⽤ iPhone 掃描

  75. Timer https://github.com/ethanhuang13/ PineappleTimer

  76. App 4

  77. 會講話的鬧鐘

  78. 原始概念很善良 • 利⽤字串搭配 AVSpeechSynthesizer(語⾳合成器) • 鬧鐘可以講話告訴我當天的天氣、有沒有下雨 • ⾏事曆、會議 • 提醒事項

    • 親友的⽣⽇
  79. 後來卻變成 X提諾狂新聞

  80. 前⾝是失敗的 iOS 版 如何在指定時間響起鬧鐘?

  81. 如何在指定時間 響起鬧鐘? ⽅法 1 Local Notification

  82. 不能呼叫語⾳合成器

  83. 如何在指定時間 響起鬧鐘? ⽅法 2 Push Notification

  84. 我⽣平知道最邪惡的 iOS 技巧

  85. 需要確保網路暢通

  86. 如何在指定時間 響起鬧鐘? ⽅法3 Audio Session

  87. 開啟指定長度的錄⾳ session

  88. 有可能會被中斷、隱私問題

  89. 實⽤度也是個問題

  90. watchOS 6 Extended Runtime: Smart Alarm


  91. 狂鬧鐘

  92. 百聞不如⼀⾒ 如果播⾳失敗的話
 請到這個連結

  93. 限制 • Alarm 偶爾會失效 • AVSpeechSynthesizer 只能講話 30 秒。我還在找⽅法 •

    規定要 call 震動 API。⼀開始會震動,講話完就不震動了
  94. 狂鬧鐘 https://github.com/ethanhuang13/Crazy 如果 404 的話表⽰還沒上傳

  95. 實際開發遇到的

  96. 新專案預設名稱是 OOO WatchKit App • 在 WatchKit App 的 Info.plist

    或 Targets General 把 Display Name 改掉 • 或是新增 InfoPlist.strings 再加上 CFBundleDisplayName 的 key value • 注意 target 是 WatchKit App ⽽不是 WatchKit Extension
  97. ⼿錶⼀直戴在⼿上很⿇煩 • 把 Pass Code 關掉 • 放在充電座上 • ⼿機還是要插線接

    Xcode
  98. 不能同時跑兩個 Simulator? • 對... ⾄少 Xcode 11 GM 2 還是這樣

    • 有⼈知道怎麼解決的話請告訴我
  99. 安裝到實機裝不起來 • 你不孤單 • 檢查⼀下是不是有另外的 session 在跑 • 其實它已經裝起來了,但是不會進入 debug

    session • 有⼈知道怎麼解決的話請告訴我
  100. 怎樣同時 Preview 多個裝置 • 裝置名稱的字串規格在此

  101. 怎樣同時 Preview 多個裝置

  102. PreviewProvider 怎麼傳入 @Binding • 可以⽤ .constant(Value) • 不過在 Preview 裡它的值就不會有變化

  103. WKHostingViewController<View> 要怎麼加 environmentObject • 因為 Type 會不⼀樣 • ⽤ AnyView()

    包起來 • 但是據說⽤ type eraser 效能會差,盡量避免
  104. WKHostingViewController<View> 要怎麼加 environmentObject • 那就再包⼀層就好

  105. 怎麼做 Paged Navigation? • 左右滑動換⾴ • SwiftUI 沒有直接⽀援的⽅法 • 可以⽤

    WatchKit 本來的作法,也就是在 Storyboard 裡⾯ 拉 next page 的 segue • ⽤ HostingController 的 didAppear() 來判斷⽬前畫⾯
  106. None
  107. 狀態機 • 以往我們會⽤ enum 來表⽰狀態,以狂鬧鐘為例,就是 「未設定鬧鐘」、「已設定鬧鐘」、「鈴響中」三種狀態 • SwiftUI 的 View

    採⽤的 function builder 還是半殘狀態,
 只⽀援 if,不⽀援 switch case • 所以就是各種 if else,⾃⼰⼩⼼
  108. 怎麼做圓形的圖案 • .clipShape(Circle()) • 其他形狀同理

  109. List 怎麼改背景顏⾊ • iOS 在⽤的 listRowBackground(_:) 沒有作⽤,
 watchOS 要⽤ listRowPlatterColor

    • 直接⽤ List() 的話不起作⽤ • 要寫成這樣才⾏
  110. 並排的東⻄⽤ List 會變成共⽤背景 • watchOS 的 List 應該是⽤ WatchKit 原⽣的

    • 暫時只能⽤ ScrollView + HStack
  111. @Binding 不能⽤在 collection • Beta 7 之後改掉了... • Release notes:

    51624798 • 直接照抄程式碼
  112. None
  113. None
  114. None
  115. Context Menu • SwiftUI Preview 不⽀援 • ⽤ Simulator 要注意

    Hardware ➡ Touch Pressure 是 Shallow or Deep
  116. Haptic Feedback • 沿⽤ WatchKit 的 API

  117. SwiftUI 常⽤快速鍵 • 繼續跑 Preview: Cmd + Opt + P

    • 叫出選單 Cmd + Shift + L
  118. SwiftUI 常⽤快速鍵 • 打開 Preview:Cmd + Opt + Enter •

    關閉 Preview:Cmd + Enter
  119. 單元測試? • watchOS 根本沒有 test target 可以建立 • 就算開 watchOS

    framework 也是沒有 • 這個時候可以考慮開個 Swift Package • CI 什麼的可以順理成章不做(誤)
  120. Digital Crown API • 先幫 View 加上 .focusable(_:onFocusChange:) • 再加上

    
 .digitalCrownRotation(_:from:through:by:sensitivity:isConti nuous:isHapticFeedbackEnabled:)
  121. None
  122. 怎麼在左上⾓顯⽰標題 • watchOS 的 SwiftUI 沒有 NavigationView • 但是還是可以直接設定 .navigationBarTitle("

    Timer") • 剛進 app 常常壞掉
  123. 要注意錶冠⽅向 • 系統設定可以改錶冠⽅向,API 是 WKInterfaceDevice 的 crownOrientation(感謝 @klandortw 測試與回報問題) •

    但是模擬器是壞的
  124. Complications 「複雜功能」

  125. Complications 官⽅ HIG 不好讀,看 cheat sheet 比較快

  126. None
  127. None
  128. None
  129. 怎麼更新 Complication • ClockKit API 有更新次數限制,每個 app 每⽇的呼叫上限是 50 次

    • Apple 希望你⼀次提供⼀系列針對不同時間的 complications,⽽ 不是每隔幾分鐘呼叫系統來更新⼀次 • watchOS 6 也可以⽤ PushKit • 使⽤ CLKRelativeDateTextProvider 來做計時或倒數 • 舊錶⾯在⽤的 RingText 不⽀援每秒的更新 • 新錶⾯在⽤的 GaugeProvider ⽀援每秒的更新
  130. Complication Time Travel? • 其實這功能在 watchOS 5 已經砍掉了,
 但沒有標 deprecated

    • getSupportedTimeTravelDirections() 還是要放個空的
  131. 程式碼架構 • 我書讀得少,不懂架構 • 不過我幾乎都可以⽤⼀個 ObservableObject 作為 user data/state 的

    singleton 來解決 • SwiftUI 的部分,乖乖從 HostingController 丟 .environmentObject() 進去 • 其他地⽅則直接使⽤同⼀個 singleton(例如 ExtensionDelegate or ComplicationController)
  132. Code 寫得不好 但是我都會開源

  133. ⼼靈雞湯配⼤餅

  134. ⼀起來寫 Watch App? • iOS ⼯程師的盲點?Apple Watch 能做的事真的很少嗎?還 是我們想得太多 •

    Apple 平台還有很多,iOS ⼯程師可以嘗試把技能遷移過去 • 做這種很少資源可以查詢,要⾃⼰摸索的⼩題⽬⼩產品, 很有回到初⼼的感覺
  135. ⼀起來寫 Watch App? • 不要想得太複雜 • 兩天就可以做完的 side project •

    伸⼿下去做了才會有 fu
  136. #我有⼀個⼤膽的想法

  137. #我有⼀個⼤膽的想法

  138. Just Do It.

  139. 謝謝 請不吝指教

  140. Reference • Creating a watchOS App with SwiftUI (sample code)

    • Creating Independent Watch Apps (WWDC19, 208) • SwiftUI on watchOS (WWDC19, 219) • Extended Runtime for watchOS Apps (WWDC19, 251) • Exploring Tinted Graphic Complications (WWDC19, 253) • Streaming Audio on watchOS 6 (WWDC19, 716) • Building Activity Classification Models in Create ML (WWDC19, 426)