App-Store Vorbereitung: Privacy-Manifest + Konto-Löschen + Region de
- PrivacyInfo.xcprivacy: NSPrivacyTracking=false, deklariert gesammelte Daten (Email, Name, User-ID, präziser/grober Standort, Fotos, Fitness, Sonstiger Nutzerinhalt) — alle linked to user, kein Tracking, nur AppFunctionality. Required-Reason APIs: UserDefaults (CA92.1), FileTimestamp (C617.1), DiskSpace (E174.1), SystemBootTime (35F9.1). - SettingsView: Section 'Konto löschen' mit zwei Bestätigungs-Alerts → DELETE /api/profile/account → automatischer logout. Erfüllt Apple- Pflicht seit iOS 16.4 (in-App-Löschung statt Web-Redirect). - Info.plist: CFBundleDevelopmentRegion explizit 'de' (statt der $(DEVELOPMENT_LANGUAGE)-Variable, die sonst 'en' auflöst) → Store zeigt App als deutschsprachig an. - NSHealthShareUsageDescription präziser formuliert (Reviewer-Hint).
This commit is contained in:
parent
09a90f7325
commit
3d229d42ce
3 changed files with 196 additions and 2 deletions
144
BanYaroGo/PrivacyInfo.xcprivacy
Normal file
144
BanYaroGo/PrivacyInfo.xcprivacy
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyTrackingDomains</key>
|
||||
<array/>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeEmailAddress</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeName</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeUserID</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypePreciseLocation</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeCoarseLocation</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypePhotosorVideos</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeFitness</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeOtherUserContent</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>CA92.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>E174.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -5,6 +5,10 @@ struct SettingsView: View {
|
|||
@AppStorage("autoPauseEnabled") private var autoPauseEnabled = true
|
||||
@AppStorage("healthKitSyncEnabled") private var healthKitSyncEnabled = false
|
||||
@State private var showHealthPermissionAlert = false
|
||||
@State private var showDeleteConfirm1 = false
|
||||
@State private var showDeleteConfirm2 = false
|
||||
@State private var isDeleting = false
|
||||
@State private var deleteError: String?
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
|
|
@ -145,6 +149,26 @@ struct SettingsView: View {
|
|||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
Button(role: .destructive) {
|
||||
showDeleteConfirm1 = true
|
||||
} label: {
|
||||
if isDeleting {
|
||||
HStack { ProgressView(); Text("Wird gelöscht…") }
|
||||
} else {
|
||||
Label("Konto unwiderruflich löschen", systemImage: "trash")
|
||||
}
|
||||
}
|
||||
.disabled(isDeleting)
|
||||
if let deleteError {
|
||||
Text(deleteError).font(.footnote).foregroundStyle(.red)
|
||||
}
|
||||
} header: {
|
||||
Text("Konto löschen")
|
||||
} footer: {
|
||||
Text("Löscht dein banyaro-Konto, alle Hunde, Touren, Tagebuch-Einträge, Ausgaben und Fotos endgültig. Das gilt App-übergreifend (auch für banyaro.app im Browser). Die Aktion kann nicht rückgängig gemacht werden.")
|
||||
}
|
||||
|
||||
Section("Über") {
|
||||
Text("Ban Yaro Go ist die native iOS-Ergänzung zur banyaro.app PWA. Phase 1: deine Touren ansehen.")
|
||||
.font(.footnote)
|
||||
|
|
@ -158,6 +182,32 @@ struct SettingsView: View {
|
|||
} message: {
|
||||
Text("Du kannst die Berechtigung in den iOS-Einstellungen unter Datenschutz & Sicherheit → Health → Ban Yaro Go nachträglich ändern.")
|
||||
}
|
||||
.alert("Konto wirklich löschen?", isPresented: $showDeleteConfirm1) {
|
||||
Button("Abbrechen", role: .cancel) {}
|
||||
Button("Weiter", role: .destructive) { showDeleteConfirm2 = true }
|
||||
} message: {
|
||||
Text("Alle Hunde, Touren, Tagebuch, Ausgaben und Fotos werden endgültig gelöscht — App und banyaro.app gleichermaßen. Diese Aktion kann nicht rückgängig gemacht werden.")
|
||||
}
|
||||
.alert("Letzte Bestätigung", isPresented: $showDeleteConfirm2) {
|
||||
Button("Abbrechen", role: .cancel) {}
|
||||
Button("Endgültig löschen", role: .destructive) {
|
||||
Task { await deleteAccount() }
|
||||
}
|
||||
} message: {
|
||||
Text("Bist du dir sicher? Dein Konto und alle Daten werden jetzt sofort und unwiderruflich entfernt.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func deleteAccount() async {
|
||||
isDeleting = true
|
||||
deleteError = nil
|
||||
defer { isDeleting = false }
|
||||
do {
|
||||
try await APIClient.shared.delete("/api/profile/account")
|
||||
auth.logout()
|
||||
} catch {
|
||||
deleteError = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue