Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/hyun99999/workoutstutorial-watchos
๐ฐ๏ธ WorkoutsTutorial WatchOS
https://github.com/hyun99999/workoutstutorial-watchos
Last synced: 27 days ago
JSON representation
๐ฐ๏ธ WorkoutsTutorial WatchOS
- Host: GitHub
- URL: https://github.com/hyun99999/workoutstutorial-watchos
- Owner: hyun99999
- Created: 2022-11-03T05:30:02.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2022-11-18T10:16:53.000Z (over 2 years ago)
- Last Synced: 2024-11-25T21:35:36.480Z (3 months ago)
- Language: Swift
- Size: 81.1 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# ๐ฐ๏ธ WorkoutsTutorial-WatchOS
- [WWDC22) Build a workout app for Apple Watch](https://developer.apple.com/videos/play/wwdc2021/10009/) ์ธ์ ์์ ์์๋ณด๋ HealthKit ์ ๋ํด์ ๊ธฐ๋กํด๋ณด์์ต๋๋ค.
- HealthKit ์ heart rate ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ๋ชฉํ๋ฅผ ๊ฐ์ง๊ณ ์์ฒญํ ์ธ์ ์ ๋๋ค.*๋ค์์ ์ธ์ ์ ๊ฒฐ๊ณผ์ ๋๋ค.*
## ๐ย Build a workout app for Apple Watch
์ธ์ ์ค ์ฌ๋ฐ์๋ฅผ ์์งํ ์ ์๋ `HKWorkoutSession` ํด๋์ค์ ๋ํด์ ๋ค์ ์ ์์๋ค.
- `HKWorkoutSession` ์ ๋ฐ์ดํฐ ์์ง์ ์ํด ์ฅ์น์ ์ผ์๋ฅผ ์ค๋นํ๋ฏ๋ก, ์ด๋๊ณผ ๊ด๋ จ๋ ๋ฐ์ดํฐ๋ฅผ ์ ํํ๊ฒ ์์งํ ์ ์์ต๋๋ค **(์นผ๋ก๋ฆฌ์ ์ฌ๋ฐ์์ ๊ฐ์ ์ ๋ณด ์์ง)** . ๋ํ ์ด๋์ด ํ์ฑํ๋์ด ์์ ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์คํ๋๋๋ก ํฉ๋๋ค.
- `HKLiveWorkoutBuilder` ๋ HKWorkout ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ์ ์ฅํฉ๋๋ค. ์๋์ผ๋ก ์ํ๊ณผ ์ด๋ฒคํธ๋ฅผ ์์งํฉ๋๋ค.`**New ways to work with workouts**` ์ธ์ ์์ ๋ง์ ๋ด์ฉ์ ํ์ธํ ์ ์์ต๋๋ค.
### ๐ย request authorization ๋จ๊ณ
```swift
/// Request authorization to access HealthKit.
func requestAuthorization() {
// The quantity type to write to the health store.
let typesToShare: Set = [
HKQuantityType.workoutType()
]// The quantity types to read from the health store.
let typesToRead: Set = [
HKQuantityType.quantityType(forIdentifier: .heartRate)!,
HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning)!,
HKQuantityType.quantityType(forIdentifier: .distanceCycling)!,
HKObjectType.activitySummaryType()
]// Request authorization for those quantity types.
healthStore.requestAuthorization(toShare: typesToShare, read: typesToRead) { (success, error) in
// Handle error.
}
}// โ ๋ทฐ๊ฐ ๋ํ๋ ๋ ๊ถํ์ ์์ฒญํ๋ฉด ๋ฉ๋๋ค.
// .onAppear {
// workoutManager.requestAuthorization()
// }
```### ๐ย ํ๋ก์ ํธ ์ธํ
- watch app target ์ `HealthKit` ์ ์ถ๊ฐํฉ๋๋ค.
- ๋ฐฑ๊ทธ๋ผ์ด๋์์ workout session ์ด ์คํ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์ `background modes capability` ๋ฅผ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
- ํด๋น target ์ด HealthKit ์ ์ง์ํ๋๋ก ์ค์ ํฉ๋๋ค.
- apple developer ์์ Identifiers ์์ App ID ๋ก ์ถ๊ฐํฉ๋๋ค.
- HealthKit ์ง์์ ์ ํํ๊ณ ๋ฒ๋ค ์์ด๋๋ฅผ ์ ๋ ฅํด์ App ID ๋ฅผ ์์ฑํฉ๋๋ค.
- info.plist ์์ ๊ถํ์ ์ป์ ๋ ๋ฌธ๊ตฌ๋ฅผ ์ค์ ํด์ค๋๋ค.
- `NSHealthShareUsageDescription` ํน์ `Privacy - Health Share Usage Description` key ๋ฅผ ์ถ๊ฐํด์ค๋๋ค.(์ฑ์ด ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์์ผ ํ๋์ง ์ค๋ช ํฉ๋๋ค.)
- `NSHealthUpdateUsageDescription` ํน์ `Privacy - Health Update Usage Description` key ๋ฅผ ์ถ๊ฐํด์ค๋๋ค.(์ฑ์์ ์์ฑํ๋ ค๋ ๋ฐ์ดํฐ์ ๋ํด์ ์ค๋ช ํฉ๋๋ค.)
### ๐ย ๋น๋ํ์ฌ Authorization ํ์ธ
- ๋น๋ ํด๋ณด๊ฒ ์ต๋๋ค. (`์ฌ์ฅ - ์ฌ๋ฐ์` ์ ์ฝ๊ธฐ ๊ถํ์ ์ป์ ์ ์์ต๋๋ค.)
- ์ฐ๋ฆฌ๊ฐ ์ ๊ณตํ ์ค๋ช ๋ค์ ๋ณผ ์ ์์ต๋๋ค.
- ์ฐ๋ฆฌ๊ฐ ์ถ๊ฐํ ๊ถํ์ ๋ํด์ ๋ณผ ์ ์์ต๋๋ค.
**๊ถํ์ ์ป์์ผ๋ ์ฌ๋ฐ์์ ์ ๋ณด๋ฅผ ์ป์ด๋ณด๊ฒ ์ต๋๋ค.**
### ๐ย heart rate(์ฌ๋ฐ์)
- builder ์๋ก์ด ์ํ์ ์์งํ ๋๋ง๋ค ํธ์ถ๋๋ delegate method ๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํํ์์ต๋๋ค.
- statistics ์ค heartRate ์ ํด๋นํ๋ ๊ฒฝ์ฐ๋ฅผ switch ๋ฌธ์ผ๋ก ๋์ํด์ฃผ์์ต๋๋ค.```swift
// MARK: - HKLiveWorkoutBuilderDelegateextension WorkoutManager: HKLiveWorkoutBuilderDelegate {
/// called whenever the builder collects an events.
func workoutBuilderDidCollectEvent(_ workoutBuilder: HKLiveWorkoutBuilder) {
// ...
}/// โ called whenever the builder collects new samples.
func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set) {
for type in collectedTypes {
guard let quantityType = type as? HKQuantityType else { return }let statistics = workoutBuilder.statistics(for: quantityType)
// โ Update the published values.
updateForStatistics(statistics)
}
}
}// ...
// MARK: - Workout Metrics
@Published var averageHeartRate: Double = 0
@Published var heartRate: Double = 0
func updateForStatistics(_ statistics: HKStatistics?) {
guard let statistics = statistics else { return }DispatchQueue.main.async {
switch statistics.quantityType {
// โ We want beats per minute, so we use a count HKUnit divided by a minute HKUnit.
case HKQuantityType.quantityType(forIdentifier: .heartRate):
let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute())
self.heartRate = statistics.mostRecentQuantity()?.doubleValue(for: heartRateUnit) ?? 0
self.averageHeartRate = statistics.averageQuantity()?.doubleValue(for: heartRateUnit) ?? 0// ...
}
}
}
```### โ ย Data flow
data flow ์ ๋ํด์ ์์๋ณด์.
WorkoutManager(์ฐ๋ฆฌ๊ฐ ๋ง๋๋ HealthKit ์ ๋งค๋์ง ํ ์ ์๋ ํด๋์ค)๋ HealthKit ๊ณผ์ ์ธํฐํ์ด์ค๋ฅผ ๋ด๋นํฉ๋๋ค.
HKWorkoutSession ๊ณผ ์ธํฐํ์ด์คํ์ฌ ์ด๋์ ์์, ์ผ์ ์ค์ง ๋ฐ ์ข ๋ฃ ํ ์ ์์ต๋๋ค.
HKLiveWorkoutBuilder ์ ์ธํฐํ์ด์คํ์ฌ ์ด๋ ์ํ์ ์์ ํ๊ณ ํด๋น ๋ฐ์ดํฐ๋ฅผ ๋ทฐ์ ์ ๊ณตํ ์ ์์ต๋๋ค.environmentObject ๋ก WorkoutManager ๋ฅผ ํ ๋นํ์ฌ NavigationView ์ ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ์ ์๋ ๋ทฐ์ ์ ํํ ์ ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ค์ ๋ทฐ๋ @EnvironmentObject ๋ฅผ ์ ์ธํ์ฌ WorkoutManager ์ ๋ํ ์ก์ธ์ค ๊ถํ์ ์ป์ ์ ์์ต๋๋ค.์ถ์ฒ:
[Build a workout app for Apple Watch - WWDC21 - Videos - Apple Developer](https://developer.apple.com/videos/play/wwdc2021/10009/)
---
### ๐ย ์ ํ ์์น ๊ธฐ๊ธฐ๋ก Xcode ์์ ๋น๋ ํ ๋
[Apple Developer Documentation - Testing custom notification interfaces](https://developer.apple.com/documentation/watchos-apps/testing-custom-notification-interfaces)
์๋ Notification interfaces ๋ฅผ ๊ธฐ๊ธฐ์์ ๋น๋ํ๋ ๋ฐฉ๋ฒ์ ์๊ฐํด์ค ๊ธ์ ๋๋ค. ์ด๋ฅผ ํตํด ์ฐ๋ฆฌ๋ ์๋ชฉ์ ์ฐฉ์ฉํ์ง ์์๋ ์ด๋ค ์กฐ๊ฑด์ผ๋ก ๊ธฐ๊ธฐ์์ ๋น๋ํ ์ ์๋์ง ์ ์ ์์ต๋๋ค.
_**๊ธฐ๊ธฐ๋ฅผ ์๋ชฉ์ ์ฐฉ์ฉํ์ง ์์ ์ํ์์ notification interfaces ๋ฅผ ํ ์คํธํ๋ ค๋ฉด ๋ค์์ ๋จ๊ณ๋ฅผ ๋ฐ๋ฅด๋ฉด ๋ฉ๋๋ค.**_
- ์ ํ ์์น์์ ์๋ชฉ ๊ฐ์ง๋ฅผ ๋นํ์ฑํ ํฉ๋๋ค. companion iPhone ์ watch ์ฑ ๋๋ watch ์ Setting ์์ ์ค์ ํ ์ ์์ต๋๋ค. ์ต์ ์ย **Passcode > Wrist Detection**ย ์ ์์ต๋๋ค.
- ์ ํ ์์น๊ฐ ์ถฉ์ ๊ธฐ์ ์ฐ๊ฒฐ๋์ด ์์ง ์์์ง ํ์ธํฉ๋๋ค.
- iPhone ์ ์ ๊ธ๋๋ค.watch-only app ์ ๋ง๋ค๋๋ผ๋ ๋ด iPhone ๊ธฐ๊ธฐ๋ฅผ ํตํด์ ์ ํ์์น์ ์ ๊ทผํ ์ ์์์ต๋๋ค.
์ด๋ provisioning profile ์ ์ ํ ์์น ๊ธฐ๊ธฐ๋ฅผ ์ถ๊ฐํ์ฌ ๋น๋ํ ์ ์๋ค๋ ์ฐฝ์ด ๋ฑ์ฅํฉ๋๋ค. ์ด๋ฅผ ํตํด ์ ๋ขฐํ๊ณ ๋น๋ํ ์ ์๋ ๊ธฐ๊ธฐ๋ก ์ถ๊ฐํ ์ ์์์ต๋๋ค.
![]()