Bu swiftui eğitimi, büyük dosyaları uygulama arka plandayken yüklemek için URLSession background transfer, kalıcı iş kaydı, BGProcessingTask ve gözlemlenebilir hata yönetimini uçtan uca ele alır.
SwiftUI Eğitiminde Arka Plan Dosya Yükleme ve BGTask Tasarımı
SwiftUI eğitiminde URLSession background transfer yaşam döngüsü
Bir ios eğitimi veya ios kursu içeriğinde arka plan yüklemeyi yalnızca `Task {}` ile başlatmak yanıltıcıdır: uygulama askıya alındığında bu task çalıştırma zamanı kaybeder. Sistem tarafından ayrı bir süreçte yönetilen `URLSessionConfiguration.background` ise yükleme soketini uygulama belleğinden bağımsız sürdürür. Arka plan oturumunun tanımlayıcısı sabit ve bundle kimliğiyle isim alanına ayrılmış olmalıdır; her açılışta rastgele UUID üretmek, sistemin eski oturumu yeni delegate'e bağlamasını engeller.
import Foundation
final class UploadSessionDelegate: NSObject, URLSessionDelegate {
var finishEvents: (() -> Void)?
func urlSessionDidFinishEvents(
forBackgroundURLSession session: URLSession
) {
DispatchQueue.main.async { [weak self] in
self?.finishEvents?()
self?.finishEvents = nil
}
}
}
final class UploadService {
static let shared = UploadService()
let delegate = UploadSessionDelegate()
lazy var session: URLSession = {
let config = URLSessionConfiguration.background(
withIdentifier: "com.acme.fieldapp.uploads"
)
config.waitsForConnectivity = true
config.sessionSendsLaunchEvents = true
return URLSession(configuration: config, delegate: delegate, delegateQueue: nil)
}()
func enqueue(fileURL: URL, uploadID: UUID) {
var request = URLRequest(
url: URL(string: "https://api.acme.example/uploads/\(uploadID.uuidString)")!
)
request.httpMethod = "PUT"
request.setValue(uploadID.uuidString, forHTTPHeaderField: "Idempotency-Key")
let task = session.uploadTask(with: request, fromFile: fileURL)
task.taskDescription = uploadID.uuidString
task.resume()
}
}Buradaki kritik ayrıntı `uploadTask(with:fromFile:)` seçimidir. Background session, gövdeleri yeniden başlatma sonrası okuyabilmek için dosya URL'si ister; `from: Data` ile bellekte tuttuğunuz 800 MB video, uygulama sonlandırıldığında yeniden üretilemez. Dosyayı `Application Support/UploadStaging` altına atomik olarak taşıyın ve transfer tamamlanmadan silmeyin. SwiftUI giriş noktasında `handleEventsForBackgroundURLSession` completion handler'ını çağırmamak da yaygın bir hatadır: sistem bu completion çağrısını bekler, erken çağırmak ise delegate olayları bitmeden uygulamanın tekrar askıya alınmasına yol açabilir.
import SwiftUI
final class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void
) {
guard identifier == "com.acme.fieldapp.uploads" else {
completionHandler()
return
}
UploadService.shared.delegate.finishEvents = completionHandler
}
}
@main
struct FieldApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup { UploadListView() }
}
}iOS MVVM mimarisi içinde kalıcı yükleme kuyruğu ve idempotency
ios mvvm mimarisi içinde ViewModel'i transferin doğruluk kaynağı yapmayın. `@Observable` veya `@Published` durumları yeniden başlatmada kaybolur; ViewModel yalnızca kalıcı kuyruktaki işi ekrana projekte etmelidir. Üretimde GRDB ile `upload_jobs` tablosunda istemci üretimli UUID, dosya yolu, sunucu durumu, deneme sayısı ve son hata kodunu saklamak; uygulama yeniden açıldığında `URLSession.getAllTasks` sonucu ile bu kaydı uzlaştırmak için yeterli bir taban sağlar.
import GRDB
struct UploadJob: FetchableRecord, PersistableRecord {
var id: String
var filePath: String
var state: String
var attemptCount: Int
var lastHTTPStatus: Int?
}
let migrator = DatabaseMigrator()
migrator.registerMigration("createUploadJobs") { db in
try db.create(table: "upload_jobs") { table in
table.column("id", .text).primaryKey()
table.column("filePath", .text).notNull()
table.column("state", .text).notNull().defaults(to: "queued")
table.column("attemptCount", .integer).notNull().defaults(to: 0)
table.column("lastHTTPStatus", .integer)
}
}
func reconcile(session: URLSession, dbQueue: DatabaseQueue) async throws {
let tasks = await session.allTasks
let activeIDs = Set(tasks.compactMap { $0.taskDescription })
try dbQueue.write { db in
try db.execute(
sql: "UPDATE upload_jobs SET state = 'queued' WHERE state = 'uploading' AND id NOT IN (\(activeIDs.map { _ in "?" }.joined(separator: ",")))",
arguments: StatementArguments(activeIDs)
)
}
}Örnekteki SQL, `activeIDs` boşken `NOT IN ()` üretir ve SQLite sözdizimi hatası verir. Bu edge case'i ayrı ele alın: aktif task yoksa doğrudan `UPDATE upload_jobs SET state = 'queued' WHERE state = 'uploading'` çalıştırın. Daha önemlisi, `taskDescription` tek başına sunucu tarafı doğruluk garantisi değildir. İstemcinin zaman aşımı yaşadığı ama sunucunun nesneyi kaydettiği durumda aynı dosya ikinci kez gidebilir. Bu nedenle `Idempotency-Key` değerini hem kalıcı tabloda hem HTTP başlığında saklayın; sunucu bu anahtarla ilk başarılı yanıtı tekrar döndürmeli veya aynı kaynak URI'sine `PUT` işlemini idempotent yapmalıdır.
Dosyanın staging alanından silinme koşulunu HTTP başarı koduna bağlayın. `didCompleteWithError` içinde `error == nil` olması tek başına yeterli değildir; örneğin sunucu 413 döndürdüğünde taşıma katmanı hatasız tamamlanır ama iş kalıcı olarak reddedilmiştir. Delegate tarafında `URLSessionTaskDelegate.urlSession(_:task:didCompleteWithError:)` ile hata nesnesini, `URLSessionDataDelegate.urlSession(_:dataTask:didReceive:)` ile HTTP durumunu job kaydına yazın; yalnızca API sözleşmenizde başarılı kabul edilen 200, 201 veya 204 sonrasında staging dosyasını silin.
Swift kursu için BGProcessingTask ile uzlaştırma tasarımı
BGProcessingTask, dosya baytlarını taşıyan mekanizma değildir; background URLSession bu işi zaten sistem daemon'u üzerinden yürütür. BGProcessingTask'i yarım kalmış işleri uzlaştırmak, süresi dolmuş imzalı URL'leri yenilemek ve bozulmuş staging kayıtlarını temizlemek için kullanın. Sistem bu görevi `earliestBeginDate` sonrasında uygun gördüğü bir anda çalıştırır, tam zamanlı cron gibi davranmaz. Bu nedenle kullanıcı 'Yükle' düğmesine basınca URLSession task'ını hemen oluşturun, BGProcessingTask'i yalnızca toparlama işi için planlayın.
import BackgroundTasks
final class BackgroundCoordinator {
static let taskID = "com.acme.fieldapp.upload-reconcile"
func register() {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: Self.taskID,
using: nil
) { task in
self.handle(task: task as! BGProcessingTask)
}
}
func scheduleReconcile() {
BGTaskScheduler.shared.getPendingTaskRequests { requests in
guard !requests.contains(where: { $0.identifier == Self.taskID }) else { return }
let request = BGProcessingTaskRequest(identifier: Self.taskID)
request.requiresNetworkConnectivity = true
request.requiresExternalPower = false
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
try? BGTaskScheduler.shared.submit(request)
}
}
private func handle(task: BGProcessingTask) {
let work = Task {
let succeeded = await UploadReconciler.shared.run()
task.setTaskCompleted(success: succeeded)
}
task.expirationHandler = {
work.cancel()
}
}
}Kaydı uygulama başlangıcında, özellikle `application(_:didFinishLaunchingWithOptions:)` içinde yapın; ekran açıldıktan sonra register etmek bazı sistem başlatmalarında geç kalabilir. `Info.plist` içine izinli kimliği eklemeden `submit` çağrısı hata üretir. Xcode eğitimi sırasında bu ayarı yalnızca Signing and Capabilities ekranında aramak yerine plist çıktısını doğrulamak daha güvenlidir.
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.acme.fieldapp.upload-reconcile</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>processing</string>
</array>Expiration handler içinde sadece `Task.cancel()` çağırmak yeterli sayılmaz: `UploadReconciler.run()` döngüsü her veritabanı sayfası ve her token yenileme isteğinden önce `try Task.checkCancellation()` kontrolü yapmalıdır. Buna karşılık sistemin yürüttüğü background URLSession upload task'larını expiration anında körlemesine iptal etmeyin; uzlaştırma görevinin süresinin dolması, transferin geçersiz olduğu anlamına gelmez.
iPhone uygulama geliştirme, loglama ve app store yayınlama kontrolü
iphone uygulama geliştirme sürecinde bu akışı yalnızca Simulator'da doğrulamak yetersizdir. Fiziksel cihazda Network Link Conditioner ile `%100 Loss`, yüksek gecikme ve değişken bant genişliği profilleri uygulayın; ardından uygulamayı arka plana alın, cihazı kilitleyin ve Xcode'dan Debug Memory Graph yerine cihaz loglarını inceleyin. Kullanıcının uygulamayı uygulama değiştiriciden zorla kapatması ayrı bir senaryodur: sistemin arka plan başlatmasına güvenmeyin, sonraki foreground açılışında `getAllTasks` ve veritabanı uzlaştırmasını mutlaka çalıştırın.
Transfer tanısını `os.Logger` ve `URLSessionTaskMetrics` ile iş kimliğine bağlayın. `URLSessionTaskMetrics` içindeki `countOfRequestBodyBytesSent`, TLS el sıkışması ve yeniden yönlendirme sayısı, istemcinin gerçekten kaç bayt gönderdiğini gösterir; yalnızca UI ilerleme yüzdesi bu bilgiyi vermez. Sunucudaki `Idempotency-Key` logu ile aynı UUID'yi eşleştirerek 401 token yenileme döngüsünü, 413 kalıcı reddini ve bağlantı kopmasını birbirinden ayırabilirsiniz.
import OSLog
final class MetricsDelegate: NSObject, URLSessionTaskDelegate {
private let logger = Logger(subsystem: "com.acme.fieldapp", category: "upload")
func urlSession(
_ session: URLSession,
task: URLSessionTask,
didFinishCollecting metrics: URLSessionTaskMetrics
) {
let protocolName = metrics.transactionMetrics.last?.networkProtocolName ?? "unknown"
logger.info(
"upload=\(task.taskDescription ?? "missing", privacy: .public) bytes=\(metrics.countOfRequestBodyBytesSent) redirects=\(metrics.redirectCount) protocol=\(protocolName, privacy: .public)"
)
}
}app store yayınlama öncesinde Background Modes beyanını gerçek kullanımınızla sınırlayın: BGProcessingTask kullanıyorsanız `processing` gerekçesini gönderim notlarında somutlaştırın, fakat sadece arka plan URLSession transferi için gereksiz mod eklemeyin. swift eğitimi ve swift kursu çalışmalarında sık görülen hata, kullanıcı fotoğraf seçtikten sonra geçici dosya URL'sini kuyruğa yazmaktır. `PHPicker` veya `UIDocumentPickerViewController` ile gelen güvenlik kapsamlı URL'yi staging klasörüne kopyalamadan erişim izni sona erebilir; önce `startAccessingSecurityScopedResource()`, sonra koordineli kopyalama, en son `stopAccessingSecurityScopedResource()` uygulayın.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
SwiftUI eğitimi içinde büyük video yüklemek için URLSession background mı kullanmalıyım?
Evet, kullanıcı uygulamayı arka plana aldığında da sürmesi gereken transfer için `URLSessionConfiguration.background(withIdentifier:)` ve `uploadTask(with:fromFile:)` kullanın. Dosyayı Application Support altında saklayın; bellekten verilen `Data` gövdesi yeniden başlatma sonrası güvenilir bir kaynak değildir.
iOS MVVM mimarisi arka plan yükleme durumunu ViewModel'de tutmak doğru mu?
Hayır. ViewModel ekran ömrüne bağlıdır. İş kaydını GRDB, Core Data veya eşdeğer kalıcı depoda tutun; uygulama açıldığında `URLSession.getAllTasks` ile aktif görevleri ve kalıcı `upload_jobs` kayıtlarını uzlaştırın. Sunucu tekrarlarını önlemek için her kayda idempotency anahtarı ekleyin.
Xcode eğitimi sırasında BGProcessingTask neden cihazda hiç çalışmıyor?
Önce `BGTaskSchedulerPermittedIdentifiers` içinde identifier'ın bulunduğunu, register işleminin uygulama başında yapıldığını ve `UIBackgroundModes` altında `processing` değerinin tanımlı olduğunu kontrol edin. Ardından Xcode scheme'inde Debug > Simulate Background Processing Task ile handler akışını tetikleyin; gerçek çalıştırma zamanının sistem tarafından seçildiğini varsayarak testinizi fiziksel cihazda da yapın.
app store yayınlama için arka plan dosya yüklemesinde hangi kanıtları hazırlamalıyım?
İnceleme notlarında transferin kullanıcı tarafından başlatıldığını, dosyanın ne zaman gönderildiğini ve `processing` modunun yalnızca yarım kalan yüklemeleri uzlaştırmak için kullanıldığını yazın. Test hesabı gerekiyorsa yükleme uç noktasına erişen hesap, örnek dosya ve beklenen başarılı HTTP yanıtını ekleyin.
AI / LLM Discovery
Bu makale Opendart Akademi iOS / Swift eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.


