Open hyun99999 opened 2 years ago
Build a research and care app, part 1: Setup onboarding - WWDC21 - Videos - Apple Developer
*๋ณธ ๊ธ์ WWDC ๋ฅผ ๋ณด๊ณ , ๋ฒ์ญ ๋ฐ ์์ฝ ๊ทธ๋ฆฌ๊ณ ์คํํด๋ณด๋ ์คํฐ๋ ํ๋ก์ ํธ์ ์ผํ์ ๋๋ค.
ResearchKit ๊ณผ CareKit ์ ๋ํด์ ๋ ๋ง์ ์ ๋ณด๋ฅผ ์ป๊ณ ์ถ๋ค๋ฉด ์๋์ ์๊ฐ๊ธ๋ ๋์์ด ๋ ๊ฒ์ ๋๋ค.
ResearchKit
์ Apple ์ด GitHub ๋ก ์ ์งํ๋ ์คํ์์ค iOS framework ์
๋๋ค. ์ฐธ๊ฐ์๊ฐ ์ ์ฒด์ ๋๋ ์ธ์ง์ ์ด๋๊ณผ ๊ฐ์ ํ๋์ ์ํํ๋ ๋์ ๋์ ์์ง, ์ค๋ฌธ ์กฐ์ฌ ๊ด๋ฆฌ ๋ฐ ์ผ์ ๋ฐ์ดํฐ ๊ธฐ๋ก์ ์ํ UI ์์๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก CareKit
๋ ์คํ์์ค ํ๋ ์์ํฌ์ด๋ฉฐ, ํ์๋ฅผ ์๋ํ๋ ์น๋ฃ ์ฑ์ ๊ตฌ์ถํ๋ ๋ฐ ๋์์ด ๋๋๋ก ์ค๊ณ๋์์ต๋๋ค. ์ด ๊ธฐ๋ฅ์ ์ฌ์ฉ์๊ฐ ์ํ๋ ์์
์ ์์ฝํ๋ ๋ฐ ํ์ํฉ๋๋ค. ์ฝ์ ๋ณต์ฉํ๊ฑฐ๋ ์์ฌ๋ฅผ ๋ถ๋ฅด๋ ๋ฑ ์ํ๋ ์์
์ ์์ฝํ๋๋ฐ ํ์ํฉ๋๋ค. ๋ํ ๊ฑด๊ฐ ๊ด๋ จ ๋ฐ์ดํฐ๋ฅผ ์ํ ์์ ํ ์ง์์ฑ ๋ ์ด์ด๋ฅผ ์ ๊ณตํ๊ณ ์ฐจํธ ์์ฑ ์ ํธ๋ฆฌํฐ๋ฅผ ๊ฐ์ถ๊ณ ์์ต๋๋ค.
๊ณผ๊ฑฐ WWDC ์ธ์ ์์๋ ์ด ๋ ๊ฐ์ง ํ์ ํ๋ ์์ํฌ์ ๋ํด ์์ฃผ ๋ ผ์ํ์ง๋ง ์ฌํด๋ ์ด ๋ ๊ฐ์ง ํ๋ ์์ํฌ๋ฅผ ํจ๊ป ์ฌ์ฉํ์ฌ ํจ์ฌ ๋ ๋์ ํ๊ฒฝ์ ๋ง๋ค ์ ์๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ๋๋ฆด ๊ฒ์ ๋๋ค.
โ๏ธ WWDC ๋ฅผ ์งํํ๋ค๊ฐ ๊ฐ์๊ธฐ ํ์ด์คํ์์ด ์์โฆ ๋ฐ๋๋ผ๊ตฌ์โฆ? ์ค๊ฐ์ค๊ฐ ์ง๋ฃจํ์ง ์์ ์ฐ์ถ๊ณผ ํฅ๋ฏธ๋ก์ด ์ํฉ์ ๋ง๋๋ ๋ชจ์ต์ด ๋ณด์ฌ์ ๋ฒ์ญํด๋ณด์์ต๋๋ค. ๋ค์์ ์๋ง ๋ฒ์ญ์ ์ ๋ฌธ์ ๋๋ค.
๐คฆโโ๏ธย Erik
: ํ์๋ฅผ ์ํ ๊ฒฝํ... ์ฃ์กํฉ๋๋ค. ๊ทธ๋ฅ ๋ฌด์์ผ๋ก ํ ๊ฑธ ๊ทธ๋ฌ๋? ์๋ฅด๊ณ ๊ฐ๊น?
Crew: ์๋, ๊ทธ๋ฅ ๋ฐ์.
๐คฆโโ๏ธ Erik
: ์ผ, ์ ์ด๋ฏธ. ๋ฌด์จ ์ผ์ด์ผ?
๐คฆ๐ปโโ๏ธย Jamie
: ์๋
, ์๋ฆญ. ๋ค๊ฐ ์ง๊ธ WWDC๋ฅผ ์ค๋นํ๋๋ผ ๋ฐ์๋ค๋ ๊ฑด ์์ง๋ง, ์ฐ๋ฆฌ ๋ฌผ๋ฆฌ์น๋ฃ ์ฐ๊ตฌ ์ฑ์ ์๊ธ์ด ๋ค์ด์๋ค๋ ๊ฑธ ๋งํด์ฃผ๊ณ ์ถ์์ด! ์ด ์ต์ฒจ๋จ ์ฐ๊ตฌ๊ฐ ์ ๋ง ๋ง์ ์ฌ๋๋ค์๊ฒ ๋์์ด ๋ ๊ฑฐ์ผ! ๊ทธ๋์, ์ฐ๋ฆฌ๊ฐ ๊ทธ ์ฑ์ ์ฒซ ๋ฒ์งธ rev(=revision)์ ๋น์ฅ ๋ด๋์ ์ ์์ ๊ฒ ๊ฐ์?
๐คฆโโ๏ธย Erik
: ํค์ด, ์ ์ด๋ฏธ, ๊ทธ๊ฑฐ ์ ๋ง ๋๋จํ ์์์ด์ผ. ๊ทธ๋ฆฌ๊ณ ๋ ์ ๋ง ํฅ๋ถ๋ผ. ์ค์ํ ๊ฑด ์ฐ๋ฆฐ ์ง๊ธ Dub Dub์ ์ฝ๋ ์งํ์ค์ด๋ผ๋ ๊ฑฐ์์.
๐คฆ๐ปโโ๏ธย Jamie
: ์, ๊ทธ๋ผ ๋ฒ์จ ์ฝ๋ฉ์ ํ์๋ ๊ฑด๊ฐ์? ์๋ฒฝํ๋ค์! ์ด ์ฑ์ Recover
๋ผ๊ณ ๋ถ๋ฆด ๊ฒ๋๋ค. ์์ ํ ์ฐธ๊ฐ์๋ค์ด ๋ฌด๋ฆ์ ํผํผํ ํ ์ ์๋๋ก ๋์์ฃผ๋ ๋ฌผ๋ฆฌ์น๋ฃ ์ฑ์ด ๋ ๊ฒ์
๋๋ค. ๊ทธ๋ฆฌ๊ณ game changer๊ฐ ๋ ๊ฒ์
๋๋ค! ์ ๋ ๋ง์ ํ๋ฅญํ ์์ด๋์ด๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. ํ์ง๋ง, ์ฐ์ , ์ฐธ๊ฐ์๋ค์ด ์ฑ์ ์ฌ์ฉํ๊ธฐ ์ ์ ์ฐ๊ตฌ์ ์ฐธ์ฌํ๋ ๊ฒ์ ๋์ํด์ผ ํฉ๋๋ค ๋ชจ๋ ๋ฒ๋ฅ ์ฉ์ด์ ๋ํด ์ค๋ช
ํด์ผ ํฉ๋๋ค. ํ์ง๋ง ๊ฒ์ ์ฃผ์ง ์๋ ๋ฐฉ๋ฒ์ผ๋ก ํ๊ณ ์ถ์ต๋๋ค, you know? ์ฐธ๊ฐ์๋ค์ ์๋ช
์ ๋ฐ์ ๋ฐ์ดํฐ ๊ณต์ ์ ๋์ํ๋๋ก ํด์ผ ํฉ๋๋ค. Uh, one sec, Erik, ์์ํ์ผ๋ก๋ถํฐ ๋ฉ์์ง๋ฅผ ๋ฐ๊ณ ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ์ฐธ๊ฐ์๋ค์ ์์์ผ์ ์์ ํ(post-op) ํด์ํ ๊ฒ ๊ฐ์ต๋๋ค. Erik, ์ผ๋จ onboarding ์ ์์ํ ์ ์์๊น์? ๋๋จธ์ง ํ์๋ค์ ๋ฐ๋ผ์ก์์ผ ํ๋๋ฐ, ์ ์ ํ์ ๋ค์ ์ ํ ๋๋ฆฌ๊ฒ ์ต๋๋ค. ์ ๋ง ์ข์ ๊ฒ ๊ฐ์ต๋๋ค.
๐คฆโโ๏ธErik
: OK. Uhโฆ Alrightly then! ์ด๊ฒ์ด ์ฐ๋ฆฌ์ ์ฝ๋์ผ ์ผ ๊ฒ ๊ฐ๋ค์!
Recover
์ด๋ผ๋ ์ฑ์ onboarding ์ ๋ํด์ ์ฝ๋๋ฅผ ์์ฑํด๋ด
์๋ค.
์ฐธ๊ฐ์์๊ฒ ์ฐ๋ฆฌ๊ฐ ์์งํ ๋ฐ์ดํฐ, ์ก์ธ์คํ ์ ์๋ ์ฌ๋, ๋ฌด์์ ์ํด ์ฌ์ฉ๋ ๊ฑด์ง, ๋ณด๊ด ๊ธฐ๊ฐ์ ์ค๋ช ํ๊ณ ์ดํดํ๊ธฐ ์ฝ๊ฒ ๋ง๋๋ ๊ฒ์ด ๊ฐ์ฅ ์ค์ํฉ๋๋ค.
์ฑ์์ ์ด ์ค์ํ ๋ถ๋ถ์ ์ฌ๋ฐ๋ฅด๊ฒ ์ํํ๋๋ฐ ๋์์ด ๋๋ ReserachKit
์ ๋ช ๊ฐ์ง ์๋ก์ด ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ณด์ฌ๋๋ฆฌ๊ฒ ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ณด๋์ค๋ก ์จ๋ณด๋ฉ ๋ฐ ๋์ flow ๋ฅผ CareKit
๊ธฐ๋ฐ ์ฑ์ ์ ์ฉํ๋ ์๋ฆฌํ ๋ฐฉ๋ฒ์ ๋ณด์ฌ๋๋ฆฌ๊ฒ ์ต๋๋ค.
๊ตฌ์ฒด์ ์ผ๋ก, ํ์ค CareKit
์ฑ์ ์ค์ ํ์ง๋ง, ์ฐธ๊ฐ์๊ฐ care plan ์ ๋์ ์ปจํ
์ธ ๊ฐ ๊ณต๊ฐ๋๊ธฐ ์ ์ ์จ๋ณด๋ฉ์ ์๋ฃํด์ผํ๋ ๋ฐฉ์์
๋๋ค.
์ด๋ฌํ consent and onboarding flow ์ ๋ถ๋ถ์ผ๋ก ์ฐธ๊ฐ์์๊ฒ ๋ฌด์์ ์์ฒญํ๋์ง ์๋ ค์ฃผ๋ ๋ช ๊ฐ์ง ์ง์นจ ๋จ๊ณ๋ฅผ ๊ตฌ์ถํ ๊ฒ์ ๋๋ค.
์ค์ ๋์ ๋ถ๋ถ์ผ๋ก ๋์ด๊ฐ๊ธฐ ์ ์ ์ด์ ๊ฐ์ด ์ฐธ๊ฐ์๋ฅผ ์๋ดํ๋ ๊ฒ์ด ๊ฒฝํ์ ๊ฐ์ ํ๋ ๋ฐ ์ค์ ๋ก ๋์์ด ๋๋ค๋ ๊ฒ์ ์๊ฒ ๋์์ต๋๋ค.
์๋ช ์ ์์งํ๋ ๋ฐฉ๋ฒ์ ๋ํด์ ์์๋ณผ ๊ฒ์ ๋๋ค.
์จ๋ณด๋ฉ์ ์ผ๋ถ๋ก ResearchKit ์ ์ฌ์ฉํ์ฌ HealthKit, notifications, device motion ์ ๋ํด์ ๋ฏธ๋ฆฌ ๊ถํ ์น์ธ์ ์์ฒญํ๋ ๋ฐฉ๋ฒ๋ ์์๋ณผ ๊ฒ์ ๋๋ค.
session resources ์ ์๋ starter project ๋ฅผ ํตํด์ ์งํํ๊ฒ ์ต๋๋ค.
https://github.com/carekit-apple/WWDC21-RecoverApp
(ํด๋น ๊นํ๋ธ ๋ ํฌ์งํ ๋ฆฌ์ Setup ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.)
git clone --recurse-submodule https://github.com/carekit-apple/WWDC21-RecoverApp.git
Recover
ย and run the app.(Recover Part1 ์ Recover ์ ์ ํํ๋ฉด ๋ฉ๋๋ค.)
CareKit ์ ๋ฏธ๋ฆฌ SPM ์ ํตํด์ ์ธํ ๋์ด ์์ต๋๋ค.
https://github.com/carekit-apple/CareKit
4๊ฐ์ ํ์ผ์ ์ด๋ฒ ์ธ์
์์ ๋ค๋ฃฐ ๊ฒ์
๋๋ค. ์ฒซ๋ฒ์งธ๋ก AppDelegate
์
๋๋ค. ์ฌ๊ธฐ์์ CareKit store ์ ์ํธ์์ฉํ ๊ฒ์
๋๋ค.
AppDelegate.swift
: Contains logic that sets up the CareKit store.Surveys.swift
: Defines ResearchKit tasks such as surveys and onboarding.CareFeedViewController.swift
, InsightsViewController.swift
ย : Building out the UI for our app.starter app ์ ์คํํ๊ฒ ๋๋ฉด ์ง๊ธ์ ์ฌ์ค์ ์๋ฌด๊ฒ๋ ์์ต๋๋ค. ์ปจํ ์ธ ๊ฐ ์๋ OCKDailyPageViewController ๊ฐ ์์๋ฟ์ ๋๋ค. ํ๋จ์ Insights ํญ๋ ์์ง๋ง ๋น์ด์์ต๋๋ค. part 3 ๋์ ์ด๊ฒ๋ค์ ์ฑ์ธ ๊ฒ์ ๋๋ค.
์ด์ ์ฐธ๊ฐ์์ feed ์ consent card ๊ฐ ๋ํ๋๋๋ก ํด๋ด ์๋ค. ์ด consent card ๋ ๋๋จธ์ง ์ฑ์ ๋ํ ์ก์ธ์ค๋ฅผ ์ ๊ณตํฉ๋๋ค. ์๋ฃ๋๊ธฐ ์ ๊น์ง ๋ค๋ฅธ ์์ ์ด ํ์๋์ง ์์ผ๋ฉฐ, ์๋ฃ๋ ํ์๋ ๋ค๋ฅธ ๋ชจ๋ ์์ ์ด ํ์๋์ด์ผ ํฉ๋๋ค.
import CareKit
import CareKitStore
import UIKit
import os.log
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
let storeManager = OCKSynchronizedStoreManager(
wrapping: OCKStore(
name: "com.apple.wwdc.carekitstore",
type: .inMemory
)
)
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
seedTasks()
return true
}
// MARK: UISceneSession Life Cycle
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
UISceneConfiguration(
name: "Default Configuration",
sessionRole: connectingSceneSession.role
)
}
// MARK: Seeding the Store
private func seedTasks() {
// 1.1 Persist an onboarding task
// ์ผ๋ง๋ ์์ฃผ ํ์๋๋์ง ์ง์ ํ๋ schedule ์ ์ ์ํด์ผํฉ๋๋ค.
// ์จ๋ณด๋ฉ์ ์ํด ์ฐธ๊ฐ์๊ฐ ๋์๋ฅผ ํ ๋๊น์ง ๋งค์ผ๋งค์ผ schedule ์ฌ์ฉ.
let onboardSchedule = OCKSchedule.dailyAtTime(
hour: 0, minutes: 0,
start: Date(), end: nil,
text: "Task Due!",
duration: .allDay)
// ๋ฐฉ๊ธ ๋ง๋ schedule ์ ์ผ์ ์ ์ ๋ฌํด์ task ๋ฅผ ์ ์ํด์ผํฉ๋๋ค.
// id ๋ ์ํ๋ ๋๋ก ์ง์ ํ๊ณ , ๊ณ ์ ํ๋ฉด๋๋ค. ํ์ฌ๋ ๋ค๋ฅธ ํ์ผ์์ ์ ์ธ๋ ์์๋ฅผ ์ฌ์ฉํฉ๋๋ค.
var onboardTask = OCKTask(
id: TaskIDs.onboarding,
title: "Onboard",
carePlanUUID: nil,
schedule: onboardSchedule)
// instructions ๋ฅผ ์ง์ ํ๊ณ , ์จ๋ณด๋ฉ์ ๊ณ ์ํ๋ ๊ฒ์ด ์ํฅ๋ฐ์ง ์์์ผ ํ๋ค๋ ๊ฒ์ ๋ํ๋ด์ผ ํฉ๋๋ค.
/// Instructions about how this task should be performed.
onboardTask.instructions = "You'll need to agree to some terms and conditions before we get started!"
/// If true, completion of this task will be factored into the patientโs overall adherence. True by default.
onboardTask.impactsAdherence = false
// ์ด๊ฒ์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋๋ถ๋ถ์ CareKit ๋งจ ์์ ์๋ฃ๋ง์ ์ฑ์ฐ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋์ง ์๋ ๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค.
// store ์ task ๋ฅผ ์ ์งํ ์ ์๋ค.
storeManager.store.addAnyTasks([onboardTask],
callbackQueue: .main) { result in
switch result {
case let .success(tasks):
Logger.store.info("Seeded \(tasks.count) tasks")
case let .failure(error):
Logger.store.warning("Failed to seed tasks:\(error as NSError)")
}
}
}
}
์ด์ ์จ๋ณด๋ฉ ์์ ์ store ์ ์ค๋นํ์ต๋๋ค. ์ฐธ๊ฐ์์ ํผ๋์ ๋ณด์ฌ์ค ์ค๋น๊ฐ ๋์์ต๋๋ค.
import CareKit
import CareKitStore
import CareKitUI
import ResearchKit
import UIKit
import os.log
final class CareFeedViewController: OCKDailyPageViewController,
OCKSurveyTaskViewControllerDelegate {
/// ์ฌ์ฉ์๊ฐ ์ ๋ ์ง๋ก ์ค์์ดํํ ๋๋ง๋ค ํธ์ถ๋ฉ๋๋ค.
/// ๋ ์ง๋ฅผ ๊ฒ์ฌํ๊ณ , ํด๋น ๋ ์ง์ ํ์ํ ๋ด์ฉ์ ๊ฒฐ์ ํ ๋ค์, ์ ์ ํ ๋ด์ฉ์ listViewController์ ์ถ๊ฐํ๋ ๊ฒ์ด ์ผ์
๋๋ค.
override func dailyPageViewController(
_ dailyPageViewController: OCKDailyPageViewController,
prepare listViewController: OCKListViewController,
for date: Date) {
// ๋จผ์ , ์ฐธ๊ฐ์๊ฐ ์จ๋ณด๋ฉ์ ์๋ฃํ๋์ง ํ์ธํด์ผ ํฉ๋๋ค.
// 1.3 Check if onboarding is complete.
checkIfOnboardingComlete { isOnboarding in
// 1.5 If isn't, show an onboarding card.
guard isOnboarding else {
}
}
}
// 1.2 Define a method that checks if onboarding is complete
/// ๋ฉ์๋ ์์ OCKOutomeQuery๋ฅผ ๋ง๋ค๊ณ ์จ๋ณด๋ฉ ์์
๊ณผ ๊ด๋ จ๋ ๋ชจ๋ ๊ฒฐ๊ณผ๋ฅผ ์ฟผ๋ฆฌํฉ๋๋ค.
private func checkIfOnboardingComlete(_ completion: @escaping (Bool) -> Void) {
var query = OCKOutcomeQuery()
query.taskIDs = [TaskIDs.onboarding]
// query ๋ฅผ ๊ฐ์ง๊ณ ๊ฒฐ๊ณผ๊ฐ ๋ฐํ๋์๋์ง ์ฌ๋ถ๋ฅผ ํ์ธํ๋ค.
storeManager.store.fetchAnyOutcomes(
query: query,
callbackQueue: .main) { result in
switch result {
case .failure:
Logger.feed.error("Failed to fetch onboarding outcomes!")
completion(false)
// ์๋ค๋ฉด ์จ๋ณด๋ฉ์ด ์๋ ์๋ฃ๋์ง ์์ ๊ฒ.
case let .success(outcomes):
completion(!outcomes.isEmpty)
}
}
}
// 1.6 Refresh the content when onboarding completes
}
์ง๊ธ๊น์ง์ ๋จ๊ณ์ ๋๋ค.
import CareKitStore
import ResearchKit
struct Surveys {
private init() {}
// MARK: Onboarding
// 1.4 Construct an ORKTask for onboarding
static func onboardingSurvey() -> ORKTask {
// 1.4.1 The Welcome Instruction step.
let welcomeInstructionStep = ORKInstructionStep(
identifier: "onboarding.welcome"
)
welcomeInstructionStep.title = "Welcome!"
welcomeInstructionStep.detailText = "Thank you for joining our study. Tap Next to learn more before signing up."
welcomeInstructionStep.image = UIImage(named: "welcome-image")
welcomeInstructionStep.imageContentMode = .scaleAspectFill
// 1.4.2 The Informed Consent Instruction step.
// ...
// 1.4.3 The Signature step (using WebView).
// ...
// 1.4.4 The Request Permissions step.
// ...
// 1.4.5 Completion Step
// ...
}
}
// 1.4.2 The Informed Consent Instruction step.
let studyOverviewInstructionStep = ORKInstructionStep(
identifier: "onboarding.overview"
)
studyOverviewInstructionStep.title = "Before You Join"
studyOverviewInstructionStep.iconImage = UIImage(systemName: "checkmark.seal.fill")
// ๋ณธ๋ฌธ ํญ๋ชฉ ๋ง๋ค๊ธฐ.
// ๊ธ๋จธ๋ฆฌ ๋์ ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉํฉ๋๋ค.
let heartBodyItem = ORKBodyItem(
text: "The study will ask you to share some of your health data.",
detailText: nil,
image: UIImage(systemName: "heart.fill"),
learnMoreItem: nil,
bodyItemStyle: .image
)
let completeTasksBodyItem = ORKBodyItem(
text: "You will be asked to complete various tasks over the duration of the study.",
detailText: nil,
image: UIImage(systemName: "checkmark.circle.fill"),
learnMoreItem: nil,
bodyItemStyle: .image
)
let signatureBodyItem = ORKBodyItem(
text: "Before joining, we will ask you to sign an informed consent document.",
detailText: nil,
image: UIImage(systemName: "signature"),
learnMoreItem: nil,
bodyItemStyle: .image
)
let secureDataBodyItem = ORKBodyItem(
text: "Your data is kept private and secure.",
detailText: nil,
image: UIImage(systemName: "lock.fill"),
learnMoreItem: nil,
bodyItemStyle: .image
)
studyOverviewInstructionStep.bodyItems = [
heartBodyItem,
completeTasksBodyItem,
signatureBodyItem,
secureDataBodyItem
]
// 1.4.3 The Signature step (using WebView).
// ๋คํํ๋ ResearchKit ์์ ์ด ๋ถ๋ถ์ ์ฝ๊ฒ ๊ตฌํ ๊ฐ๋ฅํฉ๋๋ค.
// signature ์ ORKWebViewStep ์ผ๋ก ์์งํ ์ ์์ต๋๋ค.
// informedConsentHTML ์ HTML ์ธ์ด๋ก ์์ฑ๋์๊ณ WebView ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
let webViewStep = ORKWebViewStep(
identifier: "onboarding.signatureCapture",
html: informedConsentHTML
)
// true ๋ก ์ค์ ํ๊ฒ๋๋ฉด, ResearchKit ์์ signature box ๋ฅผ ํ์ํฉ๋๋ค.
webViewStep.showSignatureAfterContent = true
// 1.4.4 The Request Permissions step.
// HealthKit ๊ถํ์ ์์งํฉ๋๋ค. HealthKitPermissionType ์ ๋๊ฐ์ง ์ง์ ํด์ผํ๋๋ฐ ์ธ ๊ถํ๊ณผ ์ฝ์ ๊ถํ์
๋๋ค.
// (wwdc20 ์์ ์๊ฐํ ํ์
.)
let healthKitTypesToWrite: Set<HKSampleType> = [
HKObjectType.quantityType(forIdentifier: .bodyMassIndex)!,
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKObjectType.workoutType()
]
let healthKitTypesToRead: Set<HKObjectType> = [
HKObjectType.characteristicType(forIdentifier: .dateOfBirth)!,
HKObjectType.workoutType(),
HKObjectType.quantityType(forIdentifier: .appleStandTime)!,
HKObjectType.quantityType(forIdentifier: .appleExerciseTime)!
]
let healthKitPermissionType = ORKHealthKitPermissionType(
sampleTypesToWrite: healthKitTypesToWrite,
objectTypesToRead: healthKitTypesToRead
)
// wwdc21 ์์๋ ์ถ๊ฐ์ ์ผ๋ก ์๋ก์ด ๋ ๊ฐ์ง ๊ถํ์ ๋์
ํฉ๋๋ค.
/// ์๋ฆผ ํ์, ์ฑ ๋ฐฐ์ง, ์ฌ์ด๋ ์ฌ์์ ๋ํ ๊ถํ์ ์์ฒญํ๋ type ์
๋๋ค.
let notificationsPermissionType = ORKNotificationPermissionType(
authorizationOptions: [.alert, .badge, .sound]
)
/// device motion data ์ ์ก์ธ์ค๋ฅผ ์์ฒญํ๋๋ฐ ๋์์ด๋๋ type ์
๋๋ค.
let motionPermissionType = ORKMotionActivityPermissionType()
// read and write, notification, activity ์ก์ธ์ค ์์ฒญ.
let requestPermissionsStep = ORKRequestPermissionsStep(
identifier: "onboarding.requestPermissionsStep",
permissionTypes: [
healthKitPermissionType,
notificationsPermissionType,
motionPermissionType
]
)
requestPermissionsStep.title = "Health Data Request"
requestPermissionsStep.text = "Please review the health data types below and enable sharing to contribute to the study."
// 1.4.5 Completion Step
// Jamie ์ ์ฐ๊ตฌ์ ์ฐธ์ฌํด ์ค ๊ฒ์ ๋ํด ๊ฐ์ฌํ๋ ๋จ๊ณ์
๋๋ค.
let completionStep = ORKCompletionStep(
identifier: "onboarding.completionStep"
)
completionStep.title = "Enrollment Complete"
completionStep.text = "Thank you for enrolling in this study. Your participation will contribute to meaningful research!"
/// ์ง๊ธ๊น์ง์ ORKCompletionStep ์ ํตํด ORKOrderedTask ๋ฅผ ๋ฐํ.
let surveyTask = ORKOrderedTask(
identifier: "onboard",
steps: [
welcomeInstructionStep,
studyOverviewInstructionStep,
webViewStep,
requestPermissionsStep,
completionStep
]
)
return surveyTask
CareFeedViewController ๋ก ๋์๊ฐ์ CareKit ๊ธฐ๋ฐ์ ์ฑ์์ ์ด ๊ธฐ๋ฅ์ ํ์ํ๋ ๋ฐฉ๋ฒ์ ๋ค์ ์ดํด๋ณด์!
// MARK: SurveyTaskViewControllerDelegate
// 1.6 Refresh the content when onboarding completes
//
func surveyTask(
viewController: OCKSurveyTaskViewController,
for task: OCKAnyTask,
didFinish result: Result<ORKTaskViewControllerFinishReason, Error>) {
// ์ฐธ๊ฐ์๊ฐ ์ค๊ฐ์ ์ทจ์ํ ๊ฒ์ด ์๋๋ผ ์จ๋ณด๋ฉ์ ๋๊น์ง ์ฑ๊ณต์ํจ ๊ฒฝ์ฐ์
๋๋ค.
// ์ด๋ ํผ๋์ ์๋ก๊ณ ์นจ ์งํ.
if case let .success(reason) = result, reason == .completed {
reload()
}
}
์, ์ด์ ์ฑ์ด ์ ๋๋ก ์๋ํ๋์ง ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค.
๐ก ๋จ,
HealthKit
์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ํด๋น Bundle Identifier ๋ฅผ App Deleveloper ์์ ๋ฑ๋กํด์ค์ผ ํฉ๋๋ค~!
์จ๋ณด๋ฉ์ด ๋๋๊ฒ๋๋ฉด consent ๋ฅผ ์๋ฃํ๋ผ๋ ๋ฉ์์ง๊ฐ ๋ ์ด์ ํ์๋์ง ์์ต๋๋ค. ๋์ , ์ฑ์ ๋ชจ๋ ๋ด์ฉ์ด ํ์๋ฉ๋๋ค.
๋ค์ ์ธ์ ์์ ์ด ๋ด์ฉ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค!
โ๏ธ Hey Siri, can you message Jamieย and tell him that weโre done with the onboarding and consent?
ํฌโฆ ๊ฐ์ง