Tüm Yazılar
KategoriSwiftUI
Okuma Süresi
8 dk
Yayın Tarihi
2026-10-15
Kelime Sayısı
1.282kelime

Kahveni hazırla - bu içerikli bir makale!

SwiftUI Components 2.1: hazır view'lar ve güvenilmeyen desenlere süre sınırı

Özet

SwiftUI-Components view'ları nasıl sade tutuyor, mantığı test edilen value type'lara nasıl taşıyor ve JSON formdaki regex'leri nasıl sınırlıyor.

  • Bu yazı paketin çözdüğü sorunu, arkasındaki tasarım kararlarını, commit geçmişinden bir hatayı, kullanımını ve yapmadığı şeyleri anlatıyor.
  • Bileşenler durumlarını @State ve @Binding ile tutuyor.
  • Paketi Xcode'da File › Add Package Dependencies… ile https://github.com/muhittincamdali/SwiftUI-Components.git adresini girerek ya da Package.swift içinde from: "2.1.0" ile ekleyin.
  • README'deki yol haritası sonraki adımları sıralıyor: Swift Package Index (.spi.yml repoda hazır), form alanlarından başlayarak tek tek view'lara test ve FormSchema'da koşullu alanlar.
SwiftUI Components 2.1: hazır view'lar ve güvenilmeyen desenlere süre sınırı

SwiftUI-Components, iOS 17 ve macOS 14 için hazır SwiftUI view'larından oluşan bir Swift

paketi: buton, kart, grafik, doğrulamalı form alanları, gesture'lar, listeler, overlay'ler,

yükleme göstergeleri ve toast'lar. 2.1.0 sürümü 14 Ekim 2026'da yayınlandı. Bu yazı paketin

çözdüğü sorunu, arkasındaki tasarım kararlarını, commit geçmişinden bir hatayı, kullanımını ve

yapmadığı şeyleri anlatıyor.

İçindekiler


Sorun

Çoğu uygulama aynı birkaç view'a ihtiyaç duyar: yükleme durumu gösteren bir buton, bir kart,

küçük bir grafik, e-posta adresini kontrol eden bir alan, bir toast. Bunları yazmak zor değil,

ama ayrıntılar zaman alıyor ve hatalar yerleşimde değil mantıkta çıkıyor: beklenmedik bir

karakterde çöken kart numarası alanı, bir önceki toast'ın zamanlayıcısı yüzünden erken kapanan

toast, adımı sıfır olduğunda çöken slider.

Paket, bir bileşeni sıfırdan yazmak yerine bugün çalışan bir tane isteyen geliştiriciler için.

Her bileşen, initializer ile yapılandırılan düz bir SwiftUI View. Benimsenecek bir tema sistemi

ya da izlenecek bir mimari yok; bir iki view alıp gerisini görmezden gelebilirsiniz. Paketin

hiçbir bağımlılığı yok.

Paketin geçmişi kısa ve sürüm numaralarında görünüyor. Repo Şubat 2026'da açıldı ve 1.0.0 ilk

commit'e etiketlendi. Haziran 2026'da bileşenler kaldırılıp yerine placeholder kod kondu (commit

c7b3799). 14 Ekim 2026'da 2.0.0 olarak geri getirildiler: iOS ve macOS için Swift 6 dil

modunda derlenir hâle geldiler ve yeni testlerin bulduğu hatalar düzeltildi. Birkaç saat sonra

2.1.0; form doğrulama, JSON'dan form, gesture'lar ve kontrast kontrolü ekledi. Bunlar yazarın üç

eski paketinden (SwiftUI-Forms-Kit, SwiftUI-Gesture-Library ve iOSUIComponents) yeniden yazıldı,

o üç repo da arşivlendi. README ve CHANGELOG bu geçmişi aynı şekilde anlatıyor.

Tasarım kararları ve alternatifler

Sade view'lar, mantık value type'larda. Bileşenler durumlarını @State ve @Binding ile

tutuyor. Public referans tipler yalnızca SnackBarManager, BannerManager, PaginatedListViewModel

ve AsyncValidation; hepsi main actor'da. Ekran gerektirmeyen kararlar value type'larda ya da

statik fonksiyonlarda duruyor: Validator, FormSchema, StepFlow, SwipeDirection,

SwipeSequenceMatcher, TransformState ve UnistrokeRecognizer; ayrıca

CreditCardInput.isLuhnValid(_:) ve FlowLayout.arrange(sizes:maxWidth:spacing:) gibi

yardımcılar. Alternatif, view'ları bir view inceleme kütüphanesiyle doğrudan test etmekti. Bu,

test target'ına bağımlılık eklerdi; statik bir fonksiyonu test etmek ise hem daha hızlı hem daha

basit. Bedeli README'de yazıyor: view'ların çoğunun kendi testi yok.

Platform farkları büyük ölçüde tek dosyada. Extensions/Platform.swift, UIKit'e özel

API'lerin neredeyse tamamını (sistem renkleri, haptics, klavye tipi, büyük harf ayarı, wheel

picker'lar) AppKit karşılığına ya da no-op'a eşliyor. Her bileşene #if os(...) serpmek de

çalışırdı, ama her dosyayı okumayı zorlaştırırdı. Bu ara katman sayesinde

TextInputField(keyboardType:), UIKeyboardType yerine paketin kendi KeyboardKind enum'unu

alıyor. İki istisna var. TestimonialCarousel'ın kendi #if os(iOS) dalı var: macOS'ta

sayfalı TabView olmadığı için orada yorum kartları tek tek gösteriliyor. cornerRadius(_:corners:)

ise UIKit tipi (UIRectCorner) alan tek public API; bu yüzden yalnızca iOS'ta var.

Yalnızca derlenen platformu iddia etmek. 2.0.0 manifest'i tvOS, watchOS ve visionOS'u

çıkardı, çünkü paket bu platformlar için hiç derlenmemişti. swift-tools-version da CI'ın

gerçekten kullandığı araç zinciri olan 6.2'ye çıktı. Minimumu iOS 17 ve macOS 14'e yükseltmek,

geri getirilen kodun onChange(of:initial:_:) API'sini #available dalları olmadan

kullanmasını sağladı. Bu kırıcı bir değişiklikti; o sürümün 2.0.0 olmasının nedeni bu.

Snapshot dosyaları yerine render kontrolleri. RenderTests ve GaugeNeedleTests view'ları

ImageRenderer ile render ediyor. Galeri sayfaları açık ve koyu temada çiziliyor; bu, çöken ya

da sıfır boyuta düşen view'ları yakalıyor. Gösterge testi ise ibrenin nereyi gösterdiğini

pikselleri okuyarak kontrol ediyor. Piksel piksel karşılaştıran bir snapshot kütüphanesi

bağımlılık eklerdi ve OS sürümleri arasındaki anti-aliasing farklarında kırılırdı. Ödünleşim şu:

bu kontroller renk ya da yerleşim kaymasını yakalamıyor.

Derlenen örnekler. README'deki kod örnekleri

Examples/ComponentGallery/ComponentGallery/ReadmeSamples.swift dosyasından kopyalanıyor. Bu

dosyayı örnek uygulama derliyor; test target'ı da bir symlink üzerinden derliyor ve bir render

testi onu çiziyor. Bir örnek API'den saparsa build kırılıyor.

Gerçek bir hata: motoru sınırlamak yerine deseni taramak

FormSchema JSON'dan form kuruyor ve bu JSON genelde bir sunucudan geliyor. Bir alan pattern

taşıyabiliyor; yani desen metni güvenilmeyen girdi. Geri izleme (backtracking) yapan bir regex

motoru, (a|a)+ gibi desenlerde üstel süre harcayabiliyor. JSON formları pakete getiren commit

(697c48f), iptal edilemeyen Swift Regex için ölçümü kaydediyor: (a|a)+, 24 "a" ve ardından

gelen "!" üzerinde 5 saniye sürdü.

İlk çözüm, aynı commit'teki PatternSafety adlı bir ön tarama katmanıydı. Deseni çalıştırmadan önce

okuyor, iç içe tekrarları ve belirsiz alternasyonu reddediyor, 256 karakterlik bir değer için en

kötü durumu tahmin ediyordu. Commit mesajı, bu katmanın deseni motorun ayrıştırıcısıyla değil

kendi okumasıyla değerlendirdiğini zaten yazıyordu.

Hata tam da bu boşluktaydı. Sonraki commit e471e0b'nin kaydettiğine göre FormSchema.decode,

yalnızca public API kullanılarak \e{(a|a)+} desenini kabul ediyordu: ön tarama süslü

parantezleri kaçış dizisinin parçası saydı, motor saymadı ve errors(in:) kısa bir girdide

29,6 saniye koştu; her fazladan "a" ile süre ikiye katlanıyordu. 256 sınırı da Character

sayıyordu, oysa motorun işi code unit ile büyüyor; \w*\w*\w*z ön taramadan geçti ve 7,4 saniye

sürdü.

Düzeltme ön taramayı yamamadı. Desen metnini taramak her yavaş deseni hızlı olandan ayıramaz;

bu yüzden e471e0b ön taramayı kaldırdı ve motorun kendisini sınırladı. Artık regex çalıştıran

tek kod PatternMatcher; Validator.matches ve FormSchema ikisi de onu çağırıyor:

  • Desenleri NSRegularExpression derleyip çalıştırıyor; yani kabul edilen desen, çalışan desenin

ta kendisi.

  • Eşleştirme .reportProgress kullanıyor; bir eşleşme 20 ms sonra ya da çağıranın belirlediği süre sınırında

durduruluyor ve başarısız sayılıyor. Kendi sınırına takılan desen hatırlanıyor ve sonraki

değerlerde hiç çalıştırılmadan başarısız oluyor.

  • 1.000 UTF-16 code unit'ten uzun metin eşleştirilmeden reddediliyor; tek bir errors(in:)

çağrısındaki bütün eşleşmeler 100 ms'lik bir bütçeyi paylaşıyor.

  • Şemanın kendisinin de kaynak sınırları var: 1 MB'tan büyük JSON, 200'den fazla alan, 50'den

fazla desenli alan ya da 500'den fazla seçenek reddediliyor.

Daha sonraki 803ee6b commit'i daha küçük bir boşluğu kapattı: bütçe bittikten sonra bile kalan

her alan, başarısız olmadan önce desenini derliyordu. Süre sınırı kontrolü artık derlemeden önce

yapılıyor. Ön tarama katmanı hiçbir sürüme girmedi; PR #6 içinde yazıldı ve kaldırıldı. Çıkarılacak ders

genel: bir filtre girdiyi, onu çalıştıracak motordan farklı bir ayrıştırıcıyla okuyorsa ikisi

farklı sonuca varabilir. Sınır, motorun kendisinde olmalı.

Kullanım

Paketi Xcode'da File › Add Package Dependencies… ile

https://github.com/muhittincamdali/SwiftUI-Components.git adresini girerek ya da

Package.swift içinde from: "2.1.0" ile ekleyin. README'deki hızlı başlangıç, yeni bir

uygulamadaki ContentView.swift dosyasının yerine geçiyor:

swift
1import SwiftUI
2import SwiftUIComponents
3 
4struct ContentView: View {
5 @State private var rating = 3.0
6 
7 var body: some View {
8 VStack(spacing: 20) {
9 Text("Rate this app")
10 .font(.headline)
11 RatingInput(rating: $rating)
12 PrimaryButton("Send rating") {
13 print("Rating: \(rating)")
14 }
15 }
16 .padding()
17 }
18}

JSON ile tanımlanan bir form çözülürken yinelenen id'ler, imkânsız sınırlar, geçersiz regex'ler

ve yukarıdaki boyut sınırları kontrol ediliyor:

swift
1func loadContactSchema() throws -> FormSchema {
2 try FormSchema.decode(from: Data(contactFormJSON.utf8)) // throws for malformed JSON
3}

Doğrulamalı bir alan ValidatedValue kullanıyor; hata yalnızca kullanıcı alanı düzenledikten ya

da formu gönderdikten sonra görünüyor:

swift
1@State private var email = ValidatedValue("", .required().and(.email()))

Butonlar, grafikler, ödeme formu, snackbar'lar, FlowLayout, JSON formu, gesture'lar ve

ColorContrast için tam örnekler README'nin Usage bölümünde.

Sınırlar

  • View'ların çoğunun kendi testi yok. 163 Swift Testing testi mantığı, durum yöneticilerini, uç

girdileri ve galeri sayfalarını kapsıyor; render testleri görünümü değil boyutu ve çökmeyi

kontrol ediyor.

  • macOS'ta haptics yok, klavye tipi ve büyük harf ayarı yok sayılıyor, wheel picker'lar stepper

alanına ya da açılır menüye dönüşüyor. cornerRadius(_:corners:) yalnızca iOS'ta var.

Platform Differences makalesi her durumu listeliyor.

  • CreditCardInput biçimi, Luhn hanesini, son kullanma tarihini ve CVV uzunluğunu kontrol ediyor.

Bu girdi doğrulaması; ödeme doğrulaması değil.

  • Desen süre sınırı duvar saati (wall-clock) süresi; çok yüklü bir cihazda meşru ama pahalı bir

desen zaman aşımına düşebilir. Desenler Swift Regex değil, ICU söz dizimi kullanıyor.

  • .snackBarHost() ve .bannerHost() paylaşılan yöneticileri kullanıyor; kendi örneğinizi

bunlara vermenin henüz bir yolu yok.

  • DocC sitesi, her view'ın View'dan miras aldığı üyeleri göstermiyor. Bunlar dahil edildiğinde

arşiv 379.410 dosyaya (2,5 GB) çıkıyordu; GitHub Pages için fazla büyüktü (c896b96).

README'deki yol haritası sonraki adımları sıralıyor: Swift Package Index (.spi.yml repoda hazır),

form alanlarından başlayarak tek tek view'lara test ve FormSchema'da koşullu alanlar.

Kaynak kod: github.com/muhittincamdali/SwiftUI-Components

Etiketler

#swiftui#swift-package#form-validation#gestures#testing
Muhittin Çamdalı

Muhittin Çamdalı

Senior Mobile Engineer

12+ yıllık deneyime sahip Senior Mobile Engineer. iOS, Flutter ve React Native ile mobil ürünler geliştiriyorum; full-stack ve AI-native çalışıyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Onay e-postasındaki bağlantıyı açıp “Aboneliğimi onayla” düğmesine bastığında aboneliğin başlar. Bültende açılma/tıklama istatistikleri tutulur; dilediğin an tek tıkla ayrılabilirsin. Gizlilik

Paylaş

İlgili İçerik