현재 문서는 iOS15 기준 WWDC 영상을 보고 정리한 글이며, iOS16 에서 업데이트 된 내용은 What's new in Screen Time API 문서로 정리하겠습니다. Tublock 프로젝트를 진행하면서 ScreenTimeAPI를 사용한 App Blocking 기능을 구현하기 위해 공부해 보았습니다. 오역 및 오타 피드백 환영합니다. 😁
스크린 타임은 사용자와 가족이 앱과 웹사이트를 얼마나 자주 사용하는지 추적하고, 제한을 설정하여 시간을 관리하고, 가족 구성원과 사용량을 공유하여 기기 사용 현황을 파악할 수 있으며, 마지막으로 자녀가 누구와 소통하는지 등을 관리할 수 있습니다.
📱 사용 환경
iOS 15
iPadOS 15
✅ 3가지 기본 원칙
기존 제한에 대한 직접 API 액세스를 위한 최신 on Device 프레임워크 제공
사용자 개인 정보 보호
개발자가 새롭고 환상적인 동적 자녀 보호 환경을 만들 수 있도록 보장하는 것
🎞️ 3가지 프레임워크
✨ 1) Managed Setting Framework(관리 설정)
앱에서 스크린 타임에서 사용할 수 있는 제한 사항에 직접 액세스할 수 있다.
제한 설정할 수 있는 것
계정 잠금
비밀번호 변경 방지
휍 트래픽 필터링
애플리케이션 차단
✨ 2) Family Controls Framework(가족 제어)
가족 제어는 트위터의 개인정보 보호정책을 주도한다. 가족 공유를 활용하여 보호자 승인 없이 스크린 타임 API에 액세스 할 수 없도록 한다. 보호자의 승인을 받은 앱은 보호자 승인 없이 기기에서 제거할 수 없다. 또한 앱과 웹 사이트를 나타내는 토큰을 제공하여 스크린 타임 API 전체에서 사용량을 모니터링하거나 제한하는 데 사용된다. (가족 그룹 외부에서는 볼 수 없도록 정보가 모두 보호된다.)
✨ 3) Device Activity(디바이스 활동)
디바이스 활동은 앱을 실행하지 않고도 코드를 실행할 수 있는 기능을 제공한다. 앱에 웹 & 앱 사용을 모니터링하고 필요할 때 코드를 실행할 수 있는 새로운 방법을 제공한다. 디바이스 활동 일정과 이벤트
기기 활동 일정 : 시작될 때와 끝날 때 애플리케이션의 확장 프로그램을 호출하는 시간 창
이벤트 : 유저가 기기 활동 일정의 사용 임계값에 도달할 때 내선 번호를 호출하는 사용량 모니터.
앱은 어떤 유형의 사용량을 언제 모니터링할지 선언하기만 하면 된다.
🙄 세 가지 프레임워크를 결합하면?
보호자와 자녀의 기기 모두 앱을 설치
보호자가 자녀 기기에서 앱을 열기
앱은 가족 제어를 통해 승인됨
추후 보호자 기기의 앱에서 설정, 제한 및 규칙을 선택
앱이 해당 정보를 자녀 기기로 보냄
자녀 기기에서 앱이 장치 활동으로 일정 및 이벤트를 생성
일정이 발생하거나 이벤트가 발생하면 앱의 장치 활동 확장이 호출됨
확장 프로그램에서 관리 설정으로 제한 설정
📲 Demo App : Homework
보호자가 원하는 다른 앱의 사용량이 누적될 때까지 특정 앱에 대한 자녀의 **액세스를 제한**하여 좋은 습관을 장려하는 것이 주 기능이다.
// APP: 권한 요청하기
import FamilyControls
AuthorizationCenter.shared.requestAuthorization { result in
switch result {
case .success():
...
case .failure(let error):
...
}
}
// APP: Monitor a DeviceActivitySchedule
import DeviceActivity
// 1. 활동을 참조할 수 있는 이름 생성
extension DeviceActivityName {
static let daily = Self("daily")
}
// 2. 활동을 모니터링할 시간 범위 설정
let schedule = DeviceActivitySchedule(
intervalStart: DateComponents(hour: 0, minute: 0),
intervalEnd: DateComponents(hour: 23, minute: 59),
repeat: true
)
// 3. center를 통해 방금 생성한 이름과 일정으로 `startMonitoring` 호출
let center = DeviceActivityCenter()
try center.startMonitoring(.daily, during: schedule)
// APP: Choose the Apps to Discourage
import FamilyControls
import SwiftUI
@StateObject var model = MyModel()
@State var isPresented = false
var body: some View {
Button("Select Apps to Discourage") {
isPresented = true
}
.familyActivityPicker(isPresented: $isPresented, selection: $model.selectionTpDiscourage)
}
// EXTENSION: Shied the Discouraged Apps
import DeviceActivity
import ManagedSettings // 추가
class MyMonitor: DeviceActivityMonitor {
let store = ManagedSettingsStore() // 추가
override func intervalDidStart(for activity: DeviceActivityName) {
super.intervalDidStart(for: activity)
let model = MyModel() // 모델 인스턴스
let applicationos = model.selectionToDiscourage.applications // 선택된 앱 List
store.shield.applications = applications.isEmpty ? nil : applications // store 등록
}
override func intervalDidEnd(for activity: DeviceActivityName) {
super.intervalDidEnd(for: activity)
store.shield.applications = nil
}
}
// APP: Adding a DeviceActivityEvent
import DeviceActivity
extension DeviceActivityName {
static let daily = Self("daily")
}
extension DeviceActivityEvent.Name { // 이벤트 이름 등록
static let encouraged = Self("encouraged")
}
let schedule = DeviceActivitySchedule(
intervalStart: DateComponents(hour: 0, minute: 0),
intervalEnd: DateComponents(hour: 23, minute: 59),
repeat: true
)
let model = MyModel()
let events = [DeviceActivityEvent.Name: DeviceActivityEvent] = [
.encouraged: DeviceActivityEvent( // 이벤트 이름에 맞는 이벤트 생성
applications: model.selectionToEncourage.applicationTokens
threshold: DataComponents(minute: minutes)
)
]
let center = DeviceActivityCenter()
try center.startMonitoring(.daily, during: schedule, events: events) // 이벤트 등록
// EXTENSION: Implement theh Threshold Function
import DeviceActivity
import ManagedSettings
class MyMonitor: DeviceActivityMonitor {
let store = ManagedSettingStore() // store 인스턴스
...
// 허용된 앱의 사용량이 누적되었을 때 호출됨
override func eventDidReachThreshold(_ event: DeviceActivityEvent.Name, activity: DeviceActivityName) {
super.eventDidReachThreshold(event, activity: activity)
store.shield.applications = nil // 해당 이벤트가 충족되면 제한 해제
}
}