Agentero
Connect your own Agent, keep research context in a local Vault. Seamlessly migrate from Zotero, read, understand, link, and reuse your research context.
AI translation
다이나믹 아일랜드 · PiP 시계와 서버 시간
Translation pending
ProductivitySwift · HTMLGitHub @heopillOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
섬타임: 서버시간 · PiP 시계 (앱스토어 배포)
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗| 프로필 | 이름 | 역할 | 담당 업무 | GitHub |
|---|---|---|---|---|
| 허성필 | PM, iOS | 프로젝트 매니징, Live Activity · PiP 시계 구현, 서버 시간 측정, 설정 기능, UI 컴포넌트 개발 | 🔗 |
| 분류 | 기술 명세 |
|---|---|
| Architecture | TCA (The Composable Architecture) |
| UI Framework | SwiftUI, WidgetKit |
| Live Activity | ActivityKit (Dynamic Island / Lock Screen Live Activity) |
| Picture in Picture | AVKit (AVPictureInPictureController), AVFoundation (AVSampleBufferDisplayLayer, AVAudioSession), CoreMedia, CoreVideo |
| Async / State | Swift Concurrency (async/await, AsyncStream), TCA Dependencies, Swift Sharing (@Shared) |
| Network | URLSession (HTTP Date 헤더 기반 서버 시각 측정) |
| Storage | App Group UserDefaults, App Group Container (File Storage) |
| System | WebKit(WebView), MessageUI(Mail), OSLog |
| Font | Pretendard, Doto (위젯 확장은 서브셋 폰트 사용) |
| Localization | String Catalog (.xcstrings) — 한국어 / 영어 |
| Deployment | iOS 18.0+, iPhone / iPad, Swift 5.0 |
| Ads | Google Mobile Ads SDK (AdMob), User Messaging Platform (UMP) |
HomeFeature 리듀서로 모아, 서버 시간 시계와 일반 시계가 동시에 켜지지 않는 규칙을 한 곳에서 보장했습니다.Reducer로 분리하고 @Reducer enum Destination과 ifLet으로 화면 전환을 조합했습니다. 서버 시간 화면은 delegate 액션으로 시작 · 종료만 요청하고 실제 시계 제어는 홈이 맡아 책임을 나눴습니다.LiveActivityClient · PipClockClient · ServerTimeClient로 정의하고 @Dependency로 주입해, ActivityKit · AVKit · 네트워크 같은 시스템 의존성을 리듀서 밖으로 분리했습니다.@DependencyClient로 클라이언트를 추상화해, 테스트와 프리뷰 환경에서 손쉽게 대체할 수 있도록 설계했습니다.@Shared(.appStorage)로 App Group UserDefaults에 저장해, 설정 화면에서 값을 바꾸는 즉시 홈 · Live Activity · PiP가 같은 값을 바라보도록 했습니다.fileStorage로 App Group 컨테이너에, 실행 중인 서버 시간 시계는 inMemory로 관리해 데이터의 수명에 맞게 저장 위치를 나눴습니다.Text(.currentDate, format:)로 시스템이 직접 시각을 갱신하게 했습니다. 앱은 설정이 바뀔 때만 Live Activity를 업데이트합니다.Text(timerInterval:) 방식은 자정이 지나면 24:00:xx로 넘어가는 문제가 검증 과정에서 확인되어, 최소 지원 버전을 iOS 18.0으로 올렸습니다.Date.FormatStyle은 기기의 24시간제 설정에 따라 표시가 달라져, Date.VerbatimFormatStyle로 형식을 직접 지정했습니다. 한글 글자가 없는 폰트(모노 · 도트)의 12시간제는 "오후" 대신 AM/PM으로 표시합니다.isDynamicIslandLimitedInWidth 환경 값으로 분기해, 폭이 제한된 가로 아일랜드에서는 카메라 위에 시 · 분, 아래에 초를 나눠 그리고 ViewThatFits로 글자 크기를 단계적으로 맞췄습니다.ImageRenderer로 그려 CVPixelBuffer → CMSampleBuffer로 변환한 뒤 AVSampleBufferDisplayLayer에 넣어 AVPictureInPictureController로 띄웠습니다. 창을 키워도 선명하도록 6배 해상도로 그리고, 픽셀 버퍼 풀을 재사용합니다..playback + .mixWithOthers로 설정해 다른 앱의 소리를 끊지 않으며, canStartPictureInPictureAutomaticallyFromInline으로 앱이 백그라운드로 갈 때 PiP가 자동으로 시작되도록 했습니다.HEAD(막히면 GET) 요청을 보내 응답의 Date 헤더로 서버 시각을 얻습니다. 본문은 받지 않고 헤더가 도착하는 즉시 요청을 끊어 왕복 시간을 줄였습니다.Date 헤더는 초 단위라, 응답 순간의 서버 시각이 [S, S+1) 범위에 있다는 점을 이용했습니다. 네트워크 지연을 왕복 시간의 절반으로 보정한 여러 표본의 범위를 교집합으로 좁혀, 폰 시각 대비 오프셋과 오차(±) 를 계산합니다.폰 시각 + 오프셋을 사용합니다. PiP는 밀리초까지 그대로 더하고, Live Activity는 시스템 갱신 텍스트에 오프셋만큼 옮긴 시간대(TimeZone)를 적용했습니다.@Shared(.adConsent) 하나에 저장해 모든 화면의 배너와 설정 화면이 같은 값을 읽습니다.safeAreaInset으로 화면 하단에 고정하고, 동의 확인 · 로딩 중에는 배너 높이만큼 자리를 비워 두며, 광고를 받지 못하면 자리를 접어 레이아웃이 흔들리지 않게 했습니다.| Version | Date | Description |
|---|---|---|
| v26.0.0 | 2026.10.10 | 첫 공식 배포 (Initial Release) |
| v26.1.0 | 2026.10.15 | 티켓팅 연습 기능 추가 (정각 클릭, 포도알 클릭) |
| v26.2.0 | 2026.10.20 | 애드몹(AdMob) 광고 배너 추가, 일본어 지원 |
| v26.3.0 | 2026.10.22 | 목표 시각 카운트다운 · 알림 · 진동 기능 추가, PiP 시계 깜빡임 추가 |
| v26.3.1 | 2026.10.26 | 보안 문자 연습 기능 추가, 목표 시간 10초 후 서버 시계 자동 꺼짐 기능 추가 |
SomTime/
├── SomTime/ # 앱 메인 타깃
│ ├── SomTimeApp.swift # 앱 진입점
│ ├── Info.plist # 앱 Info.plist (폰트, 백그라운드 오디오 모드, AdMob 앱 ID · SKAdNetwork)
│ ├── InfoPlist.xcstrings # 앱 이름 다국어 리소스 (섬타임 / SomTime)
│ ├── Localizable.xcstrings # 다국어 문자열 리소스 (한국어 / 영어 / 일본어)
│ ├── SomTime.entitlements # App Group 권한 (앱↔위젯 데이터 공유)
│ │
│ ├── Features/ # 기능별 화면 모듈 (View · Feature)
│ │ ├── Home/ # 홈 (시계 토글 · 미리보기, 시계 전환의 중심)
│ │ │ ├── HomeFeature.swift
│ │ │ ├── HomeView.swift
│ │ │ └── HomePreviewArea.swift # └ 아일랜드 · PiP 창 미리보기
│ │ ├── ServerTime/ # 서버 시간 (측정 · 표시 방식 선택 · 시작)
│ │ │ ├── ServerTimeFeature.swift
│ │ │ ├── ServerTimeView.swift
│ │ │ └── ServerTimeModels.swift
│ │ ├── Settings/ # 설정 (디자인 · 색상 · 표시 옵션 · 문의하기 · 광고 개인정보 설정)
│ │ │ ├── SettingsFeature.swift
│ │ │ ├── SettingsView.swift
│ │ │ └── MailComposeView.swift
│ │ ├── TicketingPractice/ # 티켓팅 연습 (정각 클릭 · 포도알 클릭)
│ │ │ ├── TicketingPracticeFeature.swift
│ │ │ ├── TicketingPracticeView.swift # └ 연습 선택 화면
│ │ │ ├── TimingPracticeFeature.swift
│ │ │ ├── TimingPracticeView.swift # └ 정각 클릭 연습
│ │ │ ├── SeatPracticeFeature.swift
│ │ │ ├── SeatPracticeView.swift # └ 포도알 클릭 연습
│ │ │ └── TicketingPracticeModels.swift
│ │ └── PrivacyPolicy/ # 개인정보 처리방침
│ │ ├── PrivacyPolicyFeature.swift
│ │ └── PrivacyPolicyView.swift
│ │
│ ├── Clients/ # TCA Dependency Client
│ │ ├── LiveActivityClient.swift # Live Activity 시작 · 업데이트 · 종료 (ActivityKit)
│ │ ├── PipClockClient.swift # PiP 시계 시작 · 업데이트 · 종료
│ │ ├── ServerTimeClient.swift # 서버 시각 측정 (HTTP Date 헤더)
│ │ └── AdClient.swift # 광고 동의 확인 · SDK 시작 · 광고 개인정보 설정
│ │
│ ├── Ads/ # AdMob 배너 광고
│ │ ├── AdController.swift # UMP 동의 처리 · Google Mobile Ads SDK 시작
│ │ ├── AdPlacement.swift # 화면별 광고 단위 ID (Debug는 테스트 광고)
│ │ └── BannerAdView.swift # SDK BannerView를 감싼 UIViewRepresentable
│ │
│ ├── PictureInPicture/ # PiP 시계 구현
│ │ ├── PipClockController.swift # AVPictureInPictureController · 오디오 세션 관리
│ │ └── PipClockFrameRenderer.swift # SwiftUI 시계 → CMSampleBuffer 렌더링
│ │
│ ├── Components/ # 공통 UI 컴포넌트
│ │ ├── AdBannerSlot.swift # 화면 하단 광고 배너 자리
│ │ ├── Chip.swift
│ │ ├── ColorSwatch.swift
│ │ ├── DesignCard.swift
│ │ ├── DifficultyButton.swift
│ │ ├── FeatureCard.swift
│ │ ├── HomeTile.swift
│ │ ├── IconButton.swift
│ │ ├── IconTile.swift
│ │ ├── IslandPill.swift
│ │ ├── MiniSeatGrid.swift
│ │ ├── PipClockWindow.swift
│ │ ├── PracticeClockText.swift
│ │ ├── PracticeModeCard.swift
│ │ ├── PrimaryButton.swift
│ │ ├── RecordList.swift
│ │ ├── ResultIcon.swift
│ │ ├── ScreenHeader.swift
│ │ ├── SeatButton.swift
│ │ ├── SegmentedPicker.swift
│ │ ├── SelectionButton.swift
│ │ ├── SwitchIndicator.swift
│ │ ├── TimeBreakdownBar.swift
│ │ ├── ToastMessage.swift
│ │ ├── URLInputField.swift
│ │ └── WebView.swift
│ │
│ ├── Storage/ # @Shared 저장 키
│ │ ├── AdSharedKeys.swift # 광고 동의 상태 (inMemory)
│ │ ├── ClockSettingsSharedKeys.swift # 시계 설정 (App Group UserDefaults)
│ │ ├── ServerTimeSharedKeys.swift # 서버 시간 입력값 · 측정 결과 · 실행 상태
│ │ └── TicketingPracticeSharedKeys.swift # 티켓팅 연습 기록 · 옵션 (앱 전용 저장소)
│ │
│ ├── Extensions/ # 디자인 · 색상 · PiP 비율 · 난이도 표시 이름
│ │ ├── ClockColor+Name.swift
│ │ ├── ClockDesign+Name.swift
│ │ ├── PipAspectRatio+Name.swift
│ │ └── TicketingPractice+Name.swift
│ │
│ ├── Utilities/
│ │ ├── DeviceCapability.swift # 다이나믹 아일랜드 · PiP 지원 여부 판별
│ │ └── UINavigationController+SwipeBack.swift # 스와이프 백 제스처
│ │
│ ├── Resources/ # 개인정보 처리방침 HTML (ko / en / ja)
│ └── Assets.xcassets/ # 앱 아이콘
│
├── Shared/ # 앱 · 위젯 확장 공유 코드
│ ├── ClockActivityAttributes.swift # Live Activity 공유 Attributes
│ ├── ClockDisplaySettings.swift # 시계 표시 설정 묶음 (서버 시간 오프셋 포함)
│ ├── ClockSettingsStorage.swift # App Group UserDefaults 키 · 기본값
│ ├── ClockTimeFormatter.swift # 시각 문자열 형식 (VerbatimFormatStyle)
│ ├── ClockDesign.swift # 시계 디자인 (기본 · 굵게 · 모노 · 도트)
│ ├── ClockColor.swift # 시계 색상 6종 · 강조색
│ ├── PipAspectRatio.swift # PiP 창 비율 (10:1 · 2:1)
│ ├── ServerTimeClock.swift # 서버 시간 시계 정보 (호스트 · 오프셋)
│ ├── DesignMetrics.swift # 디자인 수치
│ ├── FontStyle.swift # 폰트 스타일 정의
│ ├── SharedAssets.xcassets/ # 디자인 토큰 · 시계 색상 에셋
│ └── Fonts/ # Pretendard, Doto
│
└── SomTimeWidget/ # 위젯 익스텐션 타깃 (다이나믹 아일랜드 / Live Activity)
├── SomTimeWidgetBundle.swift # └ 위젯 번들 진입점
├── ClockLiveActivity.swift # 잠금 화면 · 다이나믹 아일랜드 UI
├── IslandClockViews.swift # 컴팩트 · 미니멀 · 가로 모드 시계 뷰
├── LiveClockText.swift # 시스템이 갱신하는 현재 시각 텍스트
├── Fonts/ # Live Activity 용량 제한용 Pretendard 서브셋
├── Localizable.xcstrings # 위젯 다국어 문자열
├── SomTimeWidgetExtension.entitlements # 위젯 App Group 권한
└── Info.plist # 위젯 익스텐션 Info.plist
Copyright © 2026 Seongpil Heo. All rights reserved.