RunningTime์ ์ง์ค ํ์ด๋จธ๋ฅผ ์ค์ ์ด๋ ๊ธฐ๋ก์ฒ๋ผ ์๊ฐํํ๋ iOS ์ฑ์ ๋๋ค. ์ฌ์ฉ์๋ ์ง์ ๋ง๋ ๊ฒฝ๋ก ๋๋ MapKit ๋๋ณด ๊ฒฝ๋ก๋ฅผ ์ ํํ๊ณ , ์ค์ ํ ์๊ฐ ๋์ ํ์ด๋จธ๋ฅผ ์คํํฉ๋๋ค. ์ฑ์ ๊ฒฝ๋ก ์์ ํ์ฌ ์์น, ์๋, ๋จ์ ์๊ฐ, Look Around ๋๋ 3D ์ง๋ ํ๋ฉด์ ๋ณด์ฌ ์ฃผ๋ฉฐ, ์๋ฃ๋ ์ธ์ ์ Core Data์ ์ ์ฅ๋์ด ํ๊ณผ ์ ๋์์ ๋ค์ ํ์ธํ ์ ์์ต๋๋ค.
- UIKit ๊ธฐ๋ฐ iOS ์ฑ์ด๋ฉฐ, ์์ ฏ๊ณผ Live Activity๋ WidgetKit/SwiftUI/ActivityKit์ ์ฌ์ฉํฉ๋๋ค.
- ํ๋ฉด ์ํ ๊ด๋ฆฌ๋ ReactorKit + RxSwift ํจํด์ ์ฌ์ฉํฉ๋๋ค.
- ํ๋ฉด ์ด๋์
AppCoordinator,WalkCoordinator,JournalCoordinator์ค์ฌ์ผ๋ก ๋ถ๋ฆฌ๋์ด ์์ต๋๋ค. - ๊ฒฝ๋ก ์์ฑ์ MapKit
MKDirections๋๋ณด ๊ฒฝ๋ก์ ์ฌ์ฉ์๊ฐ ์ง์ ๊ทธ๋ฆฌ๋ ๋๋ก์ ๊ฒฝ๋ก๋ฅผ ๋ชจ๋ ์ง์ํฉ๋๋ค. - ๊ฒฝ๋ก ํ์์ Core Data์ ์ง์ ๋ฃ์ง ์๊ณ
Documents/trails/{UUID}.geojsonํ์ผ๋ก ์ ์ฅํฉ๋๋ค. - ์ธ์ , ์ฝ์ค ๋ฉํ๋ฐ์ดํฐ, ์ฆ๊ฒจ์ฐพ๊ธฐ ์ํ๋ Core Data์ ์ ์ฅํฉ๋๋ค.
- ์์ ฏ๊ณผ Live Activity ๋ณต์ ๋ฐ์ดํฐ๋ App Group
UserDefaults์ App Group ํ์ผ ์ปจํ ์ด๋๋ฅผ ์ฌ์ฉํฉ๋๋ค. - ํ์ด๋จธ ์งํ์ ์ค์ GPS ์ถ์ ์ด ์๋๋ผ, ๊ฒฝ๊ณผ ์๊ฐ์ ๊ฒฝ๋ก waypoint ๋น์จ์ ๋งคํํ๋ ๊ฐ์ ์งํ ๋ฐฉ์์ ๋๋ค.
| ์์ญ | ์ฌ์ฉ ๊ธฐ์ |
|---|---|
| ์ธ์ด | Swift |
| ๋ฉ์ธ UI | UIKit |
| ์์ ฏ/Live Activity UI | SwiftUI, WidgetKit, ActivityKit |
| ์ํ ๊ด๋ฆฌ | ReactorKit 3.2.0, RxSwift 6.10.2, RxCocoa |
| ๋ ์ด์์ | SnapKit 5.7.1 |
| ์ง๋/๊ฒฝ๋ก | MapKit, CoreLocation, MKDirections, MKLookAroundSceneRequest |
| ์ ์ฅ์ | Core Data, FileManager, App Group UserDefaults |
| ์คํฌ๋ฆฐํ์ | FamilyControls, ManagedSettings |
| ํ๋ก์ ํธ | Xcode project, Swift Package Manager |
xcodebuild -list -project RunningTime.xcodeproj ๊ธฐ์ค์ผ๋ก RunningTime, RunningTimeTests, RunningTimeUITests, FavoriteCourseWidgetExtension ํ๊น๊ณผ RunningTime, FavoriteCourseWidgetExtension ์คํด์ด ์์ต๋๋ค.
RunningTime
โโโ App
โ โโโ AppCoordinator
โ โโโ AppDIContainer
โ โโโ CustomTabBarController
โ โโโ Coordinators
โโโ DesignSystem
โ โโโ Colors / Typography / Spacing / Animation
โ โโโ Components
โโโ Features
โ โโโ Home
โ โโโ RouteBuilder
โ โโโ PlaceSearch
โ โโโ CourseDetail
โ โโโ TimerSetting
โ โโโ WalkTimer
โ โโโ SessionReport
โ โโโ Journal
โโโ Shared
โ โโโ Services
โ โโโ db
โ โโโ AppGroup
โ โโโ Models
โ โโโ Utils
FavoriteCourseWidget
โโโ Widget / Control Widget / Live Activity
์ฑ ๋ฃจํธ๋ AppCoordinator๊ฐ ์์ ํฉ๋๋ค.
Walk ํญ์ WalkCoordinator๊ฐ Home, RouteBuilder, CourseDetail, WalkTimer, SessionReport, PlaceSearch ์ด๋์ ๊ด๋ฆฌํฉ๋๋ค. Journal ํญ์ JournalCoordinator๊ฐ Journal ๋ชฉ๋ก๊ณผ ์ธ์
์์ธ ํ๋ฉด ์ด๋์ ๊ด๋ฆฌํฉ๋๋ค.
์ง์ํ๋ ๋ฅ๋งํฌ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
runningtime://homerunningtime://course?id={UUID}runningtime://resume-timer
AppDIContainer๋ ํ๋ฉด๊ณผ Reactor ์กฐํฉ์ ์์ฑํ๊ณ ์ฑ ์๋ช
๋์ ๊ณต์ ๋๋ ์๋น์ค๋ฅผ ๋ณด๊ดํฉ๋๋ค.
PersistenceController.sharedLookAroundServiceLookAroundPlaceServiceSessionSaveService
WalkTimerReactor๋ LookAroundServiceProtocol, LookAroundPlaceServiceProtocol, SessionSaveServiceProtocol, ScreenTimeServiceProtocol์ ์์ฑ์ ์ฃผ์
์ผ๋ก ๋ฐ์ต๋๋ค.
๊ฐ ์ฃผ์ ํ๋ฉด์ Reactor์ Action -> Mutation -> State ํ๋ฆ์ ๋ฐ๋ฆ
๋๋ค. ํ๋ฉด ์ด๋์ฒ๋ผ ํ ๋ฒ๋ง ์๋น๋์ด์ผ ํ๋ ์ด๋ฒคํธ๋ ReactorKit์ @Pulse๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์์:
- Home: ๊ฒ์์ด, ์ ์ฅ๋ ์ฝ์ค ๋ชฉ๋ก, RouteBuilder/CourseDetail ์ด๋ ์ ํธ
- RouteBuilder: ์ถ๋ฐ์ง/๋์ฐฉ์ง, ๋ก๋ฉ, MapKit ๊ฒฝ๋ก, ๋๋ก์ ๊ฒฝ๋ก, ์ ์ฅ ์ํ, WalkTimer ์ด๋ ์ ํธ
- WalkTimer: ํ์ด๋จธ ๊ฒฝ๊ณผ ์๊ฐ, ์ผ์์ ์ง, ๊ฒฝ๋ก ์งํ๋ฅ , Look Around ์ํ, Live Activity ์ํ, ์ ์ฅ ์ํ
- Journal: ์ ํ ๋ ์ง, ์ฃผ/์ ๋ฒ์, ์ธ์ ๋ชฉ๋ก, ํต๊ณ
Home์ Core Data์ ์ ์ฅ๋ Route๋ฅผ ๋ถ๋ฌ์ ์ฆ๊ฒจ์ฐพ๊ธฐ ์ฝ์ค์ ์ผ๋ฐ ์ฝ์ค๋ก ๋ถ๋ฆฌํด ๋ณด์ฌ ์ค๋๋ค.
๊ตฌํ๋ ๋์:
isFavorite == true์ฝ์ค์isFavorite == false์ฝ์ค๋ฅผ ๋ณ๋ ์น์ ์ผ๋ก ํ์- ์ฆ๊ฒจ์ฐพ๊ธฐ๋ ์ด๋ฆ์, ์ผ๋ฐ ์ฝ์ค๋ ์์ฑ์ผ ์ญ์ ์ ๋ ฌ
- ๊ฒ์์ด๋ก ์ ๋ชฉ, ๋์, ์ถ๋ฐ์ง, ๋์ฐฉ์ง ํํฐ๋ง
- ์ฝ์ค ์ ํ ์ CourseDetail๋ก ์ด๋
- ๊ฒฝ๋ก ๋ง๋ค๊ธฐ ๋ฒํผ์ ํตํด RouteBuilder๋ก ์ด๋
- Home ์ง์ ํ RouteBuilder๋ฅผ ๋ฏธ๋ฆฌ ์์ฑํด MapKit ์ด๊ธฐํ ๋น์ฉ์ ์๋น๊ธฐ๋ ํ๋ฆฌ๋ก๋ ๋ก์ง
Place Search๋ MapKit ๊ฒ์ API๋ฅผ ์ฌ์ฉํด ์ฅ์๋ฅผ ์ ํํ๋ ํ๋ฉด์ ๋๋ค.
๊ตฌํ๋ ๋์:
MKLocalSearchCompleter๊ธฐ๋ฐ ์๋์์ฑ- ์๋์์ฑ ์ ํ ์
MKLocalSearch๋ก ์ขํ์ ์ฃผ์ ์ ๋ณด ํ์ PlaceItem์ ์ฝ๋ฐฑ์ผ๋ก RouteBuilder์ ์ ๋ฌ- ๋ค๋ก๊ฐ๊ธฐ ์ก์
RouteBuilder๋ ํ์ด๋จธ์ ์ฌ์ฉํ ๊ฒฝ๋ก๋ฅผ ๋ง๋๋ ํ๋ฉด์ ๋๋ค.
๊ตฌํ๋ ๋์:
- ์ถ๋ฐ์ง์ ๋์ฐฉ์ง ์ ํ
MKDirections.Request.transportType = .walking๊ธฐ๋ฐ ๋๋ณด ๊ฒฝ๋ก ๊ณ์ฐ- ์ถ๋ฐ/๋์ฐฉ annotation ๋๋๊ทธ ํ reverse geocoding ๋ฐ ๊ฒฝ๋ก ์ฌ๊ณ์ฐ
- ์ฌ์ฉ์๊ฐ ์ง๋ ์์ ์ง์ ๊ฒฝ๋ก๋ฅผ ๊ทธ๋ฆฌ๋ Drawing Mode
- ์ง์ ๊ทธ๋ฆฐ ๊ฒฝ๋ก์ ์ด ๊ฑฐ๋ฆฌ ๊ณ์ฐ
- ๊ทธ๋ฆฐ ๊ฒฝ๋ก๋ฅผ ํ์ ํ๋ฉด ๋์ฐฉ ์ขํ๋ฅผ reverse geocoding
- ๊ฒฝ๋ก ๊ฒฐ๊ณผ์์ ํ์ด๋จธ ์ค์ ํ๋ฉด์ผ๋ก ์ง์
tapWalk์ ํ์ฌ ๊ฒฝ๋ก๋ฅผCourse๊ฐ์ฒด๋ก ๋ณํํด WalkTimer๋ก ์ ๋ฌ
TimerSetting ํ๋ฉด์ ์ ํํ ๊ฒฝ๋ก๋ฅผ ์ด๋ค ์๊ฐ์ผ๋ก ์งํํ ์ง ์ ํ๋ ๋จ๊ณ์ ๋๋ค.
๊ตฌํ๋ ๋์:
- ์ฝ์ค ๊ฑฐ๋ฆฌ์ ์์ ์๊ฐ ํ์
- ์ฌ์ฉ์๊ฐ ์ปค์คํ ํ์ด๋จธ ์๊ฐ์ ์ ํ
- ์ ํํ ์๊ฐ์
customTimerMinutes๋ก WalkTimer์ ์ ๋ฌ
CourseDetail์ ์ ์ฅ๋ ์ฝ์ค์ ์์ธ ํ๋ฉด์ ๋๋ค.
๊ตฌํ๋ ๋์:
- ์ฝ์ค ์ ๋ชฉ, ์ถ๋ฐ์ง, ๋์ฐฉ์ง, ๊ฑฐ๋ฆฌ, ์์ ์๊ฐ ํ์
- ์ ์ฅ๋ ์ธ๋ค์ผ์ด ์์ผ๋ฉด ์ธ๋ค์ผ ์ฌ์ฉ
- ์ธ๋ค์ผ์ด ์์ผ๋ฉด MapKit ๊ฒฝ๋ก polyline์ผ๋ก ์ง๋ ํ์
- ์ฆ๊ฒจ์ฐพ๊ธฐ ํ ๊ธ
- ์ฆ๊ฒจ์ฐพ๊ธฐ ์ค์ ์ ์์ ฏ ๋ฐ์ดํฐ ๊ฐฑ์
- ์ฝ์ค ์ญ์
- ์ญ์ ์ Core Data Route ์ญ์ ๋ฐ GeoJSON ํ์ผ ์ญ์
- ํ์ด๋จธ ์ค์ ํ๋ฉด์ผ๋ก ์ง์
WalkTimer๋ ์ด ํ๋ก์ ํธ์ ํต์ฌ ํ๋ฉด์ ๋๋ค. ์ค์ ์์น๋ฅผ ์ถ์ ํ๋ ์ด๋ ๊ธฐ๋ก์ด ์๋๋ผ, ์ ํด์ง ํ์ด๋จธ ์๊ฐ ๋์ ์ ํํ ๊ฒฝ๋ก ์๋ฅผ ๊ฐ์์ผ๋ก ์ด๋ํ๋ ๊ฒฝํ์ ์ ๊ณตํฉ๋๋ค.
๊ตฌํ๋ ๋์:
DispatchSourceTimer๋ฅผ 0.1์ด ์ฃผ๊ธฐ๋ก ์คํ- ์์ ์๊ฐ
Date๋ฅผ ๊ธฐ์ค์ผ๋ก ๊ฒฝ๊ณผ ์๊ฐ์ ๊ณ์ฐ - ๊ฒฝ๊ณผ ์๊ฐ / ์ ์ฒด ์๊ฐ ๋น์จ์ ๊ฒฝ๋ก waypoint ์งํ๋ฅ ๋ก ๋ณํ
- ํ์ฌ ์ขํ, waypoint index, ์งํ๋ฅ , bearing ๊ณ์ฐ
- 1์ด ๋จ์ Live Activity ๊ฐฑ์
- 10์ด ๋จ์ Look Around ๋๋ ์ฅ์ ์ ๋ณด ๊ฐฑ์
- Look Around ์ง์ ์ฌ๋ถ ํ์ธ
- Look Around ๋ฏธ์ง์ ๋๋ ์คํจ ์ 3D MapKit ์นด๋ฉ๋ผ ๋ชจ๋ ์ฌ์ฉ
- ์ผ์์ ์ง, ์ฌ๊ฐ, ์๋ฃ, ์ทจ์
- ํ์ด๋จธ ์์ ์ ํ์ฑ ์ธ์ ์ค๋ ์ท ์ ์ฅ
- ์ฑ ๊ฐ์ ์ข
๋ฃ ํ Live Activity๊ฐ ์ด์ ์์ผ๋ฉด
runningtime://resume-timer๋ก ์ธ์ ๋ณต์ - ์ ํ์ ์ผ๋ก Screen Time ์ฐจ๋จ ๊ธฐ๋ฅ ์คํ
- ์๋ฃ ์ SessionSaveService๋ฅผ ํตํด ์ธ์ ์ ์ฅ
ํ์ด๋จธ ๋ณต์ ๋ฐฉ์:
- ์งํ ์ค ์ธ์
์ App Group UserDefaults์
activeSession์ ์ ์ฅ๋ฉ๋๋ค. - ์ ์ฅ ๋ด์ฉ์๋ ์ฝ์ค ID, ์ด๋ฆ, ์ถ๋ฐ/๋์ฐฉ์ง, ๊ฑฐ๋ฆฌ, ์ด ์๊ฐ, waypoint lat/lng ๋ฐฐ์ด, ์ค์ฌ ์ขํ, ์์ ์๊ฐ, ์ผ์์ ์ง ์ฌ๋ถ, ์ง์ ์ถ์ฒ, ์ธ๋ค์ผ ๊ฒฝ๋ก, ์ฆ๊ฒจ์ฐพ๊ธฐ ์ฌ๋ถ, Look Around ์ง์ ์ฌ๋ถ, Screen Time ํ์ฑ ์ฌ๋ถ๊ฐ ํฌํจ๋ฉ๋๋ค.
courseDetail์์ ์์ํ ์ธ์ ์ Core Data Route๋ฅผ ๋ค์ ์กฐํํด ๋ณต์ํฉ๋๋ค.routeBuilder์์ ์์ํ ์ธ์ ์ ์์ง DB์ ์์ ์ ์์ด snapshot waypoint๋ก Course๋ฅผ ์ฌ๊ตฌ์ฑํฉ๋๋ค.- snapshot์ waypoint๋ฅผ ์ต๋ 500๊ฐ๋ก ์ค์ฌ ์ ์ฅํฉ๋๋ค.
SessionReport๋ ํ์ด๋จธ ์๋ฃ ํ ๋ณด์ฌ ์ฃผ๋ ์์ฝ ํ๋ฉด์ ๋๋ค.
๊ตฌํ๋ ๋์:
- ์ฝ์ค๋ช , ์ถ๋ฐ์ง, ๋์ฐฉ์ง, ๋ ์ง ํ์
- ํ์ด๋จธ ์๊ฐ, ๊ฑฐ๋ฆฌ, ํ์ด์ค ํ์
- ๊ฒฝ๋ก ์๊ฐํ
- ๋ซ๊ธฐ ์ก์ ์ ํตํด ํ์ผ๋ก ์ด๋
SessionSummaryViewController๋ฅผ ๊ธฐ๋ฐ ํด๋์ค๋ก ๋๊ณ JournalDetail๊ณผ ๊ณตํต UI๋ฅผ ๊ณต์ ํฉ๋๋ค.
Journal์ ์๋ฃ๋ ์ธ์ ์ ์บ๋ฆฐ๋์ ๋ชฉ๋ก์ผ๋ก ํ์ธํ๋ ํ๋ฉด์ ๋๋ค.
๊ตฌํ๋ ๋์:
- ์ฃผ๊ฐ/์๊ฐ ์บ๋ฆฐ๋ scope ์ ํ
- ์ด์ /๋ค์ ๋ฌ ์ด๋
- ์ธ์ ์ด ์๋ ๋ ์ง dot ํ์
- ์ ํํ ๋ ์ง์ ์ธ์ ๋ง ํํฐ๋ง
- ์ ํ ๋ฒ์์ ์ด ์ง์ค ์๊ฐ, ์ธ์ ์, ์ด ๊ฑฐ๋ฆฌ ๊ณ์ฐ
- ์ธ์ ํ ์ ํ ์ ์์ธ ํ๋ฉด ์ด๋
- JournalDetail์์ ๊ณผ๊ฑฐ ์ธ์ ์์ฝ๊ณผ ์ธ๋ค์ผ ํ์
ํต๊ณ ๊ณ์ฐ ๋ฐฉ์:
- ์ง์ค ์๊ฐ: ์ธ์
elapsedSecondsํฉ๊ณ - ์ธ์
์: ํํฐ๋ง๋
SessionEntity๊ฐ์ - ๊ฑฐ๋ฆฌ: ์ฐ๊ฒฐ๋
route.distanceMetersํฉ๊ณ
FavoriteCourseWidgetExtension์ ์ธ ๊ฐ์ง WidgetBundle ํญ๋ชฉ์ ํฌํจํฉ๋๋ค.
FavoriteCourseWidgetFavoriteCourseWidgetControlFavoriteCourseWidgetLiveActivity
FavoriteCourseWidget:
- App Group UserDefaults์์ ์ฆ๊ฒจ์ฐพ๊ธฐ ์ฝ์ค ๋๋ ์ต๊ทผ ์ ์ฅ ์ฝ์ค๋ฅผ ์ฝ์ต๋๋ค.
- ์ฐ์ ์์๋ favorite course, ๊ทธ๋ค์ saved course์ ๋๋ค.
- timeline refresh policy๋ 1์๊ฐ ํ ๊ฐฑ์ ์ ๋๋ค.
- ์ํ/์คํ ์์ ฏ์ ์ง์ํฉ๋๋ค.
- ์ฝ์ค๊ฐ ์์ผ๋ฉด ์ธ๋ค์ผ ๋ฐฐ๊ฒฝ, ๊ทธ๋ผ๋์ธํธ, ์ ๋ชฉ, ๊ฑฐ๋ฆฌ, ์ถ๋ฐ/๋์ฐฉ์ง๋ฅผ ํ์ํฉ๋๋ค.
- ์ฝ์ค๊ฐ ์์ผ๋ฉด EmptyWidgetContentView๋ฅผ ํ์ํฉ๋๋ค.
- ์์ ฏ ํญ ์ ์ฝ์ค๊ฐ ์์ผ๋ฉด
runningtime://course?id={UUID}, ์์ผ๋ฉดrunningtime://home์ผ๋ก ์ด๋ํฉ๋๋ค. - Scene ์ฐ๊ฒฐ ์ App Group UserDefaults๊ฐ ๋น์ด ์์ผ๋ฉด Core Data์์ ์ฆ๊ฒจ์ฐพ๊ธฐ/์ต๊ทผ ์ ์ฅ ์ฝ์ค๋ฅผ ๋๊ธฐํํ๊ณ Widget timeline์ reloadํฉ๋๋ค.
Live Activity:
- Lock Screen/Banner์ Dynamic Island compact/minimal/expanded UI๋ฅผ ๊ตฌํํฉ๋๋ค.
- ์์ ์์น, ๋์ฐฉ ์์น, ๋์, ๋จ์ ์๊ฐ, ์ผ์์ ์ง ์๊ฐ, ์๋ฃ ์ํ๋ฅผ ํ์ํฉ๋๋ค.
- ํญ URL์
runningtime://resume-timer์ ๋๋ค.
Control Widget:
FavoriteCourseWidgetControlํ์ผ์ ์กด์ฌํ์ง๋ง ํ์ฌcurrentValue()๊ฐ ํญ์true๋ฅผ ๋ฐํํ๊ณStartTimerIntent.perform()๋ ์ค์ ํ์ด๋จธ ์์/์ ์ง ๋ก์ง ์์ด.result()๋ง ๋ฐํํฉ๋๋ค.- ๋ฐ๋ผ์ ํ์ฌ README์์๋ ์์ฑ ๊ธฐ๋ฅ์ด ์๋๋ผ placeholder ์ฑ๊ฒฉ์ ์ฝ๋๋ก ๋ถ๋ฅํฉ๋๋ค.
Core Data ๋ชจ๋ธ์ RouteEntity์ SessionEntity ๋ ๊ฐ์
๋๋ค.
| ํ๋ | ์ค๋ช |
|---|---|
id |
Route UUID |
name |
์ฝ์ค ์ด๋ฆ |
originName |
์ถ๋ฐ์ง ์ด๋ฆ |
destinationName |
๋์ฐฉ์ง ์ด๋ฆ |
distanceMeters |
๊ฒฝ๋ก ๊ฑฐ๋ฆฌ |
geoJSONPath |
Documents/trails/... ํ์์ GeoJSON ์๋ ๊ฒฝ๋ก |
thumbnailPath |
Documents ๊ธฐ์ค ์ธ๋ค์ผ ์๋ ๊ฒฝ๋ก |
isFavorite |
์ฆ๊ฒจ์ฐพ๊ธฐ ์ฌ๋ถ |
completedSessionCount |
์๋ฃ ์ธ์ ์ |
createdAt |
์์ฑ์ผ |
lastSessionDate |
๋ง์ง๋ง ์ธ์ ๋ ์ง |
sessions |
์ฐ๊ฒฐ๋ SessionEntity ๋ชฉ๋ก |
| ํ๋ | ์ค๋ช |
|---|---|
id |
Session UUID |
targetDurationSeconds |
๋ชฉํ ์๊ฐ |
elapsedSeconds |
์ค์ ๊ฒฝ๊ณผ ์๊ฐ |
isCompleted |
์๋ฃ ์ฌ๋ถ |
createdAt |
์์ฑ์ผ |
route |
์ฐ๊ฒฐ๋ RouteEntity |
Route ์ญ์ ์ ์ฐ๊ฒฐ๋ Session์ cascade ์ญ์ ๋ฉ๋๋ค. Session์์ Route๋ก์ ๊ด๊ณ ์ญ์ ๊ท์น์ nullify์ ๋๋ค.
๊ฒฝ๋ก ์ขํ๋ Core Data๊ฐ ์๋๋ผ GeoJSON ํ์ผ๋ก ์ ์ฅํฉ๋๋ค.
์ ์ฅ ์์น:
Documents/trails/{UUID}.geojson
GeoJSON ์ขํ ์์๋ ํ์ค์ ๋ง์ถฐ [longitude, latitude]์
๋๋ค. ์ฝ์ ๋๋ ๋ค์ CLLocationCoordinate2D(latitude:longitude:)๋ก ๋ณํํฉ๋๋ค.
RouteGeoJSONEncoder.loadCoordinates(from:)๋ Documents/ prefix๊ฐ ์๋ ๊ฒฝ๋ก๋ง ์ฒ๋ฆฌํฉ๋๋ค. ๋ค๋ฅธ ์๋ ๊ฒฝ๋ก ํ์์ ๋น waypoint ๋ฐฐ์ด๋ก ์ฒ๋ฆฌ๋ฉ๋๋ค.
์ธ์ ์๋ฃ ์ ์ธ๋ค์ผ์ด ์์ผ๋ฉด ๋ค์ ์์น์ ์ ์ฅํฉ๋๋ค.
Documents/thumbnails/{routeID}.jpg
์์ ฏ ํ์์ฉ ์ธ๋ค์ผ์ App Group ์ปจํ
์ด๋์ widget-thumbnails ๋๋ ํฐ๋ฆฌ์ 600px ์ดํ JPEG๋ก ๋ณต์ฌ๋ฉ๋๋ค.
App Group ID:
group.com.sunkeydokey.RunningTime
UserDefaults keys:
| Key | ๋ด์ฉ |
|---|---|
widget.favoriteCourse |
์์ ฏ์ ํ์ํ ์ฆ๊ฒจ์ฐพ๊ธฐ ์ฝ์ค |
widget.savedCourse |
์ต๊ทผ ์ ์ฅ ์ฝ์ค |
activeSession |
์งํ ์ค์ธ ํ์ด๋จธ ๋ณต์ snapshot |
์์ ฏ ์ฝ์ค ์ํธ๋ฆฌ๋ ๋ค์ ์ ๋ณด๋ฅผ ํฌํจํฉ๋๋ค.
idtitlecitydurationMinutesdistanceMetersorigindestinationsharedThumbnailPath
decoder๋ ๊ณผ๊ฑฐ ๋ฐ์ดํฐ์์ ํธํ์ ์ํด distanceMeters, origin, destination, sharedThumbnailPath ๋๋ฝ์ ํ์ฉํฉ๋๋ค.
DefaultScreenTimeService๋ FamilyControls์ ManagedSettings๋ฅผ ์ฌ์ฉํฉ๋๋ค.
๊ตฌํ๋ ๋์:
AuthorizationCenter.shared.requestAuthorization(for: .individual)๋ก ๊ถํ ์์ฒญ- ์น์ธ ์
ManagedSettingsStore๋ก ์ ์ฒด ์ฑ ์นดํ ๊ณ ๋ฆฌ, ์น ๋๋ฉ์ธ ์นดํ ๊ณ ๋ฆฌ, ์ฑ ์ค์น๋ฅผ ์ฐจ๋จ - ํ์ด๋จธ ์ข
๋ฃ ๋๋ ํด์ ์
clearAllSettings() - ๊ถํ ์คํจ ์ ํ์ด๋จธ ์์ฒด๋ ๊ณ์ ์งํ
ํ์ฌ ๊ตฌํ์ ์ฌ์ฉ์๊ฐ ํน์ ์ฑ์ ๊ณ ๋ฅด๋ ๋ฐฉ์์ด ์๋๋ผ device-wide shield์ ๊ฐ๊น์ต๋๋ค.
ํ์ฌ RunningTimeTests์๋ GeoManagerTests๊ฐ ์์ต๋๋ค.
๊ฒ์ฆ ๋ฒ์:
- GeoJSON top-level ๊ตฌ์กฐ
- GeoJSON ์ขํ ์์๊ฐ
[longitude, latitude]์ธ์ง ํ์ธ - 20m ๊ธฐ๋ณธ ๋ณด๊ฐ ๊ฐ๊ฒฉ
- ์งง์ segment๋ ๋ณด๊ฐํ์ง ์๋์ง ํ์ธ
- ๋ณด๊ฐ ๊ฒฐ๊ณผ๊ฐ ์์/๋ ์ขํ๋ฅผ ๋ณด์กดํ๋์ง ํ์ธ
- ๋ณด๊ฐ ์ขํ๊ฐ segment bounds ์์ ์๋์ง ํ์ธ
- ๋น ๋ฐฐ์ด, ๋จ์ผ ์ขํ edge case
- bearing north/south/east/west ๋ฐ 0..<360 ์ ๊ทํ
- GeoJSON ํ์ผ ์ ์ฅ ๋ฐ JSON ํ์ฑ
CLGeocoderreverse geocoding ํตํฉ ํ ์คํธMKDirectionswalking route ํตํฉ ํ ์คํธ- ๊ฐ์ ์ขํ ๊ฒฝ๋ก ์์ฒญ edge case
ํ ์คํธ ์คํ ์์:
xcodebuild test -project RunningTime.xcodeproj -scheme RunningTime -destination 'platform=iOS Simulator,name=iPhone 16'์๋ฎฌ๋ ์ดํฐ ์ด๋ฆ์ ๋ก์ปฌ Xcode ํ๊ฒฝ์ ๋ง๊ฒ ๋ฐ๊ฟ์ผ ํฉ๋๋ค.
์๊ตฌ ์กฐ๊ฑด:
- Xcode
- iOS 18 ์ด์ ์ฑ/์์ ฏ ํ๊น
- App Group capability
- Family Controls entitlement
- WidgetKit/ActivityKit ์ง์ ํ๊ฒฝ
ํ๋ก์ ํธ ์ ๋ณด ํ์ธ:
xcodebuild -list -project RunningTime.xcodeproj๋น๋ ์์:
xcodebuild build -project RunningTime.xcodeproj -scheme RunningTime -destination 'generic/platform=iOS Simulator'์ฑ ํ๊น๊ณผ ์์ ฏ ํ๊น์ deployment target์ ํ๋ก์ ํธ ํ์ผ ๊ธฐ์ค iOS 18.0์ ๋๋ค. ํ ์คํธ ํ๊น์๋ iOS 26.2 deployment target ์ค์ ์ด ๋ค์ด ์์ต๋๋ค.
์์ ฏ ์ ์ฉ ์คํด FavoriteCourseWidgetExtension์ SpringBoard๋ฅผ ํตํด ์์ ฏ ์คํ ํ๊ฒฝ์ ๊ตฌ์ฑํ๋ฉฐ _XCWidgetFamily=systemMedium, _XCWidgetKind=FavoriteCourseWidget ๊ฐ์ ์ฌ์ฉํฉ๋๋ค.