├── .cursor └── rules │ ├── app-structure.mdc │ ├── fastlane-metadata.mdc │ ├── gitignore.mdc │ ├── localization-metadata.mdc │ ├── playback-features.mdc │ ├── recording-guidelines.mdc │ ├── release-process.mdc │ ├── rule.mdc │ └── voicememo-architecture.mdc ├── .gitignore ├── .swiftlint.yml ├── APP_SPECIFICATION.md ├── AUDIO_RECORDING_TESTS.md ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── README.md ├── VoiLog.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcshareddata │ └── xcschemes │ ├── VoiLog.xcscheme │ ├── VoiLogDevelop.xcscheme │ └── VoiLogTests.xcscheme ├── VoiLog ├── AdmobBannerView.swift ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── 1024.png │ │ ├── 114.png │ │ ├── 120 1.png │ │ ├── 120.png │ │ ├── 180.png │ │ ├── 29.png │ │ ├── 40.png │ │ ├── 57.png │ │ ├── 58.png │ │ ├── 60.png │ │ ├── 80.png │ │ ├── 87.png │ │ └── Contents.json │ ├── Black.colorset │ │ └── Contents.json │ ├── Contents.json │ ├── Polygon.imageset │ │ ├── Contents.json │ │ └── Polygon.pdf │ ├── ads_black.imageset │ │ ├── Contents.json │ │ └── ads_black.pdf │ ├── ads_white.imageset │ │ ├── Contents.json │ │ └── ads_white.pdf │ └── iconImage.imageset │ │ ├── Contents.json │ │ └── pepicons-pop_microphone-circle-filled.png ├── AudioPlayer.swift ├── AudioRecoder.swift ├── ContentView.swift ├── Extention │ ├── AVAudioPCMBuffer+Extention.swift │ ├── Double+Extention.swift │ └── TimeInterval+Extention.swift ├── GoogleService-Info.plist ├── Info.plist ├── Locate │ ├── Localizable.xcstrings │ ├── de.lproj │ │ └── InfoPlist.strings │ ├── en-IN.lproj │ │ └── InfoPlist.strings │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── es.lproj │ │ └── InfoPlist.strings │ ├── fr.lproj │ │ └── InfoPlist.strings │ ├── it.lproj │ │ └── InfoPlist.strings │ ├── ja.lproj │ │ └── InfoPlist.strings │ ├── pt-PT.lproj │ │ └── InfoPlist.strings │ ├── ru.lproj │ │ └── InfoPlist.strings │ ├── tr.lproj │ │ └── InfoPlist.strings │ ├── vi.lproj │ │ └── InfoPlist.strings │ ├── zh-Hans.lproj │ │ └── InfoPlist.strings │ └── zh-Hant.lproj │ │ └── InfoPlist.strings ├── Logger.swift ├── Playlist │ ├── Playlist.swift │ ├── PlaylistDetail.swift │ ├── PlaylistDetailFeature.swift │ ├── PlaylistDetailView.swift │ ├── PlaylistListFeature.swift │ └── PlaylistListView.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Prod.xcconfig ├── Setting │ ├── AboutSimpleRecoder.swift │ ├── ErrorLogsView.swift │ └── SettingView.swift ├── Simple Voice Recorder - Audio.storekit ├── Store │ ├── MockIAPManager.swift │ ├── PaywallView.swift │ └── PurchaseManager.swift ├── Template │ ├── FeatureTemplate.swift │ └── README.md ├── VoiLogRelease.entitlements ├── Voice │ ├── AudioEditorReducer.swift │ ├── AudioEditorView.swift │ ├── AudioLevelView.swift │ ├── AudioProcessingService.swift │ ├── DependencyValues.swift │ ├── MailComposeViewControllerWrapper.swift │ ├── PlayerView.swift │ ├── RecordingMemo.swift │ ├── TutorialView.swift │ ├── VoiceDetail.swift │ ├── VoiceMemoReducer.swift │ ├── VoiceMemos.swift │ ├── VoiceMemosView.swift │ └── WaveformView.swift ├── VoiceMemo.entitlements ├── VoiceMemoApp.swift ├── data │ ├── CloudUploader.swift │ ├── Constants.swift │ ├── Playlist.xcdatamodeld │ │ └── Playlist.xcdatamodel │ │ │ └── contents │ ├── PlaylistRepository.swift │ ├── Thema.xcdatamodel │ │ └── contents │ ├── UserDefaultsClient.swift │ ├── UserDefaultsManager.swift │ ├── Voice.xcdatamodeld │ │ └── Model.xcdatamodel │ │ │ └── contents │ ├── VoiceMemoCoredataAccessor.swift │ └── VoiceMemoRepository.swift └── ff16audio.mp3 ├── VoiLogTests ├── MockCloudUploader.swift ├── MockVoiceMemoCoredataAccessor.swift ├── PlaylistDetailFeatureTests.swift ├── PlaylistListFeatureTests.swift ├── VoiLogTests.swift ├── VoiceMemoRepositoryTests.swift └── VoiceMemosTests.swift ├── VoiLogUITests ├── VoiceMemoUITests.swift └── VoiceMemoUITestsLaunchTests.swift ├── ci_scripts ├── ci_post_clone.sh └── ci_pre_xcodebuild.sh ├── fastlane ├── Appfile ├── Fastfile ├── README.md ├── Snapfile ├── metadata │ ├── copyright.txt │ ├── de-DE │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── en-CA │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── en-GB │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── en-US │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── es-ES │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── fr-FR │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── hi │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── it │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── ja │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── primary_category.txt │ ├── primary_first_sub_category.txt │ ├── primary_second_sub_category.txt │ ├── pt-PT │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── review_information │ │ ├── demo_password.txt │ │ ├── demo_user.txt │ │ ├── email_address.txt │ │ ├── first_name.txt │ │ ├── last_name.txt │ │ ├── notes.txt │ │ └── phone_number.txt │ ├── ru │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── secondary_category.txt │ ├── secondary_first_sub_category.txt │ ├── secondary_second_sub_category.txt │ ├── tr │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── vi │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── zh-Hans │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ └── zh-Hant │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt └── screenshots │ ├── README.md │ ├── de-DE │ ├── 0_APP_IPHONE_55_0.jpg │ ├── 0_APP_IPHONE_65_0.jpg │ ├── 1_APP_IPHONE_55_1.jpg │ ├── 1_APP_IPHONE_65_1.jpg │ ├── 2_APP_IPHONE_55_2.jpg │ ├── 2_APP_IPHONE_65_2.jpg │ ├── 3_APP_IPHONE_65_3.jpg │ └── 4_APP_IPHONE_65_4.jpg │ ├── en-US │ ├── .gitkeep │ ├── 0_APP_IPHONE_55_0.jpg │ ├── 0_APP_IPHONE_65_0.jpg │ ├── 1_APP_IPHONE_55_1.jpg │ ├── 1_APP_IPHONE_65_1.jpg │ ├── 2_APP_IPHONE_55_2.jpg │ ├── 2_APP_IPHONE_65_2.jpg │ ├── 3_APP_IPHONE_55_3.jpg │ ├── 3_APP_IPHONE_65_3.jpg │ └── 4_APP_IPHONE_65_4.jpg │ ├── es-ES │ ├── 0_APP_IPHONE_55_0.jpg │ ├── 0_APP_IPHONE_65_0.jpg │ ├── 1_APP_IPHONE_55_1.jpg │ ├── 1_APP_IPHONE_65_1.jpg │ ├── 2_APP_IPHONE_55_2.jpg │ └── 2_APP_IPHONE_65_2.jpg │ ├── ja │ ├── 0_APP_IPHONE_55_0.jpg │ ├── 0_APP_IPHONE_65_0.jpg │ ├── 1_APP_IPHONE_55_1.jpg │ ├── 1_APP_IPHONE_65_1.jpg │ ├── 2_APP_IPHONE_55_2.jpg │ ├── 2_APP_IPHONE_65_2.jpg │ ├── 3_APP_IPHONE_55_3.jpg │ ├── 3_APP_IPHONE_65_3.jpg │ └── 4_APP_IPHONE_65_4.jpg │ ├── pt-PT │ ├── 0_APP_IPHONE_55_0.jpg │ ├── 0_APP_IPHONE_65_0.jpg │ ├── 1_APP_IPHONE_55_1.jpg │ ├── 1_APP_IPHONE_65_1.jpg │ ├── 2_APP_IPHONE_55_2.jpg │ └── 2_APP_IPHONE_65_2.jpg │ └── zh-Hant │ ├── 0_APP_IPHONE_55_0.jpg │ ├── 0_APP_IPHONE_65_0.jpg │ ├── 1_APP_IPHONE_55_1.jpg │ ├── 1_APP_IPHONE_65_1.jpg │ ├── 2_APP_IPHONE_55_2.jpg │ └── 2_APP_IPHONE_65_2.jpg └── recordActivity ├── Assets.xcassets ├── AccentColor.colorset │ └── Contents.json ├── AppIcon.appiconset │ └── Contents.json ├── Contents.json └── WidgetBackground.colorset │ └── Contents.json ├── Info.plist ├── recordActivity.swift ├── recordActivityBundle.swift └── recordActivityLiveActivity.swift /.cursor/rules/app-structure.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: false 5 | --- 6 | # VoiceMemo App Structure 7 | 8 | VoiceMemo is an iOS voice recording app built with SwiftUI and The Composable Architecture (TCA). 9 | 10 | ## Core Components 11 | 12 | ### Voice Recording 13 | The main voice recording functionality is implemented in: 14 | - [VoiLog/Voice/VoiceMemoReducer.swift](mdc:VoiLog/Voice/VoiceMemoReducer.swift) - Core recording logic 15 | - [VoiLog/Voice/PlayerView.swift](mdc:VoiLog/Voice/PlayerView.swift) - Playback UI 16 | - [VoiLog/Voice/VoiceDetail.swift](mdc:VoiLog/Voice/VoiceDetail.swift) - Detailed view 17 | 18 | ### Audio Processing 19 | Audio handling and processing: 20 | - [VoiLog/AudioPlayer.swift](mdc:VoiLog/AudioPlayer.swift) - Audio playback management 21 | - [VoiLog/Voice/AudioEditorReducer.swift](mdc:VoiLog/Voice/AudioEditorReducer.swift) - Audio editing features 22 | 23 | ### Playlist Management 24 | Playlist features: 25 | - [VoiLog/Playlist/PlaylistListFeature.swift](mdc:VoiLog/Playlist/PlaylistListFeature.swift) - Playlist list management 26 | - [VoiLog/Playlist/PlaylistDetailFeature.swift](mdc:VoiLog/Playlist/PlaylistDetailFeature.swift) - Individual playlist handling 27 | - [VoiLog/data/PlaylistRepository.swift](mdc:VoiLog/data/PlaylistRepository.swift) - Playlist data persistence 28 | 29 | ### Metadata 30 | App metadata and localization: 31 | - [fastlane/metadata](mdc:fastlane/metadata) - App Store metadata 32 | - Release notes are maintained in multiple languages under fastlane/metadata/[lang]/release_notes.txt 33 | -------------------------------------------------------------------------------- /.cursor/rules/fastlane-metadata.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: false 5 | --- 6 | # fastlane metadata ディレクトリ構成ルール 7 | 8 | fastlaneでApp Store Connectにアプリ情報をアップロードする際、metadataディレクトリは以下のような構成にする必要があります: 9 | 10 | - 各言語ごとにディレクトリを作成(例: `ja`, `en-US`) 11 | - 各言語ディレクトリ直下に、以下のテキストファイルを配置すること 12 | - `description.txt` 13 | - `keywords.txt` 14 | - `release_notes.txt` 15 | - `title.txt` など(必要に応じて) 16 | - サブディレクトリ(例: `description/`や`keywords/`など)は不要 17 | 18 | 例: 19 | ``` 20 | fastlane/metadata/ja/description.txt 21 | fastlane/metadata/ja/keywords.txt 22 | fastlane/metadata/ja/release_notes.txt 23 | fastlane/metadata/en-US/description.txt 24 | fastlane/metadata/en-US/keywords.txt 25 | fastlane/metadata/en-US/release_notes.txt 26 | ``` 27 | 28 | 詳しくは [fastlane公式ドキュメント](https://docs.fastlane.tools/actions/deliver/#metadata) も参照してください。 29 | -------------------------------------------------------------------------------- /.cursor/rules/gitignore.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: false 5 | --- 6 | # セキュリティ・機密ファイル管理ルール 7 | 8 | このプロジェクトでは、以下の機密ファイル・公開してはいけないファイルは必ずGit管理対象外とします。 9 | 10 | - App Store Connect APIキー(例: [fastlane/AuthKey_*.p8](mdc:fastlane/AuthKey_R2Q4FFAG8D.p8)) 11 | - fastlaneの環境変数ファイル(例: [fastlane/.env*](mdc:fastlane/.env.default)) 12 | 13 | これらは[.gitignore](mdc:.gitignore)で除外されています。 14 | 15 | **理由:** 16 | - これらのファイルには認証情報や個人情報が含まれるため、リポジトリに公開・共有してはいけません。 17 | - 誤ってコミットした場合は、`git rm --cached`で追跡を解除し、再度コミットしてください。 18 | 19 | **参考:** 20 | - fastlane公式: https://docs.fastlane.tools/best-practices/source-control/#source-control 21 | -------------------------------------------------------------------------------- /.cursor/rules/localization-metadata.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: false 5 | --- 6 | # Localization and Metadata Management 7 | 8 | ## Supported Languages 9 | The app is localized in multiple languages with metadata in fastlane/metadata/[lang]: 10 | - English (US, GB, CA) 11 | - Japanese 12 | - French 13 | - German 14 | - Spanish 15 | - Italian 16 | - Portuguese 17 | - Russian 18 | - Chinese (Simplified and Traditional) 19 | - Vietnamese 20 | - Turkish 21 | - Hindi 22 | 23 | ## Metadata Structure 24 | Each language directory contains: 25 | - [fastlane/metadata/ja/description.txt](mdc:fastlane/metadata/ja/description.txt) - App description 26 | - [fastlane/metadata/ja/release_notes.txt](mdc:fastlane/metadata/ja/release_notes.txt) - Release notes 27 | - [fastlane/metadata/ja/keywords.txt](mdc:fastlane/metadata/ja/keywords.txt) - Search keywords 28 | - Other metadata files (support URL, privacy policy, etc.) 29 | 30 | ## Release Management 31 | - Version updates are managed through Fastlane 32 | - [fastlane/Fastfile](mdc:fastlane/Fastfile) controls the release process 33 | - Screenshots are stored in fastlane/screenshots/[lang] 34 | - Release notes should be updated in all supported languages before submission 35 | -------------------------------------------------------------------------------- /.cursor/rules/playback-features.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: false 5 | --- 6 | # Audio Playback Features 7 | 8 | The app supports various playback features implemented across multiple components: 9 | 10 | ## Core Playback Controls 11 | Located in [VoiLog/Voice/PlayerView.swift](mdc:VoiLog/Voice/PlayerView.swift): 12 | - Play/Pause 13 | - Skip forward/backward (10 and 60 seconds) 14 | - Playback speed control (0.5x to 2.0x) 15 | - Loop/Repeat functionality 16 | 17 | ## Detailed Playback View 18 | [VoiLog/Voice/VoiceDetail.swift](mdc:VoiLog/Voice/VoiceDetail.swift) implements: 19 | - Progress bar 20 | - Time display 21 | - Title editing 22 | - Share functionality 23 | 24 | ## Audio Processing 25 | [VoiLog/AudioPlayer.swift](mdc:VoiLog/AudioPlayer.swift) handles: 26 | - Audio session management 27 | - Playback speed implementation 28 | - Background playback 29 | - File management 30 | 31 | ## Key Features 32 | - Multiple skip intervals (10s, 60s) 33 | - Variable playback speeds 34 | - Background audio support 35 | - Progress tracking 36 | - Loop mode 37 | -------------------------------------------------------------------------------- /.cursor/rules/recording-guidelines.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: false 5 | --- 6 | # Recording Functionality Guidelines 7 | 8 | ## Alert System 9 | 10 | When implementing recording-related alerts: 11 | - Use system background color with rounded corners 12 | - Include clear, concise messages in Japanese 13 | - Provide simple action buttons 14 | - Prevent other interactions during recording 15 | 16 | ## State Management 17 | 18 | Recording state should: 19 | - Be managed through VoiceMemoReducer 20 | - Disable navigation and interaction when recording 21 | - Show appropriate visual feedback 22 | - Handle interruptions gracefully 23 | 24 | ## UI Feedback 25 | 26 | Recording UI must: 27 | - Show clear recording status 28 | - Provide immediate visual feedback 29 | - Use consistent styling with the rest of the app 30 | - Follow iOS design guidelines 31 | -------------------------------------------------------------------------------- /.cursor/rules/release-process.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: false 5 | --- 6 | # Release Process Guidelines 7 | 8 | ## Version Management 9 | 10 | 1. Update version in Xcode project 11 | 2. Create and push git tag (e.g., v0.16.7) 12 | 3. Create GitHub release with: 13 | - Appropriate release notes 14 | - Generated notes from commits 15 | - Proper tag association 16 | 17 | ## App Store Deployment 18 | 19 | Use Fastlane's `upload_metadata` lane in [fastlane/Fastfile](mdc:fastlane/Fastfile): 20 | ```bash 21 | bundle exec fastlane ios upload_metadata 22 | ``` 23 | 24 | This will: 25 | - Get version from Xcode project 26 | - Upload metadata and screenshots 27 | - Submit for App Store review 28 | 29 | ## Post-Release 30 | 31 | After successful deployment: 32 | - Verify GitHub release is properly tagged 33 | - Confirm App Store submission status 34 | - Monitor for any submission feedback 35 | -------------------------------------------------------------------------------- /.cursor/rules/rule.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: true 5 | --- 6 | 7 | # Your rule content 8 | 9 | - You can @ files here 10 | - You can use markdown but dont have to 11 | -------------------------------------------------------------------------------- /.cursor/rules/voicememo-architecture.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: false 5 | --- 6 | # VoiceMemo App Architecture 7 | 8 | ## Core Components 9 | 10 | The main view hierarchy is defined in [VoiLog/Voice/VoiceMemosView.swift](mdc:VoiLog/Voice/VoiceMemosView.swift), which contains: 11 | 12 | - Recording functionality 13 | - Playback controls 14 | - Voice memo list management 15 | - Alert/overlay handling 16 | 17 | ## Key Features 18 | 19 | ### Recording State Management 20 | - Recording state is managed through the VoiceMemoReducer 21 | - Recording alerts prevent other interactions during active recording 22 | - Custom overlay system for user feedback 23 | 24 | ### UI Components 25 | - VoiceMemoListView: Displays recorded memos 26 | - RecordingMemoView: Handles active recording UI 27 | - PlayerView: Controls playback of recorded memos 28 | 29 | ## Release Process 30 | 31 | The app uses Fastlane for deployment, configured in [fastlane/Fastfile](mdc:fastlane/Fastfile): 32 | - Version management through Xcode project 33 | - Automated metadata and screenshot uploads 34 | - App Store submission process 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | 92 | # App Store Connect API Key 93 | fastlane/AuthKey_*.p8 94 | 95 | # Environment variables 96 | fastlane/.env* 97 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | # デフォルト有効で無効にするルール 2 | disabled_rules: 3 | #- block_based_kvo 4 | #- class_delegate_protocol 5 | #- closing_brace 6 | #- closure_parameter_position 7 | #- colon 8 | #- comma 9 | #- compiler_protocol_init 10 | #- control_statement 11 | #- custom_rules 12 | #- cyclomatic_complexity 13 | #- deployment_target 14 | #- discarded_notification_center_observer 15 | #- discouraged_direct_init 16 | #- duplicate_imports 17 | #- dynamic_inline 18 | #- empty_enum_arguments 19 | #- empty_parameters 20 | #- empty_parentheses_with_trailing_closure 21 | #- file_length 22 | #- for_where 23 | #- force_cast 24 | #- force_try 25 | #- function_body_length 26 | #- function_parameter_count 27 | #- generic_type_name 28 | #- identifier_name 29 | #- implicit_getter 30 | #- inert_defer 31 | #- is_disjoint 32 | #- large_tuple 33 | #- leading_whitespace 34 | #- legacy_cggeometry_functions 35 | #- legacy_constant 36 | #- legacy_constructor 37 | #- legacy_hashing 38 | #- legacy_nsgeometry_functions 39 | #- line_length 40 | #- mark 41 | #- multiple_closures_with_trailing_closure 42 | #- nesting 43 | #- no_fallthrough_only 44 | #- notification_center_detachment 45 | #- opening_brace 46 | #- operator_whitespace 47 | #- private_over_fileprivate 48 | #- private_unit_test 49 | #- protocol_property_accessors_order 50 | #- redundant_discardable_let 51 | #- redundant_objc_attribute 52 | #- redundant_optional_initialization 53 | #- redundant_set_access_control 54 | #- redundant_string_enum_value 55 | #- redundant_void_return 56 | #- return_arrow_whitespace 57 | #- shorthand_operator 58 | #- statement_position 59 | #- superfluous_disable_command 60 | #- switch_case_alignment 61 | #- syntactic_sugar 62 | #- todo 63 | #- trailing_comma 64 | #- trailing_newline 65 | #- trailing_semicolon 66 | #- trailing_whitespace 67 | #- type_body_length 68 | #- type_name 69 | #- unneeded_break_in_switch 70 | #- unused_closure_parameter 71 | #- unused_control_flow_label 72 | #- unused_enumerated 73 | #- unused_optional_binding 74 | #- unused_setter_value 75 | #- valid_ibinspectable 76 | #- vertical_parameter_alignment 77 | #- vertical_whitespace 78 | #- void_return 79 | #- weak_computed_property 80 | #- weak_delegate 81 | #- xctfail_message 82 | 83 | # デフォルト無効で有効にするルール 84 | opt_in_rules: 85 | - anyobject_protocol 86 | - array_init 87 | - attributes 88 | - closure_body_length 89 | - closure_end_indentation 90 | - closure_spacing 91 | - collection_alignment 92 | - conditional_returns_on_newline 93 | - contains_over_first_not_nil 94 | - convenience_type 95 | #- discouraged_object_literal # オブジェクトリテラルを使いたいため 96 | - discouraged_optional_boolean 97 | - discouraged_optional_collection 98 | - empty_count 99 | - empty_string 100 | - empty_xctest_method 101 | #- explicit_acl # できる限りACLを省略したいため 102 | #- explicit_enum_raw_value # ローバリューを省略することもあるため 103 | - explicit_init 104 | #- explicit_self # 関数は `self.` を付けずに呼び出したいため 105 | #- explicit_top_level_acl # できる限りACLを省略したいため 106 | #- explicit_type_interface # できる限り型推論したいため 107 | #- extension_access_modifier # このルールの意味を理解していないため 108 | - fallthrough 109 | - fatal_error_message 110 | #- file_header # このルールの意味を理解していないため 111 | - file_name 112 | - first_where 113 | - force_unwrapping 114 | - function_default_parameter_at_end 115 | - identical_operands 116 | - implicit_return 117 | #- implicitly_unwrapped_optional # VIPERで変数を `!` で定義したいため 118 | - joined_default_parameter 119 | - last_where 120 | - legacy_random 121 | #- let_var_whitespace # 空白行を設けたくないこともあるため 122 | - literal_expression_end_indentation 123 | - lower_acl_than_parent 124 | - missing_docs 125 | - modifier_order 126 | #- multiline_arguments # 引数は同じ行に2つ入れたいこともあるため 127 | #- multiline_arguments_brackets # 括弧で行を増やしたくないため 128 | #- multiline_function_chains # 関数の呼び出しは同じ行に2つ入れたいこともあるため 129 | #- multiline_literal_brackets # 括弧で行を増やしたくないため 130 | #- multiline_parameters # 引数は同じ行に2つ入れたいこともあるため 131 | #- multiline_parameters_brackets # 括弧で行を増やしたくないため 132 | - nimble_operator 133 | #- no_extension_access_modifier # エクステンションにACLを設定したいことがあるため 134 | - no_grouping_extension # グループ化するためにエクステンションを使わないため 135 | - nslocalizedstring_key 136 | #- number_separator # 数字を `_` で区切りたくないため 137 | #- object_literal # リテラルで生成したくないこともあるため 138 | - operator_usage_whitespace 139 | - overridden_super_call 140 | - override_in_extension 141 | - pattern_matching_keywords 142 | #- prefixed_toplevel_constant # 定数のプリフィックスに `k` を付けたくないため 143 | - private_action 144 | - private_outlet 145 | #- prohibited_interface_builder # ストーリーボードを使ってビューを生成したいため 146 | - prohibited_super_call 147 | - quick_discouraged_call 148 | - quick_discouraged_focused_test 149 | - quick_discouraged_pending_test 150 | - redundant_nil_coalescing 151 | - redundant_type_annotation 152 | #- required_deinit # できる限りデイニシャライザを省略したいため 153 | - required_enum_case 154 | - single_test_class 155 | - sorted_first_last 156 | #- sorted_imports # インポート文をアルファベット順以外に並び替えたいこともあるため 157 | - static_operator 158 | - strict_fileprivate 159 | #- strong_iboutlet # `@IBOutlet` を `weak` で定義したいこともあるため 160 | - switch_case_on_newline 161 | - toggle_bool 162 | - trailing_closure 163 | - unavailable_function 164 | - unneeded_parentheses_in_closure_argument 165 | - untyped_error_in_catch 166 | - unused_import 167 | - unused_private_declaration 168 | - vertical_parameter_alignment_on_call 169 | #- vertical_whitespace_between_cases # Switch文のケース間に空白行を設けたくないこともあるため 170 | #- vertical_whitespace_closing_braces # 中括弧を閉じる前に空白行を設けたいことがあるため 171 | #- vertical_whitespace_opening_braces # 中括弧を開く前に空白行を設けたいことがあるため 172 | - xct_specific_matcher 173 | - yoda_condition 174 | 175 | # 対象のファイル・フォルダ 176 | # デフォルトからフォルダ名を変更していない場合、プロジェクト名と同名のフォルダを指定すればいい 177 | included: 178 | - {プロジェクト名} 179 | 180 | # 対象外のファイル・フォルダ 181 | excluded: 182 | - Pods 183 | - Carthage 184 | - SourcePackages 185 | - Generated 186 | 187 | line_length: 188 | warning: 300 189 | error: 500 190 | 191 | identifier_name: 192 | min_length: 193 | warning: 1 # `r` `g` `b` などを使いたいため 194 | -------------------------------------------------------------------------------- /APP_SPECIFICATION.md: -------------------------------------------------------------------------------- 1 | # シンプル録音(VoiLog)アプリ仕様書 2 | 3 | ## 1. アプリ概要 4 | 5 | 「シンプル録音」(VoiLog)は、iOSデバイス向けの音声録音・管理アプリケーションです。シンプルな操作性と高度な音声管理機能を兼ね備え、ユーザーが簡単に音声メモを録音、再生、整理できるように設計されています。 6 | 7 | ## 2. 主要機能 8 | 9 | ### 2.1 音声録音機能 10 | - マイクボタンをタップして録音を開始 11 | - 録音中の音声レベルをリアルタイム表示 12 | - 録音の一時停止と再開 13 | - 録音の停止と保存 14 | - 録音品質の設定(サンプリング周波数、量子化ビット数、チャンネル数) 15 | 16 | ### 2.2 音声再生機能 17 | - 録音した音声メモの一覧表示 18 | - タップして音声を再生 19 | - 再生中の音声波形表示 20 | - 再生速度の調整 21 | - 連続再生モード 22 | 23 | ### 2.3 プレイリスト機能 24 | - 複数の音声メモをプレイリストとしてグループ化 25 | - プレイリストの作成、編集、削除 26 | - プレイリスト内の音声メモの並べ替え 27 | - プレイリスト単位での再生 28 | 29 | ### 2.4 設定機能 30 | - 録音フォーマットの選択(m4a等) 31 | - 音質設定(サンプリング周波数、量子化ビット数、チャンネル数) 32 | - マイク音量の調整 33 | - エラーログの表示 34 | 35 | ### 2.5 チュートリアル機能 36 | - 初回起動時にアプリの使い方を説明するチュートリアル表示 37 | - 3ステップ(録音開始、録音停止、再生)の基本操作を説明 38 | - 多言語対応(12言語) 39 | 40 | 41 | ### 2.6 クラウド同期機能 42 | - iCloudを使用した音声メモの同期 43 | - 複数デバイス間でのデータ共有 44 | 45 | ### 2.7 課金機能(プレミアムサービス) 46 | - 広告の非表示 47 | - 無制限のプレイリスト作成 48 | - 高度な音声編集機能 49 | - 1ヶ月無料トライアル 50 | 51 | ## 3. 技術仕様 52 | 53 | ### 3.1 アーキテクチャ 54 | - SwiftUIを使用したUIの構築 55 | - The Composable Architecture (TCA)を採用した状態管理 56 | - CoreDataを使用したローカルデータベース 57 | - AVFoundationを使用した音声処理 58 | 59 | ### 3.2 データモデル 60 | - VoiceMemo: 音声メモのデータモデル(UUID、タイトル、日付、時間、URL、テキスト等) 61 | - Playlist: プレイリストのデータモデル(ID、名前、作成日、更新日等) 62 | 63 | ### 3.3 設定管理 64 | - UserDefaultsManagerを使用した設定の永続化 65 | - 録音設定(ファイル形式、サンプリング周波数、量子化ビット数、チャンネル数) 66 | - アプリ状態(インストール日、レビュー要求回数、チュートリアル表示状態等) 67 | 68 | ### 3.4 外部サービス連携 69 | - Firebase: クラッシュレポート、分析 70 | - RevenueCat: 課金管理 71 | - GoogleMobileAds: 広告表示 72 | - iCloud: データ同期 73 | 74 | ## 4. UI/UX仕様 75 | 76 | ### 4.1 メイン画面 77 | - 録音モードと再生モードの切り替え 78 | - 録音ボタン(赤い円形) 79 | - 録音済み音声メモのリスト表示 80 | - プレイリストへのナビゲーションリンク 81 | - 設定へのアクセス 82 | 83 | ### 4.2 録音画面 84 | - 録音ボタン(赤い円形) 85 | - 停止ボタン(赤い四角形) 86 | - 音声レベルのリアルタイム表示 87 | - 録音時間の表示 88 | 89 | ### 4.3 再生画面 90 | - 再生/一時停止ボタン 91 | - 音声波形の表示 92 | - 再生位置の表示 93 | - 再生速度の調整 94 | 95 | ### 4.4 プレイリスト画面 96 | - プレイリスト一覧 97 | - 新規プレイリスト作成ボタン 98 | - プレイリスト詳細画面(含まれる音声メモの一覧) 99 | - 音声メモの追加/削除機能 100 | 101 | ### 4.5 設定画面 102 | - 録音設定(ファイル形式、音質等) 103 | - アプリ情報 104 | - プレミアム機能の購入 105 | - 開発者サポート 106 | 107 | ### 4.6 チュートリアル画面 108 | - オーバーレイ表示 109 | - 3ステップの操作説明 110 | - 実際のボタンと同じデザインのアイコン 111 | - 「始める」ボタン 112 | 113 | ## 5. 多言語対応 114 | 115 | 以下の言語に対応: 116 | - 英語 (en) 117 | - ドイツ語 (de) 118 | - スペイン語 (es) 119 | - フランス語 (fr) 120 | - イタリア語 (it) 121 | - 日本語 (ja) 122 | - ポルトガル語 (pt-PT) 123 | - ロシア語 (ru) 124 | - トルコ語 (tr) 125 | - ベトナム語 (vi) 126 | - 簡体字中国語 (zh-Hans) 127 | - 繁体字中国語 (zh-Hant) 128 | 129 | ## 6. 課金プラン 130 | 131 | ### 6.1 無料版 132 | - 基本的な録音・再生機能 133 | - 最大3つのプレイリスト作成 134 | - 広告表示あり 135 | 136 | ### 6.2 プレミアム版 137 | - すべての機能が使い放題 138 | - 広告非表示 139 | - 無制限のプレイリスト作成 140 | - 高度な音声編集機能 141 | 142 | 143 | ## 8. テスト仕様 144 | 145 | - ユニットテスト: VoiceMemosTests 146 | - UIテスト: VoiLogUITests 147 | - 初回起動時のチュートリアル表示テスト 148 | - 録音・再生機能のテスト 149 | - 設定変更のテスト 150 | - レビュー要求表示のテスト 151 | 152 | ## 9. バージョン履歴 153 | 154 | - 現在のバージョン: 0.15.0 155 | - 最新の更新内容: チュートリアル画面の追加 -------------------------------------------------------------------------------- /AUDIO_RECORDING_TESTS.md: -------------------------------------------------------------------------------- 1 | # 📱 音声録音割り込み処理テスト項目 2 | 3 | ## 概要 4 | このドキュメントは、VoiLogアプリの音声録音機能における割り込み処理のテスト項目をまとめたものです。 5 | アラーム、電話、通知などの割り込みが発生しても録音が継続されることを確認するためのテストケースを記載しています。 6 | 7 | ## 🔧 基本機能テスト 8 | 9 | ### ✅ 録音開始・停止 10 | - [ ] 録音開始が正常に動作する 11 | - [ ] 録音停止が正常に動作する 12 | - [ ] 録音一時停止・再開が正常に動作する 13 | - [ ] 2秒未満の録音で停止処理がスキップされる 14 | 15 | ### ✅ AVAudioSession設定 16 | - [ ] `.duckOthers`オプションが正しく設定される 17 | - [ ] `.mixWithOthers`オプションが正しく設定される 18 | - [ ] Bluetoothオプションが正しく設定される 19 | - [ ] サンプリングレートが正しく設定される 20 | 21 | ## 🚨 割り込み処理テスト 22 | 23 | ### ✅ アラーム・タイマー割り込み 24 | 25 | #### アラーム割り込み 26 | **手順:** 27 | 1. アプリで録音を開始 28 | 2. 時計アプリでアラームを1分後に設定 29 | 3. アラームが鳴ったときの録音状態を確認 30 | 4. アラームを止めた後の録音状態を確認 31 | 32 | **確認項目:** 33 | - [ ] アラーム鳴動中も録音が継続される 34 | - [ ] アラーム音が録音に混入するが音量が小さい(duckOthers効果) 35 | - [ ] アラーム停止後も録音が継続される 36 | - [ ] 録音時間が正常にカウントされる 37 | - [ ] アプリがクラッシュしない 38 | 39 | #### タイマー割り込み 40 | **手順:** 41 | 1. アプリで録音を開始 42 | 2. 時計アプリでタイマーを30秒に設定 43 | 3. タイマー音が鳴ったときの録音状態を確認 44 | 4. タイマーを止めた後の録音状態を確認 45 | 46 | **確認項目:** 47 | - [ ] タイマー鳴動中も録音が継続される 48 | - [ ] タイマー音量が小さくなる(duckOthers効果) 49 | - [ ] タイマー停止後も録音が継続される 50 | 51 | ### ✅ 通知音割り込み 52 | 53 | #### システム通知 54 | **手順:** 55 | 1. アプリで録音を開始 56 | 2. 設定→通知→メッセージで通知音を有効にする 57 | 3. 別の端末からSMSを送信 58 | 4. 通知音が鳴ったときの録音状態を確認 59 | 60 | **確認項目:** 61 | - [ ] 通知音鳴動中も録音が継続される 62 | - [ ] 通知音が小さくなる(duckOthers効果) 63 | - [ ] 複数の通知が来ても録音が継続される 64 | - [ ] 通知音終了後、録音が正常に継続される 65 | 66 | #### アプリ通知 67 | **手順:** 68 | 1. アプリで録音を開始 69 | 2. 他のアプリ(LINE、Twitter等)の通知を発生させる 70 | 3. 通知音が鳴ったときの録音状態を確認 71 | 72 | ### ✅ 電話割り込み 73 | 74 | #### 着信割り込み 75 | **手順:** 76 | 1. アプリで録音を開始 77 | 2. 別の端末から電話をかける 78 | 3. 着信時の録音状態を確認 79 | 4. 電話に出る/切るときの録音状態を確認 80 | 5. 通話終了後の録音状態を確認 81 | 82 | **確認項目:** 83 | - [ ] 着信時に録音が一時停止される 84 | - [ ] 通話終了後に録音が自動再開される 85 | - [ ] アプリがクラッシュしない 86 | - [ ] 録音データに欠損がない 87 | 88 | #### 発信割り込み 89 | **手順:** 90 | 1. アプリで録音を開始 91 | 2. 電話アプリから発信する 92 | 3. 発信時の録音状態を確認 93 | 4. 通話終了後の録音状態を確認 94 | 95 | ### ✅ Siri割り込み 96 | **手順:** 97 | 1. アプリで録音を開始 98 | 2. 「Hey Siri」または電源ボタン長押しでSiriを起動 99 | 3. Siri使用中の録音状態を確認 100 | 4. Siri終了後の録音状態を確認 101 | 102 | **確認項目:** 103 | - [ ] Siri起動時の録音状態 104 | - [ ] Siri終了後の録音復旧 105 | - [ ] アプリがクラッシュしない 106 | 107 | ## 🎧 オーディオデバイス変更テスト 108 | 109 | ### ✅ Bluetoothヘッドセット 110 | 111 | #### AirPods接続/切断 112 | **手順:** 113 | 1. AirPodsをペアリングしておく 114 | 2. アプリで録音を開始 115 | 3. 録音中にAirPodsを接続する 116 | 4. 録音状態と音質を確認 117 | 5. 録音中にAirPodsを切断する 118 | 6. 録音状態と音質を確認 119 | 120 | **確認項目:** 121 | - [ ] 接続時も録音が継続される 122 | - [ ] 切断時も録音が継続される 123 | - [ ] 音質に大きな変化がない 124 | - [ ] アプリがクラッシュしない 125 | - [ ] デバイス変更時の音質に問題がない 126 | 127 | #### Bluetoothスピーカー 128 | **手順:** 129 | 1. Bluetoothスピーカーをペアリング 130 | 2. アプリで録音を開始 131 | 3. 録音中にスピーカーを接続/切断 132 | 4. 録音状態を確認 133 | 134 | ### ✅ 有線ヘッドセット 135 | 136 | #### 有線ヘッドフォン接続/切断 137 | **手順:** 138 | 1. アプリで録音を開始 139 | 2. 録音中に有線ヘッドフォンを接続 140 | 3. 録音状態を確認 141 | 4. 録音中に有線ヘッドフォンを抜く 142 | 5. 録音状態を確認 143 | 144 | **確認項目:** 145 | - [ ] 有線ヘッドセット接続時も録音が継続される 146 | - [ ] 有線ヘッドセット切断時も録音が継続される 147 | - [ ] マイク付きヘッドセットの切り替えに対応 148 | 149 | ### ✅ スピーカー切り替え 150 | - [ ] 内蔵スピーカー⇔外部スピーカー切り替えに対応 151 | - [ ] AirPodsなどの切り替えに対応 152 | 153 | ## 📱 アプリライフサイクルテスト 154 | 155 | ### ✅ バックグラウンド処理 156 | 157 | #### バックグラウンド移行 158 | **手順:** 159 | 1. アプリで録音を開始 160 | 2. ホームボタンを押してアプリをバックグラウンドに移行 161 | 3. 他のアプリを使用 162 | 4. 録音アプリに戻る 163 | 5. 録音状態を確認 164 | 165 | **確認項目:** 166 | - [ ] アプリがバックグラウンドに移行しても録音継続 167 | - [ ] バックグラウンドタスクが適切に管理される 168 | - [ ] フォアグラウンド復帰時の状態が正常 169 | 170 | #### アプリスイッチャー 171 | **手順:** 172 | 1. アプリで録音を開始 173 | 2. アプリスイッチャーを開く 174 | 3. 他のアプリに切り替える 175 | 4. 録音アプリに戻る 176 | 5. 録音状態を確認 177 | 178 | #### コントロールセンター 179 | **手順:** 180 | 1. アプリで録音を開始 181 | 2. コントロールセンターを開く 182 | 3. 音楽再生/停止、音量調整などを操作 183 | 4. 録音状態を確認 184 | 185 | ### ✅ メモリ警告 186 | - [ ] メモリ警告時も録音が継続される 187 | - [ ] メモリ警告時の適切なリソース管理 188 | 189 | ## 🔋 システム状態テスト 190 | 191 | ### ✅ 低バッテリー警告 192 | **手順:** 193 | 1. バッテリーを20%以下にする 194 | 2. アプリで録音を開始 195 | 3. 低バッテリー警告が表示されたときの録音状態を確認 196 | 197 | ### ✅ 充電開始/終了 198 | **手順:** 199 | 1. アプリで録音を開始 200 | 2. 録音中に充電ケーブルを接続 201 | 3. 録音状態を確認 202 | 4. 録音中に充電ケーブルを抜く 203 | 5. 録音状態を確認 204 | 205 | ## 🎵 他アプリとの同時使用テスト 206 | 207 | ### ✅ 音楽アプリとの併用 208 | **手順:** 209 | 1. Apple Music/Spotifyで音楽を再生 210 | 2. アプリで録音を開始 211 | 3. 録音状態と音楽再生状態を確認 212 | 4. 音楽を停止/再開して録音への影響を確認 213 | 214 | ### ✅ 動画アプリとの併用 215 | **手順:** 216 | 1. YouTube/NetflixでBGM付き動画を再生 217 | 2. アプリで録音を開始 218 | 3. 録音状態と動画再生状態を確認 219 | 220 | ## 🛡️ エラーハンドリングテスト 221 | 222 | ### ✅ クラッシュ防止 223 | - [ ] 割り込み処理中にクラッシュしない 224 | - [ ] 複数の割り込みが同時発生してもクラッシュしない 225 | - [ ] 不正な状態でのメソッド呼び出しでクラッシュしない 226 | - [ ] メモリリークが発生しない 227 | 228 | ### ✅ 状態管理 229 | - [ ] `isRecording`フラグが正しく管理される 230 | - [ ] `isPaused`フラグが正しく管理される 231 | - [ ] NotificationObserverが適切に削除される 232 | - [ ] AudioEngineの状態が正しく管理される 233 | 234 | ### ✅ エラー復旧 235 | - [ ] AudioSession再アクティブ化失敗時の処理 236 | - [ ] AudioEngine再開失敗時の処理 237 | - [ ] 予期しないエラー時の適切なログ出力 238 | 239 | ## 🔄 連続操作テスト 240 | 241 | ### ✅ 短時間での録音開始/停止 242 | **手順:** 243 | 1. 録音開始→即座に停止を10回繰り返す 244 | 2. 各操作でアプリの動作を確認 245 | 3. メモリリークやクラッシュがないか確認 246 | 247 | ### ✅ 長時間録音 248 | **手順:** 249 | 1. 30分以上の長時間録音を開始 250 | 2. 録音中に上記の各種割り込みテストを実行 251 | 3. 録音データの整合性を確認 252 | 253 | ## 🧪 ストレステスト 254 | 255 | ### ✅ 連続割り込み 256 | - [ ] 短時間に複数の割り込みが発生しても正常動作 257 | - [ ] 長時間の録音中の割り込み処理 258 | - [ ] 割り込み中にアプリ操作しても正常動作 259 | 260 | ### ✅ 極端なケース 261 | - [ ] 録音開始直後の割り込み 262 | - [ ] 録音終了直前の割り込み 263 | - [ ] 非常に短い割り込み(瞬間的な通知音など) 264 | 265 | ## 🔊 音質・パフォーマンステスト 266 | 267 | ### ✅ 録音品質 268 | - [ ] 割り込み前後で音質に変化がない 269 | - [ ] 音声データに欠損やノイズがない 270 | - [ ] サンプリングレートが維持される 271 | - [ ] 音量レベルが適切に記録される 272 | 273 | ### ✅ パフォーマンス 274 | - [ ] 割り込み処理がCPU使用率に影響しない 275 | - [ ] メモリ使用量が適切に管理される 276 | - [ ] バッテリー消費が過度でない 277 | 278 | ## 📊 ログ・デバッグテスト 279 | 280 | ### ✅ ログ出力 281 | - [ ] 割り込み開始時のログが正しく出力される 282 | - [ ] 割り込み終了時のログが正しく出力される 283 | - [ ] エラー時のログが適切に出力される 284 | - [ ] デバッグ情報が十分に記録される 285 | 286 | ### ✅ 監視・分析 287 | - [ ] Crashlyticsにクラッシュレポートが送信されない 288 | - [ ] パフォーマンス指標が正常範囲内 289 | - [ ] ユーザー体験に悪影響がない 290 | 291 | ## 🎯 優先度別テスト 292 | 293 | ### 🔴 高優先度(必須) 294 | - アラーム・通知音での録音継続 295 | - クラッシュ防止 296 | - 基本的な録音機能 297 | - 電話割り込み後の自動復旧 298 | 299 | ### 🟡 中優先度(重要) 300 | - Bluetoothデバイス変更対応 301 | - Siri使用後の録音復旧 302 | - バックグラウンド動作 303 | - エラー復旧処理 304 | 305 | ### 🟢 低優先度(推奨) 306 | - ストレステスト 307 | - 詳細なログ分析 308 | - パフォーマンス最適化 309 | - 他アプリとの併用 310 | 311 | ## 📝 テスト記録フォーマット 312 | 313 | ``` 314 | テスト日時: ____年__月__日 __:__ 315 | 端末: iPhone __ (iOS __.__) 316 | アプリバージョン: __.__.__ 317 | テスター: ________ 318 | 319 | 【テスト項目】: 320 | 【結果】: ✅成功 / ❌失敗 321 | 【詳細】: 322 | 【問題点】: 323 | 【再現手順】: 324 | 【備考】: 325 | ``` 326 | 327 | ## 🔗 関連ドキュメント 328 | 329 | - [README.md](./README.md) - プロジェクト概要 330 | - [AudioRecoder.swift](./VoiLog/AudioRecoder.swift) - 録音処理実装 331 | 332 | ## 📞 問題報告 333 | 334 | テスト中に問題を発見した場合は、以下の情報を含めて報告してください: 335 | 336 | 1. 発生した問題の詳細 337 | 2. 再現手順 338 | 3. 使用端末とiOSバージョン 339 | 4. アプリバージョン 340 | 5. ログ情報(可能であれば) 341 | 342 | --- 343 | 344 | **最終更新日**: 2024年12月19日 -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [v0.16.7] - 2024-03-24 6 | 7 | ### 🔒 安全性の向上 8 | - 録音中の画面遷移を制限する機能を追加 9 | - 録音中に他の画面へ移動しようとすると警告アラートを表示 10 | - ユーザーは録音を続けるか、停止して移動するかを選択可能 11 | - 録音データの意図しない損失を防止 12 | 13 | ### 🎨 UI改善 14 | - 録音中であることを視覚的に表示 15 | - ナビゲーションバーに録音インジケータを追加 16 | - 録音中は関連する操作ボタンの状態を明確に表示 17 | 18 | ### 🔄 ナビゲーション制御の改善 19 | - プレイリスト画面への遷移制御を追加 20 | - 設定画面への遷移制御を追加 21 | - iCloud同期操作の制御を追加 22 | - 課金画面への遷移制御を追加 -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "fastlane" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # シンプル録音 2 | 3 | - インストールいただけたら大変嬉しいです! 4 | 5 | 6 | 7 | https://apps.apple.com/us/app/%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E9%8C%B2%E9%9F%B3/id6443528409 8 | 9 | ## 開発リソース 10 | 11 | - `VoiLog/Template/` - ComposableArchitectureを使用した機能開発用のテンプレートが含まれています。詳細は[テンプレートREADME](VoiLog/Template/README.md)を参照してください。 12 | - [📱 音声録音割り込み処理テスト項目](AUDIO_RECORDING_TESTS.md) - アラーム、電話、通知などの割り込み処理に関する詳細なテスト項目とチェックリスト 13 | 14 | ## Fastlane 15 | 16 | プロジェクトはFastlaneを使用して自動化されています。以下のコマンドを使用できます: 17 | 18 | ### セットアップ 19 | 20 | ``` 21 | bundle install 22 | ``` 23 | 24 | ### 使用可能なレーン 25 | 26 | - テスト実行: 27 | ``` 28 | bundle exec fastlane tests 29 | ``` 30 | 31 | - 開発用ビルド: 32 | ``` 33 | bundle exec fastlane build 34 | ``` 35 | 36 | - TestFlightへのアップロード: 37 | ``` 38 | bundle exec fastlane beta 39 | ``` 40 | 41 | - App Storeへのリリース: 42 | ``` 43 | bundle exec fastlane release 44 | ``` 45 | 46 | ### Xcode Cloudと併用する場合 47 | 48 | ビルドはXcode Cloudで行い、メタデータとスクリーンショットの更新のみFastlaneで管理する場合は以下のレーンを使用できます: 49 | 50 | - スクリーンショット生成: 51 | ``` 52 | bundle exec fastlane screenshots 53 | ``` 54 | 55 | - メタデータのみ更新: 56 | ``` 57 | bundle exec fastlane update_metadata 58 | ``` 59 | 60 | - スクリーンショットのみ更新: 61 | ``` 62 | bundle exec fastlane update_screenshots 63 | ``` 64 | 65 | - メタデータとスクリーンショット両方を更新: 66 | ``` 67 | bundle exec fastlane update_store 68 | ``` 69 | 70 | メタデータは `fastlane/metadata` ディレクトリ内で管理します。各言語ごとに説明文、キーワード、リリースノートなどを編集できます。 71 | 72 | 詳細については、`fastlane/Fastfile`を参照してください。 73 | -------------------------------------------------------------------------------- /VoiLog.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /VoiLog.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /VoiLog.xcodeproj/xcshareddata/xcschemes/VoiLog.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 35 | 41 | 42 | 43 | 46 | 52 | 53 | 54 | 55 | 56 | 66 | 68 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /VoiLog.xcodeproj/xcshareddata/xcschemes/VoiLogDevelop.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 42 | 44 | 50 | 51 | 52 | 53 | 56 | 57 | 58 | 59 | 63 | 64 | 68 | 69 | 70 | 71 | 77 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /VoiLog.xcodeproj/xcshareddata/xcschemes/VoiLogTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 16 | 17 | 20 | 26 | 27 | 28 | 29 | 30 | 40 | 41 | 47 | 48 | 50 | 51 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /VoiLog/AdmobBannerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AdmobBannerView.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 9.6.2023. 6 | // 7 | 8 | import GoogleMobileAds 9 | import UIKit 10 | import SwiftUI 11 | 12 | struct AdmobBannerView: UIViewRepresentable { 13 | 14 | private let unitId: String 15 | init(unitId: String) { 16 | self.unitId = unitId 17 | } 18 | 19 | func makeUIView(context: Context) -> GADBannerView { 20 | let adSize = GADAdSizeFromCGSize(CGSize(width: 300, height: 50)) 21 | let view = GADBannerView(adSize: adSize) 22 | 23 | #if DEBUG 24 | view.adUnitID = "ca-app-pub-3940256099942544/2934735716" 25 | #else 26 | view.adUnitID = unitId 27 | #endif 28 | view.rootViewController = UIApplication.shared.windows.first?.rootViewController 29 | view.delegate = context.coordinator 30 | view.load(GADRequest()) 31 | return view 32 | } 33 | 34 | func updateUIView(_ uiView: GADBannerView, context: Context) { 35 | } 36 | 37 | // Adding the Coordinator for delegate handling 38 | func makeCoordinator() -> Coordinator { 39 | Coordinator() 40 | } 41 | 42 | class Coordinator: NSObject, GADBannerViewDelegate { 43 | 44 | // 広告受信時 45 | func bannerViewDidReceiveAd(_ bannerView: GADBannerView) { 46 | print("AdmobBannerView adUnitID: \(bannerView.adUnitID)") 47 | print("AdmobBannerView Ad received successfully.") 48 | 49 | } 50 | 51 | // 広告受信失敗時 52 | func bannerView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: Error) { 53 | print("AdmobBannerView Failed to load ad with error: \(error.localizedDescription)") 54 | print("AdmobBannerView adUnitID: \(bannerView.adUnitID)") 55 | 56 | } 57 | 58 | // インプレッションが記録された時 59 | func bannerViewDidRecordImpression(_ bannerView: GADBannerView) { 60 | print("AdmobBannerView Impression has been recorded for the ad.") 61 | } 62 | 63 | // 広告がクリックされた時 64 | func bannerViewDidRecordClick(_ bannerView: GADBannerView) { 65 | print("AdmobBannerView Ad was clicked.") 66 | } 67 | func bannerViewWillPresentScreen(_: GADBannerView) { 68 | print("AdmobBannerView \(#function) called") 69 | } 70 | 71 | func bannerViewWillDismissScreen(_: GADBannerView) { 72 | print("AdmobBannerView \(#function) called") 73 | } 74 | 75 | func bannerViewDidDismissScreen(_: GADBannerView) { 76 | print("AdmobBannerView \(#function) called") 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AppIcon.appiconset/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/AppIcon.appiconset/114.png -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AppIcon.appiconset/120 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/AppIcon.appiconset/120 1.png -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AppIcon.appiconset/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/AppIcon.appiconset/120.png -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AppIcon.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/AppIcon.appiconset/180.png -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AppIcon.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/AppIcon.appiconset/29.png -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AppIcon.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/AppIcon.appiconset/40.png -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AppIcon.appiconset/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/AppIcon.appiconset/57.png -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AppIcon.appiconset/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/AppIcon.appiconset/58.png -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AppIcon.appiconset/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/AppIcon.appiconset/60.png -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AppIcon.appiconset/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/AppIcon.appiconset/80.png -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AppIcon.appiconset/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/AppIcon.appiconset/87.png -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "40.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "60.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "29.png", 17 | "idiom" : "iphone", 18 | "scale" : "1x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "58.png", 23 | "idiom" : "iphone", 24 | "scale" : "2x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "87.png", 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "29x29" 32 | }, 33 | { 34 | "filename" : "80.png", 35 | "idiom" : "iphone", 36 | "scale" : "2x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "120 1.png", 41 | "idiom" : "iphone", 42 | "scale" : "3x", 43 | "size" : "40x40" 44 | }, 45 | { 46 | "filename" : "57.png", 47 | "idiom" : "iphone", 48 | "scale" : "1x", 49 | "size" : "57x57" 50 | }, 51 | { 52 | "filename" : "114.png", 53 | "idiom" : "iphone", 54 | "scale" : "2x", 55 | "size" : "57x57" 56 | }, 57 | { 58 | "filename" : "120.png", 59 | "idiom" : "iphone", 60 | "scale" : "2x", 61 | "size" : "60x60" 62 | }, 63 | { 64 | "filename" : "180.png", 65 | "idiom" : "iphone", 66 | "scale" : "3x", 67 | "size" : "60x60" 68 | }, 69 | { 70 | "filename" : "1024.png", 71 | "idiom" : "ios-marketing", 72 | "scale" : "1x", 73 | "size" : "1024x1024" 74 | } 75 | ], 76 | "info" : { 77 | "author" : "xcode", 78 | "version" : 1 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/Black.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x00", 9 | "green" : "0x00", 10 | "red" : "0x00" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0xFF", 27 | "green" : "0xFF", 28 | "red" : "0xFF" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/Polygon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Polygon.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/Polygon.imageset/Polygon.pdf: -------------------------------------------------------------------------------- 1 | %PDF-1.7 2 | 3 | 1 0 obj 4 | << >> 5 | endobj 6 | 7 | 2 0 obj 8 | << /Length 3 0 R >> 9 | stream 10 | /DeviceRGB CS 11 | /DeviceRGB cs 12 | q 13 | -1.000000 0.000000 -0.000000 -1.000000 93.300781 75.000000 cm 14 | 0.000000 0.040000 1.000000 scn 15 | 50.000000 75.000000 m 16 | 93.301270 0.000000 l 17 | 6.698730 0.000000 l 18 | 50.000000 75.000000 l 19 | h 20 | f 21 | n 22 | Q 23 | 24 | endstream 25 | endobj 26 | 27 | 3 0 obj 28 | 216 29 | endobj 30 | 31 | 4 0 obj 32 | << /Annots [] 33 | /Type /Page 34 | /MediaBox [ 0.000000 0.000000 86.601562 75.000000 ] 35 | /Resources 1 0 R 36 | /Contents 2 0 R 37 | /Parent 5 0 R 38 | >> 39 | endobj 40 | 41 | 5 0 obj 42 | << /Kids [ 4 0 R ] 43 | /Count 1 44 | /Type /Pages 45 | >> 46 | endobj 47 | 48 | 6 0 obj 49 | << /Pages 5 0 R 50 | /Type /Catalog 51 | >> 52 | endobj 53 | 54 | xref 55 | 0 7 56 | 0000000000 65535 f 57 | 0000000010 00000 n 58 | 0000000034 00000 n 59 | 0000000306 00000 n 60 | 0000000328 00000 n 61 | 0000000501 00000 n 62 | 0000000575 00000 n 63 | trailer 64 | << /ID [ (some) (id) ] 65 | /Root 6 0 R 66 | /Size 7 67 | >> 68 | startxref 69 | 634 70 | %%EOF -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/ads_black.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ads_black.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/ads_black.imageset/ads_black.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/ads_black.imageset/ads_black.pdf -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/ads_white.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ads_white.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/ads_white.imageset/ads_white.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/ads_white.imageset/ads_white.pdf -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/iconImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pepicons-pop_microphone-circle-filled.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /VoiLog/Assets.xcassets/iconImage.imageset/pepicons-pop_microphone-circle-filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/Assets.xcassets/iconImage.imageset/pepicons-pop_microphone-circle-filled.png -------------------------------------------------------------------------------- /VoiLog/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // VoiceMemo 4 | // 5 | // Created by 遠藤拓弥 on 3.9.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | var body: some View { 12 | VStack { 13 | Image(systemName: "globe") 14 | .imageScale(.large) 15 | .foregroundColor(.accentColor) 16 | Text("Hello, world!") 17 | } 18 | .padding() 19 | } 20 | } 21 | 22 | struct ContentView_Previews: PreviewProvider { 23 | static var previews: some View { 24 | ContentView() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /VoiLog/Extention/AVAudioPCMBuffer+Extention.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AVaudio.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 2.7.2023. 6 | // 7 | 8 | import AVFoundation 9 | import Accelerate 10 | 11 | extension AVAudioPCMBuffer { 12 | var waveFormHeight: CGFloat { 13 | let samples = floatChannelData!.pointee 14 | var avgValue: Float = 0 15 | vDSP_meamgv(samples, 1, &avgValue, UInt(frameLength)) 16 | let meterLevel = CGFloat(avgValue * 1) 17 | return min(meterLevel * 30, 30) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /VoiLog/Extention/Double+Extention.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Double+Extention.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 9.9.2023. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Double { 11 | func formattedAsKHz() -> String { 12 | let kHzValue = self / 1000.0 13 | return String(format: "%.1f kHz", kHzValue) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /VoiLog/Extention/TimeInterval+Extention.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 2024/11/11. 6 | // 7 | 8 | import Foundation 9 | extension TimeInterval { 10 | func formattedTime() -> String { 11 | let minutes = Int(self) / 60 12 | let seconds = Int(self) % 60 13 | return String(format: "%02d:%02d", minutes, seconds) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /VoiLog/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 950571144078-6gtsv5lo5b6aq0k9onoki06dc10g8tvr.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.950571144078-6gtsv5lo5b6aq0k9onoki06dc10g8tvr 9 | API_KEY 10 | AIzaSyDNHFswFbNCzj19idq7uKujKgZzsit2xdM 11 | GCM_SENDER_ID 12 | 950571144078 13 | PLIST_VERSION 14 | 1 15 | BUNDLE_ID 16 | com.entaku.VoiLog 17 | PROJECT_ID 18 | voilog 19 | STORAGE_BUCKET 20 | voilog.appspot.com 21 | IS_ADS_ENABLED 22 | 23 | IS_ANALYTICS_ENABLED 24 | 25 | IS_APPINVITE_ENABLED 26 | 27 | IS_GCM_ENABLED 28 | 29 | IS_SIGNIN_ENABLED 30 | 31 | GOOGLE_APP_ID 32 | 1:950571144078:ios:4d4483b9127e0479388adf 33 | ADMOB_APP_ID 34 | ca-app-pub-3484697221349891~3051778288 35 | 36 | -------------------------------------------------------------------------------- /VoiLog/Locate/de.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | VoiLog 4 | 5 | Created by 遠藤拓弥 on 9.8.2023. 6 | 7 | */ 8 | CFBundleDisplayName = "Einfacher Sprachrekorder"; 9 | -------------------------------------------------------------------------------- /VoiLog/Locate/en-IN.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | VoiLog 4 | 5 | Created by 遠藤拓弥 on 9.8.2023. 6 | 7 | */ 8 | CFBundleDisplayName = "Voice Recorder"; 9 | -------------------------------------------------------------------------------- /VoiLog/Locate/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | VoiLog 4 | 5 | Created by 遠藤拓弥 on 9.8.2023. 6 | 7 | */ 8 | CFBundleDisplayName = "Voice Recorder"; 9 | -------------------------------------------------------------------------------- /VoiLog/Locate/es.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | VoiLog 4 | 5 | Created by 遠藤拓弥 on 9.8.2023. 6 | 7 | */ 8 | CFBundleDisplayName = "Grabador de Voz Sencillo"; 9 | -------------------------------------------------------------------------------- /VoiLog/Locate/fr.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | VoiLog 4 | 5 | Created by 遠藤拓弥 on 9.8.2023. 6 | 7 | */ 8 | CFBundleDisplayName = "Enregistrement simple"; 9 | -------------------------------------------------------------------------------- /VoiLog/Locate/it.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | VoiLog 4 | 5 | Created by 遠藤拓弥 on 9.8.2023. 6 | 7 | */ 8 | CFBundleDisplayName = "Registratore vocale"; 9 | -------------------------------------------------------------------------------- /VoiLog/Locate/ja.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | VoiLog 4 | 5 | Created by 遠藤拓弥 on 9.8.2023. 6 | 7 | */ 8 | CFBundleDisplayName = "シンプル録音"; 9 | -------------------------------------------------------------------------------- /VoiLog/Locate/pt-PT.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | VoiLog 4 | 5 | Created by 遠藤拓弥 on 9.8.2023. 6 | 7 | */ 8 | CFBundleDisplayName = "Gravador de Voz"; 9 | 10 | -------------------------------------------------------------------------------- /VoiLog/Locate/ru.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | VoiLog 4 | 5 | Created by 遠藤拓弥 on 9.8.2023. 6 | 7 | */ 8 | CFBundleDisplayName = "простая запись"; 9 | -------------------------------------------------------------------------------- /VoiLog/Locate/tr.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | VoiLog 4 | 5 | Created by 遠藤拓弥 on 9.8.2023. 6 | 7 | */ 8 | CFBundleDisplayName = "Basit Kayıt"; 9 | -------------------------------------------------------------------------------- /VoiLog/Locate/vi.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | VoiLog 4 | 5 | Created by 遠藤拓弥 on 9.8.2023. 6 | 7 | */ 8 | CFBundleDisplayName = "Ghi âm đơn giản"; 9 | -------------------------------------------------------------------------------- /VoiLog/Locate/zh-Hans.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | VoiLog 4 | 5 | Created by 遠藤拓弥 on 9.8.2023. 6 | 7 | */ 8 | CFBundleDisplayName = "简单录音"; 9 | -------------------------------------------------------------------------------- /VoiLog/Locate/zh-Hant.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | VoiLog 4 | 5 | Created by 遠藤拓弥 on 9.8.2023. 6 | 7 | */ 8 | CFBundleDisplayName = "簡單語音錄音機"; 9 | -------------------------------------------------------------------------------- /VoiLog/Logger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Logger.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 10.6.2023. 6 | // 7 | 8 | import Foundation 9 | import FirebaseCrashlytics 10 | import RollbarNotifier 11 | 12 | class RollbarLogger { 13 | static let shared = RollbarLogger() 14 | 15 | private init() {} 16 | 17 | func initialize(with accessToken: String) { 18 | Crashlytics.crashlytics().setCustomValue(UUID().uuidString, forKey: "UUID") 19 | let config = RollbarConfig.mutableConfig(withAccessToken: accessToken) 20 | Rollbar.initWithConfiguration(config) 21 | } 22 | 23 | func logError(_ message: String) { 24 | Crashlytics.crashlytics().log("Error: \(message)") 25 | Rollbar.errorMessage(message) 26 | } 27 | 28 | func logInfo(_ message: String, context: String) { 29 | Rollbar.infoMessage(message, data: nil, context: context) 30 | } 31 | 32 | func logInfo(_ message: String) { 33 | Rollbar.infoMessage(message) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /VoiLog/Playlist/Playlist.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Playlist.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 2024/12/23. 6 | // 7 | 8 | import Foundation 9 | // MARK: - Models 10 | struct Playlist: Equatable, Identifiable { 11 | let id: UUID 12 | var name: String 13 | let createdAt: Date 14 | var updatedAt: Date 15 | 16 | init( 17 | id: UUID = UUID(), 18 | name: String, 19 | createdAt: Date = Date(), 20 | updatedAt: Date = Date() 21 | ) { 22 | self.id = id 23 | self.name = name 24 | self.createdAt = createdAt 25 | self.updatedAt = updatedAt 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /VoiLog/Playlist/PlaylistDetail.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlaylistDetail.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 2024/12/25. 6 | // 7 | 8 | import Foundation 9 | struct PlaylistDetail: Equatable { 10 | static func == (lhs: PlaylistDetail, rhs: PlaylistDetail) -> Bool { 11 | lhs.id == rhs.id && 12 | lhs.name == rhs.name && 13 | lhs.voices.map(\.id) == rhs.voices.map(\.id) && 14 | lhs.createdAt == rhs.createdAt && 15 | lhs.updatedAt == rhs.updatedAt 16 | } 17 | 18 | let id: UUID 19 | var name: String 20 | var voices: [VoiceMemoRepository.Voice] 21 | let createdAt: Date 22 | var updatedAt: Date 23 | var asPlaylist: Playlist { 24 | Playlist( 25 | id: id, 26 | name: name, 27 | createdAt: createdAt, 28 | updatedAt: updatedAt 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /VoiLog/Playlist/PlaylistListFeature.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlaylistListFeature.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 2024/12/24. 6 | // 7 | 8 | import Foundation 9 | import ComposableArchitecture 10 | struct PlaylistListFeature: Reducer { 11 | 12 | struct State: Equatable { 13 | var playlists: [Playlist] = [] 14 | var isLoading: Bool = false 15 | var error: String? 16 | var isShowingCreateSheet: Bool = false 17 | var newPlaylistName: String = "" 18 | var hasPurchasedPremium: Bool = false 19 | var isShowingPaywall: Bool = false 20 | 21 | } 22 | 23 | enum Action: Equatable { 24 | case onAppear 25 | case playlistsLoaded([Playlist]) 26 | case playlistsLoadingFailed(Error) 27 | case createPlaylistButtonTapped 28 | case createPlaylistSheetDismissed 29 | case updateNewPlaylistName(String) 30 | case createPlaylistSubmitted 31 | case playlistCreated(Playlist) 32 | case playlistCreationFailed(Error) 33 | case deletePlaylist(UUID) 34 | case playlistDeleted(UUID) 35 | case playlistDeletionFailed(Error) 36 | case showPaywall 37 | case paywallDismissed 38 | } 39 | 40 | @Dependency(\.playlistRepository) var playlistRepository 41 | 42 | func reduce(into state: inout State, action: Action) -> ComposableArchitecture.Effect { 43 | switch action { 44 | case .onAppear: 45 | state.isLoading = true 46 | state.hasPurchasedPremium = UserDefaultsManager.shared.hasPurchasedProduct 47 | 48 | return .run { send in 49 | do { 50 | let playlists = try await playlistRepository.fetchAll() 51 | await send(.playlistsLoaded(playlists)) 52 | } catch { 53 | await send(.playlistsLoadingFailed(error)) 54 | } 55 | } 56 | 57 | case let .playlistsLoaded(playlists): 58 | state.playlists = playlists 59 | state.isLoading = false 60 | state.error = nil 61 | return .none 62 | 63 | case let .playlistsLoadingFailed(error): 64 | state.isLoading = false 65 | state.error = error.localizedDescription 66 | return .none 67 | 68 | case .createPlaylistButtonTapped: 69 | if !state.hasPurchasedPremium && state.playlists.count >= 3 { 70 | state.isShowingPaywall = true 71 | }else{ 72 | state.isShowingCreateSheet = true 73 | } 74 | return .none 75 | 76 | case .createPlaylistSheetDismissed: 77 | state.isShowingCreateSheet = false 78 | state.newPlaylistName = "" 79 | return .none 80 | 81 | case let .updateNewPlaylistName(name): 82 | state.newPlaylistName = name 83 | return .none 84 | 85 | case .createPlaylistSubmitted: 86 | guard !state.newPlaylistName.isEmpty else { return .none } 87 | 88 | // プレミアム未購入時のプレイリスト制限チェック 89 | if !state.hasPurchasedPremium && state.playlists.count >= 3 { 90 | return .none 91 | } 92 | 93 | let name = state.newPlaylistName 94 | return .run { send in 95 | do { 96 | let playlist = try await playlistRepository.create(name: name) 97 | await send(.playlistCreated(playlist)) 98 | } catch { 99 | await send(.playlistCreationFailed(error)) 100 | } 101 | } 102 | 103 | case let .playlistCreated(playlist): 104 | state.playlists.insert(playlist, at: 0) 105 | state.isShowingCreateSheet = false 106 | state.newPlaylistName = "" 107 | state.error = nil 108 | return .none 109 | 110 | case let .playlistCreationFailed(error): 111 | state.error = error.localizedDescription 112 | return .none 113 | 114 | case let .deletePlaylist(id): 115 | guard let playlist = state.playlists.first(where: { $0.id == id }) else { return .none } 116 | return .run { send in 117 | do { 118 | try await playlistRepository.delete(playlist) 119 | await send(.playlistDeleted(id)) 120 | } catch { 121 | await send(.playlistDeletionFailed(error)) 122 | } 123 | } 124 | 125 | case let .playlistDeleted(id): 126 | state.playlists.removeAll { $0.id == id } 127 | return .none 128 | 129 | case let .playlistDeletionFailed(error): 130 | state.error = error.localizedDescription 131 | return .none 132 | case .showPaywall: 133 | state.isShowingPaywall = true 134 | return .none 135 | 136 | case .paywallDismissed: 137 | state.isShowingPaywall = false 138 | return .none 139 | } 140 | } 141 | 142 | } 143 | 144 | extension PlaylistListFeature.Action { 145 | static func == (lhs: Self, rhs: Self) -> Bool { 146 | switch (lhs, rhs) { 147 | case (.onAppear, .onAppear), 148 | (.createPlaylistButtonTapped, .createPlaylistButtonTapped), 149 | (.createPlaylistSheetDismissed, .createPlaylistSheetDismissed), 150 | (.createPlaylistSubmitted, .createPlaylistSubmitted): 151 | return true 152 | 153 | case let (.playlistsLoaded(lhs), .playlistsLoaded(rhs)): 154 | return lhs == rhs 155 | 156 | case let (.playlistsLoadingFailed(lhs), .playlistsLoadingFailed(rhs)): 157 | return (lhs as NSError) == (rhs as NSError) 158 | 159 | case let (.updateNewPlaylistName(lhs), .updateNewPlaylistName(rhs)): 160 | return lhs == rhs 161 | 162 | case let (.playlistCreated(lhs), .playlistCreated(rhs)): 163 | return lhs == rhs 164 | 165 | case let (.playlistCreationFailed(lhs), .playlistCreationFailed(rhs)): 166 | return (lhs as NSError) == (rhs as NSError) 167 | 168 | case let (.deletePlaylist(lhs), .deletePlaylist(rhs)): 169 | return lhs == rhs 170 | 171 | case let (.playlistDeleted(lhs), .playlistDeleted(rhs)): 172 | return lhs == rhs 173 | 174 | case let (.playlistDeletionFailed(lhs), .playlistDeletionFailed(rhs)): 175 | return (lhs as NSError) == (rhs as NSError) 176 | 177 | default: 178 | return false 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /VoiLog/Playlist/PlaylistListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlaylistListView.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 2024/12/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | import ComposableArchitecture 11 | // MARK: - View 12 | struct PlaylistListView: View { 13 | let store: StoreOf 14 | let admobUnitId:String 15 | 16 | var body: some View { 17 | WithViewStore(store, observe: { $0 }) { viewStore in 18 | VStack { 19 | List { 20 | ForEach(viewStore.playlists, id: \.id) { playlist in 21 | NavigationLink( 22 | destination: PlaylistDetailView( 23 | store: Store( 24 | initialState: PlaylistDetailFeature.State( 25 | id: playlist.id, 26 | name: playlist.name, 27 | voices: [], 28 | createdAt: playlist.createdAt, 29 | updatedAt: playlist.updatedAt 30 | ) 31 | ) { 32 | PlaylistDetailFeature() 33 | }, admobUnitId: admobUnitId 34 | ) 35 | ) { 36 | PlaylistRow(playlist: playlist) 37 | } 38 | .swipeActions { 39 | Button(role: .destructive) { 40 | viewStore.send(.deletePlaylist(playlist.id)) 41 | } label: { 42 | Label("削除", systemImage: "trash") 43 | } 44 | } 45 | } 46 | } 47 | 48 | if !viewStore.hasPurchasedPremium { 49 | AdmobBannerView(unitId: admobUnitId) 50 | .frame(height: 50) 51 | } 52 | } 53 | .navigationTitle("プレイリスト") 54 | .toolbar { 55 | ToolbarItem(placement: .navigationBarTrailing) { 56 | Button { 57 | viewStore.send(.createPlaylistButtonTapped) 58 | } label: { 59 | Image(systemName: "plus") 60 | } 61 | } 62 | } 63 | .sheet( 64 | isPresented: viewStore.binding( 65 | get: \.isShowingCreateSheet, 66 | send: { $0 ? .createPlaylistButtonTapped : .createPlaylistSheetDismissed } 67 | ) 68 | ) { 69 | CreatePlaylistView(store: store) 70 | } 71 | .sheet(isPresented: viewStore.binding( 72 | get: \.isShowingPaywall, 73 | send: PlaylistListFeature.Action.paywallDismissed 74 | )) { 75 | PaywallView(purchaseManager: PurchaseManager.shared) 76 | } 77 | .onAppear { viewStore.send(.onAppear) } 78 | } 79 | } 80 | } 81 | 82 | struct PlaylistRow: View { 83 | let playlist: Playlist 84 | 85 | var body: some View { 86 | VStack(alignment: .leading, spacing: 4) { 87 | Text(playlist.name) 88 | .font(.headline) 89 | 90 | HStack { 91 | Text(playlist.createdAt.formatted(date: .abbreviated, time: .omitted)) 92 | .font(.caption) 93 | .foregroundColor(.secondary) 94 | } 95 | } 96 | .padding(.vertical, 4) 97 | } 98 | } 99 | 100 | struct CreatePlaylistView: View { 101 | let store: StoreOf 102 | 103 | var body: some View { 104 | WithViewStore(store, observe: { $0 }) { viewStore in 105 | NavigationStack { 106 | Form { 107 | Section { 108 | TextField("プレイリスト名", text: viewStore.binding( 109 | get: \.newPlaylistName, 110 | send: PlaylistListFeature.Action.updateNewPlaylistName 111 | )) 112 | } 113 | } 114 | .navigationTitle("新規プレイリスト") 115 | .navigationBarTitleDisplayMode(.inline) 116 | .toolbar { 117 | ToolbarItem(placement: .cancellationAction) { 118 | Button("キャンセル") { 119 | viewStore.send(.createPlaylistSheetDismissed) 120 | } 121 | } 122 | 123 | ToolbarItem(placement: .confirmationAction) { 124 | Button("作成") { 125 | viewStore.send(.createPlaylistSubmitted) 126 | } 127 | .disabled(viewStore.newPlaylistName.isEmpty) 128 | } 129 | } 130 | } 131 | } 132 | } 133 | } 134 | 135 | #Preview { 136 | PlaylistListView( 137 | store: Store( 138 | initialState: PlaylistListFeature.State() 139 | ) { 140 | PlaylistListFeature() 141 | ._printChanges() 142 | }, admobUnitId: "ca-app-pub-3940256099942544/6300978111" 143 | ) 144 | 145 | } 146 | -------------------------------------------------------------------------------- /VoiLog/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /VoiLog/Prod.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Prod.xcconfig 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 2024/05/26. 6 | // 7 | 8 | // Configuration settings file format documentation can be found at: 9 | // https://help.apple.com/xcode/#/dev745c5c974 10 | ROLLBAR_KEY = dummy_key 11 | ADMOB_KEY = dummy_key 12 | RECORD_ADMOB_KEY = dummy_key 13 | REVENUECAT_KEY = dummy_key 14 | PLAYLIST_ADMOB_KEY = dummy_key 15 | -------------------------------------------------------------------------------- /VoiLog/Setting/AboutSimpleRecoder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutSimpleRecoder.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 18.9.2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct AboutSimpleRecoder: View { 11 | 12 | @State private var isShareSheetPresented = false 13 | 14 | var body: some View { 15 | 16 | List { 17 | 18 | Button(action: { 19 | // AppStoreのURLを指定して開く 20 | if let url = URL(string: "https://apps.apple.com/app/id6443528409") { 21 | UIApplication.shared.open(url, options: [:], completionHandler: nil) 22 | } 23 | }) { 24 | HStack { 25 | Image(systemName: "hands.sparkles") 26 | Text("AppStoreでレビューを書く") 27 | Spacer() 28 | } 29 | } 30 | 31 | Button(action: { 32 | isShareSheetPresented.toggle() 33 | }) { 34 | HStack { 35 | Image(systemName: "person.wave.2") 36 | Text("友人にアプリを教える") 37 | Spacer() 38 | } 39 | } 40 | .sheet(isPresented: $isShareSheetPresented) { 41 | ActivityViewController(activityItems: ["https://apps.apple.com/app/id6443528409"]) 42 | } 43 | 44 | } 45 | .listStyle(GroupedListStyle()) 46 | .navigationTitle("アプリについて") 47 | 48 | } 49 | 50 | } 51 | 52 | #Preview { 53 | AboutSimpleRecoder() 54 | } 55 | 56 | // UIActivityViewControllerをSwiftUIのViewに組み込むためのラッパー 57 | struct ActivityViewController: UIViewControllerRepresentable { 58 | typealias UIViewControllerType = UIActivityViewController 59 | 60 | let activityItems: [Any] 61 | 62 | func makeUIViewController(context: Context) -> UIActivityViewController { 63 | let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil) 64 | return activityViewController 65 | } 66 | 67 | func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) { 68 | // 更新が必要な場合の処理はここに追加 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /VoiLog/Setting/ErrorLogsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorLogsView.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 2024/07/21. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct ErrorLogsView: View { 12 | @ObservedObject private var viewModel = ErrorLogsViewModel() 13 | 14 | var body: some View { 15 | NavigationView { 16 | List(viewModel.errorLogs, id: \.self) { log in 17 | Text(log) 18 | .font(.body) 19 | .padding() 20 | } 21 | .navigationTitle("Error Logs") 22 | } 23 | .onAppear { 24 | viewModel.fetchErrorLogs() 25 | } 26 | } 27 | } 28 | 29 | class ErrorLogsViewModel: ObservableObject { 30 | @Published var errorLogs: [String] = [] 31 | 32 | func fetchErrorLogs() { 33 | errorLogs = UserDefaultsManager.shared.errorLogs 34 | } 35 | } 36 | 37 | struct ErrorLogsView_Previews: PreviewProvider { 38 | static var previews: some View { 39 | ErrorLogsView() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /VoiLog/Simple Voice Recorder - Audio.storekit: -------------------------------------------------------------------------------- 1 | { 2 | "identifier" : "FE6600C8", 3 | "nonRenewingSubscriptions" : [ 4 | 5 | ], 6 | "products" : [ 7 | { 8 | "displayPrice" : "2.99", 9 | "familyShareable" : false, 10 | "internalID" : "6474971238", 11 | "localizations" : [ 12 | { 13 | "description" : "developer support", 14 | "displayName" : "developer support", 15 | "locale" : "en_US" 16 | } 17 | ], 18 | "productID" : "developerSupport", 19 | "referenceName" : "developer support", 20 | "type" : "Consumable" 21 | } 22 | ], 23 | "settings" : { 24 | "_applicationInternalID" : "6443528409", 25 | "_developerTeamID" : "4YZQY4C47E", 26 | "_failTransactionsEnabled" : false, 27 | "_lastSynchronizedDate" : 734161902.82091296, 28 | "_locale" : "en_US", 29 | "_storefront" : "USA", 30 | "_storeKitErrors" : [ 31 | { 32 | "current" : null, 33 | "enabled" : false, 34 | "name" : "Load Products" 35 | }, 36 | { 37 | "current" : null, 38 | "enabled" : false, 39 | "name" : "Purchase" 40 | }, 41 | { 42 | "current" : null, 43 | "enabled" : false, 44 | "name" : "Verification" 45 | }, 46 | { 47 | "current" : null, 48 | "enabled" : false, 49 | "name" : "App Store Sync" 50 | }, 51 | { 52 | "current" : null, 53 | "enabled" : false, 54 | "name" : "Subscription Status" 55 | }, 56 | { 57 | "current" : { 58 | "index" : 3, 59 | "type" : "generic" 60 | }, 61 | "enabled" : false, 62 | "name" : "App Transaction" 63 | }, 64 | { 65 | "current" : null, 66 | "enabled" : false, 67 | "name" : "Manage Subscriptions Sheet" 68 | }, 69 | { 70 | "current" : null, 71 | "enabled" : false, 72 | "name" : "Refund Request Sheet" 73 | }, 74 | { 75 | "current" : null, 76 | "enabled" : false, 77 | "name" : "Offer Code Redeem Sheet" 78 | } 79 | ] 80 | }, 81 | "subscriptionGroups" : [ 82 | { 83 | "id" : "21472947", 84 | "localizations" : [ 85 | 86 | ], 87 | "name" : "pro", 88 | "subscriptions" : [ 89 | { 90 | "adHocOffers" : [ 91 | 92 | ], 93 | "codeOffers" : [ 94 | 95 | ], 96 | "displayPrice" : "6.99", 97 | "familyShareable" : false, 98 | "groupNumber" : 1, 99 | "internalID" : "6482985807", 100 | "introductoryOffer" : null, 101 | "localizations" : [ 102 | 103 | ], 104 | "productID" : "pro", 105 | "recurringSubscriptionPeriod" : "P1M", 106 | "referenceName" : "pro - monthly", 107 | "subscriptionGroupID" : "21472947", 108 | "type" : "RecurringSubscription" 109 | } 110 | ] 111 | } 112 | ], 113 | "version" : { 114 | "major" : 3, 115 | "minor" : 0 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /VoiLog/Store/MockIAPManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockIAPManager.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 2024/04/14. 6 | // 7 | 8 | import Foundation 9 | class MockPurchaseManager: PurchaseManagerProtocol { 10 | var productName: String 11 | var productPrice: String 12 | var shouldThrowError: Bool 13 | 14 | init(productName: String = "Premium Plan", 15 | productPrice: String = "¥1200", 16 | shouldThrowError: Bool = false) { 17 | self.productName = productName 18 | self.productPrice = productPrice 19 | self.shouldThrowError = shouldThrowError 20 | } 21 | 22 | func fetchProPlan() async throws -> (name: String, price: String) { 23 | if shouldThrowError { 24 | throw PurchaseError.productNotFound 25 | } 26 | return (name: productName, price: productPrice) 27 | } 28 | 29 | func purchasePro() async throws { 30 | if shouldThrowError { 31 | throw PurchaseError.purchaseFailed 32 | } 33 | UserDefaultsManager.shared.hasPurchasedProduct = true 34 | } 35 | 36 | func startOneTimePurchase() async throws { 37 | if shouldThrowError { 38 | throw PurchaseError.purchaseFailed 39 | } 40 | UserDefaultsManager.shared.hasSupportedDeveloper = true 41 | } 42 | 43 | func restorePurchases() async throws { 44 | if shouldThrowError { 45 | throw PurchaseError.noEntitlements 46 | } 47 | UserDefaultsManager.shared.hasPurchasedProduct = true 48 | } 49 | } 50 | 51 | // テスト用の便利なファクトリメソッド 52 | extension MockPurchaseManager { 53 | static var succeeding: MockPurchaseManager { 54 | MockPurchaseManager(shouldThrowError: false) 55 | } 56 | 57 | static var failing: MockPurchaseManager { 58 | MockPurchaseManager(shouldThrowError: true) 59 | } 60 | } 61 | 62 | // カスタムエラーの定義を追加 63 | extension MockPurchaseManager { 64 | enum PurchaseError: Error { 65 | case productNotFound 66 | case purchaseFailed 67 | case noEntitlements 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /VoiLog/Template/FeatureTemplate.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import ComposableArchitecture 3 | 4 | @Reducer 5 | struct FeatureReducer { 6 | @ObservableState 7 | struct State: Equatable { 8 | var items: [Item] = [] 9 | var searchQuery: String = "" 10 | var isLoading: Bool = false 11 | } 12 | 13 | struct Item: Identifiable, Equatable { 14 | var id: UUID 15 | var title: String 16 | var description: String 17 | var isFavorite: Bool = false 18 | 19 | init(id: UUID = UUID(), title: String, description: String) { 20 | self.id = id 21 | self.title = title 22 | self.description = description 23 | } 24 | } 25 | 26 | enum Action: ViewAction, BindableAction { 27 | case binding(BindingAction) 28 | case itemsLoaded([Item]) 29 | case toggleFavorite(Item.ID) 30 | case view(View) 31 | 32 | enum View { 33 | case itemSelected(Item.ID) 34 | case itemFavoriteToggled(Item.ID) 35 | case refreshRequested 36 | case loadItems 37 | } 38 | } 39 | 40 | @Dependency(\.continuousClock) var clock 41 | 42 | var body: some Reducer { 43 | BindingReducer() 44 | Reduce { state, action in 45 | switch action { 46 | case .binding: 47 | return .none 48 | 49 | case let .view(viewAction): 50 | switch viewAction { 51 | case let .itemSelected(id): 52 | // アイテム選択の処理 53 | return .none 54 | 55 | case let .itemFavoriteToggled(id): 56 | return .send(.toggleFavorite(id)) 57 | 58 | case .refreshRequested: 59 | return .send(.view(.loadItems)) 60 | 61 | case .loadItems: 62 | state.isLoading = true 63 | return .run { send in 64 | // データのロードをシミュレート 65 | try await clock.sleep(for: .seconds(1)) 66 | 67 | let items = [ 68 | Item(title: "アイテム1", description: "説明1"), 69 | Item(title: "アイテム2", description: "説明2"), 70 | Item(title: "アイテム3", description: "説明3") 71 | ] 72 | 73 | await send(.itemsLoaded(items)) 74 | } 75 | } 76 | 77 | case let .toggleFavorite(id): 78 | if let index = state.items.firstIndex(where: { $0.id == id }) { 79 | state.items[index].isFavorite.toggle() 80 | } 81 | return .none 82 | 83 | case let .itemsLoaded(items): 84 | state.items = items 85 | state.isLoading = false 86 | return .none 87 | } 88 | } 89 | } 90 | } 91 | 92 | @ViewAction(for: FeatureReducer.self) 93 | struct FeatureView: View { 94 | @Perception.Bindable var store: StoreOf 95 | 96 | var body: some View { 97 | NavigationStack { 98 | VStack { 99 | // 検索フィールド 100 | HStack { 101 | Image(systemName: "magnifyingglass") 102 | .foregroundColor(.gray) 103 | TextField("検索...", text: $store.searchQuery) 104 | .textFieldStyle(.roundedBorder) 105 | } 106 | .padding(.horizontal) 107 | 108 | // リスト 109 | List { 110 | ForEach(store.items.filter { 111 | store.searchQuery.isEmpty || 112 | $0.title.localizedCaseInsensitiveContains(store.searchQuery) 113 | }) { item in 114 | HStack { 115 | VStack(alignment: .leading) { 116 | Text(item.title) 117 | .font(.headline) 118 | Text(item.description) 119 | .font(.subheadline) 120 | .foregroundColor(.gray) 121 | } 122 | 123 | Spacer() 124 | 125 | Button { 126 | send(.itemFavoriteToggled(item.id)) 127 | } label: { 128 | Image(systemName: item.isFavorite ? "star.fill" : "star") 129 | .foregroundColor(item.isFavorite ? .yellow : .gray) 130 | } 131 | .buttonStyle(.plain) 132 | } 133 | .contentShape(Rectangle()) 134 | .onTapGesture { 135 | send(.itemSelected(item.id)) 136 | } 137 | } 138 | } 139 | .listStyle(.plain) 140 | .refreshable { 141 | await send(.refreshRequested).finish() 142 | } 143 | .overlay { 144 | if store.isLoading { 145 | ProgressView() 146 | .frame(maxWidth: .infinity, maxHeight: .infinity) 147 | .background(Color.black.opacity(0.2)) 148 | } 149 | } 150 | } 151 | .navigationTitle("機能名") 152 | .onAppear { 153 | send(.loadItems) 154 | } 155 | } 156 | } 157 | } 158 | 159 | #Preview { 160 | FeatureView( 161 | store: Store(initialState: FeatureReducer.State()) { 162 | FeatureReducer() 163 | } 164 | ) 165 | } -------------------------------------------------------------------------------- /VoiLog/Template/README.md: -------------------------------------------------------------------------------- 1 | # ComposableArchitecture テンプレート 2 | 3 | このディレクトリには、ComposableArchitecture (TCA) を使用した機能開発のためのテンプレートファイルが含まれています。 4 | 5 | ## FeatureTemplate.swift 6 | 7 | `FeatureTemplate.swift` は以下の機能を提供する基本的なTCAパターンを含んでいます: 8 | 9 | - 検索機能付きのリスト表示 10 | - リストアイテムのお気に入り登録機能 11 | - プルダウンでのリフレッシュ機能 12 | - ローディング状態の表示 13 | 14 | ### 使用方法 15 | 16 | 1. `FeatureTemplate.swift` をコピーして新しいファイルを作成 17 | 2. `FeatureReducer` と `FeatureView` を新しい機能名に変更 18 | 3. `State` 構造体を機能に必要なプロパティで拡張 19 | 4. `Action` 列挙型に必要なアクションを追加 20 | 5. `Item` 構造体を実際のデータモデルに合わせて調整 21 | 6. `body` メソッド内のreducerロジックを実装 22 | 7. `FeatureView` のUI部分をカスタマイズ 23 | 24 | ### 例 25 | 26 | ```swift 27 | // MyCustomFeature.swift 28 | import SwiftUI 29 | import ComposableArchitecture 30 | 31 | @Reducer 32 | struct MyCustomFeature { 33 | @ObservableState 34 | struct State: Equatable { 35 | // プロパティをカスタマイズ 36 | } 37 | 38 | // ... 他のコードを調整 39 | } 40 | 41 | @ViewAction(for: MyCustomFeature.self) 42 | struct MyCustomView: View { 43 | // ... UIをカスタマイズ 44 | } 45 | ``` 46 | 47 | ## 注意事項 48 | 49 | - このテンプレートはComposableArchitecture v1.5以降を対象としています 50 | - @ObservableStateと@ViewActionパターンを使用しています 51 | - Previewセクションを含んでいるため、すぐにUIを確認できます -------------------------------------------------------------------------------- /VoiLog/VoiLogRelease.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | com.apple.developer.icloud-container-identifiers 8 | 9 | iCloud.com.entaku.VoiLog 10 | 11 | com.apple.developer.icloud-services 12 | 13 | CloudKit 14 | 15 | com.apple.security.app-sandbox 16 | 17 | com.apple.security.files.user-selected.read-only 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /VoiLog/Voice/AudioLevelView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 2024/07/21. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct AudioLevelView: View { 12 | var audioLevel: Float 13 | 14 | let minLevel: Float = -60 15 | let maxLevel: Float = 0 16 | 17 | var body: some View { 18 | GeometryReader { geometry in 19 | ZStack(alignment: .leading) { 20 | Rectangle() 21 | .foregroundColor(.gray) 22 | .opacity(0.3) 23 | Rectangle() 24 | .foregroundColor(.blue) 25 | .frame(width: normalizedWidth(for: audioLevel, in: geometry.size.width)) 26 | .animation(.easeInOut(duration: 0.2), value: audioLevel) 27 | } 28 | .cornerRadius(10) 29 | } 30 | } 31 | 32 | private func normalizedWidth(for audioLevel: Float, in totalWidth: CGFloat) -> CGFloat { 33 | let clampedLevel = max(min(audioLevel, maxLevel), minLevel) 34 | return CGFloat((clampedLevel - minLevel) / (maxLevel - minLevel)) * totalWidth 35 | } 36 | } 37 | 38 | struct AudioLevelView_Previews: PreviewProvider { 39 | static var previews: some View { 40 | Group { 41 | AudioLevelView(audioLevel: -50) 42 | .frame(height: 20) 43 | .previewDisplayName("Low Level") 44 | 45 | AudioLevelView(audioLevel: -30) 46 | .frame(height: 20) 47 | .previewDisplayName("Medium Level") 48 | 49 | AudioLevelView(audioLevel: -10) 50 | .frame(height: 20) 51 | .previewDisplayName("High Level") 52 | 53 | AudioLevelView(audioLevel: 0) 54 | .frame(height: 20) 55 | .previewDisplayName("Max Level") 56 | } 57 | .padding() 58 | .previewLayout(.sizeThatFits) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /VoiLog/Voice/DependencyValues.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DependencyValues.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 14.10.2023. 6 | // 7 | 8 | import Dependencies 9 | import SwiftUI 10 | import XCTestDynamicOverlay 11 | 12 | extension DependencyValues { 13 | var openSettings: @Sendable () async -> Void { 14 | get { self[OpenSettingsKey.self] } 15 | set { self[OpenSettingsKey.self] = newValue } 16 | } 17 | 18 | private enum OpenSettingsKey: DependencyKey { 19 | typealias Value = @Sendable () async -> Void 20 | 21 | static let liveValue: @Sendable () async -> Void = { 22 | await MainActor.run { 23 | UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!) 24 | } 25 | } 26 | static let testValue: @Sendable () async -> Void = unimplemented( 27 | #"@Dependency(\.openSettings)"# 28 | ) 29 | } 30 | 31 | var temporaryDirectory: @Sendable () -> URL { 32 | get { self[TemporaryDirectoryKey.self] } 33 | set { self[TemporaryDirectoryKey.self] = newValue } 34 | } 35 | 36 | private enum TemporaryDirectoryKey: DependencyKey { 37 | static let liveValue: @Sendable () -> URL = { URL(fileURLWithPath: NSTemporaryDirectory()) } 38 | static let testValue: @Sendable () -> URL = XCTUnimplemented( 39 | #"@Dependency(\.temporaryDirectory)"#, 40 | placeholder: URL(fileURLWithPath: NSTemporaryDirectory()) 41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /VoiLog/Voice/MailComposeViewControllerWrapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MailComposeViewControllerWrapper.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 21.9.2023. 6 | // 7 | 8 | import SwiftUI 9 | import MessageUI 10 | import UIKit 11 | 12 | struct MailComposeViewControllerWrapper: UIViewControllerRepresentable { 13 | @Binding var isPresented: Bool 14 | 15 | class Coordinator: NSObject, MFMailComposeViewControllerDelegate { 16 | @Binding var isPresented: Bool 17 | 18 | init(isPresented: Binding) { 19 | _isPresented = isPresented 20 | } 21 | 22 | func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { 23 | isPresented = false 24 | controller.dismiss(animated: true, completion: nil) 25 | } 26 | } 27 | 28 | func makeCoordinator() -> Coordinator { 29 | Coordinator(isPresented: $isPresented) 30 | } 31 | 32 | func makeUIViewController(context: Context) -> MFMailComposeViewController { 33 | let viewController = MFMailComposeViewController() 34 | viewController.mailComposeDelegate = context.coordinator 35 | viewController.setToRecipients(["entaku19890818@gmail.com"]) 36 | viewController.setSubject("シンプル録音 問い合わせ - iOS") 37 | 38 | // iOSバージョンを取得 39 | let iOSVersion = UIDevice.current.systemVersion 40 | 41 | // アプリのバージョンを取得 42 | if let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String { 43 | let mailBody = """ 44 | 45 | 46 | 47 | [iOS: \(iOSVersion) バージョン: \(appVersion)] 48 | 49 | """ 50 | viewController.setMessageBody(mailBody, isHTML: false) 51 | } else { 52 | viewController.setMessageBody("", isHTML: false) 53 | } 54 | 55 | return viewController 56 | } 57 | 58 | func updateUIViewController(_ uiViewController: MFMailComposeViewController, context: Context) { 59 | // Nothing to do here 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /VoiLog/Voice/TutorialView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import ComposableArchitecture 3 | 4 | struct TutorialView: View { 5 | let store: StoreOf 6 | 7 | var body: some View { 8 | ZStack { 9 | Color.black.opacity(0.7) 10 | .edgesIgnoringSafeArea(.all) 11 | 12 | VStack(spacing: 20) { 13 | Text(LocalizedStringKey("シンプル録音へようこそ!")) 14 | .font(.title) 15 | .foregroundColor(.white) 16 | .padding(.top, 30) 17 | 18 | VStack(alignment: .leading, spacing: 15) { 19 | TutorialItem( 20 | icon: "mic.circle.fill", 21 | title: LocalizedStringKey("1. 録音する"), 22 | description: LocalizedStringKey("マイクボタンをタップして録音を開始します"), 23 | customView: AnyView( 24 | ZStack { 25 | Circle() 26 | .foregroundColor(Color(.label)) 27 | .frame(width: 40, height: 40) 28 | 29 | Circle() 30 | .foregroundColor(Color(.systemRed)) 31 | .padding(2) 32 | .frame(width: 36, height: 36) 33 | } 34 | ) 35 | ) 36 | 37 | TutorialItem( 38 | icon: "stop.circle.fill", 39 | title: LocalizedStringKey("2. 録音を停止"), 40 | description: LocalizedStringKey("停止ボタンをタップして録音を終了します"), 41 | customView: AnyView( 42 | ZStack { 43 | Circle() 44 | .foregroundColor(Color(.label)) 45 | .frame(width: 40, height: 40) 46 | 47 | RoundedRectangle(cornerRadius: 3) 48 | .foregroundColor(Color(.systemRed)) 49 | .frame(width: 20, height: 20) 50 | } 51 | ) 52 | ) 53 | 54 | TutorialItem( 55 | icon: "play.circle.fill", 56 | title: LocalizedStringKey("3. 再生する"), 57 | description: LocalizedStringKey("録音したメモをタップして再生できます") 58 | ) 59 | } 60 | .padding(.horizontal, 30) 61 | 62 | Button { 63 | ViewStore(store, observe: { $0 }).send(.tutorialDismissed) 64 | } label: { 65 | Text(LocalizedStringKey("始める")) 66 | .font(.headline) 67 | .foregroundColor(.white) 68 | .frame(width: 200, height: 50) 69 | .background(Color.blue) 70 | .cornerRadius(25) 71 | } 72 | .padding(.top, 30) 73 | .padding(.bottom, 40) 74 | } 75 | .background(Color(UIColor.systemBackground)) 76 | .cornerRadius(20) 77 | .padding(.horizontal, 20) 78 | } 79 | } 80 | } 81 | 82 | struct TutorialItem: View { 83 | let icon: String 84 | let title: LocalizedStringKey 85 | let description: LocalizedStringKey 86 | let customView: AnyView? 87 | 88 | init(icon: String, title: LocalizedStringKey, description: LocalizedStringKey, customView: AnyView? = nil) { 89 | self.icon = icon 90 | self.title = title 91 | self.description = description 92 | self.customView = customView 93 | } 94 | 95 | var body: some View { 96 | HStack(spacing: 15) { 97 | if let customView = customView { 98 | customView 99 | } else { 100 | Image(systemName: icon) 101 | .font(.system(size: 30)) 102 | .foregroundColor(.blue) 103 | } 104 | 105 | VStack(alignment: .leading, spacing: 5) { 106 | Text(title) 107 | .font(.headline) 108 | Text(description) 109 | .font(.subheadline) 110 | .foregroundColor(.gray) 111 | } 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /VoiLog/Voice/WaveformView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct WaveformView: View { 4 | let waveformData: [Float] 5 | let selectedRange: ClosedRange? 6 | let currentTime: Double 7 | let duration: TimeInterval 8 | let onRangeSelected: (ClosedRange?) -> Void 9 | let onSeek: (Double) -> Void 10 | 11 | @State private var isSelecting = false 12 | @State private var selectionStart: CGFloat = 0 13 | @State private var selectionEnd: CGFloat = 0 14 | @State private var dragOffset: CGFloat = 0 15 | @GestureState private var isDragging = false 16 | 17 | private let barWidth: CGFloat = 2 18 | private let barSpacing: CGFloat = 1 19 | private let minBarHeight: CGFloat = 3 20 | private let selectionColor = Color.blue.opacity(0.3) 21 | private let waveformColor = Color.blue 22 | private let currentPositionColor = Color.red 23 | 24 | var body: some View { 25 | GeometryReader { geometry in 26 | ZStack(alignment: .leading) { 27 | // 波形表示 28 | HStack(spacing: barSpacing) { 29 | ForEach(0.. [Float] { 107 | var data = [Float]() 108 | for i in 0.. 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | com.apple.developer.icloud-container-identifiers 8 | 9 | iCloud.com.entaku.VoiLog 10 | 11 | com.apple.developer.icloud-services 12 | 13 | CloudKit 14 | 15 | com.apple.security.app-sandbox 16 | 17 | com.apple.security.files.user-selected.read-only 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /VoiLog/data/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 15.6.2023. 6 | // 7 | 8 | import Foundation 9 | import CoreAudioTypes 10 | 11 | class Constants { 12 | static let defaultFileFormat: FileFormat = .WAV 13 | static let defaultSamplingFrequency: SamplingFrequency = .hz44100 14 | static let defaultQuantizationBitDepth: QuantizationBitDepth = .bit16 15 | static let defaultNumberOfChannels: NumberOfChannels = .one 16 | static let defaultMicrophonesVolume: MicrophonesVolume = .one 17 | 18 | enum FileFormat: String, CaseIterable { 19 | case WAV 20 | case AAC 21 | 22 | var audioId: AudioFormatID { 23 | switch self { 24 | case .WAV: 25 | return kAudioFormatLinearPCM 26 | case .AAC: 27 | return kAudioFormatMPEG4AAC 28 | } 29 | } 30 | } 31 | 32 | enum SamplingFrequency: Double, CaseIterable { 33 | case hz11000 = 11000 34 | case hz22000 = 22000 35 | case hz44100 = 44100 36 | case hz48000 = 48000 37 | 38 | var stringValue: String { 39 | switch self { 40 | case .hz11000: 41 | return "11,000Hz" 42 | case .hz22000: 43 | return "22,000Hz" 44 | case .hz44100: 45 | return "44,100Hz" 46 | case .hz48000: 47 | return "48,000Hz" 48 | } 49 | } 50 | } 51 | 52 | enum QuantizationBitDepth: Int, CaseIterable { 53 | case bit8 = 8 54 | case bit16 = 16 55 | case bit24 = 24 56 | case bit32 = 32 57 | 58 | var stringValue: String { 59 | switch self { 60 | case .bit8: 61 | return "8bit" 62 | case .bit16: 63 | return "16bit" 64 | case .bit24: 65 | return "24bit" 66 | case .bit32: 67 | return "32bit" 68 | } 69 | } 70 | } 71 | enum NumberOfChannels: Int, CaseIterable { 72 | case one = 1 73 | case two 74 | } 75 | 76 | enum MicrophonesVolume: Double, CaseIterable { 77 | case one = 1 78 | case ten = 10 79 | case fifty = 50 80 | case hundred = 100 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /VoiLog/data/Playlist.xcdatamodeld/Playlist.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /VoiLog/data/Thema.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /VoiLog/data/UserDefaultsClient.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Dependencies 3 | 4 | struct UserDefaultsClient { 5 | var logError: @Sendable (String) -> Void 6 | var errorLogs: @Sendable () -> [String] 7 | var selectedFileFormat: @Sendable () -> String 8 | var setSelectedFileFormat: @Sendable (String) -> Void 9 | var samplingFrequency: @Sendable () -> Double 10 | var setSamplingFrequency: @Sendable (Double) -> Void 11 | var quantizationBitDepth: @Sendable () -> Int 12 | var setQuantizationBitDepth: @Sendable (Int) -> Void 13 | var numberOfChannels: @Sendable () -> Int 14 | var setNumberOfChannels: @Sendable (Int) -> Void 15 | var microphonesVolume: @Sendable () -> Double 16 | var setMicrophonesVolume: @Sendable (Double) -> Void 17 | var installDate: @Sendable () -> Date? 18 | var setInstallDate: @Sendable (Date?) -> Void 19 | var reviewRequestCount: @Sendable () -> Int 20 | var setReviewRequestCount: @Sendable (Int) -> Void 21 | var hasSupportedDeveloper: @Sendable () -> Bool 22 | var setHasSupportedDeveloper: @Sendable (Bool) -> Void 23 | var hasPurchasedProduct: @Sendable () -> Bool 24 | var setHasPurchasedProduct: @Sendable (Bool) -> Void 25 | var hasSeenTutorial: @Sendable () -> Bool 26 | var setHasSeenTutorial: @Sendable (Bool) -> Void 27 | } 28 | 29 | extension UserDefaultsClient: DependencyKey { 30 | static let liveValue = Self( 31 | logError: { message in 32 | let timestamp = Date().description(with: .current) 33 | let logMessage = "[\(timestamp)] \(message)" 34 | let defaults = UserDefaults.standard 35 | var errorLogs = defaults.array(forKey: "ErrorLogs") as? [String] ?? [] 36 | errorLogs.append(logMessage) 37 | defaults.set(errorLogs, forKey: "ErrorLogs") 38 | defaults.synchronize() 39 | }, 40 | errorLogs: { 41 | UserDefaults.standard.array(forKey: "ErrorLogs") as? [String] ?? [] 42 | }, 43 | selectedFileFormat: { 44 | UserDefaults.standard.string(forKey: "SelectedFileFormat") ?? Constants.defaultFileFormat.rawValue 45 | }, 46 | setSelectedFileFormat: { newValue in 47 | UserDefaults.standard.set(newValue, forKey: "SelectedFileFormat") 48 | }, 49 | samplingFrequency: { 50 | let value = UserDefaults.standard.double(forKey: "SamplingFrequency") 51 | return value == 0 ? Constants.defaultSamplingFrequency.rawValue : value 52 | }, 53 | setSamplingFrequency: { newValue in 54 | UserDefaults.standard.set(newValue, forKey: "SamplingFrequency") 55 | }, 56 | quantizationBitDepth: { 57 | let value = UserDefaults.standard.integer(forKey: "QuantizationBitDepth") 58 | return value == 0 ? Constants.defaultQuantizationBitDepth.rawValue : value 59 | }, 60 | setQuantizationBitDepth: { newValue in 61 | UserDefaults.standard.set(newValue, forKey: "QuantizationBitDepth") 62 | }, 63 | numberOfChannels: { 64 | let value = UserDefaults.standard.integer(forKey: "NumberOfChannels") 65 | return value == 0 ? Constants.defaultNumberOfChannels.rawValue : value 66 | }, 67 | setNumberOfChannels: { newValue in 68 | UserDefaults.standard.set(newValue, forKey: "NumberOfChannels") 69 | }, 70 | microphonesVolume: { 71 | let value = UserDefaults.standard.double(forKey: "MicrophonesVolume") 72 | return value == 0 ? Constants.defaultMicrophonesVolume.rawValue : value 73 | }, 74 | setMicrophonesVolume: { newValue in 75 | UserDefaults.standard.set(newValue, forKey: "MicrophonesVolume") 76 | }, 77 | installDate: { 78 | UserDefaults.standard.object(forKey: "InstallDate") as? Date 79 | }, 80 | setInstallDate: { newValue in 81 | UserDefaults.standard.set(newValue, forKey: "InstallDate") 82 | }, 83 | reviewRequestCount: { 84 | UserDefaults.standard.object(forKey: "ReviewRequestCount") as? Int ?? 0 85 | }, 86 | setReviewRequestCount: { newValue in 87 | UserDefaults.standard.set(newValue, forKey: "ReviewRequestCount") 88 | }, 89 | hasSupportedDeveloper: { 90 | UserDefaults.standard.bool(forKey: "HasSupportedDeveloper") 91 | }, 92 | setHasSupportedDeveloper: { newValue in 93 | UserDefaults.standard.set(newValue, forKey: "HasSupportedDeveloper") 94 | }, 95 | hasPurchasedProduct: { 96 | UserDefaults.standard.bool(forKey: "HasPurchasedProduct") 97 | }, 98 | setHasPurchasedProduct: { newValue in 99 | UserDefaults.standard.set(newValue, forKey: "HasPurchasedProduct") 100 | }, 101 | hasSeenTutorial: { 102 | UserDefaults.standard.bool(forKey: "HasSeenTutorial") 103 | }, 104 | setHasSeenTutorial: { newValue in 105 | UserDefaults.standard.set(newValue, forKey: "HasSeenTutorial") 106 | } 107 | ) 108 | 109 | static let testValue = Self( 110 | logError: { _ in }, 111 | errorLogs: { [] }, 112 | selectedFileFormat: { Constants.defaultFileFormat.rawValue }, 113 | setSelectedFileFormat: { _ in }, 114 | samplingFrequency: { Constants.defaultSamplingFrequency.rawValue }, 115 | setSamplingFrequency: { _ in }, 116 | quantizationBitDepth: { Constants.defaultQuantizationBitDepth.rawValue }, 117 | setQuantizationBitDepth: { _ in }, 118 | numberOfChannels: { Constants.defaultNumberOfChannels.rawValue }, 119 | setNumberOfChannels: { _ in }, 120 | microphonesVolume: { Constants.defaultMicrophonesVolume.rawValue }, 121 | setMicrophonesVolume: { _ in }, 122 | installDate: { nil }, 123 | setInstallDate: { _ in }, 124 | reviewRequestCount: { 0 }, 125 | setReviewRequestCount: { _ in }, 126 | hasSupportedDeveloper: { false }, 127 | setHasSupportedDeveloper: { _ in }, 128 | hasPurchasedProduct: { false }, 129 | setHasPurchasedProduct: { _ in }, 130 | hasSeenTutorial: { false }, 131 | setHasSeenTutorial: { _ in } 132 | ) 133 | } 134 | 135 | extension DependencyValues { 136 | var userDefaults: UserDefaultsClient { 137 | get { self[UserDefaultsClient.self] } 138 | set { self[UserDefaultsClient.self] = newValue } 139 | } 140 | } -------------------------------------------------------------------------------- /VoiLog/data/UserDefaultsManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDefaultsManager.swift 3 | // VoiLog 4 | // 5 | // Created by 遠藤拓弥 on 15.6.2023. 6 | // 7 | 8 | import Foundation 9 | class UserDefaultsManager { 10 | static let shared = UserDefaultsManager() 11 | 12 | private let defaults: UserDefaults 13 | 14 | private init() { 15 | defaults = UserDefaults.standard 16 | } 17 | 18 | func logError(_ message: String) { 19 | let timestamp = Date().description(with: .current) 20 | let logMessage = "[\(timestamp)] \(message)" 21 | 22 | var errorLogs = defaults.array(forKey: "ErrorLogs") as? [String] ?? [] 23 | errorLogs.append(logMessage) 24 | 25 | defaults.set(errorLogs, forKey: "ErrorLogs") 26 | defaults.synchronize() 27 | } 28 | 29 | // Property to retrieve error logs 30 | var errorLogs: [String] { 31 | defaults.array(forKey: "ErrorLogs") as? [String] ?? [] 32 | } 33 | 34 | // ファイル形式の設定値を保存するプロパティ 35 | var selectedFileFormat: String { 36 | get { 37 | defaults.string(forKey: "SelectedFileFormat") ?? Constants.defaultFileFormat.rawValue 38 | } 39 | set { 40 | defaults.set(newValue, forKey: "SelectedFileFormat") 41 | } 42 | } 43 | 44 | var samplingFrequency: Double { 45 | get { 46 | let value = defaults.double(forKey: "SamplingFrequency") 47 | return value == 0 ? Constants.defaultSamplingFrequency.rawValue : value 48 | } 49 | set { 50 | defaults.set(newValue, forKey: "SamplingFrequency") 51 | } 52 | } 53 | 54 | var quantizationBitDepth: Int { 55 | get { 56 | let value = defaults.integer(forKey: "QuantizationBitDepth") 57 | return value == 0 ? Constants.defaultQuantizationBitDepth.rawValue : value 58 | } 59 | set { 60 | defaults.set(newValue, forKey: "QuantizationBitDepth") 61 | } 62 | } 63 | var numberOfChannels: Int { 64 | get { 65 | let value = defaults.integer(forKey: "NumberOfChannels") 66 | return value == 0 ? Constants.defaultNumberOfChannels.rawValue : value 67 | } 68 | set { 69 | defaults.set(newValue, forKey: "NumberOfChannels") 70 | } 71 | } 72 | 73 | var microphonesVolume: Double { 74 | get { 75 | let value = defaults.double(forKey: "MicrophonesVolume") 76 | return value == 0 ? Constants.defaultMicrophonesVolume.rawValue : value 77 | } 78 | set { 79 | defaults.set(newValue, forKey: "MicrophonesVolume") 80 | } 81 | } 82 | 83 | // インストール日を保存するプロパティ 84 | var installDate: Date? { 85 | get { 86 | defaults.object(forKey: "InstallDate") as? Date 87 | } 88 | set { 89 | defaults.set(newValue, forKey: "InstallDate") 90 | } 91 | } 92 | 93 | // レビューのリクエストカウントを保存する 94 | var reviewRequestCount: Int { 95 | get { 96 | defaults.object(forKey: "ReviewRequestCount") as? Int ?? 0 97 | } 98 | set { 99 | defaults.set(newValue, forKey: "ReviewRequestCount") 100 | } 101 | } 102 | 103 | // デベロッパーサポートしたかどうか? 104 | var hasSupportedDeveloper: Bool { 105 | get { 106 | defaults.bool(forKey: "HasSupportedDeveloper") 107 | } 108 | set { 109 | defaults.set(newValue, forKey: "HasSupportedDeveloper") 110 | } 111 | } 112 | 113 | var hasPurchasedProduct: Bool { 114 | get { 115 | defaults.bool(forKey: "HasPurchasedProduct") 116 | } 117 | set { 118 | defaults.set(newValue, forKey: "HasPurchasedProduct") 119 | } 120 | } 121 | 122 | // チュートリアルを表示したかどうか 123 | var hasSeenTutorial: Bool { 124 | get { 125 | defaults.bool(forKey: "HasSeenTutorial") 126 | } 127 | set { 128 | defaults.set(newValue, forKey: "HasSeenTutorial") 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /VoiLog/data/Voice.xcdatamodeld/Model.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /VoiLog/ff16audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/VoiLog/ff16audio.mp3 -------------------------------------------------------------------------------- /VoiLogTests/MockCloudUploader.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import VoiLog 3 | 4 | class MockCloudUploader: CloudUploaderProtocol { 5 | 6 | var savedVoice: VoiceMemoRepository.Voice? 7 | var fetchedVoices: [VoiceMemoRepository.Voice] = [] 8 | 9 | func saveVoice(voice: VoiceMemoRepository.Voice) async -> Bool { 10 | self.savedVoice = voice 11 | return true 12 | } 13 | 14 | func fetchAllVoices() async -> [VoiceMemoRepository.Voice] { 15 | fetchedVoices 16 | } 17 | 18 | func deleteVoice(id: UUID) async -> Bool { 19 | true 20 | } 21 | 22 | func downloadVoiceFile(id: UUID) async -> Bool { 23 | true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /VoiLogTests/MockVoiceMemoCoredataAccessor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockVoiceMemoCoredataAccessor.swift 3 | // VoiLogTests 4 | // 5 | // Created by 遠藤拓弥 on 2024/06/22. 6 | // 7 | 8 | import Foundation 9 | @testable import VoiLog 10 | 11 | class MockVoiceMemoCoredataAccessor: VoiceMemoCoredataAccessorProtocol { 12 | 13 | var insertedVoice: VoiceMemoRepository.Voice? 14 | var fetchedVoice: VoiceMemoRepository.Voice? 15 | var updatedVoice: VoiceMemoRepository.Voice? 16 | var fetchedVoices: [VoiceMemoRepository.Voice] = [] 17 | 18 | var deletedId: UUID? 19 | var updatedTitle: (uuid: UUID, newTitle: String)? 20 | 21 | func insert(voice: VoiLog.VoiceMemoRepository.Voice, isCloud: Bool) { 22 | insertedVoice = voice 23 | } 24 | 25 | func selectAllData() -> [VoiceMemoRepository.Voice] { 26 | fetchedVoices 27 | } 28 | 29 | func fetch(uuid: UUID) -> VoiceMemoRepository.Voice? { 30 | fetchedVoice 31 | } 32 | 33 | func delete(id: UUID) { 34 | deletedId = id 35 | } 36 | 37 | func update(voice: VoiceMemoRepository.Voice) { 38 | updatedVoice = voice 39 | } 40 | 41 | func updateTitle(uuid: UUID, newTitle: String) { 42 | updatedTitle = (uuid, newTitle) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /VoiLogTests/PlaylistDetailFeatureTests.swift: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /VoiLogTests/VoiLogTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VoiceMemoTests.swift 3 | // VoiceMemoTests 4 | // 5 | // Created by 遠藤拓弥 on 3.9.2022. 6 | // 7 | 8 | import XCTest 9 | 10 | final class VoiceMemoTests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | } 15 | 16 | override func tearDownWithError() throws { 17 | // Put teardown code here. This method is called after the invocation of each test method in the class. 18 | } 19 | 20 | func testExample() throws { 21 | // This is an example of a functional test case. 22 | // Use XCTAssert and related functions to verify your tests produce the correct results. 23 | // Any test you write for XCTest can be annotated as throws and async. 24 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. 25 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. 26 | } 27 | 28 | func testPerformanceExample() throws { 29 | // This is an example of a performance test case. 30 | measure { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /VoiLogTests/VoiceMemosTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // test.swift 3 | // VoiLogTests 4 | // 5 | // Created by 遠藤拓弥 on 2024/06/16. 6 | // 7 | 8 | import Foundation 9 | import XCTest 10 | import ComposableArchitecture 11 | @testable import VoiLog 12 | 13 | @MainActor 14 | final class VoiceMemosTests: XCTestCase { 15 | 16 | override func setUp() { 17 | super.setUp() 18 | // テスト前にチュートリアル表示済みに設定 19 | UserDefaultsManager.shared.hasSeenTutorial = true 20 | } 21 | 22 | func testOnAppear_FirstLaunch() async { 23 | // チュートリアル表示済みに設定 24 | UserDefaultsManager.shared.hasSeenTutorial = true 25 | 26 | let store = TestStore(initialState: VoiceMemos.State()) { 27 | VoiceMemos() 28 | } 29 | 30 | await store.send(.onAppear) 31 | XCTAssertNotNil(UserDefaultsManager.shared.installDate) 32 | } 33 | 34 | func testOnAppear_ReviewPrompt() async { 35 | // チュートリアル表示済みに設定 36 | UserDefaultsManager.shared.hasSeenTutorial = true 37 | 38 | let initialDate = Calendar.current.date(byAdding: .day, value: -8, to: Date())! 39 | UserDefaultsManager.shared.installDate = initialDate 40 | UserDefaultsManager.shared.reviewRequestCount = 0 41 | 42 | let store = TestStore(initialState: VoiceMemos.State()) { 43 | VoiceMemos() 44 | } 45 | 46 | await store.send(.onAppear) { 47 | $0.alert = AlertState( 48 | title: TextState("シンプル録音について"), 49 | message: TextState("シンプル録音に満足していますか?"), 50 | buttons: [ 51 | .default(TextState("はい"), action: .send(.onGoodReview)), 52 | .default(TextState("いいえ、フィードバックを送信"), action: .send(.onBadReview)) 53 | ] 54 | ) 55 | } 56 | 57 | XCTAssertEqual(UserDefaultsManager.shared.reviewRequestCount, 1) 58 | } 59 | 60 | func testOnDelete() async { 61 | let uuid = UUID() 62 | let voiceMemo = VoiceMemoReducer.State( 63 | uuid: uuid, 64 | date: Date(), 65 | duration: 60.0, 66 | time: 60.0, 67 | url: URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString).appendingPathExtension("m4a"), 68 | text: "サンプルメモ", 69 | fileFormat: "m4a", 70 | samplingFrequency: 44100.0, 71 | quantizationBitDepth: 16, 72 | numberOfChannels: 2, 73 | hasPurchasedPremium: false 74 | ) 75 | 76 | var initialState = VoiceMemos.State() 77 | initialState.voiceMemos = [voiceMemo] 78 | 79 | let store = TestStore(initialState: initialState) { 80 | VoiceMemos() 81 | } 82 | 83 | await store.send(.onDelete(uuid: uuid)) { 84 | $0.voiceMemos = [] 85 | } 86 | } 87 | 88 | func testRecordPermissionResponse_Denied() async { 89 | let store = TestStore(initialState: VoiceMemos.State()) { 90 | VoiceMemos() 91 | } 92 | 93 | await store.send(.recordPermissionResponse(false)) { 94 | $0.audioRecorderPermission = .denied 95 | $0.alert = AlertState(title: TextState("Permission is required to record voice memos.")) 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /VoiLogUITests/VoiceMemoUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VoiceMemoUITests.swift 3 | // VoiceMemoUITests 4 | // 5 | // Created by 遠藤拓弥 on 3.9.2022. 6 | // 7 | 8 | import XCTest 9 | 10 | final class VoiceMemoUITests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use XCTAssert and related functions to verify your tests produce the correct results. 31 | } 32 | 33 | func testLaunchPerformance() throws { 34 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { 35 | // This measures how long it takes to launch your application. 36 | measure(metrics: [XCTApplicationLaunchMetric()]) { 37 | XCUIApplication().launch() 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /VoiLogUITests/VoiceMemoUITestsLaunchTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VoiceMemoUITestsLaunchTests.swift 3 | // VoiceMemoUITests 4 | // 5 | // Created by 遠藤拓弥 on 3.9.2022. 6 | // 7 | 8 | import XCTest 9 | 10 | final class VoiceMemoUITestsLaunchTests: XCTestCase { 11 | 12 | override class var runsForEachTargetApplicationUIConfiguration: Bool { 13 | true 14 | } 15 | 16 | override func setUpWithError() throws { 17 | continueAfterFailure = false 18 | } 19 | 20 | func testLaunch() throws { 21 | let app = XCUIApplication() 22 | app.launch() 23 | 24 | // Insert steps here to perform after app launch but before taking a screenshot, 25 | // such as logging into a test account or navigating somewhere in the app 26 | 27 | let attachment = XCTAttachment(screenshot: app.screenshot()) 28 | attachment.name = "Launch Screen" 29 | attachment.lifetime = .keepAlways 30 | add(attachment) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ci_scripts/ci_post_clone.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ci_post_clone.sh 4 | # VoiLog 5 | # 6 | # Created by 遠藤拓弥 on 2024/03/30. 7 | # 8 | 9 | defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES 10 | -------------------------------------------------------------------------------- /ci_scripts/ci_pre_xcodebuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Starting pre-build script..." 4 | 5 | # プロジェクトのルートディレクトリに移動 6 | cd $CI_PRIMARY_REPOSITORY_PATH/VoiLog/ 7 | 8 | # 必要な環境変数のチェック 9 | REQUIRED_VARS=( 10 | "ROLLBAR_KEY" 11 | "ADMOB_KEY" 12 | "RECORD_ADMOB_KEY" 13 | "REVENUECAT_KEY" 14 | "PLAYLIST_ADMOB_KEY" 15 | ) 16 | 17 | # 未設定の環境変数をチェック 18 | MISSING_VARS=() 19 | for VAR in "${REQUIRED_VARS[@]}"; do 20 | if [ -z "${!VAR}" ]; then 21 | MISSING_VARS+=($VAR) 22 | fi 23 | done 24 | 25 | # 未設定の環境変数があればビルドを失敗させる 26 | if [ ${#MISSING_VARS[@]} -ne 0 ]; then 27 | echo "Error: 以下の環境変数が設定されていません:" 28 | printf '%s\n' "${MISSING_VARS[@]}" 29 | exit 1 30 | fi 31 | 32 | # 環境変数が正しく設定されている場合、Info.plistに書き込み 33 | plutil -replace ROLLBAR_KEY -string "$ROLLBAR_KEY" Info.plist 34 | plutil -replace ADMOB_KEY -string "$ADMOB_KEY" Info.plist 35 | plutil -replace RECORD_ADMOB_KEY -string "$RECORD_ADMOB_KEY" Info.plist 36 | plutil -replace REVENUECAT_KEY -string "$REVENUECAT_KEY" Info.plist 37 | plutil -replace PLAYLIST_ADMOB_KEY -string "$PLAYLIST_ADMOB_KEY" Info.plist 38 | 39 | # 結果を確認 40 | echo "Info.plistの更新を確認中..." 41 | plutil -p Info.plist 42 | 43 | echo "環境変数の設定が完了しました" 44 | exit 0 -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | app_identifier("com.entaku.VoiLog") # The bundle identifier of your app 2 | apple_id("entaku19890818@gmail.com") # Your Apple Developer Portal username 3 | team_id("4YZQY4C47E") # Developer Portal Team ID 4 | 5 | 6 | # For more information about the Appfile, see: 7 | # https://docs.fastlane.tools/advanced/#appfile 8 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # This file contains the fastlane.tools configuration 2 | # You can find the documentation at https://docs.fastlane.tools 3 | # 4 | # For a list of all available actions, check out 5 | # 6 | # https://docs.fastlane.tools/actions 7 | # 8 | # For a list of all available plugins, check out 9 | # 10 | # https://docs.fastlane.tools/plugins/available-plugins 11 | # 12 | 13 | # Uncomment the line if you want fastlane to automatically update itself 14 | # update_fastlane 15 | 16 | default_platform(:ios) 17 | 18 | platform :ios do 19 | # 各レーンで共通して実行する認証処理 20 | before_all do 21 | # APIキー認証を使用する場合 22 | app_store_connect_api_key( 23 | key_id: ENV["APP_STORE_CONNECT_API_KEY_KEY_ID"], 24 | issuer_id: ENV["APP_STORE_CONNECT_API_KEY_ISSUER_ID"], 25 | key_content: ENV["APP_STORE_CONNECT_API_KEY_CONTENT"] # ファイルパスの代わりにキーの内容を直接指定 26 | ) 27 | end 28 | 29 | # メタデータとスクリーンショットのアップロード、審査提出 30 | desc "Upload metadata and screenshots to App Store Connect and submit for review" 31 | lane :upload_metadata do 32 | # Xcodeプロジェクトからバージョン番号を取得 33 | version_number = get_version_number( 34 | xcodeproj: "VoiLog.xcodeproj", 35 | target: "VoiLog" 36 | ) 37 | 38 | # バージョン番号が取得できなかった場合はエラー 39 | UI.user_error!("バージョン番号をXcodeプロジェクトから取得できませんでした。") unless version_number && !version_number.empty? 40 | 41 | UI.success("Xcodeプロジェクトからバージョン番号を取得しました: #{version_number}") 42 | 43 | 44 | deliver( 45 | skip_binary_upload: false, # バイナリのアップロードを実行 46 | skip_app_version_update: false, # アプリバージョンの更新をスキップしない 47 | app_version: version_number, # アプリのバージョン番号を指定 48 | skip_metadata: false, # メタデータの更新をスキップしない 49 | skip_screenshots: false, # スクリーンショットのアップロードをスキップしない 50 | force: true, # 確認なしで強制的に実行 51 | overwrite_screenshots: true, # 既存のスクリーンショットを上書き 52 | ignore_language_directory_validation: true, # 言語ディレクトリの検証を無視 53 | run_precheck_before_submit: true, # 提出前の事前チェックを実行 54 | precheck_include_in_app_purchases: false, # In-app purchasesのチェックをスキップ 55 | submit_for_review: true, # 審査提出を実行 56 | automatic_release: false, # 審査通過後の自動リリースは無効 57 | submission_information: { 58 | add_id_info_uses_idfa: false, # IDFAの使用なし 59 | export_compliance_uses_encryption: false, # 暗号化なし 60 | export_compliance_platform: "ios" # プラットフォームはiOS 61 | }, 62 | build_number: "latest" # 最新のビルド番号を自動選択 63 | ) 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ---- 3 | 4 | # Installation 5 | 6 | Make sure you have the latest version of the Xcode command line tools installed: 7 | 8 | ```sh 9 | xcode-select --install 10 | ``` 11 | 12 | For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) 13 | 14 | # Available Actions 15 | 16 | ## iOS 17 | 18 | ### ios upload_metadata 19 | 20 | ```sh 21 | [bundle exec] fastlane ios upload_metadata 22 | ``` 23 | 24 | Upload metadata and screenshots to App Store Connect and submit for review 25 | 26 | ---- 27 | 28 | This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. 29 | 30 | More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). 31 | 32 | The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 33 | -------------------------------------------------------------------------------- /fastlane/Snapfile: -------------------------------------------------------------------------------- 1 | # Uncomment the lines below you want to change by removing the # in the beginning 2 | 3 | # A list of devices you want to take the screenshots from 4 | devices([ 5 | "iPhone 15 Pro", 6 | "iPhone 15 Pro Max", 7 | "iPad Pro (12.9-inch) (6th generation)", 8 | "iPad Air (5th generation)" 9 | ]) 10 | 11 | languages([ 12 | "en-US", 13 | "ja-JP" 14 | ]) 15 | 16 | # The name of the scheme which contains the UI Tests 17 | scheme("VoiLogUITests") 18 | 19 | # Where should the resulting screenshots be stored? 20 | output_directory("./fastlane/screenshots") 21 | 22 | # remove the '#' to clear all previously generated screenshots before creating new ones 23 | clear_previous_screenshots(true) 24 | 25 | # Remove the '#' to set the status bar to 9:41 AM, and show full battery and reception. See also override_status_bar_arguments for custom options. 26 | override_status_bar(true) 27 | 28 | # Arguments to pass to the app on launch. See https://docs.fastlane.tools/actions/snapshot/#launch-arguments 29 | # launch_arguments(["-favColor red"]) 30 | 31 | # For more information about all available options run 32 | # fastlane action snapshot -------------------------------------------------------------------------------- /fastlane/metadata/copyright.txt: -------------------------------------------------------------------------------- 1 | Takuya Endo 2 | -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/description.txt: -------------------------------------------------------------------------------- 1 | Simple Recorder ist eine App, die sich dem mühelosen Aufnehmen widmet. Mit nur einem Tippen können Sie Ihre Aufnahme starten, sogar im Hintergrund, und sie einfach mit anderen Apps teilen, was den Aufnahmevorgang einfacher als je zuvor macht. 2 | 3 | Funktionen: 4 | 5 | Starten und beenden Sie Ihre Aufnahme mit nur einer Taste dank des minimalistischen Designs. 6 | Teilen Sie Ihre Aufnahmedateien jederzeit und überall ganz einfach und greifen Sie darauf zu. 7 | Bietet hochwertige Audiodateien, die sich für Geschäftstreffen, Memos, Brainstorming-Sitzungen oder besondere Momente eignen. 8 | Benutzerfreundliche Oberfläche, die auch für Anfänger leicht zu navigieren ist. 9 | Mit Simple Recorder verpassen Sie nie wieder die wichtigen Momente. Wir sind hier, um Ihnen zu helfen, die Momente zu schätzen, die zählen. Laden Sie jetzt herunter und erleben Sie die Einfachheit der Aufnahme. 10 | -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/keywords.txt: -------------------------------------------------------------------------------- 1 | Aufnahme,Transkription,Aufzeichnen,Hintergrund,Karaoke,Radio,Schlafen,Reden,microphone,Konvertierung 2 | -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/name.txt: -------------------------------------------------------------------------------- 1 | Einfacher Sprachaufnahme 2 | -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/release_notes.txt: -------------------------------------------------------------------------------- 1 | Fehlerbehebungen und Stabilitätsverbesserungen 2 | - Behebung des Tonhöhenproblems bei Verwendung des Kopfhörer-Mikrofons 3 | - Verbesserte App-Stabilität 4 | 5 | Sonstiges: 6 | - Kleinere Verbesserungen und Fehlerbehebungen 7 | 8 | Verbesserte Aufnahmesicherheit: 9 | • Warnung beim Versuch, während der Aufnahme zu navigieren 10 | • Option zum Fortsetzen der Aufnahme oder zum Stoppen und Navigieren 11 | • Aufnahmeanzeige in der Navigationsleiste 12 | • Eingeschränkter Zugriff auf Playlist, Einstellungen und iCloud-Synchronisierung während der Aufnahme 13 | -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/subtitle.txt: -------------------------------------------------------------------------------- 1 | Top Qualität Aufnahme 2 | -------------------------------------------------------------------------------- /fastlane/metadata/de-DE/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-CA/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-CA/description.txt: -------------------------------------------------------------------------------- 1 | Simple Voice Recorder is an easy-to-use voice recording app that starts with just one tap. Supporting high-quality audio and background recording, it's perfect for various situations from recording meetings to voice memos and capturing ideas. 2 | Features: 3 | 4 | Start recording with a single tap 5 | Background recording support 6 | High-quality audio recording 7 | Easy sharing of recordings 8 | Simple and user-friendly design 9 | 10 | From business meetings to daily voice memos, this versatile recording app is ready to help in any situation. Download now and experience hassle-free recording. 11 | -------------------------------------------------------------------------------- /fastlane/metadata/en-CA/keywords.txt: -------------------------------------------------------------------------------- 1 | Recording,Record,Background,Karaoke,Radio,Sleep,Talk,Mic,voice memos,recording,audio recorder 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-CA/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-CA/name.txt: -------------------------------------------------------------------------------- 1 | Simple Voice Recorder - Audio 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-CA/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-CA/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-CA/release_notes.txt: -------------------------------------------------------------------------------- 1 | New Features: 2 | - Added 60-second skip forward/backward functionality 3 | 4 | Other: 5 | - Minor improvements and bug fixes 6 | -------------------------------------------------------------------------------- /fastlane/metadata/en-CA/subtitle.txt: -------------------------------------------------------------------------------- 1 | The best quality voice memos 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-CA/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-GB/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-GB/description.txt: -------------------------------------------------------------------------------- 1 | Simple Recorder is an app dedicated to effortless recording. With just one tap, you can begin your recording, even in the background, and easily share it with other apps, making the recording process simpler than ever. 2 | 3 | Features: 4 | 5 | Start and stop your recording with a single button thanks to its minimalist design. 6 | Easily share and access your recording files anytime, anywhere. 7 | Provides high-quality audio files suitable for business meetings, memos, brainstorming sessions, or any memorable moments. 8 | User-friendly interface that's easy for beginners to navigate. 9 | With Simple Recorder, you'll never miss those essential moments. We're here to help you cherish moments that matter. Download now and experience the simplicity of recording. 10 | -------------------------------------------------------------------------------- /fastlane/metadata/en-GB/keywords.txt: -------------------------------------------------------------------------------- 1 | Recording,Transcription,Record,Background,Karaoke,Radio,Sleep,Talk,Mic,Conversion,capture,extraction 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-GB/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-GB/name.txt: -------------------------------------------------------------------------------- 1 | Simple Voice Recorder - Audio 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-GB/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-GB/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-GB/release_notes.txt: -------------------------------------------------------------------------------- 1 | Bug fixes and stability improvements 2 | - Fixed audio pitch issue when using headphone microphone 3 | - Improved app stability 4 | 5 | Other: 6 | - Minor improvements and bug fixes 7 | -------------------------------------------------------------------------------- /fastlane/metadata/en-GB/subtitle.txt: -------------------------------------------------------------------------------- 1 | The best quality voice memos 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-GB/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/description.txt: -------------------------------------------------------------------------------- 1 | Simple Voice Recorder is an easy-to-use voice recording app that starts with just one tap. Supporting high-quality audio and background recording, it's perfect for various situations from recording meetings to voice memos and capturing ideas. 2 | Features: 3 | 4 | Start recording with a single tap 5 | Background recording support 6 | High-quality audio recording 7 | Easy sharing of recordings 8 | Simple and user-friendly design 9 | 10 | From business meetings to daily voice memos, this versatile recording app is ready to help in any situation. Download now and experience hassle-free recording. 11 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/keywords.txt: -------------------------------------------------------------------------------- 1 | Recording,Record,Background,Karaoke,Radio,Sleep,Talk,Mic,voice memos,recording,audio recorder 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/name.txt: -------------------------------------------------------------------------------- 1 | Simple Voice Recorder - Audio 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/release_notes.txt: -------------------------------------------------------------------------------- 1 | Bug fixes and stability improvements 2 | - Fixed audio pitch issue when using headphone microphone 3 | - Improved app stability 4 | 5 | Other: 6 | - Minor improvements and bug fixes 7 | - Improved recording stability by removing background recording notifications 8 | - Enhanced audio quality for headphone microphone input 9 | - Optimized app category display 10 | - Overall stability and performance improvements 11 | 12 | Improved recording safety: 13 | • Warning when attempting to navigate away during recording 14 | • Option to continue recording or stop and navigate 15 | • Recording indicator in the navigation bar 16 | • Restricted access to playlist, settings, and iCloud sync during recording 17 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/subtitle.txt: -------------------------------------------------------------------------------- 1 | The best quality voice memos 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/es-ES/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/es-ES/description.txt: -------------------------------------------------------------------------------- 1 | Simple Recorder es una aplicación dedicada a la grabación sin esfuerzo. Con solo un toque, puedes comenzar tu grabación, incluso en segundo plano, y compartirla fácilmente con otras aplicaciones, haciendo el proceso de grabación más simple que nunca. 2 | 3 | Características: 4 | 5 | Comienza y detén tu grabación con un solo botón gracias a su diseño minimalista. 6 | Comparte y accede a tus archivos de grabación en cualquier momento y lugar. 7 | Proporciona archivos de audio de alta calidad, adecuados para reuniones de negocios, memos, sesiones de lluvia de ideas o cualquier momento memorable. 8 | Interfaz amigable y fácil de navegar para principiantes. 9 | Con Simple Recorder, nunca te perderás esos momentos esenciales. Estamos aquí para ayudarte a apreciar los momentos que importan. Descarga ahora y experimenta la simplicidad de grabar. 10 | -------------------------------------------------------------------------------- /fastlane/metadata/es-ES/keywords.txt: -------------------------------------------------------------------------------- 1 | Grabación,Transcripción,Grabar,Fondo,Karaoke,Radio,Dormir,Hablar,Micrófono,Conversión,Captura 2 | -------------------------------------------------------------------------------- /fastlane/metadata/es-ES/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/es-ES/name.txt: -------------------------------------------------------------------------------- 1 | Grabador de Voz Sencillo 2 | -------------------------------------------------------------------------------- /fastlane/metadata/es-ES/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/es-ES/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/es-ES/release_notes.txt: -------------------------------------------------------------------------------- 1 | Correcciones de errores y mejoras de estabilidad 2 | - Corregido el problema de tono al usar el micrófono de auriculares 3 | - Mejorada la estabilidad de la aplicación 4 | 5 | Otros: 6 | - Mejoras menores y correcciones de errores 7 | - Mejora de la estabilidad de grabación al eliminar las notificaciones de grabación en segundo plano 8 | - Mejora de la calidad de audio para la entrada del micrófono de los auriculares 9 | - Optimización de la visualización de categorías de aplicaciones 10 | - Mejoras generales de estabilidad y rendimiento 11 | -------------------------------------------------------------------------------- /fastlane/metadata/es-ES/subtitle.txt: -------------------------------------------------------------------------------- 1 | Siempre listo para grabar 2 | -------------------------------------------------------------------------------- /fastlane/metadata/es-ES/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/fr-FR/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/fr-FR/description.txt: -------------------------------------------------------------------------------- 1 | Simple Recorder est une application dédiée à l'enregistrement sans effort. Avec un seul tapotement, vous pouvez commencer votre enregistrement, même en arrière-plan, et le partager facilement avec d'autres applications, rendant le processus d'enregistrement plus simple que jamais. 2 | 3 | Fonctionnalités : 4 | 5 | Démarrez et arrêtez votre enregistrement avec un seul bouton grâce à son design minimaliste. 6 | Partagez et accédez facilement à vos fichiers d'enregistrement à tout moment, n'importe où. 7 | Fournit des fichiers audio de haute qualité, adaptés pour les réunions d'affaires, les mémos, les séances de brainstorming, ou tout moment mémorable. 8 | Interface utilisateur conviviale, facile à naviguer pour les débutants. 9 | Avec Simple Recorder, vous ne manquerez jamais ces moments essentiels. Nous sommes là pour vous aider à chérir les moments qui comptent. Téléchargez maintenant et expérimentez la simplicité de l'enregistrement. 10 | -------------------------------------------------------------------------------- /fastlane/metadata/fr-FR/keywords.txt: -------------------------------------------------------------------------------- 1 | Enregistrement,Transcription,Arrière-plan,Karaoké,Radio,Sommeil,Parler,Micro, Conversion,Capture 2 | -------------------------------------------------------------------------------- /fastlane/metadata/fr-FR/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/fr-FR/name.txt: -------------------------------------------------------------------------------- 1 | Simple Voice Recorder - Audio 2 | -------------------------------------------------------------------------------- /fastlane/metadata/fr-FR/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/fr-FR/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/fr-FR/release_notes.txt: -------------------------------------------------------------------------------- 1 | Nouvelles fonctionnalités : 2 | - Ajout de la fonction avance/retour de 60 secondes 3 | 4 | Autres : 5 | - Améliorations mineures et corrections de bugs 6 | 7 | Corrections de bugs et améliorations de stabilité 8 | - Correction du problème de hauteur de son lors de l'utilisation du microphone du casque 9 | - Amélioration de la stabilité de l'application 10 | - Amélioration de la stabilité de l'enregistrement en supprimant les notifications d'enregistrement en arrière-plan 11 | - Amélioration de la qualité audio pour l'entrée du microphone des écouteurs 12 | - Optimisation de l'affichage des catégories d'applications 13 | - Améliorations générales de la stabilité et des performances 14 | 15 | Amélioration de la sécurité d'enregistrement : 16 | • Avertissement lors de la tentative de navigation pendant l'enregistrement 17 | • Option pour continuer l'enregistrement ou arrêter et naviguer 18 | • Indicateur d'enregistrement dans la barre de navigation 19 | • Accès restreint à la playlist, aux paramètres et à la synchronisation iCloud pendant l'enregistrement 20 | -------------------------------------------------------------------------------- /fastlane/metadata/fr-FR/subtitle.txt: -------------------------------------------------------------------------------- 1 | The best quality and stability 2 | -------------------------------------------------------------------------------- /fastlane/metadata/fr-FR/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/hi/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/hi/description.txt: -------------------------------------------------------------------------------- 1 | Simple Recorder is an app dedicated to effortless recording. With just one tap, you can begin your recording, even in the background, and easily share it with other apps, making the recording process simpler than ever. 2 | 3 | Features: 4 | 5 | Start and stop your recording with a single button thanks to its minimalist design. 6 | Easily share and access your recording files anytime, anywhere. 7 | Provides high-quality audio files suitable for business meetings, memos, brainstorming sessions, or any memorable moments. 8 | User-friendly interface that's easy for beginners to navigate. 9 | With Simple Recorder, you'll never miss those essential moments. We're here to help you cherish moments that matter. Download now and experience the simplicity of recording. 10 | -------------------------------------------------------------------------------- /fastlane/metadata/hi/keywords.txt: -------------------------------------------------------------------------------- 1 | Recording,Transcription,Record,Background,Karaoke,Radio,Sleep,Talk,Mic,voice memos, 2 | -------------------------------------------------------------------------------- /fastlane/metadata/hi/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/hi/name.txt: -------------------------------------------------------------------------------- 1 | साधारण वॉयस रिकॉर्डर - ऑडियो 2 | -------------------------------------------------------------------------------- /fastlane/metadata/hi/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/hi/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/hi/release_notes.txt: -------------------------------------------------------------------------------- 1 | बग फिक्स और स्थिरता सुधार 2 | - हेडफोन माइक्रोफोन का उपयोग करते समय ऑडियो पिच समस्या को ठीक किया 3 | - ऐप स्थिरता में सुधार 4 | 5 | अन्य: 6 | - छोटे सुधार और बग फिक्स 7 | -------------------------------------------------------------------------------- /fastlane/metadata/hi/subtitle.txt: -------------------------------------------------------------------------------- 1 | सर्वोत्तम गुणवत्ता और स्थिरता 2 | -------------------------------------------------------------------------------- /fastlane/metadata/hi/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/it/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/it/description.txt: -------------------------------------------------------------------------------- 1 | Ecco la traduzione in italiano del testo fornito: 2 | 3 | Simple Recorder è un'app dedicata alla registrazione senza sforzo. Con un solo tocco, puoi iniziare la tua registrazione, anche in background, e condividerla facilmente con altre app, rendendo il processo di registrazione più semplice che mai. 4 | 5 | Caratteristiche: 6 | 7 | Avvia e interrompi la registrazione con un solo pulsante grazie al design minimalista. 8 | 9 | Condividi e accedi facilmente ai tuoi file di registrazione in qualsiasi momento e ovunque. 10 | 11 | Fornisce file audio di alta qualità adatti per riunioni di lavoro, promemoria, sessioni di brainstorming o qualsiasi momento memorabile. 12 | 13 | Interfaccia user-friendly facile da navigare anche per i principianti. 14 | 15 | Con Simple Recorder, non perderai mai quei momenti essenziali. Siamo qui per aiutarti a custodire i momenti che contano. Scarica ora e sperimenta la semplicità della registrazione. 16 | -------------------------------------------------------------------------------- /fastlane/metadata/it/keywords.txt: -------------------------------------------------------------------------------- 1 | egistrazione,Trascrizione,Registra,Sfondo,Karaoke,Radio,Sonno,Conversazione,Microfono,memo vocali 2 | -------------------------------------------------------------------------------- /fastlane/metadata/it/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/it/name.txt: -------------------------------------------------------------------------------- 1 | Vocale Semplice Registratore 2 | -------------------------------------------------------------------------------- /fastlane/metadata/it/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/it/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/it/release_notes.txt: -------------------------------------------------------------------------------- 1 | Correzioni di bug e miglioramenti di stabilità 2 | - Risolto il problema del tono quando si utilizza il microfono delle cuffie 3 | - Migliorata la stabilità dell'app 4 | 5 | Altro: 6 | - Miglioramenti minori e correzioni di bug 7 | 8 | Miglioramento della sicurezza durante la registrazione: 9 | • Avviso quando si tenta di navigare durante la registrazione 10 | • Opzione per continuare la registrazione o fermarsi e navigare 11 | • Indicatore di registrazione nella barra di navigazione 12 | • Accesso limitato a playlist, impostazioni e sincronizzazione iCloud durante la registrazione 13 | -------------------------------------------------------------------------------- /fastlane/metadata/it/subtitle.txt: -------------------------------------------------------------------------------- 1 | I migliori vocali di qualità 2 | -------------------------------------------------------------------------------- /fastlane/metadata/it/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ja/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ja/description.txt: -------------------------------------------------------------------------------- 1 | 最高音質で録音可能なシンプルなボイスレコーダーです。 2 | 録音形式はロスレス圧縮のPCM(WAV)と、非可逆圧縮のAAC(M4A)を選択可能。 3 | 4 | サンプリング周波数は8kHz〜192kHzに対応。 5 | 量子化ビット数は8, 16, 24, 32bitに設定可能です。 6 | 7 | 録音機能: 8 | - 高音質なWAV形式での録音 9 | - 高圧縮なAAC(M4A)形式での録音 10 | - バックグラウンドでの録音 11 | 12 | 13 | 再生機能: 14 | - バックグラウンドでの再生 15 | - プレイリストの作成 16 | - ファイル名の変更 17 | - ファイルの並べ替え 18 | - リピート再生(一曲、全体) 19 | - 再生スピードの変更(0.5x, 0.8x, 1.2x, 1.5x, 2.0x) 20 | - 再生の10秒送り、60秒送り、10秒戻し、60秒戻し 21 | 22 | 23 | 特徴: 24 | ・1タップでかんたん録音開始 25 | ・バックグラウンドでの録音に対応 26 | ・高音質な音声録音 27 | ・録音データを簡単シェア 28 | ・シンプルで使いやすいデザイン 29 | 30 | ビジネスの会議録音から日常のボイスメモまで、あらゆる場面で活躍する録音アプリをぜひお試しください。 31 | -------------------------------------------------------------------------------- /fastlane/metadata/ja/keywords.txt: -------------------------------------------------------------------------------- 1 | 録音,シンプル録音,かんたん録音,ボイスメモ,高音質録音,音声録音,ボイスレコーダー,録音アプリ,バックグラウンド録音,pcm録音,メモ録音,ボイス,音声メモ,レコーディング,レコード 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ja/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ja/name.txt: -------------------------------------------------------------------------------- 1 | シンプル録音 - ボイスレコーダー(通話ぼいすれこーだー) 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ja/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ja/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ja/release_notes.txt: -------------------------------------------------------------------------------- 1 | バグ修正と安定性の向上 2 | - イヤホンマイク使用時の音声ピッチ問題を修正 3 | - アプリの安定性を改善 4 | - パフォーマンスの最適化 5 | - その他のバグ修正 6 | 7 | 新機能: 8 | - 60秒送り/戻し機能の追加 9 | 10 | その他: 11 | - 細かな改善とバグ修正 12 | 13 | - バックグラウンド録音の通知機能を削除し、録音機能の安定性を向上 14 | - ヘッドフォンマイク使用時の音声品質を改善 15 | - アプリのカテゴリ表示を最適化 16 | - 全体的な安定性とパフォーマンスの向上 17 | 18 | 録音中の安全性を向上しました: 19 | • 録音中に他の画面へ移動しようとすると警告を表示 20 | • 録音を続けるか、停止して移動するかを選択可能 21 | • 録音中は画面上部に録音インジケータを表示 22 | • プレイリスト、設定、iCloud同期などの操作を制限 23 | -------------------------------------------------------------------------------- /fastlane/metadata/ja/subtitle.txt: -------------------------------------------------------------------------------- 1 | だれでも簡単に使える録音アプリ、ぼいすめも編集・ろくおん電話 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ja/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/primary_category.txt: -------------------------------------------------------------------------------- 1 | BUSINESS 2 | -------------------------------------------------------------------------------- /fastlane/metadata/primary_first_sub_category.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/primary_second_sub_category.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/pt-PT/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/pt-PT/description.txt: -------------------------------------------------------------------------------- 1 | Simple Recorder é um aplicativo dedicado à gravação sem esforço. Com apenas um toque, você pode começar sua gravação, até mesmo em segundo plano, e compartilhá-la facilmente com outros aplicativos, tornando o processo de gravação mais simples do que nunca. 2 | 3 | Características: 4 | 5 | - Comece e pare sua gravação com um único botão, graças ao seu design minimalista. 6 | - Compartilhe e acesse seus arquivos de gravação facilmente a qualquer hora, em qualquer lugar. 7 | - Fornece arquivos de áudio de alta qualidade adequados para reuniões de negócios, memorandos, sessões de brainstorming ou quaisquer momentos memoráveis. 8 | - Interface amigável que é fácil para iniciantes navegarem. 9 | - Com o Simple Recorder, você nunca perderá esses momentos essenciais. Estamos aqui para ajudá-lo a valorizar os momentos que importam. Baixe agora e experimente a simplicidade da gravação. 10 | -------------------------------------------------------------------------------- /fastlane/metadata/pt-PT/keywords.txt: -------------------------------------------------------------------------------- 1 | Gravação,Transcrição,Gravar,Fundo,Karaokê,Rádio,Dormir,Falar,Microfone,Conversão,Captura,Extração 2 | -------------------------------------------------------------------------------- /fastlane/metadata/pt-PT/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/pt-PT/name.txt: -------------------------------------------------------------------------------- 1 | Gravador Simples 2 | -------------------------------------------------------------------------------- /fastlane/metadata/pt-PT/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/pt-PT/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/pt-PT/release_notes.txt: -------------------------------------------------------------------------------- 1 | Correções de erros e melhorias de estabilidade 2 | - Corrigido o problema de tom ao usar o microfone dos auscultadores 3 | - Melhorada a estabilidade da aplicação 4 | 5 | Outros: 6 | - Melhorias menores e correções de erros 7 | -------------------------------------------------------------------------------- /fastlane/metadata/pt-PT/subtitle.txt: -------------------------------------------------------------------------------- 1 | Qualidade e estabilidade tops 2 | -------------------------------------------------------------------------------- /fastlane/metadata/pt-PT/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/review_information/demo_password.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/review_information/demo_user.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/review_information/email_address.txt: -------------------------------------------------------------------------------- 1 | 2 | entaku19890818@gmail.com -------------------------------------------------------------------------------- /fastlane/metadata/review_information/first_name.txt: -------------------------------------------------------------------------------- 1 | 2 | Takuya 3 | -------------------------------------------------------------------------------- /fastlane/metadata/review_information/last_name.txt: -------------------------------------------------------------------------------- 1 | Endo -------------------------------------------------------------------------------- /fastlane/metadata/review_information/notes.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/review_information/phone_number.txt: -------------------------------------------------------------------------------- 1 | +819062216169 -------------------------------------------------------------------------------- /fastlane/metadata/ru/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ru/description.txt: -------------------------------------------------------------------------------- 1 | Simple Recorder - это приложение, предназначенное для простой записи. Одним нажатием вы можете начать запись, даже в фоновом режиме, и легко поделиться ей с другими приложениями, делая процесс записи проще, чем когда-либо. 2 | 3 | Особенности: 4 | 5 | Начинайте и останавливайте запись одной кнопкой благодаря минималистичному дизайну. 6 | Легко делитесь и получайте доступ к файлам записи в любое время и в любом месте. 7 | Обеспечивает файлы высококачественного аудио, подходящие для деловых встреч, заметок, мозговых штурмов или любых запоминающихся моментов. 8 | Интерфейс, удобный для пользователей, легкий в освоении для начинающих. 9 | С Simple Recorder вы никогда не пропустите важные моменты. Мы здесь, чтобы помочь вам сохранить важные моменты. Скачайте сейчас и оцените простоту записи. 10 | -------------------------------------------------------------------------------- /fastlane/metadata/ru/keywords.txt: -------------------------------------------------------------------------------- 1 | Запись,Транскрипция,Фон,Караоке,Радио,Сон,Разговор,Микрофон,Конвертация,Захват,Извлечение 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ru/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ru/name.txt: -------------------------------------------------------------------------------- 1 | Простой диктофон - Аудио 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ru/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ru/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ru/release_notes.txt: -------------------------------------------------------------------------------- 1 | Исправления ошибок и улучшения стабильности 2 | - Исправлена проблема с высотой звука при использовании микрофона наушников 3 | - Улучшена стабильность приложения 4 | 5 | Прочее: 6 | - Незначительные улучшения и исправления ошибок 7 | -------------------------------------------------------------------------------- /fastlane/metadata/ru/subtitle.txt: -------------------------------------------------------------------------------- 1 | Лучшее качество и стабильность 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ru/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/secondary_category.txt: -------------------------------------------------------------------------------- 1 | UTILITIES 2 | -------------------------------------------------------------------------------- /fastlane/metadata/secondary_first_sub_category.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/secondary_second_sub_category.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/tr/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/tr/description.txt: -------------------------------------------------------------------------------- 1 | Simple Recorder, kaydetmeyi her zamankinden daha basit hale getiren bir uygulamadır. Tek bir dokunuşla, hatta arka planda bile, kaydınıza başlayabilir ve kolayca diğer uygulamalarla paylaşabilirsiniz. 2 | 3 | Özellikleri: 4 | 5 | Minimalist tasarımı sayesinde kaydınızı tek bir düğmeyle başlatıp durdurabilirsiniz. 6 | Kayıt dosyalarınıza istediğiniz zaman, istediğiniz yerden kolayca erişin ve paylaşın. 7 | İş toplantıları, notlar, fikir alışverişi oturumları veya unutulmaz anlar için uygun yüksek kaliteli ses dosyaları sağlar. 8 | Başlangıç seviyesindeki kullanıcılar için bile kolayca gezinebilecekleri kullanıcı dostu arayüz. 9 | Simple Recorder ile önemli anları kaçırmayacaksınız. Sizlere önemli anları değerlendirmenizde yardımcı olmaya buradayız. Şimdi indirin ve kaydetmenin basitliğini deneyimleyin. 10 | -------------------------------------------------------------------------------- /fastlane/metadata/tr/keywords.txt: -------------------------------------------------------------------------------- 1 | Kayıt,Transkripsiyon,Arka Plan,Karaoke,Radyo,Uyku Konuşma,Mikrofon,Dönüşüm,Yakalama,Çıkarma 2 | -------------------------------------------------------------------------------- /fastlane/metadata/tr/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/tr/name.txt: -------------------------------------------------------------------------------- 1 | Basit Ses Kaydedici - Ses 2 | -------------------------------------------------------------------------------- /fastlane/metadata/tr/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/tr/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/tr/release_notes.txt: -------------------------------------------------------------------------------- 1 | Hata düzeltmeleri ve kararlılık iyileştirmeleri 2 | - Kulaklık mikrofonu kullanırken ses perdesi sorunu düzeltildi 3 | - Uygulama kararlılığı iyileştirildi 4 | 5 | Diğer: 6 | - Küçük iyileştirmeler ve hata düzeltmeleri 7 | -------------------------------------------------------------------------------- /fastlane/metadata/tr/subtitle.txt: -------------------------------------------------------------------------------- 1 | En iyi kalite ve stabilite 2 | -------------------------------------------------------------------------------- /fastlane/metadata/tr/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/vi/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/vi/description.txt: -------------------------------------------------------------------------------- 1 | Simple Recorder là ứng dụng dành riêng cho việc ghi âm một cách dễ dàng. Chỉ với một lần chạm, bạn có thể bắt đầu ghi âm, ngay cả khi ứng dụng chạy ở chế độ nền, và dễ dàng chia sẻ nó với các ứng dụng khác, làm cho quá trình ghi âm trở nên đơn giản hơn bao giờ hết. 2 | 3 | Tính năng: 4 | 5 | Bắt đầu và dừng ghi âm chỉ với một nút nhấn nhờ thiết kế tối giản. 6 | Dễ dàng chia sẻ và truy cập các tệp ghi âm của bạn bất cứ lúc nào, ở bất cứ đâu. 7 | Cung cấp các tệp âm thanh chất lượng cao phù hợp cho các cuộc họp kinh doanh, ghi chú, buổi brainstorming, hoặc bất kỳ khoảnh khắc đáng nhớ nào. 8 | Giao diện thân thiện với người dùng, dễ dàng sử dụng ngay cả với người mới bắt đầu. 9 | Với Simple Recorder, bạn sẽ không bao giờ bỏ lỡ những khoảnh khắc quan trọng. Chúng tôi ở đây để giúp bạn ghi lại những khoảnh khắc có ý nghĩa. Tải xuống ngay bây giờ và trải nghiệm sự đơn giản của việc ghi âm. 10 | -------------------------------------------------------------------------------- /fastlane/metadata/vi/keywords.txt: -------------------------------------------------------------------------------- 1 | Ghi âm,Chuyển văn bản,Ghi hậu trường,Karaoke,Radio,Ngủ,Nói chuyện,Micro,Chuyển đổi,Chụp,Trích xuấ 2 | -------------------------------------------------------------------------------- /fastlane/metadata/vi/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/vi/name.txt: -------------------------------------------------------------------------------- 1 | Máy ghi âm đơn giản - Âm thanh 2 | -------------------------------------------------------------------------------- /fastlane/metadata/vi/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/vi/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/vi/release_notes.txt: -------------------------------------------------------------------------------- 1 | Sửa lỗi và cải thiện tính ổn định 2 | - Sửa lỗi âm thanh khi sử dụng micrô tai nghe 3 | - Cải thiện tính ổn định của ứng dụng 4 | 5 | Khác: 6 | - Cải thiện nhỏ và sửa lỗi 7 | -------------------------------------------------------------------------------- /fastlane/metadata/vi/subtitle.txt: -------------------------------------------------------------------------------- 1 | Chất lượng và ổn định tốt nhất 2 | -------------------------------------------------------------------------------- /fastlane/metadata/vi/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hans/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hans/description.txt: -------------------------------------------------------------------------------- 1 | “简易录音机”是一个专注于轻松录音的应用。只需一键,即可开始录音,甚至可在后台操作,并轻松与其他应用共享,使录音过程前所未有的简单。 2 | 3 | 功能特点: 4 | 5 | 依托其极简设计,一键启停录音。 6 | 随时随地轻松分享和访问录音文件。 7 | 提供高质量音频文件,适用于商务会议、备忘录、头脑风暴会议或任何值得记忆的时刻。 8 | 用户友好界面,易于初学者导航。 9 | 有了简易录音机,您将不再错过那些关键时刻。我们在这里帮助您珍惜重要时刻。立即下载,体验录音的简便。 10 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hans/keywords.txt: -------------------------------------------------------------------------------- 1 | 录音, 转录, 记录, 后台, 卡拉OK, 收音机, 睡眠, 对话, 麦克风, 转换, 捕捉, 提取 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hans/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hans/name.txt: -------------------------------------------------------------------------------- 1 | 简易语音录音机 - 音频 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hans/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hans/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hans/release_notes.txt: -------------------------------------------------------------------------------- 1 | 错误修复和稳定性改进 2 | - 修复使用耳机麦克风时的音高问题 3 | - 提高应用稳定性 4 | 5 | 其他: 6 | - 细微改进和错误修复 7 | - 移除后台录音通知功能,提高录音稳定性 8 | - 改善耳机麦克风的音频质量 9 | - 优化应用类别显示 10 | - 整体稳定性和性能提升 11 | 12 | 提高录音安全性: 13 | • 录音时切换页面会显示警告提示 14 | • 可选择继续录音或停止并切换页面 15 | • 导航栏显示录音指示器 16 | • 录音时限制播放列表、设置和iCloud同步等操作 17 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hans/subtitle.txt: -------------------------------------------------------------------------------- 1 | 最佳质量与稳定性 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hans/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hant/apple_tv_privacy_policy.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hant/description.txt: -------------------------------------------------------------------------------- 1 | Simple Recorder 是一款專為無縫錄音而設計的應用程式。只需一下,您即可開始錄音,甚至可以在背景中進行,並輕鬆地與其他應用程序分享,使錄音過程比以往更簡單。 2 | 3 | 特色: 4 | 5 | 由於其簡約設計,您可以使用單一按鈕啟動和停止錄音。 6 | 隨時隨地輕鬆分享和訪問您的錄音文件。 7 | 提供適合業務會議、備忘錄、頭腦風暴會議或任何值得紀念的時刻的高質量音頻文件。 8 | 對初學者來說,界面友好且易於導航。 9 | 使用 Simple Recorder,您將不會錯過那些必不可少的時刻。我們在這裡幫助您珍惜重要的時刻。立即下載,體驗錄音的簡單性。 10 | 11 | 12 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hant/keywords.txt: -------------------------------------------------------------------------------- 1 | 錄音,轉寫,錄製,背景,卡拉OK,收音機,睡眠,談話,麥克風,轉換,捕捉,提取 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hant/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hant/name.txt: -------------------------------------------------------------------------------- 1 | 簡易語音錄音機 - 音頻 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hant/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://voilog.web.app/privacy_policy.html 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hant/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hant/release_notes.txt: -------------------------------------------------------------------------------- 1 | 新功能: 2 | - 新增60秒前進/後退功能 3 | 4 | 其他: 5 | - 細微改進和錯誤修復 6 | 7 | 錯誤修復和穩定性改進 8 | - 修復使用耳機麥克風時的音高問題 9 | - 提高應用穩定性 10 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hant/subtitle.txt: -------------------------------------------------------------------------------- 1 | 最佳質量與穩定性 2 | -------------------------------------------------------------------------------- /fastlane/metadata/zh-Hant/support_url.txt: -------------------------------------------------------------------------------- 1 | https://entaku0818.github.io/supports 2 | -------------------------------------------------------------------------------- /fastlane/screenshots/README.md: -------------------------------------------------------------------------------- 1 | # スクリーンショットについて 2 | 3 | このディレクトリはApp Storeに表示するスクリーンショットを保存するためのものです。 4 | 5 | ## ディレクトリ構造 6 | 7 | ``` 8 | screenshots/ 9 | ├── iphone/ # iPhone用スクリーンショット 10 | │ ├── ja/ # 日本語 11 | │ └── en-US/ # 英語(米国) 12 | └── ipad/ # iPad用スクリーンショット 13 | ├── ja/ # 日本語 14 | └── en-US/ # 英語(米国) 15 | ``` 16 | 17 | ## ファイル名の規則 18 | 19 | スクリーンショットは数字順に表示されるため、ファイル名の先頭に番号を付けることをお勧めします。 20 | 例: 21 | 22 | - `1_ホーム画面.png` 23 | - `2_録音画面.png` 24 | - `3_プレイリスト.png` 25 | - `4_音声編集.png` 26 | - `5_設定画面.png` 27 | 28 | ## App Storeへのアップロード 29 | 30 | 以下のコマンドで、スクリーンショットをApp Store Connectにアップロードできます: 31 | 32 | ``` 33 | bundle exec fastlane update_screenshots 34 | ``` 35 | 36 | ## スクリーンショットのサイズ 37 | 38 | App Storeに表示するスクリーンショットは、以下のサイズガイドラインに従ってください: 39 | 40 | ### iPhone 41 | 42 | - iPhone 6.7" (iPhone 14 Pro Max): 1290 x 2796 43 | - iPhone 6.5" (iPhone 14 Pro): 1242 x 2688 44 | - iPhone 5.5" (iPhone 8 Plus): 1242 x 2208 45 | - iPhone 5.8" (iPhone X/XS): 1125 x 2436 46 | 47 | ### iPad 48 | 49 | - iPad Pro (12.9-inch): 2048 x 2732 50 | - iPad Pro (11-inch): 1668 x 2388 51 | - iPad (10.2-inch): 1620 x 2160 52 | 53 | ## その他 54 | 55 | スクリーンショットには効果的なキャプションを付けることで、アプリの機能を強調することができます。 56 | キャプションはApp Store Connectで直接設定するか、Fastlaneの設定で自動的に付けることができます。 -------------------------------------------------------------------------------- /fastlane/screenshots/de-DE/0_APP_IPHONE_55_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/de-DE/0_APP_IPHONE_55_0.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/de-DE/0_APP_IPHONE_65_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/de-DE/0_APP_IPHONE_65_0.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/de-DE/1_APP_IPHONE_55_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/de-DE/1_APP_IPHONE_55_1.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/de-DE/1_APP_IPHONE_65_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/de-DE/1_APP_IPHONE_65_1.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/de-DE/2_APP_IPHONE_55_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/de-DE/2_APP_IPHONE_55_2.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/de-DE/2_APP_IPHONE_65_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/de-DE/2_APP_IPHONE_65_2.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/de-DE/3_APP_IPHONE_65_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/de-DE/3_APP_IPHONE_65_3.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/de-DE/4_APP_IPHONE_65_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/de-DE/4_APP_IPHONE_65_4.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/en-US/.gitkeep -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/0_APP_IPHONE_55_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/en-US/0_APP_IPHONE_55_0.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/0_APP_IPHONE_65_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/en-US/0_APP_IPHONE_65_0.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/1_APP_IPHONE_55_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/en-US/1_APP_IPHONE_55_1.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/1_APP_IPHONE_65_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/en-US/1_APP_IPHONE_65_1.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/2_APP_IPHONE_55_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/en-US/2_APP_IPHONE_55_2.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/2_APP_IPHONE_65_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/en-US/2_APP_IPHONE_65_2.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/3_APP_IPHONE_55_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/en-US/3_APP_IPHONE_55_3.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/3_APP_IPHONE_65_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/en-US/3_APP_IPHONE_65_3.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/4_APP_IPHONE_65_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/en-US/4_APP_IPHONE_65_4.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/es-ES/0_APP_IPHONE_55_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/es-ES/0_APP_IPHONE_55_0.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/es-ES/0_APP_IPHONE_65_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/es-ES/0_APP_IPHONE_65_0.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/es-ES/1_APP_IPHONE_55_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/es-ES/1_APP_IPHONE_55_1.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/es-ES/1_APP_IPHONE_65_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/es-ES/1_APP_IPHONE_65_1.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/es-ES/2_APP_IPHONE_55_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/es-ES/2_APP_IPHONE_55_2.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/es-ES/2_APP_IPHONE_65_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/es-ES/2_APP_IPHONE_65_2.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/ja/0_APP_IPHONE_55_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/ja/0_APP_IPHONE_55_0.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/ja/0_APP_IPHONE_65_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/ja/0_APP_IPHONE_65_0.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/ja/1_APP_IPHONE_55_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/ja/1_APP_IPHONE_55_1.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/ja/1_APP_IPHONE_65_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/ja/1_APP_IPHONE_65_1.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/ja/2_APP_IPHONE_55_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/ja/2_APP_IPHONE_55_2.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/ja/2_APP_IPHONE_65_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/ja/2_APP_IPHONE_65_2.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/ja/3_APP_IPHONE_55_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/ja/3_APP_IPHONE_55_3.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/ja/3_APP_IPHONE_65_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/ja/3_APP_IPHONE_65_3.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/ja/4_APP_IPHONE_65_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/ja/4_APP_IPHONE_65_4.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/pt-PT/0_APP_IPHONE_55_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/pt-PT/0_APP_IPHONE_55_0.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/pt-PT/0_APP_IPHONE_65_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/pt-PT/0_APP_IPHONE_65_0.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/pt-PT/1_APP_IPHONE_55_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/pt-PT/1_APP_IPHONE_55_1.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/pt-PT/1_APP_IPHONE_65_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/pt-PT/1_APP_IPHONE_65_1.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/pt-PT/2_APP_IPHONE_55_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/pt-PT/2_APP_IPHONE_55_2.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/pt-PT/2_APP_IPHONE_65_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/pt-PT/2_APP_IPHONE_65_2.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/zh-Hant/0_APP_IPHONE_55_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/zh-Hant/0_APP_IPHONE_55_0.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/zh-Hant/0_APP_IPHONE_65_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/zh-Hant/0_APP_IPHONE_65_0.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/zh-Hant/1_APP_IPHONE_55_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/zh-Hant/1_APP_IPHONE_55_1.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/zh-Hant/1_APP_IPHONE_65_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/zh-Hant/1_APP_IPHONE_65_1.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/zh-Hant/2_APP_IPHONE_55_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/zh-Hant/2_APP_IPHONE_55_2.jpg -------------------------------------------------------------------------------- /fastlane/screenshots/zh-Hant/2_APP_IPHONE_65_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/entaku0818/VoiceMemo/e2caf95317cc18a78ca5830551096e91b4cd7fc4/fastlane/screenshots/zh-Hant/2_APP_IPHONE_65_2.jpg -------------------------------------------------------------------------------- /recordActivity/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /recordActivity/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /recordActivity/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /recordActivity/Assets.xcassets/WidgetBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /recordActivity/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSExtension 6 | 7 | NSExtensionPointIdentifier 8 | com.apple.widgetkit-extension 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /recordActivity/recordActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // recordActivity.swift 3 | // recordActivity 4 | // 5 | // Created by 遠藤拓弥 on 2024/07/20. 6 | // 7 | 8 | import WidgetKit 9 | import SwiftUI 10 | 11 | struct Provider: TimelineProvider { 12 | func placeholder(in context: Context) -> SimpleEntry { 13 | SimpleEntry(date: Date(), emoji: "😀") 14 | } 15 | 16 | func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) { 17 | let entry = SimpleEntry(date: Date(), emoji: "😀") 18 | completion(entry) 19 | } 20 | 21 | func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { 22 | var entries: [SimpleEntry] = [] 23 | 24 | // Generate a timeline consisting of five entries an hour apart, starting from the current date. 25 | let currentDate = Date() 26 | for hourOffset in 0 ..< 5 { 27 | let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! 28 | let entry = SimpleEntry(date: entryDate, emoji: "😀") 29 | entries.append(entry) 30 | } 31 | 32 | let timeline = Timeline(entries: entries, policy: .atEnd) 33 | completion(timeline) 34 | } 35 | } 36 | 37 | struct SimpleEntry: TimelineEntry { 38 | let date: Date 39 | let emoji: String 40 | } 41 | 42 | struct recordActivityEntryView: View { 43 | var entry: Provider.Entry 44 | 45 | var body: some View { 46 | VStack { 47 | Text("Time:") 48 | Text(entry.date, style: .time) 49 | 50 | Text("Emoji:") 51 | Text(entry.emoji) 52 | } 53 | } 54 | } 55 | 56 | struct recordActivity: Widget { 57 | let kind: String = "recordActivity" 58 | 59 | var body: some WidgetConfiguration { 60 | StaticConfiguration(kind: kind, provider: Provider()) { entry in 61 | if #available(iOS 17.0, *) { 62 | recordActivityEntryView(entry: entry) 63 | .containerBackground(.fill.tertiary, for: .widget) 64 | } else { 65 | recordActivityEntryView(entry: entry) 66 | .padding() 67 | .background() 68 | } 69 | } 70 | .configurationDisplayName("シンプル録音") 71 | } 72 | } 73 | 74 | #Preview(as: .systemSmall) { 75 | recordActivity() 76 | } timeline: { 77 | SimpleEntry(date: .now, emoji: "😀") 78 | SimpleEntry(date: .now, emoji: "🤩") 79 | } 80 | -------------------------------------------------------------------------------- /recordActivity/recordActivityBundle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // recordActivityBundle.swift 3 | // recordActivity 4 | // 5 | // Created by 遠藤拓弥 on 2024/07/20. 6 | // 7 | 8 | import WidgetKit 9 | import SwiftUI 10 | 11 | @main 12 | struct recordActivityBundle: WidgetBundle { 13 | var body: some Widget { 14 | recordActivity() 15 | recordActivityLiveActivity() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /recordActivity/recordActivityLiveActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // recordActivityLiveActivity.swift 3 | // recordActivity 4 | // 5 | // Created by 遠藤拓弥 on 2024/07/20. 6 | // 7 | 8 | import ActivityKit 9 | import WidgetKit 10 | import SwiftUI 11 | 12 | struct recordActivityAttributes: ActivityAttributes { 13 | public struct ContentState: Codable, Hashable { 14 | // Dynamic stateful properties about your activity go here! 15 | var emoji: String 16 | var recordingTime: TimeInterval // 録音時間を追加 17 | } 18 | 19 | // Fixed non-changing properties about your activity go here! 20 | var name: String 21 | } 22 | 23 | struct recordActivityLiveActivity: Widget { 24 | var body: some WidgetConfiguration { 25 | ActivityConfiguration(for: recordActivityAttributes.self) { context in 26 | // Lock screen/banner UI goes here 27 | VStack { 28 | HStack { 29 | Spacer().frame(width: 16) 30 | Text("録音中") // 録音時間を表示 31 | .foregroundColor(.white) 32 | Text("\(formatTimeInterval(context.state.recordingTime))") // 録音時間を表示 33 | .font(.largeTitle) // フォントサイズを大きく設定 34 | .foregroundColor(.red) 35 | Spacer() 36 | } 37 | } 38 | .activityBackgroundTint(Color.black) 39 | .activitySystemActionForegroundColor(Color.red) 40 | 41 | } dynamicIsland: { context in 42 | DynamicIsland { 43 | // Expanded UI goes here. Compose the expanded UI through 44 | // various regions, like leading/trailing/center/bottom 45 | DynamicIslandExpandedRegion(.leading) { 46 | Text("録音中") 47 | } 48 | DynamicIslandExpandedRegion(.trailing) { 49 | Text("\(context.state.emoji)") 50 | } 51 | DynamicIslandExpandedRegion(.bottom) { 52 | VStack { 53 | HStack { 54 | Text(formatTimeInterval(context.state.recordingTime)) 55 | .font(.largeTitle) // フォントサイズを大きく設定 56 | Spacer() 57 | } 58 | } 59 | } 60 | } compactLeading: { 61 | Text("録音中") 62 | } compactTrailing: { 63 | Text("\(context.state.emoji)") 64 | } minimal: { 65 | Text(context.state.emoji) 66 | } 67 | .keylineTint(Color.red) 68 | } 69 | } 70 | 71 | private func formatTimeInterval(_ interval: TimeInterval) -> String { 72 | let minutes = Int(interval) / 60 73 | let seconds = Int(interval) % 60 74 | return String(format: "%02d:%02d", minutes, seconds) 75 | } 76 | } 77 | 78 | extension recordActivityAttributes { 79 | fileprivate static var preview: recordActivityAttributes { 80 | recordActivityAttributes(name: "World") 81 | } 82 | } 83 | 84 | extension recordActivityAttributes.ContentState { 85 | fileprivate static var recording: recordActivityAttributes.ContentState { 86 | recordActivityAttributes.ContentState(emoji: "🔴", recordingTime: 0) 87 | } 88 | } 89 | --------------------------------------------------------------------------------