Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2025-09-08 16:59:46 -05:00
parent 2904536334
commit 6d3837022d
6 changed files with 170 additions and 101 deletions

View File

@ -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] = [:]

View File

@ -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
View File

@ -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

View File

@ -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
} }

View File

@ -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] {

View File

@ -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