Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
2904536334
commit
6d3837022d
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// NoisePlayer.swift
|
// SoundPlayer.swift
|
||||||
// AudioPlaybackKit
|
// AudioPlaybackKit
|
||||||
//
|
//
|
||||||
// Created by Matt Bruce on 9/8/25.
|
// Created by Matt Bruce on 9/8/25.
|
||||||
@ -8,13 +8,13 @@
|
|||||||
import AVFoundation
|
import AVFoundation
|
||||||
import Observation
|
import Observation
|
||||||
|
|
||||||
/// Audio playback service for white noise and ambient sounds
|
/// Audio playback service for sounds and ambient audio
|
||||||
@available(iOS 17.0, tvOS 17.0, *)
|
@available(iOS 17.0, tvOS 17.0, *)
|
||||||
@Observable
|
@Observable
|
||||||
public class NoisePlayer {
|
public class SoundPlayer {
|
||||||
|
|
||||||
// MARK: - Singleton
|
// MARK: - Singleton
|
||||||
public static let shared = NoisePlayer()
|
public static let shared = SoundPlayer()
|
||||||
|
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
private var players: [String: AVAudioPlayer] = [:]
|
private var players: [String: AVAudioPlayer] = [:]
|
||||||
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// NoiseViewModel.swift
|
// SoundViewModel.swift
|
||||||
// AudioPlaybackKit
|
// AudioPlaybackKit
|
||||||
//
|
//
|
||||||
// Created by Matt Bruce on 9/8/25.
|
// Created by Matt Bruce on 9/8/25.
|
||||||
@ -8,19 +8,19 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Observation
|
import Observation
|
||||||
|
|
||||||
/// ViewModel for noise/audio playback
|
/// ViewModel for sound/audio playback
|
||||||
@available(iOS 17.0, tvOS 17.0, *)
|
@available(iOS 17.0, tvOS 17.0, *)
|
||||||
@Observable
|
@Observable
|
||||||
public class NoiseViewModel {
|
public class SoundViewModel {
|
||||||
|
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
private let noisePlayer: NoisePlayer
|
private let soundPlayer: SoundPlayer
|
||||||
private let soundConfigurationService: SoundConfigurationService
|
private let soundConfigurationService: SoundConfigurationService
|
||||||
public var isPreviewing: Bool = false
|
public var isPreviewing: Bool = false
|
||||||
public var previewSound: Sound?
|
public var previewSound: Sound?
|
||||||
|
|
||||||
public var isPlaying: Bool {
|
public var isPlaying: Bool {
|
||||||
noisePlayer.isPlaying
|
soundPlayer.isPlaying
|
||||||
}
|
}
|
||||||
|
|
||||||
public var availableSounds: [Sound] {
|
public var availableSounds: [Sound] {
|
||||||
@ -28,18 +28,18 @@ public class NoiseViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Initialization
|
// MARK: - Initialization
|
||||||
public init(noisePlayer: NoisePlayer = NoisePlayer.shared, soundConfigurationService: SoundConfigurationService = SoundConfigurationService.shared) {
|
public init(soundPlayer: SoundPlayer = SoundPlayer.shared, soundConfigurationService: SoundConfigurationService = SoundConfigurationService.shared) {
|
||||||
self.noisePlayer = noisePlayer
|
self.soundPlayer = soundPlayer
|
||||||
self.soundConfigurationService = soundConfigurationService
|
self.soundConfigurationService = soundConfigurationService
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public Interface
|
// MARK: - Public Interface
|
||||||
public func playSound(_ sound: Sound) {
|
public func playSound(_ sound: Sound) {
|
||||||
noisePlayer.playSound(sound)
|
soundPlayer.playSound(sound)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func stopSound() {
|
public func stopSound() {
|
||||||
noisePlayer.stopSound()
|
soundPlayer.stopSound()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func selectSound(_ sound: Sound) {
|
public func selectSound(_ sound: Sound) {
|
||||||
@ -61,7 +61,7 @@ public class NoiseViewModel {
|
|||||||
isPreviewing = true
|
isPreviewing = true
|
||||||
|
|
||||||
// Play preview (3 seconds)
|
// Play preview (3 seconds)
|
||||||
noisePlayer.playSound(sound)
|
soundPlayer.playSound(sound)
|
||||||
|
|
||||||
// Auto-stop preview after 3 seconds
|
// Auto-stop preview after 3 seconds
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
|
||||||
@ -71,7 +71,7 @@ public class NoiseViewModel {
|
|||||||
|
|
||||||
public func stopPreview() {
|
public func stopPreview() {
|
||||||
if isPreviewing {
|
if isPreviewing {
|
||||||
noisePlayer.stopSound()
|
soundPlayer.stopSound()
|
||||||
isPreviewing = false
|
isPreviewing = false
|
||||||
previewSound = nil
|
previewSound = nil
|
||||||
}
|
}
|
||||||
231
PRD.md
231
PRD.md
@ -63,7 +63,7 @@ TheNoiseClock is a SwiftUI-based iOS application that combines a customizable di
|
|||||||
- **Multiple sound options**:
|
- **Multiple sound options**:
|
||||||
- White Noise (`white-noise.mp3`)
|
- White Noise (`white-noise.mp3`)
|
||||||
- Heavy Rain White Noise (`heavy-rain-white-noise.mp3`)
|
- Heavy Rain White Noise (`heavy-rain-white-noise.mp3`)
|
||||||
- Fan White Noise (`fan-white-noise-heater-303207.mp3`)
|
- Fan White Noise (`fan-white-noise-heater.mp3`)
|
||||||
- **Continuous playback**: Sounds loop indefinitely
|
- **Continuous playback**: Sounds loop indefinitely
|
||||||
- **Advanced sound selection**: Category-based grid with search and preview
|
- **Advanced sound selection**: Category-based grid with search and preview
|
||||||
- **Simple controls**: Play/Stop button with visual feedback
|
- **Simple controls**: Play/Stop button with visual feedback
|
||||||
@ -77,17 +77,18 @@ TheNoiseClock is a SwiftUI-based iOS application that combines a customizable di
|
|||||||
- **Wake lock integration**: Prevents device sleep while audio is playing
|
- **Wake lock integration**: Prevents device sleep while audio is playing
|
||||||
- **Bluetooth audio support**: Works with AirPods and other Bluetooth audio devices
|
- **Bluetooth audio support**: Works with AirPods and other Bluetooth audio devices
|
||||||
- **Responsive layout**: Optimized for both portrait and landscape orientations
|
- **Responsive layout**: Optimized for both portrait and landscape orientations
|
||||||
|
- **AudioPlaybackKit integration**: Powered by reusable Swift package for audio functionality
|
||||||
|
|
||||||
### 6. Advanced Alarm System
|
### 6. Advanced Alarm System
|
||||||
- **Multiple alarms**: Create and manage unlimited alarms
|
- **Multiple alarms**: Create and manage unlimited alarms
|
||||||
- **Rich alarm editor**: Full-featured alarm creation and editing interface
|
- **Rich alarm editor**: Full-featured alarm creation and editing interface
|
||||||
- **Time selection**: Wheel-style date picker with optimized font sizing for maximum readability
|
- **Time selection**: Wheel-style date picker with optimized font sizing for maximum readability
|
||||||
- **Dynamic alarm sounds**: Configurable alarm sounds loaded from JSON configuration
|
- **Dynamic alarm sounds**: Configurable alarm sounds loaded from dedicated alarm-sounds.json configuration
|
||||||
- **Sound preview**: Play/stop functionality for testing alarm sounds before selection
|
- **Sound preview**: Play/stop functionality for testing alarm sounds before selection
|
||||||
- **Sound organization**: Alarm sounds organized in bundles with categories
|
- **Sound organization**: Alarm sounds organized in dedicated AlarmSounds.bundle with categories
|
||||||
- **Custom labels**: User-defined alarm names and descriptions
|
- **Custom labels**: User-defined alarm names and descriptions
|
||||||
- **Repeat schedules**: Set alarms to repeat on specific weekdays or daily
|
- **Repeat schedules**: Set alarms to repeat on specific weekdays or daily
|
||||||
- **Sound selection**: Choose from extensive system sounds with live preview
|
- **Sound selection**: Choose from extensive alarm sounds with live preview
|
||||||
- **Volume control**: Adjustable alarm volume (0-100%)
|
- **Volume control**: Adjustable alarm volume (0-100%)
|
||||||
- **Vibration settings**: Enable/disable vibration for each alarm
|
- **Vibration settings**: Enable/disable vibration for each alarm
|
||||||
- **Snooze functionality**: Configurable snooze duration (5, 7, 8, 9, 10, 15, 20 minutes)
|
- **Snooze functionality**: Configurable snooze duration (5, 7, 8, 9, 10, 15, 20 minutes)
|
||||||
@ -98,6 +99,7 @@ TheNoiseClock is a SwiftUI-based iOS application that combines a customizable di
|
|||||||
- **Alarm management**: Add, edit, delete, and duplicate alarms
|
- **Alarm management**: Add, edit, delete, and duplicate alarms
|
||||||
- **Next trigger preview**: Shows when the next alarm will fire
|
- **Next trigger preview**: Shows when the next alarm will fire
|
||||||
- **Responsive time picker**: Font sizes adapt to available space and orientation
|
- **Responsive time picker**: Font sizes adapt to available space and orientation
|
||||||
|
- **AlarmSoundService integration**: Dedicated service for alarm-specific sound management
|
||||||
|
|
||||||
## Advanced Clock Display Features
|
## Advanced Clock Display Features
|
||||||
|
|
||||||
@ -139,6 +141,40 @@ TheNoiseClock is a SwiftUI-based iOS application that combines a customizable di
|
|||||||
|
|
||||||
## Technical Architecture
|
## Technical Architecture
|
||||||
|
|
||||||
|
### Swift Package Architecture
|
||||||
|
|
||||||
|
TheNoiseClock has been refactored to use a modular Swift Package architecture for improved code reusability and maintainability:
|
||||||
|
|
||||||
|
#### AudioPlaybackKit Package
|
||||||
|
- **Purpose**: Reusable audio playback functionality for iOS and tvOS applications
|
||||||
|
- **Platform Support**: iOS 17.0+ and tvOS 17.0+
|
||||||
|
- **Core Components**:
|
||||||
|
- `Sound` model: Generic sound data structure with Codable support and GUID generation
|
||||||
|
- `SoundConfiguration` models: Configuration structures for sound management
|
||||||
|
- `SoundConfigurationService`: Generic sound configuration service with JSON loading
|
||||||
|
- `NoisePlayer`: Audio playback service with background support
|
||||||
|
- `WakeLockService`: Screen wake lock management
|
||||||
|
- `NoiseViewModel`: Audio playback state management
|
||||||
|
- `AudioConstants`: Audio-related constants and configuration
|
||||||
|
- **Key Features**:
|
||||||
|
- Generic sound management (no alarm-specific functionality)
|
||||||
|
- Fatal error handling for missing configuration files
|
||||||
|
- Platform-conditional compilation for tvOS compatibility
|
||||||
|
- Modern @Observable state management
|
||||||
|
- Background audio support with interruption handling
|
||||||
|
|
||||||
|
#### Package Integration
|
||||||
|
- **Local Package**: AudioPlaybackKit is included as a local Swift package dependency
|
||||||
|
- **Xcode Integration**: Properly configured in project.pbxproj with XCLocalSwiftPackageReference
|
||||||
|
- **Import Usage**: Main app imports AudioPlaybackKit for audio functionality
|
||||||
|
- **Separation of Concerns**: Generic audio functionality in package, app-specific logic in main app
|
||||||
|
|
||||||
|
#### App-Specific Extensions
|
||||||
|
- **AlarmSoundService**: Dedicated service in main app for alarm-specific sound management
|
||||||
|
- **Configuration Separation**: Alarm sounds use separate alarm-sounds.json file
|
||||||
|
- **Category Constants**: Hardcoded alarm category ID to avoid magic strings
|
||||||
|
- **Service Pattern**: Extension pattern for app-specific functionality
|
||||||
|
|
||||||
### Code Organization Principles
|
### Code Organization Principles
|
||||||
|
|
||||||
**TOP PRIORITY:** The codebase must be built with the following architectural principles from the beginning:
|
**TOP PRIORITY:** The codebase must be built with the following architectural principles from the beginning:
|
||||||
@ -200,8 +236,17 @@ These principles are fundamental to the project's long-term success and must be
|
|||||||
- Sound name with volume control
|
- Sound name with volume control
|
||||||
- Vibration settings
|
- Vibration settings
|
||||||
- Snooze duration configuration
|
- Snooze duration configuration
|
||||||
- **Sound**: Simple struct for noise file management
|
- **Sound** (AudioPlaybackKit): Generic sound data model with Codable support
|
||||||
|
- Unique GUID identifier (auto-generated)
|
||||||
- Display name and file name
|
- Display name and file name
|
||||||
|
- Category and description
|
||||||
|
- Optional bundle name for organization
|
||||||
|
- Optional isDefault flag for default sound selection
|
||||||
|
- Custom Codable implementation with GUID generation
|
||||||
|
- **SoundConfiguration** (AudioPlaybackKit): Configuration structure for sound management
|
||||||
|
- Array of Sound objects
|
||||||
|
- Sound categories and audio settings
|
||||||
|
- JSON-based configuration loading
|
||||||
- **LegacyAlarm**: Backward compatibility struct for old alarm data
|
- **LegacyAlarm**: Backward compatibility struct for old alarm data
|
||||||
|
|
||||||
### Data Persistence
|
### Data Persistence
|
||||||
@ -210,17 +255,21 @@ These principles are fundamental to the project's long-term success and must be
|
|||||||
- **Bundle resources**: Audio files stored in app bundle
|
- **Bundle resources**: Audio files stored in app bundle
|
||||||
|
|
||||||
### Audio System
|
### Audio System
|
||||||
|
- **AudioPlaybackKit Package**: Reusable audio functionality in Swift package
|
||||||
- **AVFoundation**: AVAudioPlayer for noise playback
|
- **AVFoundation**: AVAudioPlayer for noise playback
|
||||||
- **@Observable NoisePlayer**: Modern state management with preloading
|
- **@Observable NoisePlayer**: Modern state management with preloading
|
||||||
- **Looping playback**: Infinite loop for ambient sounds
|
- **Looping playback**: Infinite loop for ambient sounds
|
||||||
- **Audio session management**: Proper audio session configuration with background support
|
- **Audio session management**: Proper audio session configuration with background support
|
||||||
- **Error handling**: Graceful handling of missing audio files
|
- **Error handling**: Fatal error handling for missing configuration files
|
||||||
- **AlarmTonePlayer**: Dedicated player for alarm sound previews
|
- **AlarmTonePlayer**: Dedicated player for alarm sound previews
|
||||||
- **Background audio**: Continues playback when app is backgrounded
|
- **Background audio**: Continues playback when app is backgrounded
|
||||||
- **Interruption handling**: Automatic resume after phone calls and route changes
|
- **Interruption handling**: Automatic resume after phone calls and route changes
|
||||||
- **Wake lock integration**: Prevents device sleep during audio playback
|
- **Wake lock integration**: Prevents device sleep during audio playback
|
||||||
- **Focus mode awareness**: Monitors and respects Focus mode settings
|
- **Focus mode awareness**: Monitors and respects Focus mode settings
|
||||||
- **Notification compatibility**: Ensures alarms work with Focus modes enabled
|
- **Notification compatibility**: Ensures alarms work with Focus modes enabled
|
||||||
|
- **Configuration separation**: Separate JSON files for ambient sounds (sounds.json) and alarm sounds (alarm-sounds.json)
|
||||||
|
- **Generic sound management**: AudioPlaybackKit provides generic sound functionality
|
||||||
|
- **App-specific extensions**: AlarmSoundService handles alarm-specific sound management
|
||||||
|
|
||||||
### Battery System
|
### Battery System
|
||||||
- **@Observable BatteryService**: Modern state management for battery monitoring
|
- **@Observable BatteryService**: Modern state management for battery monitoring
|
||||||
@ -283,83 +332,95 @@ These principles are fundamental to the project's long-term success and must be
|
|||||||
## File Structure and Organization
|
## File Structure and Organization
|
||||||
|
|
||||||
### Recommended File Organization
|
### Recommended File Organization
|
||||||
Following the separation of concerns principle, the codebase should be organized into focused, single-responsibility files:
|
Following the separation of concerns principle, the codebase is organized into focused, single-responsibility files with Swift Package integration:
|
||||||
|
|
||||||
```
|
```
|
||||||
TheNoiseClock/
|
TheNoiseClock/
|
||||||
├── App/
|
├── AudioPlaybackKit/ # Swift Package for reusable audio functionality
|
||||||
│ ├── TheNoiseClockApp.swift # App entry point and configuration
|
│ ├── Package.swift # Package configuration (iOS 17.0+, tvOS 17.0+)
|
||||||
│ └── ContentView.swift # Main tab navigation coordinator
|
│ └── Sources/AudioPlaybackKit/
|
||||||
├── Core/
|
│ ├── Models/
|
||||||
│ ├── Constants/
|
│ │ ├── Sound.swift # Generic sound data model with Codable support
|
||||||
│ │ ├── AppConstants.swift # App-wide constants and configuration
|
│ │ └── SoundConfiguration.swift # Sound configuration models and service
|
||||||
│ │ ├── UIConstants.swift # UI-specific constants (colors, sizes, etc.)
|
│ ├── Services/
|
||||||
│ │ └── AudioConstants.swift # Audio-related constants
|
│ │ ├── NoisePlayer.swift # Audio playback service
|
||||||
│ ├── Extensions/
|
│ │ └── WakeLockService.swift # Screen wake lock management
|
||||||
│ │ ├── Color+Extensions.swift # Color utilities and extensions
|
│ ├── ViewModels/
|
||||||
│ │ ├── Date+Extensions.swift # Date formatting and utilities
|
│ │ └── NoiseViewModel.swift # Audio playback state management
|
||||||
│ │ └── View+Extensions.swift # Common view modifiers and responsive utilities
|
│ └── Constants/
|
||||||
│ └── Utilities/
|
│ └── AudioConstants.swift # Audio-related constants
|
||||||
│ ├── ColorUtils.swift # Color manipulation utilities
|
├── TheNoiseClock/ # Main application
|
||||||
│ ├── FontUtils.swift # Font sizing, typography, and customization utilities
|
│ ├── App/
|
||||||
│ └── NotificationUtils.swift # Notification helper functions
|
│ │ ├── TheNoiseClockApp.swift # App entry point and configuration
|
||||||
├── Models/
|
│ │ └── ContentView.swift # Main tab navigation coordinator
|
||||||
│ ├── ClockStyle.swift # Clock customization data model
|
│ ├── Core/
|
||||||
│ ├── Alarm.swift # Alarm data model
|
│ │ ├── Constants/
|
||||||
│ ├── Sound.swift # Sound data model
|
│ │ │ ├── AppConstants.swift # App-wide constants and configuration
|
||||||
│ └── LegacyAlarm.swift # Backward compatibility model
|
│ │ │ └── UIConstants.swift # UI-specific constants (colors, sizes, etc.)
|
||||||
├── ViewModels/
|
│ │ ├── Extensions/
|
||||||
│ ├── ClockViewModel.swift # Clock display logic and state
|
│ │ │ ├── Color+Extensions.swift # Color utilities and extensions
|
||||||
│ ├── AlarmViewModel.swift # Alarm management logic
|
│ │ │ ├── Date+Extensions.swift # Date formatting and utilities
|
||||||
│ └── NoiseViewModel.swift # Audio playback state management
|
│ │ │ └── View+Extensions.swift # Common view modifiers and responsive utilities
|
||||||
├── Views/
|
│ │ └── Utilities/
|
||||||
│ ├── Clock/
|
│ │ ├── ColorUtils.swift # Color manipulation utilities
|
||||||
│ │ ├── ClockView.swift # Main clock display view
|
│ │ ├── FontUtils.swift # Font sizing, typography, and customization utilities
|
||||||
│ │ ├── ClockSettingsView.swift # Clock customization interface
|
│ │ └── NotificationUtils.swift # Notification helper functions
|
||||||
│ │ └── Components/
|
│ ├── Models/
|
||||||
│ │ ├── TimeDisplayView.swift # Advanced segmented time display with fixed-width digits
|
│ │ ├── ClockStyle.swift # Clock customization data model
|
||||||
│ │ ├── BatteryOverlayView.swift # Battery level overlay
|
│ │ ├── Alarm.swift # Alarm data model
|
||||||
│ │ ├── DateOverlayView.swift # Date display overlay
|
│ │ └── LegacyAlarm.swift # Backward compatibility model
|
||||||
│ │ └── TopOverlayView.swift # Combined overlay container
|
│ ├── ViewModels/
|
||||||
│ ├── Alarms/
|
│ │ ├── ClockViewModel.swift # Clock display logic and state
|
||||||
│ │ ├── AlarmView.swift # Main alarm management view
|
│ │ └── AlarmViewModel.swift # Alarm management logic
|
||||||
│ │ ├── AddAlarmView.swift # Alarm creation interface
|
│ ├── Views/
|
||||||
│ │ └── Components/
|
│ │ ├── Clock/
|
||||||
│ │ ├── AlarmRowView.swift # Individual alarm row component
|
│ │ │ ├── ClockView.swift # Main clock display view
|
||||||
│ │ ├── TimePickerSection.swift # Time selection component
|
│ │ │ ├── ClockSettingsView.swift # Clock customization interface
|
||||||
│ │ ├── TimeUntilAlarmSection.swift # Time calculation display
|
│ │ │ └── Components/
|
||||||
│ │ ├── SoundSelectionView.swift # Sound selection with preview
|
│ │ │ ├── TimeDisplayView.swift # Advanced segmented time display with fixed-width digits
|
||||||
│ │ ├── LabelEditView.swift # Label editing interface
|
│ │ │ ├── BatteryOverlayView.swift # Battery level overlay
|
||||||
│ │ └── SnoozeSelectionView.swift # Snooze duration selection
|
│ │ │ ├── DateOverlayView.swift # Date display overlay
|
||||||
│ └── Noise/
|
│ │ │ └── TopOverlayView.swift # Combined overlay container
|
||||||
│ ├── NoiseView.swift # Main white noise player interface
|
│ │ ├── Alarms/
|
||||||
│ └── Components/
|
│ │ │ ├── AlarmView.swift # Main alarm management view
|
||||||
│ ├── SoundCategoryView.swift # Advanced grid-based sound selection
|
│ │ │ ├── AddAlarmView.swift # Alarm creation interface
|
||||||
│ └── SoundControlView.swift # Playback controls component
|
│ │ │ └── Components/
|
||||||
├── Services/
|
│ │ │ ├── AlarmRowView.swift # Individual alarm row component
|
||||||
│ ├── NoisePlayer.swift # Audio playback service with background support
|
│ │ │ ├── TimePickerSection.swift # Time selection component
|
||||||
│ ├── AlarmService.swift # Alarm management service with Focus mode integration
|
│ │ │ ├── TimeUntilAlarmSection.swift # Time calculation display
|
||||||
│ ├── NotificationService.swift # Notification handling service
|
│ │ │ ├── SoundSelectionView.swift # Sound selection with preview
|
||||||
│ ├── WakeLockService.swift # Screen wake lock management service
|
│ │ │ ├── LabelEditView.swift # Label editing interface
|
||||||
│ ├── FocusModeService.swift # Focus mode integration and notification management
|
│ │ │ └── SnoozeSelectionView.swift # Snooze duration selection
|
||||||
│ └── BatteryService.swift # Battery monitoring and state management service
|
│ │ └── Noise/
|
||||||
└── Resources/
|
│ │ ├── NoiseView.swift # Main white noise player interface
|
||||||
├── sounds.json # Sound configuration and definitions
|
│ │ └── Components/
|
||||||
├── Ambient.bundle/ # Ambient sound category
|
│ │ ├── SoundCategoryView.swift # Advanced grid-based sound selection
|
||||||
│ └── white-noise.mp3
|
│ │ └── SoundControlView.swift # Playback controls component
|
||||||
├── Nature.bundle/ # Nature sound category
|
│ ├── Services/
|
||||||
│ └── heavy-rain-white-noise.mp3
|
│ │ ├── AlarmService.swift # Alarm management service with Focus mode integration
|
||||||
├── Mechanical.bundle/ # Mechanical sound category
|
│ │ ├── NotificationService.swift # Notification handling service
|
||||||
│ └── fan-white-noise-heater.mp3
|
│ │ ├── FocusModeService.swift # Focus mode integration and notification management
|
||||||
├── AlarmSounds.bundle/ # Alarm sound category
|
│ │ ├── BatteryService.swift # Battery monitoring and state management service
|
||||||
│ ├── digital-alarm.mp3
|
│ │ └── AlarmSoundService.swift # Alarm-specific sound management service
|
||||||
│ ├── iphone-alarm.mp3
|
│ └── Resources/
|
||||||
│ ├── classic-alarm.mp3
|
│ ├── sounds.json # Ambient sound configuration and definitions
|
||||||
│ ├── beep-alarm.mp3
|
│ ├── alarm-sounds.json # Alarm sound configuration and definitions
|
||||||
│ ├── siren-alarm.mp3
|
│ ├── Ambient.bundle/ # Ambient sound category
|
||||||
│ └── voice-wakeup.mp3
|
│ │ └── white-noise.mp3
|
||||||
└── Assets.xcassets/
|
│ ├── Nature.bundle/ # Nature sound category
|
||||||
└── [Asset catalogs]
|
│ │ └── heavy-rain-white-noise.mp3
|
||||||
|
│ ├── Mechanical.bundle/ # Mechanical sound category
|
||||||
|
│ │ └── fan-white-noise-heater.mp3
|
||||||
|
│ ├── AlarmSounds.bundle/ # Alarm sound category
|
||||||
|
│ │ ├── digital-alarm.caf
|
||||||
|
│ │ ├── classic-alarm.caf
|
||||||
|
│ │ ├── beep-alarm.caf
|
||||||
|
│ │ ├── siren-alarm.caf
|
||||||
|
│ │ └── buzzing-alarm.caf
|
||||||
|
│ └── Assets.xcassets/
|
||||||
|
│ └── [Asset catalogs]
|
||||||
|
└── TheNoiseClock.xcodeproj/ # Xcode project with AudioPlaybackKit dependency
|
||||||
|
└── project.pbxproj # Project configuration with local package reference
|
||||||
```
|
```
|
||||||
|
|
||||||
### File Naming Conventions
|
### File Naming Conventions
|
||||||
@ -464,6 +525,12 @@ The following changes **automatically require** PRD updates:
|
|||||||
- **iOS 26 Features**: Latest platform capabilities where available
|
- **iOS 26 Features**: Latest platform capabilities where available
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
- **AudioPlaybackKit**: Local Swift package for reusable audio functionality
|
||||||
|
- iOS 17.0+ and tvOS 17.0+ support
|
||||||
|
- Generic sound management and configuration
|
||||||
|
- Audio playback with background support
|
||||||
|
- Wake lock management
|
||||||
|
- Modern @Observable state management
|
||||||
- **SwiftUI**: Native iOS UI framework with latest features
|
- **SwiftUI**: Native iOS UI framework with latest features
|
||||||
- **AVFoundation**: Audio playback with modern async patterns and background support
|
- **AVFoundation**: Audio playback with modern async patterns and background support
|
||||||
- **UserNotifications**: Alarm notifications with rich content support
|
- **UserNotifications**: Alarm notifications with rich content support
|
||||||
@ -514,11 +581,13 @@ The following changes **automatically require** PRD updates:
|
|||||||
### Project Information
|
### Project Information
|
||||||
- **Created**: September 7, 2025
|
- **Created**: September 7, 2025
|
||||||
- **Framework**: SwiftUI with iOS 18.0+ target (latest stable features)
|
- **Framework**: SwiftUI with iOS 18.0+ target (latest stable features)
|
||||||
- **Architecture**: Modern SwiftUI with @Observable pattern and MVVM
|
- **Architecture**: Modern SwiftUI with @Observable pattern, MVVM, and Swift Package modularity
|
||||||
|
- **Package Architecture**: AudioPlaybackKit Swift package for reusable audio functionality
|
||||||
- **Testing**: Comprehensive unit and UI test targets with Swift Testing
|
- **Testing**: Comprehensive unit and UI test targets with Swift Testing
|
||||||
- **Version control**: Git repository with feature branch workflow
|
- **Version control**: Git repository with feature branch workflow
|
||||||
- **Performance**: Optimized for battery life and smooth operation
|
- **Performance**: Optimized for battery life and smooth operation
|
||||||
- **Modern iOS**: Uses latest iOS 18+ and iOS 26 features with Swift 6 language improvements
|
- **Modern iOS**: Uses latest iOS 18+ and iOS 26 features with Swift 6 language improvements
|
||||||
|
- **Code Reusability**: Modular Swift package architecture enables code reuse across projects
|
||||||
|
|
||||||
### Modern iOS Development Practices
|
### Modern iOS Development Practices
|
||||||
- **Swift 6**: Latest language features including strict concurrency checking
|
- **Swift 6**: Latest language features including strict concurrency checking
|
||||||
|
|||||||
@ -14,7 +14,7 @@ struct SoundSelectionView: View {
|
|||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
// Use shared player instance to avoid audio conflicts
|
// Use shared player instance to avoid audio conflicts
|
||||||
private let noisePlayer = NoisePlayer.shared
|
private let soundPlayer = SoundPlayer.shared
|
||||||
private let alarmSounds = AlarmSoundService.shared.getAlarmSounds().sorted { $0.name < $1.name }
|
private let alarmSounds = AlarmSoundService.shared.getAlarmSounds().sorted { $0.name < $1.name }
|
||||||
|
|
||||||
@State private var isPlaying = false
|
@State private var isPlaying = false
|
||||||
@ -73,13 +73,13 @@ struct SoundSelectionView: View {
|
|||||||
stopSound()
|
stopSound()
|
||||||
|
|
||||||
// Start playing the new sound
|
// Start playing the new sound
|
||||||
noisePlayer.playSound(sound)
|
soundPlayer.playSound(sound)
|
||||||
isPlaying = true
|
isPlaying = true
|
||||||
currentlyPlayingSound = selectedSound
|
currentlyPlayingSound = selectedSound
|
||||||
}
|
}
|
||||||
|
|
||||||
private func stopSound() {
|
private func stopSound() {
|
||||||
noisePlayer.stopSound()
|
soundPlayer.stopSound()
|
||||||
isPlaying = false
|
isPlaying = false
|
||||||
currentlyPlayingSound = nil
|
currentlyPlayingSound = nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ struct SoundCategoryView: View {
|
|||||||
@Binding var selectedSound: Sound?
|
@Binding var selectedSound: Sound?
|
||||||
@State private var selectedCategory: String = "all"
|
@State private var selectedCategory: String = "all"
|
||||||
@State private var searchText: String = ""
|
@State private var searchText: String = ""
|
||||||
@State private var viewModel = NoiseViewModel()
|
@State private var viewModel = SoundViewModel()
|
||||||
|
|
||||||
// MARK: - Computed Properties
|
// MARK: - Computed Properties
|
||||||
private var filteredSounds: [Sound] {
|
private var filteredSounds: [Sound] {
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import AudioPlaybackKit
|
|||||||
struct NoiseView: View {
|
struct NoiseView: View {
|
||||||
|
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
@State private var viewModel = NoiseViewModel()
|
@State private var viewModel = SoundViewModel()
|
||||||
@State private var selectedSound: Sound? {
|
@State private var selectedSound: Sound? {
|
||||||
didSet {
|
didSet {
|
||||||
// Stop current playback when selecting a new sound
|
// Stop current playback when selecting a new sound
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user