fix(ios): address App Store review requirements

Use compliant permission prompts, align cloud ASR privacy disclosures, and default Flow sessions to a shorter inactivity window for safer resubmission.
This commit is contained in:
Rocky
2026-07-23 15:37:38 +08:00
parent c0c9dad149
commit e6f99d2744
18 changed files with 178 additions and 83 deletions
+2 -2
View File
@@ -94,9 +94,9 @@
<false/>
</dict>
<key>NSMicrophoneUsageDescription</key>
<string>OSGKeyboard uses the microphone for voice dictation and keeps a background audio session active while a voice session is running.</string>
<string>OSGKeyboard uses the microphone for voice dictation, including active Flow sessions while you type in other apps.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>OSGKeyboard uses on-device speech recognition to transcribe your voice. Audio is processed on your device and is not uploaded for transcription.</string>
<string>OSGKeyboard uses speech recognition to transcribe your voice. Audio is processed on-device by default, or sent to your configured speech provider only when you enable cloud recognition.</string>
<key>NSSupportsLiveActivities</key>
<true/>
<key>UIApplicationSceneManifest</key>
+2 -2
View File
@@ -15,7 +15,7 @@
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeAudioData</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<true/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
@@ -27,7 +27,7 @@
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeOtherUserContent</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<true/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
+6 -6
View File
@@ -22,12 +22,12 @@
<p class="lang"><a href="#zh">中文</a> · <a href="#top">English</a></p>
<div id="top">
<h1>OSGKeyboard Privacy Policy</h1>
<p><strong>Last updated:</strong> July 8, 2026</p>
<p><strong>Last updated:</strong> July 23, 2026</p>
<p>OSGKeyboard is a custom iOS keyboard that turns your voice into text. This policy explains what data the app processes and how it is used.</p>
<h2>What we collect</h2>
<ul>
<li><strong>Voice audio</strong> — captured only while you actively record. On-device mode transcribes locally with Apples speech APIs; raw audio is not uploaded by OSGKeyboard.</li>
<li><strong>Voice audio</strong> — captured only while you actively record. The default on-device mode transcribes locally with Apples speech APIs and does not upload raw audio. If you explicitly enable cloud recognition, recordings are sent to the speech provider you configure for transcription; that providers privacy policy applies. OSGKeyboard does not store or proxy the audio on its own servers.</li>
<li><strong>Transcribed text</strong> — in Cloud polish mode, the final text (not audio) may be sent to the LLM provider you configure (e.g. OpenAI) for punctuation and formatting.</li>
<li><strong>API credentials</strong> — stored in the iOS Keychain and shared between the main app and keyboard extension. When iCloud settings sync is enabled, keys replicate through iCloud Keychain (not iCloud KVS JSON).</li>
<li><strong>App preferences</strong> — engine mode, language, and keyboard settings stored in App Group UserDefaults. Optional iCloud sync mirrors preferences, usage statistics, and voice history through your private iCloud account.</li>
@@ -48,7 +48,7 @@
</ul>
<h2>Third parties</h2>
<p>When you choose Cloud polish mode, transcribed text is sent to the API endpoint you configure. That providers privacy policy applies to those requests.</p>
<p>When you enable cloud recognition, recordings are sent directly to the speech provider you configure. When you choose Cloud polish mode, transcribed text is sent to the API endpoint you configure. Those providers privacy policies apply to the requests.</p>
<h2>Data retention</h2>
<p>Settings remain on your device until you delete the app or reset settings. With iCloud sync enabled, API keys use iCloud Keychain; preferences, statistics, and history may sync via your private iCloud account.</p>
@@ -60,12 +60,12 @@
<hr id="zh">
<h1>OSGKeyboard 隐私政策</h1>
<p><strong>更新日期:</strong>2026 年 7 月 8</p>
<p><strong>更新日期:</strong>2026 年 7 月 23</p>
<p>OSGKeyboard 是一款 iOS 自定义键盘,可将语音转为文字。本政策说明应用处理哪些数据及用途。</p>
<h2>我们处理的数据</h2>
<ul>
<li><strong>语音音频</strong> — 仅在你主动录音时采集。本地模式在设备端通过 Apple 语音识别转写,OSGKeyboard 不会上传原始录音</li>
<li><strong>语音音频</strong> — 仅在你主动录音时采集。默认本地模式通过 Apple 语音能力在设备端转写,不会上传原始录音。若你主动启用云端识别,录音会发送到你配置的语音服务商完成转写,并适用该服务商的隐私政策。OSGKeyboard 自身不会存储或中转音频</li>
<li><strong>转写文字</strong> — 云端润色模式下,最终文字(非音频)可能发送到你配置的 LLM 服务商以整理标点和格式。</li>
<li><strong>API 凭证</strong> — 保存在设备 Keychain,在主 App 与键盘扩展间共享。开启 iCloud 设置同步后,经 iCloud 钥匙串同步(非 iCloud KVS JSON)。</li>
<li><strong>应用偏好</strong> — 引擎、语言等设置保存在 App Group。可选 iCloud 同步经私有 iCloud 账户镜像偏好、统计与语音历史。</li>
@@ -86,7 +86,7 @@
</ul>
<h2>第三方</h2>
<p>选择云端润色时,转写文字会发你配置的 API,该服务商的隐私政策适用于相关请求</p>
<p>启用云端识别时,录音会直接发送到你配置的语音服务商;选择云端润色时,转写文字会发送到你配置的 API。相关请求适用对应服务商的隐私政策。</p>
<h2>数据保留</h2>
<p>设置保留在设备上,直至卸载或重置。开启 iCloud 同步后,API 密钥走 iCloud 钥匙串;偏好、统计与历史可能经私有 iCloud 账户同步。</p>
+4 -2
View File
@@ -460,7 +460,8 @@ private struct MicPermissionPage: View {
switch status {
case .granted: return "onboarding.permission.status.granted"
case .denied: return "onboarding.permission.openSettings"
case .undetermined: return "onboarding.permission.mic.allow"
// App Store 5.1.1(iv): pre-permission CTA must not say "Allow".
case .undetermined: return "common.next"
}
}
@@ -526,7 +527,8 @@ private struct SpeechPermissionPage: View {
switch status {
case .granted: return "onboarding.permission.status.granted"
case .denied, .restricted: return "onboarding.permission.openSettings"
case .undetermined: return "onboarding.permission.speech.allow"
// App Store 5.1.1(iv): pre-permission CTA must not say "Allow".
case .undetermined: return "common.next"
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
/* Permission prompts — English */
"NSMicrophoneUsageDescription" = "OSGKeyboard uses the microphone for voice dictation and keeps a background audio session active while a voice session is running.";
"NSMicrophoneUsageDescription" = "OSGKeyboard uses the microphone for voice dictation, including active Flow sessions while you type in other apps.";
"NSSpeechRecognitionUsageDescription" = "OSGKeyboard transcribes your voice with on-device speech recognition by default. If you explicitly switch to a cloud engine in Settings, recordings are sent to the ASR provider you configure.";
+7 -4
View File
@@ -5,14 +5,15 @@
/* Onboarding */
"onboarding.progress" = "Step %1$d of %2$d";
"onboarding.welcome.tagline" = "Speak it. Its typed.";
"onboarding.permission.preface" = "iOS will ask for two permissions — please allow both.";
"onboarding.permission.preface" = "iOS will ask for microphone and speech recognition next.";
"onboarding.permission.mic.title" = "Microphone";
"onboarding.permission.mic.body" = "Records your voice.";
"onboarding.permission.mic.allow" = "Allow";
/* Pre-system prompt CTA — use Next/Continue, never Allow (App Store 5.1.1(iv)). */
"onboarding.permission.mic.allow" = "Next";
"onboarding.permission.mic.deniedHint" = "Open Settings to enable, or tap Next to set up later.";
"onboarding.permission.speech.title" = "Speech Recognition";
"onboarding.permission.speech.body" = "Turns your voice into text.";
"onboarding.permission.speech.allow" = "Allow";
"onboarding.permission.speech.allow" = "Next";
"onboarding.permission.speech.deniedHint" = "Open Settings to enable, or tap Next to set up later.";
"onboarding.permission.openSettings" = "Open Settings";
"onboarding.permission.status.undetermined" = "Not requested yet";
@@ -327,7 +328,7 @@
"home.setup.permission.mic" = "Microphone access is off — voice input won't work.";
"home.setup.permission.speech" = "Speech recognition is off — voice input won't work.";
"home.setup.permission.both" = "Microphone and speech recognition are off — voice input won't work.";
"home.setup.permission.request" = "Grant access";
"home.setup.permission.request" = "Next";
"home.setup.cloudIncomplete" = "Cloud engine needs an API key. Open the Settings tab.";
"home.setup.keyboardHint" = "Don't see OSGKeyboard? Add it in iOS Settings.";
"home.setup.keyboardHint.dismiss" = "Got it";
@@ -420,6 +421,8 @@
"settings.flow.skipAppSwitch.title" = "Skip app switch";
"settings.flow.skipAppSwitch.subtitle" = "After a cold start, try to return to the app you came from.";
"settings.flow.inactivity.title" = "End session after inactivity";
"settings.flow.inactivity.1m" = "1 minute";
"settings.flow.inactivity.5m" = "5 minutes";
"settings.flow.inactivity.10m" = "10 minutes";
"settings.flow.inactivity.30m" = "30 minutes";
"settings.flow.inactivity.3h" = "3 hours";
+1 -1
View File
@@ -1,4 +1,4 @@
/* 权限说明 — 简体中文 */
"NSMicrophoneUsageDescription" = "OSGKeyboard 使用麦克风进行语音听写,并在语音会话运行期间保持后台音频会话。";
"NSMicrophoneUsageDescription" = "OSGKeyboard 使用麦克风进行语音听写,包括你在其他 App 中输入时保持启用的 Flow 会话。";
"NSSpeechRecognitionUsageDescription" = "OSGKeyboard 默认使用设备端语音识别将你的语音转为文字。若你在设置中主动切换到云端引擎,录音会发送到你配置的识别服务商。";
@@ -5,14 +5,15 @@
/* Onboarding */
"onboarding.progress" = "第 %1$d / %2$d 步";
"onboarding.welcome.tagline" = "开口即文字。";
"onboarding.permission.preface" = "系统会弹出两次授权,请「允许」权限申请。";
"onboarding.permission.preface" = "接下来系统会分别请求麦克风与语音识别权限。";
"onboarding.permission.mic.title" = "麦克风";
"onboarding.permission.mic.body" = "用于录制语音。";
"onboarding.permission.mic.allow" = "允许";
/* 系统弹窗前的引导按钮须用「下一步/继续」,勿用「允许」(App Store 5.1.1(iv))。 */
"onboarding.permission.mic.allow" = "下一步";
"onboarding.permission.mic.deniedHint" = "去设置打开,或先点「下一步」。";
"onboarding.permission.speech.title" = "语音识别";
"onboarding.permission.speech.body" = "把你说的话转成文字。";
"onboarding.permission.speech.allow" = "允许";
"onboarding.permission.speech.allow" = "下一步";
"onboarding.permission.speech.deniedHint" = "去设置打开,或先点「下一步」。";
"onboarding.permission.openSettings" = "打开设置";
"onboarding.permission.status.undetermined" = "尚未请求";
@@ -326,7 +327,7 @@
"home.setup.permission.mic" = "麦克风还没授权,语音输入用不了。";
"home.setup.permission.speech" = "语音识别还没授权,语音输入用不了。";
"home.setup.permission.both" = "麦克风和语音识别还没授权,语音输入用不了。";
"home.setup.permission.request" = "去授权";
"home.setup.permission.request" = "下一步";
"home.setup.cloudIncomplete" = "选了云端引擎,先去「设置」填 API Key。";
"home.setup.keyboardHint" = "列表里没有?去系统设置里添加键盘。";
"home.setup.keyboardHint.dismiss" = "知道了";
@@ -419,6 +420,8 @@
"settings.flow.skipAppSwitch.title" = "跳过应用切换";
"settings.flow.skipAppSwitch.subtitle" = "冷启动完成后,尝试自动返回来源 App。";
"settings.flow.inactivity.title" = "无活动后结束会话";
"settings.flow.inactivity.1m" = "1 分钟";
"settings.flow.inactivity.5m" = "5 分钟";
"settings.flow.inactivity.10m" = "10 分钟";
"settings.flow.inactivity.30m" = "30 分钟";
"settings.flow.inactivity.3h" = "3 小时";