WidgetKit iOS 17+: App Intents ile Etkileşimli Widget'lar
WidgetKit ve App Intents ile etkileşimli iOS widget'ları oluşturmak için kapsamlı kılavuz. iOS 17+ için 2026'da düğmeler, anahtarlar, animasyonlar ve en iyi uygulamalar.

iOS 17, native etkileşim sunarak WidgetKit'te devrim yarattı. Widget'lar artık statik gösterimler değil: artık uygulamayı açmadan ana ekrandan doğrudan kullanıcı eylemlerine yanıt verebiliyorlar. Bu önemli evrim, App Intents framework'üne dayanıyor ve akıcı, modern bir kullanıcı deneyimi sunuyor.
Bu makale, proje yapılandırmasından animasyonlar ve durum yönetimi ile gelişmiş kalıplara kadar iOS 17+ etkileşimli widget'ların eksiksiz oluşturulmasını sunmaktadır.
Etkileşimli Widget Mimarisi
iOS 17+ widget'larının etkileşimi App Intents framework'ü aracılığıyla çalışır. Uygulamayı açacak geleneksel deep link'lerin aksine, App Intents kodun doğrudan widget'tan yürütülmesine izin verir, ardından görüntüyü otomatik olarak yeni verilerle yeniler.
import WidgetKit
import SwiftUI
import AppIntents
// Mimari üç ana bileşene dayanır:
// 1. Widget Timeline Provider - verileri sağlar
// 2. Widget View - Button/Toggle ile arayüzü gösterir
// 3. App Intent - dokunduğunda eylemi yürütür
struct TaskWidget: Widget {
// Benzersiz widget tanımlayıcısı
let kind: String = "TaskWidget"
var body: some WidgetConfiguration {
// Parametresiz widget'lar için StaticConfiguration
StaticConfiguration(
kind: kind,
provider: TaskTimelineProvider()
) { entry in
TaskWidgetView(entry: entry)
// App Intents için zorunlu
.containerBackground(.fill.tertiary, for: .widget)
}
.configurationDisplayName("Görevler")
.description("Ana ekrandan görevlerinizi yönetin.")
.supportedFamilies([.systemSmall, .systemMedium])
}
}Widget yapılandırmasını bildirir ve verileri sağlayacak provider'ı belirtir. .containerBackground özelliği, etkileşimli widget'lar için iOS 17'den beri zorunludur.
Timeline Provider Oluşturma
Timeline Provider, widget'ın ne zaman ve nasıl yenileneceğini belirler. Etkileşimli widget'lar için App Intents tarafından tetiklenen değişikliklere de tepki vermelidir.
import WidgetKit
import SwiftUI
// Belirli bir andaki widget durumunu temsil eden Entry
struct TaskEntry: TimelineEntry {
let date: Date
let tasks: [Task]
// Görsel geri bildirim için yükleme durumu
var isLoading: Bool = false
}
// Uygulama ve widget arasında paylaşılan veri modeli
struct Task: Identifiable, Codable {
let id: UUID
var title: String
var isCompleted: Bool
var priority: Priority
enum Priority: String, Codable {
case low, medium, high
}
}
struct TaskTimelineProvider: TimelineProvider {
// İlk yükleme sırasında gösterilen placeholder
func placeholder(in context: Context) -> TaskEntry {
TaskEntry(
date: Date(),
tasks: [
Task(id: UUID(), title: "Örnek görev", isCompleted: false, priority: .medium)
]
)
}
// Widget galerisi için snapshot
func getSnapshot(in context: Context, completion: @escaping (TaskEntry) -> Void) {
let entry = TaskEntry(
date: Date(),
tasks: TaskDataManager.shared.fetchTasks().prefix(3).map { $0 }
)
completion(entry)
}
// Yenileme politikası ile tam timeline
func getTimeline(in context: Context, completion: @escaping (Timeline<TaskEntry>) -> Void) {
let tasks = TaskDataManager.shared.fetchTasks()
let entry = TaskEntry(date: Date(), tasks: Array(tasks.prefix(3)))
// 15 dakika sonra veya kullanıcı eyleminden sonra yenileme
let nextUpdate = Calendar.current.date(
byAdding: .minute,
value: 15,
to: Date()
) ?? Date()
let timeline = Timeline(
entries: [entry],
policy: .after(nextUpdate)
)
completion(timeline)
}
}Provider, verilere erişmek için paylaşılan bir TaskDataManager kullanır. Bu yaklaşım, ana uygulama ve widget arasında senkronizasyonu garanti eder.
Uygulama ve widget arasında veri paylaşmak için, proje capabilities'inde bir App Group yapılandırmak gerekir. UserDefaults veya dosyalar bu paylaşılan grubu kullanmalıdır.
Paylaşılan Veri Yöneticisi
Uygulama ve widget arasında veri paylaşımı, App Group aracılığıyla erişilebilen ortak bir konteyner gerektirir.
import Foundation
final class TaskDataManager {
// Global erişim için Singleton
static let shared = TaskDataManager()
// Xcode'da yapılandırılmış App Group tanımlayıcısı
private let appGroupID = "group.com.example.taskapp"
// Uygulama ve widget arasında paylaşılan UserDefaults
private var sharedDefaults: UserDefaults? {
UserDefaults(suiteName: appGroupID)
}
private let tasksKey = "tasks"
private init() {}
// Görevleri paylaşılan depolamadan alır
func fetchTasks() -> [Task] {
guard let data = sharedDefaults?.data(forKey: tasksKey),
let tasks = try? JSONDecoder().decode([Task].self, from: data) else {
return []
}
return tasks
}
// Widget bildirimi ile kaydeder
func saveTasks(_ tasks: [Task]) {
guard let data = try? JSONEncoder().encode(tasks) else { return }
sharedDefaults?.set(data, forKey: tasksKey)
}
// Belirli bir görevi günceller
func updateTask(_ task: Task) {
var tasks = fetchTasks()
if let index = tasks.firstIndex(where: { $0.id == task.id }) {
tasks[index] = task
saveTasks(tasks)
}
}
// Tamamlanma durumunu değiştirir
func toggleTaskCompletion(taskID: UUID) {
var tasks = fetchTasks()
if let index = tasks.firstIndex(where: { $0.id == taskID }) {
tasks[index].isCompleted.toggle()
saveTasks(tasks)
}
}
}Bu yönetici tüm kalıcılık mantığını kapsar ve hem uygulama hem de widget App Intents tarafından kullanılacaktır.
Etkileşim için App Intent Oluşturma
App Intent, kullanıcı widget ile etkileşime girdiğinde yürütülen eylemi tanımlar. iOS bu eylemi arka planda yürütür ve ardından widget'ı otomatik olarak yeniler.
import AppIntents
import WidgetKit
// Görev durumunu değiştirme intent'i
struct ToggleTaskIntent: AppIntent {
// Siri kısayollarında gösterilen başlık
static var title: LocalizedStringResource = "Görev durumunu değiştir"
// Erişilebilirlik için açıklama
static var description = IntentDescription("Bir görevi tamamlandı veya tamamlanmadı olarak işaretler.")
// Parametre: değiştirilecek görevin ID'si
@Parameter(title: "Görev ID'si")
var taskID: String
// AppIntent için gerekli initializer
init() {}
// View'dan oluşturma için parametreli initializer
init(taskID: UUID) {
self.taskID = taskID.uuidString
}
// Eylem yürütme
func perform() async throws -> some IntentResult {
// String ID'nin UUID'ye dönüşümü
guard let uuid = UUID(uuidString: taskID) else {
return .result()
}
// Görevin güncellenmesi
TaskDataManager.shared.toggleTaskCompletion(taskID: uuid)
// Widget yenilemesi ister
WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget")
return .result()
}
}WidgetCenter.shared.reloadTimelines çağrısı eylemden sonra widget'ı anında yeniler ve anlık görsel geri bildirim sağlar.
iOS mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Etkileşimli Düğmelerle Widget Görünümü
Widget görünümü, eylem olarak intent ile standart SwiftUI Button bileşenini kullanır. iOS 17+, App Intent'i yürütmek için bu etkileşimleri otomatik olarak yakalar.
import SwiftUI
import WidgetKit
struct TaskWidgetView: View {
let entry: TaskEntry
// Widget boyutuna uyum
@Environment(\.widgetFamily) var family
var body: some View {
VStack(alignment: .leading, spacing: 8) {
// Başlık ve sayaç ile header
headerView
// Etkileşimli düğmelerle görev listesi
ForEach(entry.tasks.prefix(tasksLimit)) { task in
TaskRowView(task: task)
}
Spacer(minLength: 0)
}
.padding()
}
// Boyuta göre görev sayısı
private var tasksLimit: Int {
switch family {
case .systemSmall: return 2
case .systemMedium: return 3
default: return 4
}
}
private var headerView: some View {
HStack {
Text("Görevler")
.font(.headline)
.fontWeight(.bold)
Spacer()
// Kalan görev sayısı ile rozet
let remaining = entry.tasks.filter { !$0.isCompleted }.count
Text("\(remaining)")
.font(.caption.bold())
.foregroundStyle(.white)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(remaining > 0 ? Color.orange : Color.green)
.clipShape(Capsule())
}
}
}
struct TaskRowView: View {
let task: Task
var body: some View {
// Eylem olarak App Intent ile düğme
Button(intent: ToggleTaskIntent(taskID: task.id)) {
HStack(spacing: 12) {
// Tamamlanma göstergesi
Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
.font(.title3)
.foregroundStyle(task.isCompleted ? .green : .secondary)
// Görev başlığı
Text(task.title)
.font(.subheadline)
.strikethrough(task.isCompleted)
.foregroundStyle(task.isCompleted ? .secondary : .primary)
.lineLimit(1)
Spacer()
// Öncelik göstergesi
priorityIndicator
}
.padding(.vertical, 6)
.padding(.horizontal, 10)
.background(Color(.systemBackground).opacity(0.5))
.cornerRadius(8)
}
.buttonStyle(.plain)
}
@ViewBuilder
private var priorityIndicator: some View {
switch task.priority {
case .high:
Image(systemName: "exclamationmark.circle.fill")
.foregroundStyle(.red)
case .medium:
Image(systemName: "minus.circle.fill")
.foregroundStyle(.orange)
case .low:
EmptyView()
}
}
}Button(intent:) sözdizimi düğmeyi doğrudan App Intent'e bağlar. Dokunulduğunda iOS perform() yürütür ve ardından widget'ı otomatik olarak yeniler.
Widget'lar için Etkileşimli Toggle
Açık/kapalı türü eylemler için, Toggle bileşeni native iOS tarzıyla düğmeye alternatif sunar.
import SwiftUI
import AppIntents
// Açık durumlu Toggle için özel intent
struct SetTaskCompletionIntent: AppIntent {
static var title: LocalizedStringResource = "Görev durumunu ayarla"
@Parameter(title: "Görev ID'si")
var taskID: String
// Hedef durum: true = tamamlandı, false = tamamlanmadı
@Parameter(title: "Tamamlandı")
var isCompleted: Bool
init() {}
init(taskID: UUID, isCompleted: Bool) {
self.taskID = taskID.uuidString
self.isCompleted = isCompleted
}
func perform() async throws -> some IntentResult {
guard let uuid = UUID(uuidString: taskID) else {
return .result()
}
var tasks = TaskDataManager.shared.fetchTasks()
if let index = tasks.firstIndex(where: { $0.id == uuid }) {
// Durumu açıkça ayarlar (toggle değil)
tasks[index].isCompleted = isCompleted
TaskDataManager.shared.saveTasks(tasks)
}
WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget")
return .result()
}
}
struct TaskToggleRowView: View {
let task: Task
var body: some View {
HStack {
Text(task.title)
.font(.subheadline)
.strikethrough(task.isCompleted)
Spacer()
// Intent ile etkileşimli toggle
Toggle(
isOn: task.isCompleted,
intent: SetTaskCompletionIntent(
taskID: task.id,
isCompleted: !task.isCompleted
)
)
.toggleStyle(.switch)
.labelsHidden()
}
.padding(.vertical, 4)
}
}Toggle, ikili durumlar için daha sezgisel bir etkileşim sağlar ve iOS tasarımlarına doğal olarak entegre olur.
Widget'lar uyarılar, sheet veya gezinti gösteremez. Tüm eylemler bağımsız olmalı ve görünür durumu doğrudan güncellemelidir.
Yenileme Animasyonları ve Geçişler
iOS 17+, eylemden sonra widget yenileme sırasında geçişlerin animasyonuna izin verir. .contentTransition modifier'ı bu animasyonları kontrol eder.
import SwiftUI
import WidgetKit
struct AnimatedTaskRowView: View {
let task: Task
var body: some View {
Button(intent: ToggleTaskIntent(taskID: task.id)) {
HStack(spacing: 12) {
// Geçiş animasyonlu ikon
Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
.font(.title3)
.foregroundStyle(task.isCompleted ? .green : .secondary)
// Değişimde ikon animasyonu
.contentTransition(.symbolEffect(.replace))
Text(task.title)
.font(.subheadline)
.strikethrough(task.isCompleted)
.foregroundStyle(task.isCompleted ? .secondary : .primary)
// Metin animasyonu
.contentTransition(.opacity)
Spacer()
}
.padding(.vertical, 6)
.padding(.horizontal, 10)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(task.isCompleted ? Color.green.opacity(0.1) : Color.clear)
)
// Arka plan animasyonu
.animation(.easeInOut(duration: 0.3), value: task.isCompleted)
}
.buttonStyle(.plain)
}
}
// Animasyonlu invalidasyon ile widget
struct AnimatedTaskWidget: Widget {
let kind: String = "AnimatedTaskWidget"
var body: some WidgetConfiguration {
StaticConfiguration(
kind: kind,
provider: TaskTimelineProvider()
) { entry in
AnimatedTaskWidgetView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
}
.configurationDisplayName("Animasyonlu Görevler")
.description("Akıcı animasyonlu widget'lar.")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
// İçerik animasyonlarının etkinleştirilmesi
.contentMarginsDisabled()
}
}
struct AnimatedTaskWidgetView: View {
let entry: TaskEntry
var body: some View {
VStack(alignment: .leading, spacing: 8) {
headerView
ForEach(entry.tasks) { task in
AnimatedTaskRowView(task: task)
}
Spacer(minLength: 0)
}
.padding()
}
private var headerView: some View {
HStack {
Text("Görevler")
.font(.headline.bold())
Spacer()
let completed = entry.tasks.filter(\.isCompleted).count
let total = entry.tasks.count
// Animasyonlu ilerleme
Text("\(completed)/\(total)")
.font(.caption.bold())
.foregroundStyle(.secondary)
.contentTransition(.numericText())
}
}
}.symbolEffect(.replace) ve .numericText() animasyonları durumlar arasında akıcı geçişler oluşturur ve kullanıcı deneyimini önemli ölçüde iyileştirir.
AppIntentConfiguration ile Yapılandırılabilir Widget
Kullanıcı tarafından özelleştirilebilir widget'lar için (filtreler, kategoriler), AppIntentConfiguration StaticConfiguration'ın yerini alır.
import WidgetKit
import SwiftUI
import AppIntents
// Kullanıcıya açık yapılandırma
struct TaskWidgetConfigurationIntent: WidgetConfigurationIntent {
static var title: LocalizedStringResource = "Görev yapılandırması"
static var description = IntentDescription("Görev görüntüsünü özelleştirin.")
// Önceliğe göre filtre
@Parameter(title: "Öncelik", default: .all)
var priorityFilter: PriorityFilter
// Tamamlanan görevleri göster
@Parameter(title: "Tamamlananları göster", default: true)
var showCompleted: Bool
// Maksimum görev sayısı
@Parameter(title: "Görev sayısı", default: 3)
var maxTasks: Int
}
// Öncelik filtresi için enum
enum PriorityFilter: String, AppEnum {
case all
case high
case medium
case low
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Öncelik"
static var caseDisplayRepresentations: [PriorityFilter: DisplayRepresentation] = [
.all: "Tümü",
.high: "Yüksek",
.medium: "Orta",
.low: "Düşük"
]
}
// Yapılandırmaya uyarlanmış provider
struct ConfigurableTaskProvider: AppIntentTimelineProvider {
typealias Entry = TaskEntry
typealias Intent = TaskWidgetConfigurationIntent
func placeholder(in context: Context) -> TaskEntry {
TaskEntry(date: Date(), tasks: [])
}
func snapshot(for configuration: TaskWidgetConfigurationIntent, in context: Context) async -> TaskEntry {
let tasks = filteredTasks(for: configuration)
return TaskEntry(date: Date(), tasks: tasks)
}
func timeline(for configuration: TaskWidgetConfigurationIntent, in context: Context) async -> Timeline<TaskEntry> {
let tasks = filteredTasks(for: configuration)
let entry = TaskEntry(date: Date(), tasks: tasks)
let nextUpdate = Date().addingTimeInterval(15 * 60)
return Timeline(entries: [entry], policy: .after(nextUpdate))
}
// Yapılandırma filtrelerini uygular
private func filteredTasks(for config: TaskWidgetConfigurationIntent) -> [Task] {
var tasks = TaskDataManager.shared.fetchTasks()
// Önceliğe göre filtreleme
if config.priorityFilter != .all {
let priority = Task.Priority(rawValue: config.priorityFilter.rawValue) ?? .medium
tasks = tasks.filter { $0.priority == priority }
}
// Gerekirse tamamlananları filtreleme
if !config.showCompleted {
tasks = tasks.filter { !$0.isCompleted }
}
// Sayı sınırlaması
return Array(tasks.prefix(config.maxTasks))
}
}
// Kullanıcı yapılandırmalı widget
struct ConfigurableTaskWidget: Widget {
let kind: String = "ConfigurableTaskWidget"
var body: some WidgetConfiguration {
// Yapılandırılabilir widget'lar için AppIntentConfiguration
AppIntentConfiguration(
kind: kind,
intent: TaskWidgetConfigurationIntent.self,
provider: ConfigurableTaskProvider()
) { entry in
TaskWidgetView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
}
.configurationDisplayName("Özelleştirilmiş Görevler")
.description("Görevlerinizi filtreleyin ve özelleştirin.")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
}
}Kullanıcı artık uzun basma ile widget'ı yapılandırabilir ve uygulamada ek kod olmadan kişiselleştirilmiş bir deneyim elde edebilir.
Hata Yönetimi ve Yükleme Durumları
İyi bir UX, etkileşimler sırasında hata durumlarını ve ara durumları yönetmeyi gerektirir.
import AppIntents
import WidgetKit
struct ToggleTaskWithFeedbackIntent: AppIntent {
static var title: LocalizedStringResource = "Görevi feedback ile değiştir"
@Parameter(title: "Görev ID'si")
var taskID: String
init() {}
init(taskID: UUID) {
self.taskID = taskID.uuidString
}
func perform() async throws -> some IntentResult & ReturnsValue<Bool> {
guard let uuid = UUID(uuidString: taskID) else {
// Sessiz hata döndürür
return .result(value: false)
}
// Async işlem simülasyonu (örn. sunucu sync)
do {
try await Task.sleep(for: .milliseconds(100))
TaskDataManager.shared.toggleTaskCompletion(taskID: uuid)
WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget")
return .result(value: true)
} catch {
// Hata: widget'ı güncelleme
return .result(value: false)
}
}
}
// Yükleme durumlu görünüm
struct TaskRowWithLoadingView: View {
let task: Task
@State private var isLoading = false
var body: some View {
Button(intent: ToggleTaskIntent(taskID: task.id)) {
HStack(spacing: 12) {
// Koşullu gösterge
Group {
if isLoading {
ProgressView()
.scaleEffect(0.8)
} else {
Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
.foregroundStyle(task.isCompleted ? .green : .secondary)
}
}
.frame(width: 24, height: 24)
Text(task.title)
.font(.subheadline)
.strikethrough(task.isCompleted)
Spacer()
}
.padding(.vertical, 6)
.padding(.horizontal, 10)
.background(Color(.systemBackground).opacity(0.5))
.cornerRadius(8)
}
.buttonStyle(.plain)
.disabled(isLoading)
.opacity(isLoading ? 0.6 : 1.0)
}
}Anında görsel geri bildirim (azaltılmış opaklık, yükleme göstergesi) kullanıcıya eyleminin kaydedildiğini bildirir.
En İyi Uygulamalar ve Optimizasyonlar
Birkaç kalıp performanslı ve güvenilir etkileşimli widget'lar sağlar.
import WidgetKit
import SwiftUI
// 1. Modifikasyondan sonra her zaman önbelleği geçersiz kıl
final class WidgetRefreshManager {
static func refreshAllWidgets() {
// Tüm uygulama widget'larının yenilenmesi
WidgetCenter.shared.reloadAllTimelines()
}
static func refreshWidget(kind: String) {
// Belirli bir widget'ın yenilenmesi
WidgetCenter.shared.reloadTimelines(ofKind: kind)
}
// Veri modifikasyonundan sonra uygulamadan çağrı
static func notifyDataChanged() {
Task { @MainActor in
refreshAllWidgets()
}
}
}
// 2. Görünüm karmaşıklığını sınırla
struct OptimizedWidgetView: View {
let entry: TaskEntry
var body: some View {
// GeometryReader olmadan basit görünümleri tercih edin
VStack(alignment: .leading, spacing: 8) {
ForEach(entry.tasks.prefix(3)) { task in
// Hafif bileşenler
minimalTaskRow(task)
}
}
.padding()
}
// Performans için minimal görünüm
@ViewBuilder
private func minimalTaskRow(_ task: Task) -> some View {
Button(intent: ToggleTaskIntent(taskID: task.id)) {
HStack {
Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
Text(task.title)
.lineLimit(1)
}
}
.buttonStyle(.plain)
}
}
// 3. Basit durumlar için @AppStorage kullan
struct QuickSettingsWidgetView: View {
// Paylaşılan UserDefaults'a doğrudan erişim
@AppStorage("showCompletedTasks", store: UserDefaults(suiteName: "group.com.example.app"))
private var showCompleted = true
var body: some View {
// Durum yenilemeler arasında kalıcıdır
Text(showCompleted ? "Tümünü gösteriyor" : "Tamamlananları gizliyor")
}
}
// 4. Veriyi provider'da önceden hesapla
struct OptimizedTaskEntry: TimelineEntry {
let date: Date
let tasks: [Task]
// Önceden hesaplanmış veri
let completedCount: Int
let pendingCount: Int
let highPriorityCount: Int
init(date: Date, tasks: [Task]) {
self.date = date
self.tasks = tasks
// Hesaplamalar bir kez yapılır
self.completedCount = tasks.filter(\.isCompleted).count
self.pendingCount = tasks.filter { !$0.isCompleted }.count
self.highPriorityCount = tasks.filter { $0.priority == .high && !$0.isCompleted }.count
}
}Bu optimizasyonlar bataryayı aşırı tüketmeyen tepkili widget'lar sağlar.
Debug için Xcode'daki widget şemasını kullanın. Canvas önizleme cihaza yükleme olmadan farklı boyutları ve durumları test etmenizi sağlar.
Sonuç
App Intents ile WidgetKit iOS 17+, widget'ları iOS uygulamalarının gerçek etkileşimli uzantılarına dönüştürür. Bu deklaratif mimari, native ve akıcı bir kullanıcı deneyimi sunarken geliştirmeyi önemli ölçüde basitleştirir.
iOS 17+ Etkileşimli Widget Kontrol Listesi
- ✅ Veri paylaşımı için App Group yapılandırın
- ✅ Uygun yenileme ile Timeline Provider oluşturun
- ✅ Her eylem için App Intents uygulayın
- ✅ Etkileşim için
Button(intent:)veyaToggle(intent:)kullanın - ✅ Modifikasyondan sonra
WidgetCenter.shared.reloadTimelinesçağırın - ✅ iOS 17+ için zorunlu
.containerBackgroundekleyin - ✅ Akıcı geçiş animasyonları uygulayın
- ✅ Yükleme ve hata durumlarını yönetin
- ✅ Görünümleri batarya performansı için optimize edin
- ✅ Tüm desteklenen widget boyutlarında test edin
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Etiketler
Paylaş
İlgili makaleler

App Intents ve Siri Shortcuts: gelişmiş iOS otomasyonu 2026
iOS 18+ için App Intents ve Siri Shortcuts hakkında eksiksiz rehber. Özel Siri eylemleri oluşturma, Apple Intelligence entegrasyonu ve 2026'da Swift uygulamasını otomatikleştirme.

Swift'te Combine vs async/await: Aşamalı Geçiş Desenleri
Swift'te Combine'dan async/await'e geçiş için kapsamlı kılavuz: aşamalı stratejiler, köprüleme desenleri ve iOS kod tabanlarında paradigma birlikte yaşamı.

2026'da iOS Erişilebilirlik Mülakat Soruları: VoiceOver ve Dynamic Type
iOS mülakatlarına hazırlık için kritik erişilebilirlik soruları: VoiceOver, Dynamic Type, semantik trait'ler ve denetimler.