iOS’ta “deep linking” lafı geçtiğinde üç farklı teknolojiden bahsedilebilir: custom URL scheme, Universal Link, App Clip. Her biri farklı bir dönem için tasarlandı, her biri farklı problem’i çözüyor.
Son 10 yılda üçünü de production’da kullandım. Bu yazıda hangi senaryoda hangisinin uygun olduğunu anlatacağım.
Custom URL Scheme (eski, hâlâ kullanım var)
İlk yaklaşım, iOS’un başından beri var. Info.plist’e URL scheme kaydı yaparsın:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>Artık myapp://user/123 şeklinde URL app’i açabiliyor. Basit.
Avantaj:
– Minimum setup
– Server tarafında hiçbir şey gerekmez
– İki app arası iletişim için ideal (X app Y app’i açıyor)
Dezavantaj:
– Güvensiz: Başka bir app aynı scheme’i register edebilir. Bankacılık app’inin scheme’ini malware kullanabilir.
– Unreliable: Scheme conflict olduğunda iOS hangisini çağıracak belirsiz.
– Web linki değil: URL’yi web sayfasında tıklayamıyorsun (Safari “unknown URL” gösteriyor).
– Universal Link’in eski kuzeni: Apple custom URL scheme’lerini deprecate ediyor.
Ne zaman hâlâ kullan:
- İki uygulama arası iletişim (örn. my-main-app ve my-extension-app)
- OAuth redirect (provider → app callback’i)
- Legacy sistemlerden bağlantı
Yeni projede başka kullanma. Universal Link var.
Universal Link (modern, recommended)
iOS 9’da (2015) geldi. Şu an iOS’un “officially recommended” deep link çözümü.
Setup 4 adım:
Adım 1: Web server’da /.well-known/apple-app-site-association dosyası koyuyorsun:
{
"applinks": {
"details": [
{
"appIDs": ["TEAM_ID.com.example.app"],
"components": [
{ "/": "/user/*" },
{ "/": "/product/*" }
]
}
]
}
}JSON formatında, content-type application/json olarak serve edilecek.
Adım 2: Xcode Signing & Capabilities’te Associated Domains entitlement’ı ekliyorsun:
applinks:example.comAdım 3: App delegate veya SwiftUI App lifecycle’ta URL handle ediyorsun:
.onOpenURL { url in
if url.pathComponents.contains("user") {
let userId = url.lastPathComponent
navigateToUser(userId)
}
}Adım 4: Test ediyorsun (yukarıdaki yazıda detayı vardı).
Avantaj:
– Web linki çalışıyor: Safari’de, Mail’de, WhatsApp’ta her yerden
– Secure: ancak senin domain’inle associate edilmiş app’in linkleri yakalayabiliyor
– Fallback: app yüklü değilse web sayfası açılıyor
– SEO friendly: regular URL’ler
Dezavantaj:
– Server setup gerekiyor (AASA dosyası serve)
– Debugging zor (AASA caching)
– Deferred deep linking out-of-box yok
App Clips (özel senaryolar için)
iOS 14’te (2020) geldi. App olmadan app functionality’si.
Universal Link’in özel bir varyasyonu. URL açıldığında app yüklü değilse, App Clip indirilip çalışıyor (full app yerine).
Kullanım senaryosu: kullanıcı fiziksel bir yerden bir işlem yapmak istiyor (ödeme, kiralama). Hiç app’i önceden indirmemiş. App Clip 5 saniyede açılıyor, iş bitiyor.
Ne zaman kullan: App Clips yazımda detaylıca anlattım. Kısaca: offline ödeme, etkinlik giriş, scooter kiralama gibi senaryolar.
Karar ağacı
Yeni bir iOS projesi deep linking için:
- Web sayfasından app’e link gelecek mi? → Universal Link
- OAuth callback veya benzeri tek-seferlik redirect mi? → Custom URL scheme
- Fiziksel interaction (NFC, QR) + tek seferlik işlem mi? → App Clip
- Native-to-native (başka app’im) iletişim mi? → Custom URL scheme
- Standart kullanıcı deneyimi linkle engagement? → Universal Link
Default: Universal Link. Özel duruma göre diğerleri.
Link structure tasarımı
Deep link URL’lerinizi nasıl yapılandıracağız? Kural:
Good: https://example.com/user/123
– Web’de de anlamlı (user profile sayfası)
– App’te de parse edilebilir
– Versiyonlama yapılırsa backward compatible kalabilir
Bad: https://example.com/?action=openUser&id=123
– Web’de deploy edildiğinde path parameter yok
– Query param’lar kısa ve semantic değil
– Analytics’te tracking zor
URL path hierarchy:
/user/123 # User profile
/user/123/posts # User'ın post'ları
/user/123/posts/456 # Specific post
/product/xyz # Product
/product/xyz/reviews # Reviews
/checkout/cart # Cart
/checkout/payment # Payment flowBu hierarchy hem web sitesinde hem app’te aynı yapıda. Kod’da path parsing standardize ediliyor.
Parameter parsing
Deep link’te query parameter’lar ek context verebilir:
https://example.com/product/xyz?ref=email_campaign&variant=bApp tarafında parse:
func handle(_ url: URL) {
let components = URLComponents(url: url, resolvingAgainstBaseURL: true)
let queryItems = components?.queryItems ?? []
var ref: String?
var variant: String?
for item in queryItems {
switch item.name {
case "ref": ref = item.value
case "variant": variant = item.value
default: break
}
}
// Attribution tracking
if let ref = ref {
analytics.track("deep_link_arrival", properties: ["ref": ref])
}
}Bu sayede marketing campaign’leri attribute edebiliyorsun.
Testing strategy
Development environment:
– Simulator veya physical device
– Universal Link developer mode (query string fark ediliyor)
– AASA file doğru serve ediliyor mu (curl ile check)
– Entitlements doğru provisioning profile’da mı
Staging environment:
– Gerçek domain’den AASA serve
– TestFlight build ile test
– Real network (not localhost proxy)
Production verification:
– Smoke test: çeşitli yerlerden (Mail, Safari, Notes, WhatsApp) link tıkla
– Analytics: deep link arrival event’leri geliyor mu?
– Fallback: app uninstall edilmişse web sayfası açılıyor mu?
Migration: URL scheme → Universal Link
Eski bir app’te custom URL scheme varsa Universal Link’e geçiş:
- Coexist: İki sistem birlikte çalışabilir. Eski URL scheme handle’ı kalabilir, yeni Universal Link handle’ı eklenebilir.
- Gradual migration: Email template’leri, push notification payload’ları yavaş yavaş Universal Link URL’leri kullanmaya başlar.
- Sunset: 6-12 ay sonra URL scheme’i deprecate et. Eski URL’lere gelen user’lara “update your app” mesajı göster.
Bu geçiş 2-3 sprint alır ama güvenlik ve UX açısından önemli.
Sonuç
Deep linking iOS geliştirmenin göründüğünden daha büyük bir konusu. Doğru teknoloji seçimi (Universal Link default, URL scheme niche), doğru URL structure, doğru testing.
Yeni projede Universal Link ile başla. Mevcut projeler için migration planla. App Clip’i sadece gerçek use case’de kullan. Custom URL scheme legacy veya OAuth için sakla.