├── .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 |
--------------------------------------------------------------------------------