İçeriğe geç

HandySwiftUI Yeni Tipler: SwiftUI Geliştirme için Olmazsa Olmaz View'lar ve Tipler

#if kontrolleri olmadan platforma özel değerlerden gelişmiş seçim kontrollerine ve asenkron durum yönetimine kadar -- uygulama geliştirmeyi hızlandıran temel SwiftUI tiplerini keşfet. Savaş meydanında test edilmiş bu view'lar ve tipler, SwiftUI geliştirmesindeki yaygın boşlukları dolduruyor.

HandySwiftUI Yeni Tipler: SwiftUI Geliştirme için Olmazsa Olmaz View'lar ve Tipler

Bu API’leri kendi uygulamalarımda 4 yıl boyunca geliştirdikten sonra, HandySwiftUI’ın ilk etiketli sürümünü paylaşmaktan mutluluk duyuyorum. Bu paket, yalnızca geçen yıl 10 uygulama yayınlamamda büyük rol oynayan çeşitli yardımcı araçlar ve kolaylık API’leri içeriyor. HandySwift paketimin Foundation için sunduğu kolaylıklara benzer şekilde, SwiftUI geliştirme için pratik çözümler sunuyor.

Bu yazıda, TranslateKit, FreemiumKit ve CrossCraft gibi uygulamalarımda günlük geliştirme sürecinde en değerli bulduğum yeni tiplerin bir seçkisini paylaşacağım. HandySwiftUI çok daha fazla yardımcı araç içerse de, bu tipler gerçek dünya uygulamalarında defalarca kendini kanıtladı ve senin SwiftUI projelerinde de işine yarayabilir.

Platforma Özel Değerler

HandySwiftUI, platforma özel değerleri yönetmek için zarif bir yol sunuyor:

struct AdaptiveView: View {
    enum TextStyle {
        case compact, regular, expanded
    }

    var body: some View {
        VStack {
            // Her platform için farklı sayısal değerler
            Text("Welcome")
                .padding(Platform.value(default: 20.0, phone: 12.0))

            // Her platform için farklı renkler
            Circle()
                .fill(Platform.value(default: .blue, mac: .indigo, pad: .purple, vision: .cyan))
        }
    }
}

Last30days

FreemiumKit’te .font(Platform.value(default: .title2, phone: .headline)) ile platformlar arası benzer bir başlık görünümü elde etmek.

Platform.value herhangi bir tiple çalışır – basit sayılardan renklere, fontlara veya kendi özel tiplerine kadar. Sadece bir varsayılan değer verip ihtiyaç duyduğun platformları geçersiz kılman yeterli. iPad için pad adında özel bir case bile var, yani telefonları ve tabletleri ayrı ayrı hedefleyebiliyorsun. Bu özellik inanılmaz kullanışlı.

Bu, benim en çok kullandığım HandySwiftUI yardımcısı – bir sürü #if kontrolünden kurtarıyor. Basit ama çok güçlü!

Okunabilir Önizleme Tespiti

Geliştirme sırasında sahte veri sağla ve yüklenme durumlarını simüle et:

Task {
   loadState = .inProgress

   if Xcode.isRunningForPreviews {
       // Önizlemelerde ağ gecikmesini simüle et
       try await Task.sleep(for: .seconds(1))
       self.data = Data()
       loadState = .successful
   } else {
       do {
           self.data = try await loadFromAPI()
           loadState = .successful
       } catch {
           loadState = .failed(error: error.localizedDescription)
       }
   }
}

Xcode.isRunningForPreviews, gerçek ağ isteklerini atlamana ve bunun yerine yalnızca SwiftUI önizlemelerinde anında veya gecikmeli sahte yanıtlar sağlamana olanak tanır. Prototipleme ve UI geliştirme için mükemmel. Ayrıca geliştirme sırasında sınırlı kaynakları tüketmekten kaçınmak için de kullanışlı – API hız limitleri, istatistikleri bozabilecek analitik olayları veya istek başına ücret alan servisler gibi. Bunları if !Xcode.isRunningForPreviews kontrolüne sarman yeterli.

Verimli Görsel Yükleme

CachedAsyncImage, yerleşik önbellekleme ile verimli görsel yükleme sağlar:

struct ProductView: View {
    let product: Product

    var body: some View {
        VStack {
            CachedAsyncImage(url: product.imageURL)
                .frame(width: 200, height: 200)
                .clipShape(RoundedRectangle(cornerRadius: 10))

            Text(product.name)
                .font(.headline)
        }
    }
}

.resizable() ve .aspectRatio(contentMode: .fill) öğelerinin içerideki Image view’ına zaten uygulanmış olduğunu not et.

Gelişmiş Seçim Kontrolleri

Farklı kullanım senaryoları için birden fazla sofistike picker tipi:

struct SettingsView: View {
    @State private var selectedMood: Mood?
    @State private var selectedColors: Set<Color> = []
    @State private var selectedEmoji: Emoji?

    var body: some View {
        Form {
            // İkonlu dikey seçenek picker'ı
            VPicker("Select Mood", selection: $selectedMood)

            // Özel stilli yatay picker
            HPicker("Rate your experience", selection: $selectedMood)

            // Platforma uyarlanabilir UI ile çoklu seçim
            MultiSelector(
                label: { Text("Colors") },
                optionsTitle: "Select Colors",
                options: [.red, .blue, .green],
                selected: $selectedColors,
                optionToString: \.description
            )

            // Emoji veya SF Symbol seçimi için aranabilir ızgara picker'ı
            SearchableGridPicker(
                title: "Choose Emoji",
                options: Emoji.allCases,
                selection: $selectedEmoji
            )
        }
    }
}

Settings view

HandySwiftUI, yaygın emoji ve sembolleri içeren Emoji ve SFSymbol enum’larını barındırır. Ayrıca SearchableOption‘a uyum sağlayıp her case için searchTerms sağlayarak arama işlevini destekleyen özel enum’lar da oluşturabilirsin.

Asenkron Durum Yönetimi

ProgressState ile asenkron işlemleri tip güvenli durum yönetimiyle takip et:

struct DocumentView: View {
    @State private var loadState: ProgressState<String> = .notStarted

    var body: some View {
        Group {
            switch loadState {
            case .notStarted:
                AsyncButton("Load Document") {
                    loadState = .inProgress
                    try await loadDocument()
                    loadState = .successful
                } catchError: { error in
                    loadState = .failed(error: error.localizedDescription)
                }

            case .inProgress:
                ProgressView("Loading document...")

            case .failed(let errorMessage):
                VStack {
                    Text("Failed to load document:")
                        .foregroundStyle(.secondary)
                    Text(errorMessage)
                        .foregroundStyle(.red)

                  AsyncButton("Try Again") {
                      loadState = .inProgress
                      try await loadDocument()
                      loadState = .successful
                  } catchError: { error in
                      loadState = .failed(error: error.localizedDescription)
                  }
                }

            case .successful:
                VStack {
                    DocumentContent()
                }
            }
        }
    }
}

Bu örnek, tüm durumları tip güvenli bir şekilde ele almayı gösteriyor:

  • .notStarted ilk yükleme düğmesini gösterir

  • .inProgress bir yükleme göstergesi görüntüler

  • .failed hatayı yeniden deneme seçeneğiyle gösterir

  • .successful yüklenen içeriği sunar

NSOpenPanel‘i SwiftUI’a Taşı

Yerel macOS dosya erişimini SwiftUI’a köprüleme, özellikle güvenlik kapsamlı kaynakların yönetiminde kullanışlı:

struct SecureFileLoader {
    @State private var apiKey = ""

    func loadKeyFile(at fileURL: URL) async {
        #if os(macOS)
        // macOS'te dosyaya erişim için kullanıcı onayı gerekiyor
        let panel = OpenPanel(
            filesWithMessage: "Provide access to read key file",
            buttonTitle: "Allow Access",
            contentType: .data,
            initialDirectoryUrl: fileURL
        )
        guard let url = await panel.showAndAwaitSingleSelection() else { return }
        #else
        let url = fileURL
        #endif

        guard url.startAccessingSecurityScopedResource() else { return }
        defer { url.stopAccessingSecurityScopedResource() }

        do {
            apiKey = try String(contentsOf: url)
        } catch {
            print("Failed to load file: \(error.localizedDescription)")
        }
    }
}

Open panel

Doğrudan FreemiumKit’ten alınan bu örnek, OpenPanel‘in macOS’te sürüklenen öğeler için güvenlik kapsamlı dosya erişimini nasıl basitleştirdiğini ve aynı zamanda çapraz platform uyumluluğunu nasıl koruduğunu gösteriyor.

Dikey Sekme Navigasyonu

SwiftUI’ın TabView’ına alternatif olarak, macOS ve iPadOS uygulamalarında sıkça görülen kenar çubuğu stilinde navigasyon:

struct MainView: View {
    enum Tab: String, CaseIterable, Identifiable, CustomLabelConvertible {
        case documents, recents, settings

        var id: Self { self }
        var description: String {
            rawValue.capitalized
        }
        var symbolName: String {
            switch self {
            case .documents: "folder"
            case .recents: "clock"
            case .settings: "gear"
            }
        }
    }

    @State private var selectedTab: Tab = .documents

    var body: some View {
        SideTabView(
            selection: $selectedTab,
            bottomAlignedTabs: 1  // Ayarları alta yerleştirir
        ) { tab in
            switch tab {
            case .documents:
                DocumentList()
            case .recents:
                RecentsList()
            case .settings:
                SettingsView()
            }
        }
    }
}

Side tab view

SideTabView, ikonlar ve etiketlerle dikey bir kenar çubuğu sağlar ve alta hizalanmış sekmeler desteğiyle büyük ekranlar için optimize edilmiştir. View, platforma özgü stilleri ve hover efektlerini otomatik olarak yönetir.

Hemen Başla

Bu tipleri projelerinde benim kadar faydalı bulacağını umuyorum. İyileştirme fikirlerin veya SwiftUI topluluğuna katkı sağlayabilecek ek tipler için GitHub’da katkıda bulunmaktan çekinme:

github.comFlineDev / HandySwiftUIHandy SwiftUI features that didn’t make it into SwiftUI (yet)

Bu yazı, HandySwiftUI’ın özelliklerini keşfeden dört makaleden oluşan serinin ilki. View Modifier’lar, Extension’lar ve Stiller hakkındaki yaklaşan yazıları takipte kal!

Bu yazıyı beğendin mi? Swift ipuçları ve indie geliştirici güncellemeleri için Bluesky ve Mastodon üzerinden takip et.