Her düzeltme bir sonraki katmanı açtı. Sonuncusu kodda değil, iOS’un ATT istemini ne zaman göstermeyi kabul ettiğindeydi.
Reklam gösteren bir iOS uygulamasında iki ayrı izin var ve birbirinin yerine geçmiyorlar:
Apple’ın kuralı basit görünüyor: sistem istemi ilk gelmek zorunda. Uygulamayı üç kere reddettirmesi bir ay sürdü.
Ret metni, ATT yerine “özel bir istem” gösterildiğini söylüyordu. Reddedilen ekran şuydu:
Our app wants to stay free for you. Ads help support our business. Tap “Allow” on the next screen to give permission to show ads that are more relevant to you.
Apple’ın tam olarak yasakladığı kalıp: kullanıcıya “Allow’a bas” demek. Ama o metni aradım ve kodda bulamadım — ne kaynakta ne de derlenmiş binary’de tek eşleşme vardı. Metin çalışma zamanında Google’ın UMP SDK’sı tarafından AdMob konsolundan çekiliyordu.
Çözüm koda değil konsola girdi: AdMob → Privacy & messaging → App Tracking Transparency altındaki “IDFA açıklayıcı” mesajı uygulamanın hedef listesinden çıkarıldı. UMP mesajı her açılışta sunucudan çekiyor, binary’de tutmuyor — yani yeni build gerekmedi, mesaj yayından kalktığı an mevcut sürüm o ekranı göstermeyi bıraktı.
⚠️ Diğer iki karta (Avrupa tüzükleri / ABD eyalet yönetmelikleri) dokunulmadı: birincisi AEA’da yasal zorunluluk, ikincisi gizlilik bildirimi. İhlal eden yalnız IDFA açıklayıcısıydı.
Düzeltme ABD bölgeli bir simülatörde doğrulandı: konum izninden doğrudan sistem ATT diyaloğuna geçiliyor, arada hiçbir ekran yok. Temiz.
Apple aynı maddeyi tekrar yazdı. Ama ret ekran görüntüsünde bu sefer IDFA açıklayıcısı yoktu; yerine Google’ın TCF formu vardı. Yani konsol düzeltmesi çalışmıştı, sorun taşınmıştı.
Apple’ın inceleme cihazı AEA’daydı. Kodun oradaki sırası şuydu:
_ensureConsent() → GDPR/TCF formu ← Apple bunu "özel istem" sayıyor
_ensureAttPermission() → sistem ATT diyaloğu
MobileAds.initialize()
Birinci düzeltme ABD bölgeli simülatörde doğrulanmıştı ve orada rıza akışı hiçbir şey göstermeden hemen dönüyor — GDPR formu o turda hiç çıkmadı, dolayısıyla sıra sorunu görünmedi.
Bölgeye bağlı davranışı yanlış bölgede test etmek, test etmemekle aynı şey. Temiz çıktı tek başına kanıt değil.
Sıra çevrildi: ATT → UMP → SDK. UMP formunu tamamen kaldırmak seçenek değildi — AEA’da hukuken gerekli ve yayımlanmış gizlilik politikamız onu açıkça vaat ediyor; kaldırmak yanlış beyan olurdu. Elimizdeki tek değişken sıraydı.
AEA simülasyonlu bir koşuda ölçüldü:
[ads] ATT sonucu: notDetermined ← kullanıcı HİÇBİR ŞEY yanıtlamadan
...20 sn sonra: rıza akışı zaman aşımına uğradı
Mekanizma şuydu: iOS, ATT istemini yalnız uygulama resumed durumundayken gösteriyor. Açılışta bizim konum izni diyaloğumuz öndeyken uygulama inactive oluyor ve requestTrackingAuthorization() istemi hiç göstermeden anında notDetermined dönüyor.
Kod bunu “soruldu” sanıp devam ediyor, UMP formunu açıyordu; ATT diyaloğu sonradan formun üstüne düşüyordu. Kullanıcının ve reviewer’ın gördüğü sıra yine UMP → ATT, yani reddedilen sıra.
awaitawait vardı ama beklenen şeyi beklemiyordu. Çağrı “sordum” değil “şu an soramam” diyordu ve ikisi aynı değeri dönüyordu.
Düzeltme: uygulama öne gelene kadar bekleyen bir yardımcı, istem gösterilmeden dönerse üç kereye kadar tekrar. 🔴 Bekleme bütçesi ile deneme sayacı ayrı tutuldu — ilk denemede birleşiklerdi ve konum diyaloğu uzun açık kalınca üç deneme de beklemede tükeniyordu.
Apple bu sefer başka bir madde yazdı — yani ilerleme gerçekti:
…the app shows a GDPR prompt that asks permission to track after the user has already selected “Ask App Not to Track”…
Google’ın TCF formu ilk satırında “Personalised advertising and content” rızası istiyor. Kullanıcı “takip etme” dedikten sonra o formu açmak, Apple’a göre reddedilen izni ikinci kez sormak.
Düzeltme ATT cevabına göre dallandı:
| ATT cevabı | UMP formu | Reklam isteği |
|---|---|---|
| Allow | AEA’da gösterilir | kişiselleştirilmiş |
| Ask App Not to Track | gösterilmez | kişiselleştirilmemiş; AEA’da rıza olmadığı için hiç istenmez |
| Android / iOS 14 öncesi | eskisi gibi | değişmedi |
Kararsız her durum — denied, restricted, yanıtlanamamış notDetermined, istisna — “takip yok” sayıldı. Belirsizlikte kısıtlayan taraf seçildi.
Bunu ararken çok daha ciddi bir şey bulundu: rıza durumunu tutan canRequestAds değeri okunuyor ve yalnız log’a yazılıyordu. Reklam SDK’sı ve üç ayrı reklam isteği ondan bağımsız çalışıyordu.
Yani AEA’da rızayı reddeden kullanıcıya da reklam isteniyordu — hem Google’ın entegrasyon şartına hem GDPR’a aykırı. Apple’ın “kullanıcı izin verene kadar hiçbir takip faaliyeti olmamalı” cümlesinin tam hedefi. Dört yükleme yolunun dördü de bu bayrağa bağlandı.
Rıza durumu çözülemezse (ilk açılış + ağ yok, ya da akış istisna atarsa) reklam hiç gösterilmiyor. Rızasız reklam istemektense bir oturumluk gelir kaybı seçildi.
⚠️ Bunun bir izleme noktası olması şart: rıza yapılandırması bir gün “her yerde gerekli” hâline gelirse gelir sessizce sıfırlanır ve bu, kod hatasından ayırt edilemez. Teşhis tek bir log satırından okunuyor.