İçeriğe geç

Binding: Equatable vs EquatableBinding

Binding'i Equatable'a uyumlu hale getirmek yerine bir Property Wrapper kullanarak uygulamamdaki ince bir SwiftUI Picker hatasını nasıl düzelttiğimi anlatıyorum.

Binding: Equatable vs EquatableBinding

Mantığa aykırı gelebilir ama RemafoX uygulamamda, SwiftUI ile birlikte gelen ve view’lardaki değişiklikleri veriye bağlamak için kullanılan Binding türünü, bir Binding nesnesini uygulamam veri katmanında dolaştırabilmek için Equatable protokolüne uyumlu hale getirmem gerekti. Extension’ın implementasyonu şöyleydi:

extension Binding: Equatable where Value: Equatable {
   static func == (left: Binding<Value>, right: Binding<Value>) -> Bool {
      left.wrappedValue == right.wrappedValue
   }
}

Bu çözümden %100 memnun olmasam da, ilk geliştirdiğimde düzgün çalışıyordu ve yayınladım. Ama sonra, bir macOS güncellemesiyle uygulamdaki tüm Picker view’larına sinsi bir hata sızmaya başladı. Picker’lar çoğu zaman hala çalışıyordu ama bazen garip davranıyorlardı. Her zaman yeniden üretebildiğim tek davranış, binding’e programatik olarak seçili bir değer atadığımda ve daha sonra kullanıcı başka bir değere değiştirdiğinde, Picker açılır menüsünde her iki değerin de onay işaretiyle gösterilmesiydi — bu muhtemelen SwiftUI’daki bir hata:

Kök nedeni bulmak için saatlerce kod yorumlayarak araştırdım ve sonunda yukarıda bahsettiğim Binding extension’ı olduğu ortaya çıktı. Bir şekilde, bunu uygulamamda tanımlamak SwiftUI’daki Picker view’ının dahili implementasyonunu etkilemiş. Ve bunu bilerek, kim bilir başka ne yan etkilere neden olmuş veya gelecekteki sistem güncellemelerinde neden olabilir? Bu yüzden açıkça SwiftUI view’larının davranışını etkilemeyecek daha iyi bir çözüm bulmam gerekiyordu.

Binding’i Equatable’a uyumlu hale getirmek istememin nedeni, veri katmanımda — yani TCA mimarisindeki bazı State türlerinde — sakladığım tüm verilerin de Equatable’a uyumlu olmasını gerektiren gereksinimlerim olmasıydı. Şöyle bir şey:

struct AppState: Equatable {
   // other properties

   var configFile: Binding<ConfigFile>
}

✨ Reklamını burada görmek ister misin? Benimle iletişime geçmek için [email protected] adresine yaz.


Bulduğum çözüm oldukça basit, her ne kadar bir property wrapper yazmayı “öğrenmem” gerekmiş olsa da — bu benim ilk kez kendi property wrapper’ıma ihtiyaç duymam oldu. Ama oldukça basitti, hiç yazmamış olsan bile anlayacağına inanıyorum:

@propertyWrapper
public struct EquatableBinding<Wrapped: Equatable>: Equatable {
   public var wrappedValue: Binding<Wrapped>

   public init(wrappedValue: Binding<Wrapped>) {
      self.wrappedValue = wrappedValue
   }

   public static func == (left: EquatableBinding<Wrapped>, right: EquatableBinding<Wrapped>) -> Bool {
      left.wrappedValue.wrappedValue == right.wrappedValue.wrappedValue
   }
}

@propertyWrapper‘ın tek gereksinimidir wrappedValue property’si ve bu wrapper’ı modülerleştirilmiş uygulamamın tamamında paylaşmak istediğim için, bir public initializer de yazmam gerekti, ama hepsi oldukça basit. == fonksiyonu Equatable protokolünün tek gereksinimidir ve o da basit.

Bununla artık tüm Binding property’lerimi @EquatableBinding ile işaretleyebilirim:

struct AppState: Equatable {
   // other properties

   @EquatableBinding<ConfigFile>
   var configFile: Binding<ConfigFile>
}

Ve hepsi bu, AppState türü artık tamamen Equatable, garip açılır menü sorunu çözüldü ve hiçbir yan etki riski yok çünkü başka framework’lerden var olan türleri yeni protokollere uyumlu hale getirmiyorum. Bunun yerine, sadece diğer framework’lerin haberi bile olmayan yeni bir tür tanıttım, dolayısıyla etkilenmeleri mümkün değil.

Buradan çıkarılacak ders: sahip olmadığın türleri sahip olmadığın protokollere asla uyumlu hale getirme. Bunu derleyici uyarısı yapmak için bir Swift önerisi bile var. Çözümün her zaman bir property wrapper olmadığını not et. Protokollere uyumlu hale getirirken kendi türlerini dahil etmenin birçok yolu var, sadece bunu yapmayı unutma.

💁🏻‍♂️ RemafoX nedir? Xcode ile entegre olarak senin uygulamanı çevirmeye yardımcı olan yerel bir Mac uygulaması. Geliştirme sırasında zaman kazanmak ve yerelleştirmeyi kolaylaştırmak için hemen edin.

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