# iOS Privacy Compliance & ATT: Eksiksiz Rehber
Dijital gizlilik artik bir tercih degil, yasal zorunluluk. Apple'in App Tracking Transparency (ATT) framework'u ile kullanicilarin izlenmesi konusu tamamen degisti. 2024 itibariyle Privacy Manifest zorunlulugu, Required Reason API'ler ve siki App Store Review surecleri ile uyumlu olmak her iOS gelistiricinin gorevidir. Bu rehberde tum sureci bastan sona ele alacagiz.
Not: Bu rehber Apple'in resmi dokumantasyonu, WWDC session'lari ve gercek App Store Review deneyimlerine dayanmaktadir.
Icindekiler
- Privacy Neden Onemli?
- App Tracking Transparency (ATT)
- Privacy Manifest Dosyasi
- Required Reason API
- Privacy Nutrition Label
- GDPR ve KVKK Uyumu
- Consent Management
- Server-Side Tracking Alternatifleri
- App Store Review Sureci
- Test ve Dogrulama
- Sonuc ve Oneriler
1. Privacy Neden Onemli?
Apple'in privacy-first yaklasimi sadece bir pazarlama stratejisi degil. iOS 14.5'ten bu yana ATT framework'u zorunlu hale geldi ve kullanicilarin sadece %25-35'i izlemeye izin veriyor. Bu da reklam gelirlerini, attribution'i ve analytics'i dogrudan etkiliyor.
Tarihsel Surecin Ozeti
Yil | Gelisme | Etki |
|---|---|---|
**iOS 14.5** | ATT zorunlu | IDFA erisimi izne bagli |
**iOS 15** | Mail Privacy Protection | Email tracking engellendi |
**iOS 16** | Lockdown Mode | Ekstra koruma katmani |
**iOS 17** | Link Tracking Protection | URL tracker parametreleri temizleniyor |
**iOS 17** | Privacy Manifest zorunlu | 3rd-party SDK'lar manifest sunmali |
**iOS 18** | Required Reason API | Belirli API'ler icin neden belirtme zorunlu |
Kullanici Guveni Istatistikleri
- Kullanicilarin %78'i gizlilik politikasini okuyor (Apple arastirmasi)
- %89: 'u izlemeyi reddettikten sonra uygulamayi kullanmaya devam ediyor
- Privacy label'da "Data Not Collected" yazan uygulamalar %32 daha fazla indiriliyor
2. App Tracking Transparency (ATT)
ATT, kullanicinin IDFA'sina (Identifier for Advertisers) erismeden once izin istemek icin kullanilir.
Temel Implementasyon
swift
1import AppTrackingTransparency2import AdSupport3 4final class TrackingManager {5 static let shared = TrackingManager()6 7 /// ATT izin durumunu kontrol et8 var currentStatus: ATTrackingManager.AuthorizationStatus {9 ATTrackingManager.trackingAuthorizationStatus10 }11 12 /// IDFA'yi guvenli sekilde al13 var advertisingIdentifier: String? {14 guard currentStatus == .authorized else { return nil }15 let idfa = ASIdentifierManager.shared().advertisingIdentifier16 // Sifir UUID kontrolu - izin verilmemis demektir17 guard idfa.uuidString != "00000000-0000-0000-0000-000000000000" else {18 return nil19 }20 return idfa.uuidString21 }22 23 /// Izin iste - SADECE uygun zamanda cagir24 func requestPermission() async -> ATTrackingManager.AuthorizationStatus {25 // iOS 17.4+ icin kontrol26 guard #available(iOS 14.5, *) else {27 return .authorized // Eski iOS'larda IDFA serbest28 }29 30 return await ATTrackingManager.requestTrackingAuthorization()31 }32 33 /// Izin durumuna gore analytics yapilandir34 func configureAnalytics() {35 switch currentStatus {36 case .authorized:37 enableFullTracking()38 case .denied, .restricted:39 enablePrivacyFriendlyAnalytics()40 case .notDetermined:41 // Henuz sorulmadi - daha sonra sor42 break43 @unknown default:44 enablePrivacyFriendlyAnalytics()45 }46 }47 48 private func enableFullTracking() {49 // Firebase Analytics, Adjust, AppsFlyer vb. tam izleme50 }51 52 private func enablePrivacyFriendlyAnalytics() {53 // Anonim metrikler, aggregate data, SKAdNetwork54 }55}ATT Dialog Zamanlama Stratejisi
ATT dialog'unu hemen gostermek en kotu yaklasimdir. Kullaniciya once deger gosterin:
swift
1import SwiftUI2 3struct OnboardingATTView: View {4 @State private var showATTExplanation = false5 6 var body: some View {7 VStack(spacing: 24) {8 Image(systemName: "hand.raised.fill")9 .font(.system(size: 60))10 .foregroundStyle(.blue)11 12 Text("Deneyiminizi Kisisellestirmek Istiyoruz")13 .font(.title2.bold())14 15 // Kullaniciya NEDEN izin istediginizi aciklayin16 VStack(alignment: .leading, spacing: 12) {17 BenefitRow(icon: "star.fill", text: "Size ozel icerik onerileri")18 BenefitRow(icon: "bell.fill", text: "Ilginizi cekecek bildirimler")19 BenefitRow(icon: "shield.fill", text: "Verileriniz guvendedir")20 }21 .padding()22 .background(.ultraThinMaterial)23 .cornerRadius(16)24 25 Button("Devam Et") {26 Task {27 let status = await TrackingManager.shared.requestPermission()28 handleTrackingResult(status)29 }30 }31 .buttonStyle(.borderedProminent)32 33 Button("Daha Sonra") {34 // Kullaniciyi zorlamayin35 navigateToHome()36 }37 .foregroundStyle(.secondary)38 }39 .padding()40 }41 42 private func handleTrackingResult(_ status: ATTrackingManager.AuthorizationStatus) {43 TrackingManager.shared.configureAnalytics()44 navigateToHome()45 }46 47 private func navigateToHome() {48 // Ana sayfaya yonlendir49 }50}51 52struct BenefitRow: View {53 let icon: String54 let text: String55 56 var body: some View {57 HStack(spacing: 12) {58 Image(systemName: icon)59 .foregroundStyle(.blue)60 Text(text)61 .font(.subheadline)62 }63 }64}Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
3. Privacy Manifest Dosyasi
iOS 17'den itibaren tum uygulamalar ve 3rd-party SDK'lar PrivacyInfo.xcprivacy dosyasi icermek zorunda.
Privacy Manifest Yapisi
swift
1// PrivacyInfo.xcprivacy ornegi (XML/Plist formati)2// Xcode'da Privacy Manifest olarak eklenir3//4// Ornek yapi:5// NSPrivacyTracking: false6// NSPrivacyTrackingDomains: []7// NSPrivacyCollectedDataTypes:8// - NSPrivacyCollectedDataType: NSPrivacyCollectedDataTypeDeviceID9// NSPrivacyCollectedDataTypeLinked: false10// NSPrivacyCollectedDataTypeTracking: false11// NSPrivacyCollectedDataTypePurposes:12// - NSPrivacyCollectedDataTypePurposeAnalytics13// NSPrivacyAccessedAPITypes:14// - NSPrivacyAccessedAPIType: NSPrivacyAccessedAPICategoryUserDefaults15// NSPrivacyAccessedAPITypeReasons: ["CA92.1"]16 17// Swift'te Privacy Manifest icerigini kontrol etme18import Foundation19 20struct PrivacyManifestValidator {21 struct ValidationResult {22 let isValid: Bool23 let warnings: [String]24 let errors: [String]25 }26 27 static func validate(manifestPath: String) -> ValidationResult {28 var warnings: [String] = []29 var errors: [String] = []30 31 guard let data = FileManager.default.contents(atPath: manifestPath),32 let plist = try? PropertyListSerialization.propertyList(33 from: data, format: nil34 ) as? [String: Any] else {35 return ValidationResult(isValid: false, warnings: [], errors: ["Manifest okunamiyor"])36 }37 38 // NSPrivacyTracking kontrolu39 if plist["NSPrivacyTracking"] == nil {40 errors.append("NSPrivacyTracking alani eksik")41 }42 43 // Tracking true ise domain listesi olmali44 if let tracking = plist["NSPrivacyTracking"] as? Bool, tracking {45 if let domains = plist["NSPrivacyTrackingDomains"] as? [String], domains.isEmpty {46 errors.append("Tracking aktif ama domain listesi bos")47 }48 }49 50 // Required Reason API kontrolu51 if let apiTypes = plist["NSPrivacyAccessedAPITypes"] as? [[String: Any]] {52 for api in apiTypes {53 if let reasons = api["NSPrivacyAccessedAPITypeReasons"] as? [String], reasons.isEmpty {54 let apiType = api["NSPrivacyAccessedAPIType"] as? String ?? "Bilinmeyen"55 warnings.append("API tipi icin neden belirtilmemis: \(apiType)")56 }57 }58 }59 60 return ValidationResult(61 isValid: errors.isEmpty,62 warnings: warnings,63 errors: errors64 )65 }66}Zorunlu Privacy Manifest Alanlari
Alan | Aciklama | Zorunlu mu? |
|---|---|---|
**NSPrivacyTracking** | Uygulama izleme yapiyor mu | Evet |
**NSPrivacyTrackingDomains** | Izleme domainleri | Tracking true ise evet |
**NSPrivacyCollectedDataTypes** | Toplanan veri turleri | Evet |
**NSPrivacyAccessedAPITypes** | Kullanilan Required Reason API'ler | Evet |
4. Required Reason API
Apple, belirli API'lerin kullanilmasi icin gercek bir neden (reason code) belirtilmesini zorunlu kildi.
API Kategorisi | Ornek Kullanim | Neden Kodu |
|---|---|---|
**UserDefaults** | Ayar saklama | CA92.1 |
**File timestamp** | Dosya degisiklik tarihi | DDA9.1 |
**System boot time** | Uptime olcumu | 35F9.1 |
**Disk space** | Bos alan kontrolu | E174.1 |
**Active keyboard** | Klavye listesi | 54BD.1 |
5. Privacy Nutrition Label
App Store Connect'te Privacy Label'i dogru doldurmak kritik oneme sahiptir. Yanlis beyan App Store reddi ile sonuclanir.
Veri Turu | Ornek | Linked / Not Linked |
|---|---|---|
Contact Info | Email, telefon | Genelde Linked |
Identifiers | IDFA, Device ID | Tracking ise Linked |
Usage Data | Urun etkilesimi | Not Linked olabilir |
Diagnostics | Crash log | Genelde Not Linked |
Location | GPS, IP-based | Kullanima bagli |
6. GDPR ve KVKK Uyumu
Avrupa'da GDPR ve Turkiye'de KVKK, kullanici verileri konusunda siki kurallar getirir.
swift
1// GDPR/KVKK uyumlu consent yonetimi2protocol ConsentManager {3 func hasConsent(for purpose: ConsentPurpose) -> Bool4 func requestConsent(for purposes: [ConsentPurpose]) async -> ConsentResult5 func revokeConsent(for purpose: ConsentPurpose)6 func exportUserData() async -> UserDataExport7 func deleteUserData() async throws8}9 10enum ConsentPurpose: String, CaseIterable {11 case analytics = "analytics"12 case advertising = "advertising"13 case personalization = "personalization"14 case thirdPartySharing = "third_party_sharing"15 case crashReporting = "crash_reporting"16 17 var isRequired: Bool {18 // Crash reporting genelde legitimate interest altinda19 self == .crashReporting20 }21 22 var description: String {23 switch self {24 case .analytics: return "Uygulama kullanim istatistikleri"25 case .advertising: return "Kisisellestrilmis reklamlar"26 case .personalization: return "Icerik kisisellestirme"27 case .thirdPartySharing: return "Ucuncu taraf veri paylasimi"28 case .crashReporting: return "Hata raporlama"29 }30 }31}32 33final class GDPRConsentManager: ConsentManager {34 private let storage: UserDefaults35 private let apiClient: APIClient36 37 init(storage: UserDefaults = .standard, apiClient: APIClient) {38 self.storage = storage39 self.apiClient = apiClient40 }41 42 func hasConsent(for purpose: ConsentPurpose) -> Bool {43 if purpose.isRequired { return true }44 return storage.bool(forKey: "consent_\(purpose.rawValue)")45 }46 47 func requestConsent(for purposes: [ConsentPurpose]) async -> ConsentResult {48 // UI goster ve kullanici secimini kaydet49 let result = await showConsentUI(purposes: purposes)50 for (purpose, granted) in result.decisions {51 storage.set(granted, forKey: "consent_\(purpose.rawValue)")52 }53 // Server'a da bildir54 try? await apiClient.syncConsent(result)55 return result56 }57 58 func revokeConsent(for purpose: ConsentPurpose) {59 storage.set(false, forKey: "consent_\(purpose.rawValue)")60 // Ilgili verileri sil61 Task { try? await apiClient.revokeConsent(purpose) }62 }63 64 /// GDPR Article 20 - Data portability65 func exportUserData() async -> UserDataExport {66 await apiClient.requestDataExport()67 }68 69 /// GDPR Article 17 - Right to erasure70 func deleteUserData() async throws {71 try await apiClient.requestDataDeletion()72 // Lokal verileri de temizle73 clearLocalData()74 }75 76 private func clearLocalData() {77 let domain = Bundle.main.bundleIdentifier ?? ""78 storage.removePersistentDomain(forName: domain)79 }80 81 private func showConsentUI(purposes: [ConsentPurpose]) async -> ConsentResult {82 // Consent UI implementasyonu83 fatalError("UI implementasyonu gerekli")84 }85}86 87struct ConsentResult {88 let decisions: [(ConsentPurpose, Bool)]89 let timestamp: Date90 let version: String // Consent policy versiyonu91}92 93struct UserDataExport {94 let data: Data95 let format: String // JSON96 let generatedAt: Date97}7. Consent Management UI
Kullaniciya seffaf ve anlasilir bir consent ekrani sunmak hem yasal zorunluluk hem de guven insasi acisindan kritiktir. Cookie banner yerine, mobilde "Privacy Settings" ekrani olusturulmalidir.
8. Server-Side Tracking Alternatifleri
ATT sonrasi dunyanin en buyuk degisikliklerinden biri: client-side tracking yerine server-side attribution modelleri.
- SKAdNetwork 4.0: Apple'in privacy-friendly attribution sistemi
- Aggregated Measurement: Meta, Google'in toplu olcum API'leri
- Probabilistic Attribution: IP + User Agent bazli (Apple onaylamaz)
- First-Party Data: Kendi toplanan veri en degerli kaynak
9. App Store Review Sureci
Privacy konusunda en sik reddedilme nedenleri:
Red Nedeni | Cozum |
|---|---|
Privacy label yanlis | Tum SDK'lari ve veri toplamalarini dogru beyan edin |
ATT dialog oncesi aciklama yok | Pre-ATT ekrani ekleyin |
Gereksiz veri toplama | Minimum veri prensibi uygulayin |
Privacy Manifest eksik | xcprivacy dosyasi ekleyin |
Purpose string yetersiz | Kullaniciya faydayi aciklayin |
10. Test ve Dogrulama
ATT ve privacy ozelliklerini test etmek icin:
- Simulatorde ATT testi: Settings > Privacy > Tracking
- Privacy Report: Settings > Privacy > App Privacy Report
- Network Inspector: Charles/Proxyman ile tracking domain kontrolu
- Xcode Privacy Report: Product > Analyze ile privacy sorunu taramasi
11. Sonuc ve Oneriler
Privacy compliance sureci tek seferlik bir is degil, surekli bir surectir. Her iOS guncellemesi yeni gereksinimler getirebilir.
Aksiyon Plani
- Privacy Manifest dosyasini olusturun veya guncelleyin
- Tum Required Reason API kullanim nedenlerini belgeleyin
- ATT dialog zamanlamasini optimize edin
- Privacy Nutrition Label'i dogru doldurun
- GDPR/KVKK consent mekanizmasini entegre edin
- 3rd-party SDK'larin privacy manifest'lerini kontrol edin
- Server-side attribution'a gecis planlayin
- Duzgun test ve dogrulama yapin
ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.
Okuyucu Ödülü
Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var:

