Apple uygulamamı üç kez 'fazla Türkçe' diye reddetti
On bir gün, üç reject, bir lansman penceresi. Stork mobil uygulamasını App Store review'dan geçirme hikâyesi — ilk şikâyet 'dil İngilizce değil' idi.
İlk red maili 4 Nisan 2026 gecesi, saat 03:14'te düştü. Ertesi sabah lansman vardı. Reviewer üç cümle yazmıştı: bir UI davranışını doğrulayamamış, çünkü dil "İngilizce değil." Türk müşterilere Türkçe bir alışveriş uygulaması yapıyorduk. Türkçe olması zaten meselenin kendisiydi. Mail 4.0, Design — Spam koduyla gelmişti.
Maili iki kez okudum. Sonra mutfağa gittim, kahve yaptım, üçüncü kez okudum. 04:00'te günlüğüme tek satır yazdım: "Türkçeyi spam sanıyorlar." Altını çizdim. Hâlâ orada duruyor.
Bu yazı, o 03:14 mailinden temiz bir onaya kadar geçen on bir günün hikâyesi. Üç red. Üç çözüm. Süreç başlamadan önce kestiremeyeceğim bir ders.
Merchant pilot bağlamı
Stork, Türkçe bir alışveriş ve kargo takip uygulaması. App Store'a submit ettiğimizde pilotta altı ödeme yapan merchant ve TestFlight'ta yaklaşık 1.800 aktif beta tester vardı. Uygulamadaki her şey Türkçe, çünkü kullanan herkes Türkçe okuyor. TestFlight build'i üç haftadır stabildi. İç build'ler daha da uzun süredir. Başıboş çıkmamıştık.
Submit paketinde Türkçe demo hesap, Türkçe ekran görüntüleri ve hedef pazarın Türkiye olduğunu açıklayan tek paragraflık bir review notu vardı. O notun yeterli olacağını sandım. Yanılmışım.
Reject 1 — 4.0 Design, Spam
Reviewer "sepete ekle" akışını doğrulayamadığını yazmıştı; in-app metinler İngilizce değildi. Aklımda kalan cümle şu: "Lütfen İngilizce UI içeren bir build veya her adımı İngilizce anlatan bir video gönderin."
Burada dikkatli olmak istiyorum. Apple'ın Türkçe uygulamalara karşı bir önyargısı olduğunu düşünmüyorum. Bence olan şu — ve bu süreçten geçen herkese aynısını söylerim: reviewer'ın varsayılan beklentisi English-first. UI'ı okuyamadığında "doğrulayamıyorum" diyor ve eline en yakın red kategorisi 4.0 oluyor. Kişisel değil. Bizim tarafımızda bir süreç hatası: reviewer'a, okuyabildiği bir dilde uygulamayı değerlendirme imkânı sunmamışız.
Çözüm 1 — anlatımlı video
Ertesi öğleden sonra altı dakikalık bir ekran kaydı aldım. Her ekranı İngilizce anlattım: "Bu ana sekme. Türkçesi Anasayfa. Ürün kartına dokunduğunda ürün detay ekranı açılır." Resolution Center'a ek olarak yolladım.
Bu adımdan iki şey öğrendim:
- Videonun cilalı olması şart değil. Benimkinde dördüncü dakikada arka planda köpek havlıyordu. Kabul edildi.
- Reviewer çevrilmiş bir build istemiyor. Akışı takip edebileceği bir yol istiyor. Anlatımlı ekran kaydı yetiyor.
Aynı gün 19:40'ta bir sonraki review queue'ya geçtik.
Reject 2 — 5.1 Privacy
Bu ikinci red iki gün sonra, daha insani bir saatte, 11:20'de geldi. Reviewer onboarding sırasında telefon numarası topladığımızı ve privacy declaration'ımızın bu toplamayı gerekçelendirmediğini yazmıştı. Submit ettiğimiz declaration telefon numaralarını tek kategori altında listeliyordu: "marketing."
Sorun şu. Stork telefon numaralarını bir değil, üç sebepten topluyor:
- Giriş sırasında OTP doğrulama (güvenlik)
- Kargo durumu değiştiğinde bildirim (uygulama işlevi)
- Merchant müşteriye ulaşması gerektiğinde destek araması (müşteri desteği)
Privacy manifest'i submit anında aceleyle doldurmuş, üçünü de "marketing" altına koymuştum. Çünkü form öyle daha kısaydı. Doğru değildi. Zar zor doğruydu. Reviewer haklı olarak yakaladı.
// PrivacyInfo.xcprivacy — önce (tek bucket, yanlış)
{
"NSPrivacyCollectedDataTypes": [
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypePhoneNumber",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeProductPersonalization"
]
}
]
}// PrivacyInfo.xcprivacy — sonra (üç ayrı disclosure)
{
"NSPrivacyCollectedDataTypes": [
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypePhoneNumber",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAppFunctionality",
"NSPrivacyCollectedDataTypePurposeAnalytics",
"NSPrivacyCollectedDataTypePurposeCustomerSupport"
]
}
]
}Çözüm 2 — bir disclosure değil, üç
Privacy manifest'i aynı veri tipi için üç ayrı amaç bildirecek şekilde yeniden yazdım. App Store Connect'teki App Privacy detaylarını da aynı şekilde güncelledim. Bu kısmı unutmak çok kolay; reviewer iki tarafı yan yana koyup karşılaştırıyor.
Buradan tekrar tekrar dönüp baktığım ders şu: privacy manifest'ler üründen sonra değil, önce yazılmalı. Biz elimizdeki veriyi formun diliyle tarif ettik. Doğrusu, formdan başlayıp ne toplamamıza izin verildiğini sormaktı.
Altıncı gün resubmit. Bir sonraki queue'ya 22:08'de geçti.
Reject 3 — 2.5.4 Background Modes
Sekizinci gün. Reviewer UIBackgroundModes içinde location deklare ettiğimizi ve bunun için "net ve anlamlı bir kullanıcı yararı" göremediğini yazmıştı. Tam cümle: "in-app bildirimler bu olmadan da çalışıyor."
Okudum, projeyi açtım ve reviewer'ın haklı olduğunu gördüm. Background location'ı daha eski bir feature planından — haritada canlı kargo takibi — miras almışız. O özelliği altı hafta önce sessizce push bildirimlerine indirgemiştik ama Info.plist kaydı hiç silinmemişti. Uygulama kullanmadığı bir izin istiyordu.
<!-- Info.plist — önce -->
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
<string>location</string>
</array><!-- Info.plist — sonra -->
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>Çözüm 3 — izni kaldır
location kaydını sildim. Kullanılmayan CLLocationManager başlatma kodunu sildim. Başka ölü permission var mı diye aradım. Bir tane daha buldum: NSCalendarsUsageDescription, hiç çıkarmadığımız bir feature'dan. O da çıktı.
Dokuzuncu gün submit. On birinci gün, yerel saatle 14:46'da temizlendi.
Beklemediğim şey
Apple'ın üçüncü Resolution Center mesajı — yani onay bildirimi — "Sevgili Geliştirici" diye başlıyordu.
Bunu bir insan mı yoksa bir script mi yazdı bilmiyorum. Pek de fark etmiyor. On bir gün boyunca Apple'dan gördüğüm en kibar cümleydi.
Aynı süreçten geçecek birine söyleyeceklerim
Sırasız, birkaç spesifik şey:
- Reviewer'a UI'ın içinden geçen bir yol ver. Anlatımlı ekran kaydı build'i çevirmekten hızlı, her dilde işe yarıyor.
- Privacy manifest'i üründen sonra değil, önce yaz. Üç amacı tek bucket'a sıkıştırmak — sırf form kısa olsun diye — sonunda bir red mailine dönüşen küçük bir yalan.
- Her submission'dan önce Info.plist audit'i çek. Ölü izinler 2.5.4 fırsatı kolluyor. Özellikle background modes; Apple bunları aktif olarak arıyor.
- On bir gün felaket değil. Üç redden ikisi bir günden kısa sürede çevrildi. Asıl darboğaz hep review queue'ydu, fix değildi.
Tahmin edemeyeceğim ters yön şuydu: engel reviewer değildi. Reviewer submission'ımızı, ona verdiğimiz dilde, dikkatle okuyordu. Engel, ürünümüzü inşa ettiğimizden daha az dürüst tarif etmiş olmamızdı. Her red, Apple'ın söylediklerimizle yaptıklarımız arasındaki açığı yakalamasıydı.
On ikinci gün lansman. Ertesi sabah iOS üzerinden onboard olan ilk merchant ana ekranın bir ekran görüntüsünü yolladı; başlığı tek bir kelimeydi: "yayında mıyız?" Cevabı yazmama gerek kalmadı.
// madem buradasın
- 10 dk okuma
Proje ortasında React Native'den Flutter'a — dürüst hesap
Altı hafta, üç TestFlight beta, ödeme yapan altı merchant. Kasım 2024'te çalışan bir React Native uygulamasını Flutter'a yeniden yazdım — neler kırıldı, neye mal oldu, neyi tekrar yapardım.
mobileflutterreact-nativemigration - 8 dk okuma
React Native, native driver ve nihayet hissedebildiğin jank
useNativeDriver sana ne zaman gerçekten bir şey kazandırır, ne zaman yalan söyler ve barkod ekranını yavaş hissettiren o düşen frame'i nasıl bulursun.
react-nativeperformancemobile