Skip to content

thingineeer/pumpWater-iOS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

46 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PumpWater App Icon

PumpWater - Water Eject

Speaker Cleaner & Sound Fix
과학적으로 최적화된 음파 주파수를 사용하여 iPhone & iPad 스피커에서 물을 배출합니다.

App Store v2.0.0 iOS 17+ Swift 6.0 SwiftUI Platform License


동작 원리

PumpWater는 디바이스 스피커를 통해 정밀한 165Hz 사인파를 생성합니다. 이 주파수는 iPhone 및 iPad 스피커의 물리적 크기에 맞춰 최적화되어 있으며, 스피커 멤브레인을 최적의 속도로 진동시켜 스피커 그릴에 갇힌 물을 물리적으로 밀어냅니다.

Apple Watch의 Water Lock 기능에서 영감을 받았으며, 고급 사용자를 위해 더 넓은 주파수 범위(20Hz -- 2,600Hz)를 지원합니다.

주요 기능

기능 설명
최적화 주파수 165Hz 사인파 -- iPhone 스피커 물 배출에 가장 효과적인 주파수
커스텀 주파수 범위 20Hz ~ 2,600Hz 전체 슬라이더 제어
빠른 프리셋 Low(165Hz), Mid(300Hz), High(500Hz) 원탭 전환
타이머 최대 10분까지 설정 가능, 자동 정지 기능
물 애니메이션 Canvas + TimelineView 기반 30fps 실시간 유체 애니메이션
안전 경고 2,000Hz 이상 주파수 설정 시 청력 위험 경고 표시
무음 모드 지원 디바이스 무음 스위치가 켜져 있어도 정상 작동 (.playback 오디오 카테고리)
다크 & 라이트 모드 Liquid Glass 디자인 기반 완벽한 외관 대응
유니버설 앱 iPhone 및 iPad 최적화 레이아웃
8개 언어 지원 한국어, 영어, 독일어, 스페인어, 프랑스어, 일본어, 중국어 간체 & 번체
접근성 VoiceOver 라벨 & 힌트, Reduce Motion 대응, 44pt 최소 터치 영역

기술 스택

카테고리 기술 용도
UI SwiftUI 전체 사용자 인터페이스
오디오 AVFoundation (AVAudioEngine + AVAudioPlayerNode) 실시간 사인파 생성
애니메이션 Canvas + TimelineView 30fps 물결 파형 + 물방울 파티클
디자인 시스템 Liquid Glass (커스텀) ultraThinMaterial, 그래디언트, 글라스 카드
상태 관리 @Observable (Observation 프레임워크) HomeViewModel
분석 Firebase Analytics 9개 이벤트 추적
원격 설정 Firebase Remote Config 강제 업데이트 버전 체크
광고 Google AdMob (Banner) 하단 배너 광고
개인정보 App Tracking Transparency IDFA 동의 프롬프트
모듈화 Local Swift Packages (4개 패키지) Clean Architecture 레이어 분리
패키지 관리 Swift Package Manager Firebase SDK, Google Mobile Ads SDK
배포 Fastlane Match 코드 서명, TestFlight & App Store 업로드
CI/CD GitHub Actions PR 빌드 검증 + 자동 TestFlight 배포
로깅 os.log (Logger) 오디오 엔진 구조화된 로깅

아키텍처

Clean Architecture 원칙에 따라 단방향 의존성을 가진 4개의 로컬 Swift Package로 구성되어 있습니다. Presentation과 Data는 서로 독립적인 형제 레이어이며, Domain 프로토콜을 통해서만 연결됩니다.

┌─────────────────────────────────────────────────────┐
│                     App.swift                        │
│               (Composition Root + DI)                │
│         imports: Data, Presentation, Domain          │
└───────┬──────────────────┬──────────────────┬───────┘
        │                  │                  │
        ▼                  ▼                  ▼
┌───────────────┐  ┌───────────────┐  ┌───────────────┐
│ Presentation  │  │     Data      │  │    Domain     │
│               │  │               │  │               │
│ HomeView      │  │ AudioEngine   │  │ Protocols     │
│ HomeViewModel │  │ Service       │  │ Entities      │
│ Components    │  │ Firebase      │  │ FrequencyConf │
│ Theme         │  │ Analytics     │  │ TimerConfig   │
│               │  │ ForceUpdate   │  │               │
│ (SwiftUI,     │  │ Service       │  │ (Foundation   │
│  GoogleAds)   │  │               │  │  only, zero   │
│               │  │ (Firebase SDK)│  │  dependencies)│
└───────┬───────┘  └──┬─────┬──────┘  └───────────────┘
        │             │     │                 ▲  ▲
        │             │     │                 │  │
        └─────────────┼─────┼─────────────────┘  │
                      │     │                    │
                      └─────┼────────────────────┘
                            │
                            ▼
                    ┌───────────────┐
                    │     Core      │
                    │               │
                    │ Audio engine  │
                    │ settings,     │
                    │ App Store ID, │
                    │ AdMob IDs     │
                    │               │
                    │ (Foundation   │
                    │  only, zero   │
                    │  dependencies)│
                    └───────────────┘

의존성 방향:

  • Domain -- 외부 의존 없음 (최내부 레이어). 비즈니스 규칙, 프로토콜, 엔티티, 도메인 설정 포함
  • Presentation → Domain (+ GoogleMobileAds 외부 SDK)
  • Data → Domain + Core (+ Firebase SDK)
  • Core -- 외부 의존 없음. 인프라 상수만 포함
  • App.swift → 모든 레이어 (Composition Root에서 의존성 조립)

핵심 패턴:

  • Protocol-oriented DI -- AudioEngineProtocol, AnalyticsProtocol, ForceUpdateProtocol을 Domain에 정의하고, Data에서 구현
  • Composition Root -- App.swift에서 @UIApplicationDelegateAdaptor를 통해 모든 의존성 조립
  • 형제 레이어 분리 -- Presentation과 Data가 서로 의존하지 않아 독립적 테스트 및 교체 가능
  • @Observable -- HomeViewModel이 Observation 프레임워크 사용
  • ViewModifier 패턴 -- LiquidGlassCard, AdaptiveBackground, ForceUpdateModifier

프로젝트 구조

PumpWater-iOS/
├── PumpWater-iOS/
│   ├── PumpWater-iOS.xcodeproj/
│   ├── PumpWater-iOS/
│   │   ├── App/
│   │   │   ├── App.swift                    # @main, Composition Root, ATT, Splash
│   │   │   └── ContentView.swift            # Preview wrapper
│   │   ├── Assets.xcassets/
│   │   │   ├── AppIcon.appiconset/          # 앱 아이콘
│   │   │   ├── AccentColor.colorset/
│   │   │   ├── LaunchBackground.colorset/   # 스플래시 배경 (다크/라이트)
│   │   │   └── LaunchIcon.imageset/         # 스플래시 아이콘
│   │   ├── Info.plist                       # ATT, AdMob, SKAdNetwork, LaunchScreen
│   │   ├── {ko,en,ja,de,es,fr,zh-Hans,zh-Hant}.lproj/
│   │   │   └── InfoPlist.strings            # 다국어 ATT 설명
│   │   └── GoogleService-Info.plist         # Firebase 설정
│   └── Packages/
│       ├── PumpWaterCore/                   # Config, 상수 (Foundation only)
│       ├── PumpWaterDomain/                 # 프로토콜, 엔티티
│       ├── PumpWaterData/                   # 서비스 구현체 + Firebase
│       └── PumpWaterPresentation/           # SwiftUI 뷰, 컴포넌트, 테마
│           ├── Home/                        #   HomeView, HomeViewModel
│           ├── Components/                  #   PlayButton, TimerDisplay, FrequencySlider,
│           │                                #   WaterAnimation, ForceUpdateView,
│           │                                #   PrivacyPolicyView, FrequencySafetyInfoView,
│           │                                #   SplashView
│           ├── Common/                      #   Theme (Liquid Glass), AdBannerView
│           └── Resources/                   #   8개 언어 x Localizable.strings
├── fastlane/
│   ├── Fastfile                             # Lanes: beta, release, sync_certificates
│   ├── Appfile / Matchfile / Deliverfile
│   ├── metadata/                            # 7개 로케일 메타데이터
│   └── screenshots/                         # 7개 언어 스크린샷
├── .github/workflows/ci.yml                # GitHub Actions CI/CD
├── Gemfile / Gemfile.lock
├── CLAUDE.md
└── README.md

빌드

요구사항: Xcode 16+, iOS 17+ 시뮬레이터 또는 실기기

xcodebuild -project PumpWater-iOS/PumpWater-iOS.xcodeproj \
  -scheme PumpWater-iOS \
  -sdk iphonesimulator \
  -destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=18.5' \
  build

배포

Fastlane 기반으로 배포하며, 코드 서명은 Match로 관리합니다.

# 인증서 동기화 (최초 1회)
bundle exec fastlane sync_certificates

# TestFlight 배포
bundle exec fastlane beta

# App Store 배포 (자동 출시 + 단계적 배포)
bundle exec fastlane release

CI/CD: GitHub Actions가 모든 PR에서 빌드 검증을 수행하고, main 브랜치 push 시 자동으로 TestFlight에 배포합니다.

커밋 컨벤션

이 프로젝트는 Conventional Commits를 사용합니다.

<type>: <description>
Type 용도
feat 새로운 기능
fix 버그 수정
refactor 코드 구조 개선
chore 유지보수, 설정, 의존성
docs 문서
test 테스트
style 서식, 공백
perf 성능 개선

예시:

feat: Firebase Remote Config 기반 강제 업데이트 기능 추가
fix: iPad 오디오 엔진 크래시 수정
chore: 버전 2.0.0 Build 1 업데이트
docs: README 아키텍처 다이어그램 업데이트

라이선스

이 프로젝트는 MIT License에 따라 라이선스가 부여됩니다.


v1.0.0 → v2.0.0 변경 이력
항목 v1.0.0 v2.0.0
아키텍처 단일 ContentView Clean Architecture + 4개 Swift Package
디자인 하드코딩 흰색 배경 Liquid Glass 리디자인
최소 iOS 14.0 17.0
다크 모드 미지원 완벽 지원
iPad 미지원 풀스크린 레이아웃 지원
광고 없음 AdMob Adaptive Banner (iPhone/iPad)
분석 없음 Firebase Analytics 9개 이벤트
강제 업데이트 없음 Firebase Remote Config
다국어 영어만 8개 언어
ATT 없음 App Tracking Transparency
개인정보 없음 인앱 개인정보 처리방침
배포 수동 Fastlane + GitHub Actions CI/CD
애니메이션 없음 Canvas + TimelineView 30fps 물 애니메이션
주파수 안전 없음 2,000Hz 이상 경고
스플래시 없음 앱 아이콘 + 페이드 아웃
무음 모드 작동 안함 .playback 카테고리로 작동

About

⚡️ 주파수를 이용해 💦 물을 빼내 보세요!

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors