Basit tarifler uygulaması oluşturarak iOS en iyi uygulamalarını öğrenin

Kaynak: ChefStep

İçindekiler

  • Başlamak
  • Xcode ve Swift sürümü
  • Desteklenecek minimum iOS sürümü
  • Xcode projesini organize etme
  • Tarifler uygulaması yapısı
  • Kod kuralları
  • belgeleme
  • Kod bölümlerini işaretleme
  • Kaynak kontrolü
  • Bağımlılıklar
  • Projeye girme
  • API
  • Ekranı başlatmak
  • Uygulama simgesi
  • SwiftLint ile satır kodu
  • Güvenli yazı tipi
  • Bana kodu göster
  • Model tasarımı
  • FlowController ile daha iyi gezinme
  • Otomatik Düzen
  • Mimari
  • Massive View Denetleyicisi
  • Giriş kontrolu
  • Tembel özellikler
  • Kod parçacıkları
  • Ağ kodu nasıl test edilir
  • Çevrimdışı destek için önbellek uygulama
  • Önbellek nasıl test edilir
  • Uzak Görüntüler Yükleme
  • Görüntü yüklemeyi UIImageView için daha uygun hale getirme
  • UITableView ve UICollectionView için Genel Veri Kaynağı
  • Denetleyici ve Görünüm
  • Çocuk Görüntüleme Denetleyicisiyle sorumlulukları işleme
  • Kompozisyon ve Bağımlılık Enjeksiyonu
  • App Transport Güvenliği
  • Özel Kaydırılabilir Görünüm
  • Arama işlevi ekleme
  • Sunum içeriğini anlama
  • Arama eylemlerini yeniden başlatma
  • Tersine çevrilmiş beklenti ile ayrılma testi
  • Kullanıcı arayüzünü UITest'lerle test etme
  • Ana iplik koruması
  • Performansları ve sorunları ölçme
  • Bahçesi ile prototipleme
  • Buradan nereye

İOS 7 açıklandığında iOS geliştirmeye başladım. Ve çalışarak, meslektaşlarımdan ve iOS topluluğundan tavsiyeler alarak biraz öğrendim.

Bu makalede, basit tarifler uygulamasının örneğini alarak birçok iyi uygulamayı paylaşmak istiyorum. Kaynak kodu GitHub Tariflerinde.

Uygulamanın ayrıntılı bilgileri ile birlikte tariflerin bir listesini vitrine geleneksel bir usta detay uygulamasıdır.

Bir sorunu çözmenin binlerce yolu vardır ve bir sorunun üstesinden gelme şekli kişisel zevkinize de bağlıdır. Umarım, bu yazı boyunca yararlı bir şeyler öğreneceksiniz - bu projeyi yaparken çok şey öğrendim.

Daha fazla okumanın faydalı olacağını düşündüğüm bazı anahtar kelimelere bağlantılar ekledim. Bu yüzden kesinlikle onları kontrol edin. Herhangi bir geri bildirim açığız.

Öyleyse başlayalım…

İşte inşa edeceğiniz şeye genel bir bakış.

Başlamak

Kullandığımız araç ve proje ayarlarına karar verelim.

Xcode ve Swift sürümü

WWDC 2018'de Apple, Swift 4.2 ile Xcode 10'u tanıttı. Ancak, yazı yazarken, Xcode 10 hala beta 5'tedir. Öyleyse, kararlı Xcode 9 ve Swift 4.1'e bağlı kalalım. Xcode 4.2 bazı harika özelliklere sahip - bu muhteşem Oyun Alanı ile oynayabilirsiniz. Swift 4.1'den büyük değişiklikler getirmediğinden, gerektiğinde uygulamamızı yakın gelecekte kolayca güncelleyebiliriz.

Swift sürümünü hedef ayarların yerine Proje ayarında ayarlamalısınız. Bu, projedeki tüm hedeflerin aynı Swift versiyonunu (4.1) paylaşması anlamına gelir.

Desteklenecek minimum iOS sürümü

2018 yazından itibaren, iOS 12 genel beta 5’dedir ve iOS 12’yi Xcode 10 olmadan hedefleyemiyoruz. Bu yazıda Xcode 9'u ve SDK'nin bazını iOS 11 kullanıyoruz. eski iOS sürümlerini desteklemeniz gerekir. İOS kullanıcıları, Android kullananlara göre daha yeni iOS sürümleri benimseme eğiliminde olsalar da, eski sürümlerde kalanlar da var. Elma tavsiyesine göre, iOS 10 ve iOS 11 olan en son iki sürümü desteklememiz gerekir. App Store tarafından 31 Mayıs 2018 tarihinde ölçüldüğü gibi, kullanıcıların yalnızca% 5'i iOS 9 ve önceki sürümleri kullanır.

Yeni iOS sürümlerini hedeflemek, Apple mühendislerinin her yıl geliştirdiği yeni SDK'ların avantajlarından yararlanabileceğimiz anlamına geliyor. Apple geliştirici web sitesinde gelişmiş bir değişiklik günlüğü görünümü bulunur. Şimdi neyin eklendiğini veya değiştirildiğini görmek daha kolay.

İdeal olarak, eski iOS sürümleri için desteğin ne zaman bırakılacağını belirlemek için, kullanıcıların uygulamamızı nasıl kullandıkları hakkında analitiklere ihtiyacımız var.

Xcode projesini organize etme

Yeni projeyi oluştururken, testleri erken yazmak için önerilen bir uygulama olarak hem “Birim Testlerini Dahil Et” hem de “UI Testlerini Dahil Et” seçeneğini seçin. XCTest çerçevesindeki son değişiklikler, özellikle de UI Testlerinde, testi kolaylaştırır ve oldukça kararlıdır.

Projeye yeni dosyalar eklemeden önce, bir duraklama yapın ve uygulamanızın yapısını düşünün. Dosyaları nasıl düzenlemek istiyoruz? Birkaç seçeneğimiz var. Dosyaları özellik / modül veya rol / türlere göre düzenleyebiliriz. Her birinin artıları ve eksileri var ve bunları aşağıda tartışacağım.

Rol / türe göre:

  • Artıları: Dosyaları nereye koyacağınızla ilgili daha az düşünce var. Komut dosyalarını veya filtreleri uygulamak da daha kolaydır.
  • Eksileri: Aynı özellik ile ilgili birden fazla dosya bulmak istersek ilişki kurmak zor. Gelecekte yeniden kullanılabilir bileşenlere dönüştürmek istiyorsak, dosyaları yeniden düzenlemek zaman alacaktır.

Özellik / modül

  • Artıları: Her şeyi modüler kılar ve kompozisyonu teşvik eder.
  • Eksileri: Farklı türden birçok dosya bir araya getirildiğinde dağınık olabilir.

Modüler kalıyor

Şahsen kodumu olabildiğince özellikler / bileşenler ile düzenlemeye çalışıyorum. Bu, düzeltilecek ilgili kodu tanımlamayı ve gelecekte yeni özellikleri eklemeyi kolaylaştırır. “Bu dosya nedir?” Yerine “Bu uygulama ne yapıyor?” Sorusunu yanıtlıyor. İşte bu konuda iyi bir makale.

Hangi yapıyı seçerseniz seçin, iyi bir kural tutarlı kalmaktır.

Tarifler uygulaması yapısı

Aşağıdaki tarif uygulamamızın kullandığı uygulama yapısıdır:

Kaynak

Kaynak kod dosyalarını içerir, bileşenlere ayırın:

  • Özellikler: Uygulamadaki ana özellikler
  • Ana sayfa: Ana ekran, yemek tariflerinin bir listesini ve açık bir aramayı gösterir.
  • Liste: bir tarifin yeniden yüklenmesi ve bir tarif bulunmadığında boş bir görünüm gösterilmesi dahil olmak üzere tariflerin bir listesini gösterir.
  • Arama: arama ve geri alma işlemini kaldırma
  • Detay: detay bilgilerini gösterir

Kütüphane

Uygulamamızın temel bileşenlerini içerir:

  • Akış: akışları yönetmek için Akış Kontrolünü içerir
  • Bağdaştırıcı: UICollectionView için genel veri kaynağı
  • Uzantı: ortak işlemler için uygun uzantılar
  • Model: Uygulamadaki model, JSON'dan ayrıştırıldı

Kaynak

Plist, kaynak ve Storyboard dosyaları içerir.

Kod kuralları

Raywenderlich / Swift-Style-Guide ve Github / Swift-Style-Guide'daki stil rehberlerinin çoğuna katılıyorum. Bunlar Swift projesinde kullanımı basit ve makul. Ayrıca, Swift ekibi tarafından Apple'da yapılan daha iyi Swift kodunun nasıl yazılacağına ilişkin resmi API Tasarım Kuralları'nı inceleyin.

Hangi stil kılavuzunu izlemeyi seçerseniz seçin, kod netliği en önemli hedefiniz olmalıdır.

Girinti ve tab-uzay savaşı hassas bir konudur, ancak yine, tadı bağlıdır. Android projelerinde dört boşluk girintisini, iOS ve React'te iki boşluk kullanıyorum. Bu Tarifler uygulamasında, burada ve burada yazdığım tutarlı ve kolay anlaşılır girintiyi takip ediyorum.

belgeleme

İyi kod kendini açık bir şekilde açıklamalıdır, böylece yorum yazmanıza gerek kalmaz. Bir kod grubunun anlaşılması zorsa, duraklatmak ve açıklayıcı adlara sahip bazı yöntemlere göre yeniden değerlemek iyidir, bu nedenle kod grubunun anlaşılması daha açıktır. Ancak, dokümantasyon sınıflarını ve yöntemlerini iş arkadaşlarınız ve gelecekteki benliğiniz için de iyi buluyorum. Swift API tasarım kurallarına göre,

Her bildirim için bir dokümantasyon yorumu yazınız. Belgelendirme yazarak edinilen bilgiler, tasarımınız üzerinde çok büyük bir etkiye neden olabilir, bu yüzden bunu ertelemeyin.

Cmd + Alt + / ile Xcode'da yorum şablonu /// oluşturmak çok kolaydır. Kodunuzu gelecekte başkalarıyla paylaşmak için bir çerçeveye yeniden yansıtmayı planlıyorsanız, jazzy gibi araçlar diğer kişilerin de takip edebilmesi için belgeler oluşturabilir.

Kod bölümlerini işaretleme

MARK kullanımı kod bölümlerini ayırmak için yardımcı olabilir. Ayrıca Gezinti Çubuğu'ndaki işlevleri güzelce gruplar. Ayrıca uzatma grupları, ilgili özellikler ve yöntemler de kullanabilirsiniz.

Basit bir UIViewController için aşağıdaki MARK'ları tanımlayabiliriz:

// MARK: - Init
// MARK: - Yaşam döngüsünü görüntüle
// MARK: - Kurulum
// MARK: - İşlem
// MARK: - Veriler

Kaynak kontrolü

Git şu anda popüler bir kaynak kontrol sistemidir. .Gitignore şablonunu gitignore.io/api/swift adresinden kullanabiliriz. Bağımlılık dosyalarının (CocoaPods ve Carthage) kontrolünde hem artılar hem de eksiler var. Bu, projenize bağlı, ancak kod temelini karıştırmamak için kaynak kontrolünde bağımlılıklar (node_modules, Carthage, Pods) işleme eğiliminde değilim. Ayrıca, Çekme isteklerinin incelenmesini kolaylaştırır.

Pod dizininde kontrol edip etmediğiniz, Podfile ve Podfile.lock sürüm kontrolü altında daima tutulmalıdır.

Her iki iTerm2'yi de komutları yürütmek için, Kaynak Ağacı'nı da dalları ve aşamaları görüntülemek için kullanıyorum.

Bağımlılıklar

Üçüncü taraf çerçevelerini kullandım ve ayrıca açık kaynak kodlara çok katkıda bulundum ve katkıda bulundum. Bir çerçeve kullanmak, başlangıçta size bir destek sağlar, ancak gelecekte sizi de çok kısıtlayabilir. Çalışması çok zor bazı önemsiz değişiklikler olabilir. SDK kullanırken aynı şey olur. Tercihim aktif açık kaynaklı çerçeveleri seçmek. Kaynak kodu okuyun ve çerçeveleri dikkatlice kontrol edin ve kullanmayı planlıyorsanız ekibinize danışın. Biraz ekstra dikkat, zarar vermez.

Bu uygulamada, mümkün olduğunca az bağımlılık kullanmaya çalışıyorum. Sadece bağımlılıkları nasıl yöneteceğinizi gösterecek kadar. Bazı deneyimli geliştiriciler, size tam kontrol sağlayan bir bağımlılık yöneticisi olan Carthage'ı tercih edebilir. Burada CocoaPod'ları seçiyorum çünkü kullanımı kolay ve şimdiye kadar çok iyi çalıştı.

CocoaPods'a bu projenin Swift 4.1'i kullandığını söylemek için projenin kökündeki .swift-4.1 değerinde bir dosya var. Bu basit görünüyor ama anlamak için biraz zaman aldı.

Projeye girme

Projeye güzel bir görünüm vermek için bazı resimler ve simgeler oluşturalım.

API

İOS ağlarını öğrenmenin kolay yolu, halka açık ücretsiz API hizmetleridir. Burada food2fork kullanıyorum. Http://food2fork.com/about/api adresinden bir hesap açabilirsiniz. Bu kamuya açık depoda birçok harika API var.

Kimlik bilgilerinizi güvenli bir yerde saklamak iyidir. Şifrelerimi oluşturmak ve saklamak için 1Password'ü kullanıyorum.

Kodlamaya başlamadan önce, hangi tür istekleri istediklerini ve yanıtlarını verdiklerini görmek için API'lerle oynayalım. API yanıtlarını test etmek ve analiz etmek için Insomnia aracını kullanıyorum. Açık kaynak kodlu, ücretsiz ve harika çalışıyor.

Ekranı başlatmak

İlk izlenim önemlidir, Başlatma Ekranı da öyle. Tercih edilen yol, statik bir Başlatma resmi yerine LaunchScreen.storyboard kullanmaktır.

Öğe Kataloğu'na bir başlangıç ​​görüntüsü eklemek için, LaunchScreen.storyboard'u açın, UIImageView ekleyin ve UIView'in kenarlarına sabitleyin. Görüntünün tam ekran olmasını istediğimizden görüntüyü Güvenli Alan'a sabitlememeliyiz. Ayrıca, Otomatik Düzen kısıtlamalarındaki kenar boşluklarını da kaldırın. UIImageView'ın contentMode'unu Aspect Fill (Düz Dolgu) olarak ayarlayın, böylece doğru en boy oranıyla uzanır.

LaunchScreen'de düzeni yapılandırın.

Uygulama simgesi

İyi bir uygulama, desteklediğiniz her cihaz için ve ayrıca Bildirim, Ayarlar ve Sıçrama Tahtası gibi yerler için gerekli tüm uygulama simgelerini sağlamaktır. Her görüntünün saydam pikselinin olmadığından emin olun, aksi halde siyah arka plana neden olur. Bu ipucu İnsan Arayüzü Kılavuzundan - App Icon'dan alınmıştır.

Arka planı basit tutun ve saydamlıktan kaçının. Simgenizin opak olduğundan ve arka planı karışmadığından emin olun. Basit bir arka plan verin, böylece yakındaki diğer uygulama simgelerinden fazla güç almaz. Simgenin tamamını içerikle doldurmanıza gerek yoktur.

1024 x 1024'ten büyük kare görüntüler tasarlamamız gerekir, böylece her biri daha küçük görüntülere küçültülebilir. Bunu elle, komut dosyasıyla yapabilir veya yaptığım bu küçük IconGenerator uygulamasını kullanabilirsiniz.

IconGenerator uygulaması iPhone, iPad, macOS ve watchOS uygulamalarında iOS için simgeler oluşturabilir. Sonuç, doğrudan Varlık Kataloğuna sürükleyebildiğimiz AppIcon.appiconset. Varlık Kataloğu, modern Xcode projelerine geçmenin yoludur.

SwiftLint ile satır kodu

Hangi platformda geliştirildiğimize bakılmaksızın, tutarlı sözleşmeleri uygulamak için bir linter'e sahip olmak iyidir. Swift projeleri için en popüler araç, Realm'deki harika insanlar tarafından yapılan SwiftLint.

Kurmak için Podfile 'SwiftLint', '~> 0.25' bölmesini ekleyin. Ayrıca bağımlılıkların sürümünü belirlemek iyi bir uygulamadır; pod kurulumu yanlışlıkla uygulamanızı bozabilecek ana sürüme güncellenmez. Sonra tercih ettiğiniz yapılandırma ile bir .swiftlint.yml ekleyin. Burada örnek bir yapılandırma bulunabilir.

Son olarak, derleme işleminden sonra swiftlint'i çalıştırmak için yeni bir Run Script Phrase ekleyin.

Güvenli yazı tipi

Kaynakları güvenli bir şekilde yönetmek için R.swift kullanıyorum. Yazı tipine, yerelleştirilebilir dizelere ve renklere erişmek için güvenli yazı sınıfları oluşturabilir. Kaynak dosya adlarını ne zaman değiştirirsek, örtük bir kilitlenme yerine derleme hataları alıyoruz. Bu, aktif olarak kullanımda olan kaynaklardan çıkmamızı önler.

imageView.image = R.image.notFound ()

Bana kodu göster

Modelden, akış kontrol cihazlarından ve servis sınıflarından başlayarak koda dayalım.

Model tasarımı

Sıkıcı gelebilir, ancak müşteriler API yanıtını göstermenin daha güzel bir yoludur. Model belki de en temel şeydir ve uygulamada çok kullanıyoruz. Bu kadar önemli bir rol oynar, ancak hatalı biçimlendirilmiş modeller ve dikkate alınması gereken bir modelin nasıl ayrıştırılması gerektiği ile ilgili varsayımlarla ilgili bazı belirgin hatalar olabilir.

Uygulamanın her modeli için test etmeliyiz. İdeal olarak, modelin arka uçtan değişmesi durumunda otomatik olarak modellerin API yanıtlarından test edilmesi gerekir.

Swift 4.0'dan başlayarak, JSON ile kolayca seri hale getirmek için modelimizi Codable ile uyumlu hale getirebiliriz. Modelimiz değişmez olmalıdır:

struct Tarif: Katlanabilir {
  Yayıncı izin ver: String
  URL yapalım: URL
  let sourceUrl: String
  let id: String
  let başlığı: String
  imageUrl: dize olsun
  socialRank izin verin: Çift
  publisherUrl olsun: URL
enum CodingKeys: String, CodingKey {
    vaka yayıncısı
    case url = "f2f_url"
    case sourceUrl = "source_url"
    case id = "recipe_id"
    dava başlığı
    case imageUrl = "image_url"
    case socialRank = "social_rank"
    case publisherUrl = "publisher_url"
  }
}

Fantezi sözdizimini veya RSpec stilini seviyorsanız bazı test çerçevelerini kullanabiliriz. Bazı üçüncü taraf test çerçevelerinin sorunları olabilir. XCTest'i yeterince iyi buluyorum.

XCTest'i içe aktar
@testable import Tarifler
sınıf TariflerTestler: XCTestCase {
  func testParsing () atar {
    let json: [String: Any] = [
      "yayıncı": "İki Bezelye ve Podları",
      "f2f_url": "http://food2fork.com/view/975e33",
      "title": "No-Bake Çikolata Fıstık Ezmesi Pretzel Kurabiye",
      "source_url": "http://www.twopeasandtheirpod.com/no-bake-chocolate-peanut-butter-pretzel-cookies/",
      "recipe_id": "975e33",
      "image_url": "http://static.food2fork.com/NoBakeChocolatePeanutButterPretzelCookies44147.jpg",
      "social_rank": 99.99999999999974,
      "publisher_url": "http://www.twopeasandtheirpod.com"
    ]
let veri = JSONSerialization.data'yı deneyin (withJSONObject: json, seçenekler: [])
    Let decoder = JSONDecoder ()
    letipe = denemek decoder.decode (Recipe.self, from: data)
XCTAssertEqual (recipe.title, "No-Bake Çikolata Fıstık Ezmesi Pretzel Kurabiye")
    XCTAssertEqual (recipe.id, "975e33")
    XCTAssertEqual (recipe.url, URL (string: "http://food2fork.com/view/975e33")!)
  }
}

FlowController ile daha iyi gezinme

Daha önce, projelerimde Pusula'yı bir yönlendirme motoru olarak kullandım, ancak zamanla basit Yönlendirme kodu yazmanın da işe yaradığını gördüm.

FlowController, birçok UIViewController ile ilgili bileşeni ortak bir özelliğe yönetmek için kullanılır. Diğer kullanım durumları için FlowController ve Koordinatörü okumak ve daha iyi bir anlayış elde etmek isteyebilirsiniz.

Değişen rootViewController'ı yöneten AppFlowController var. Şimdilik RecipeFlowController'ı başlatıyor.

window = UIWindow (çerçeve: UIScreen.main.bounds)
window? .rootViewController = appFlowController
Pencere? .makeKeyAndVisible ()
appFlowController.start ()

RecipeFlowController (aslında olduğu gibi) HomeViewController, RecipesDetailViewController, SafariViewController'ı zorlayan UINavigationController'ı yönetir.

son sınıf RecipeFlowController: UINavigationController {
  /// Akışı başlat
  func start () {
    let service = RecipesService (ağ iletişimi: NetworkService ())
    let controller = HomeViewController (yemek tarifleriServis: servis)
    viewControllers = [denetleyici]
    controller.select = {[zayıf ben] tarifi
      kendini? .startDetail (yemek tarifi: yemek tarifi)
    }
  }
özel func startDetail (yemek tarifi: Tarif) {}
  özel işlev startWeb (url: URL) {}
}

UIViewController, FlowController'a akıştaki değişiklikler veya sonraki ekranlar hakkında bildirimde bulunmak için temsilci veya kapanışı kullanabilir. Delege için, aynı sınıfın iki örneği olup olmadığını kontrol etmeniz gerekebilir. Burada sadelik için kapatmayı kullanırız.

Otomatik Düzen

Otomatik Düzen, iOS 5'ten bu yana her gün daha iyi hale geldi. Bazı insanlar hala bu konuda bir sorun yaşasa da, çoğunlukla kısıtlamaların ve performansın karıştırılmasından dolayı, ancak kişisel olarak, Auto Layout'u yeterince iyi buluyorum.

Uyarlanabilir bir kullanıcı arayüzü oluşturmak için Otomatik Düzen'i mümkün olduğunca kullanmaya çalışıyorum. Açıklayıcı ve hızlı Otomatik Düzen yapmak için Çapalar gibi kütüphaneler kullanabiliriz. Ancak bu uygulamada, iOS 9'dan beri NSLayoutAnchor'u kullanacağız. Aşağıdaki kod Kısıtlama'dan ilham almıştır. Auto Layout'un en basit haliyle, translatesAutoresizingMaskIntoConstraints değiştirmeyi ve isActive kısıtlamaları etkinleştirmeyi içerdiğini unutmayın.

uzantı NSLayoutConstraint {
  statik işlev etkinleştirme (_ kısıtlamalar: [NSLayoutConstraint]) {
    kısıtlamalar.forEach {
      ($ 0.firstItem olarak? UIView)?, Tercüme eder.
      $ 0.isActive = doğru
    }
  }
}

GitHub'da gerçekten birçok düzen motorları var. Hangisinin kullanmaya uygun olacağı hakkında bir fikir edinmek için LayoutFrameworkBenchmark sayfasını inceleyin.

Mimari

Mimarlık muhtemelen en çok sinirli ve tartışılan konu. Mimarileri keşfetme hayranıyım, burada farklı mimarilerle ilgili daha fazla yazı ve çerçeve görüntüleyebilirsiniz.

Bana göre, tüm mimariler ve desenler her nesne için rolleri ve bunların nasıl bağlanacağını tanımlar. Seçtiğiniz mimari için bu yol gösterici ilkeleri unutmayın:

  • değişken olanı kapsama
  • mirasa karşı kompozisyon oluşturma
  • program arayüzüne, uygulamaya değil

Rx'le ve Rx'siz birçok farklı mimariyle oynadıktan sonra, basit MVC'nin yeterince iyi olduğunu öğrendim. Bu basit projede, yardımcı Servis sınıflarına yerleştirilmiş bir mantıkla sadece UIViewController var,

Massive View Denetleyicisi

İnsanların, UIViewController'ın ne kadar büyük olduğu konusunda şaka yaptığını duymuş olabilirsiniz, ancak gerçekte, kitlesel bir görüntü denetleyicisi yoktur. Sadece biz yanlış kod yazıyoruz. Ancak onu inceltmek için yollar vardır.

Kullandığım tarifler uygulamasında,

  • Tek bir görevi gerçekleştirmek için görünüm denetleyicisine enjekte edilecek servis
  • Görünümü hareket ettirmek ve Görünüm katmanına bildirimi kontrol etmek için Genel Görünüm
  • Alt görünüm denetleyicisi, daha fazla özellik oluşturmak üzere alt görünüm denetleyicileri oluşturmak için

İşte büyük denetleyicileri inceltmek için 8 ipucu ile çok iyi bir makale.

Giriş kontrolu

SWIFT belgeleri, “erişim kontrolünün, kodunuzun diğer kısımlarındaki diğer kaynak dosya ve modüllerdeki bölümlere erişimi kısıtladığını belirtir. Bu özellik, kodunuzun uygulama ayrıntılarını gizlemenizi ve bu kodun erişilip kullanılabileceği tercih edilen bir arabirim belirlemenizi sağlar. ”

Her şey varsayılan olarak özel ve nihai olmalıdır. Bu aynı zamanda derleyiciye yardımcı olur. Bir kamu mülkü görürken, onunla daha fazla bir şey yapmadan önce proje boyunca aramamız gerekir. Eğer mülk sadece bir sınıf içinde kullanılıyorsa, onu özel yapmak, başka bir yerde kırılıp ayrılmayacağımıza bakmamıza gerek olmadığı anlamına gelir.

Mümkün olduğunca nihai özellikleri ilan etmek.

son sınıf HomeViewController: UIViewController {}

Özellikleri özel veya en azından özel (ilan edilmiş) olarak bildirin.

son sınıf RecipeDetailView: UIView {
  private let scrollableView = ScrollableView ()
  private (set) tembel var imageView: UIImageView = self.makeImageView ()
}

Tembel özellikler

Daha sonra erişilebilen özellikler için, bunları tembel olarak ilan edebilir ve hızlı inşaat için kapatmayı kullanabileceğini açıklayabiliriz.

son sınıf RecipeCell: UICollectionViewCell {
  private (set) tembel var containerView: UIView = {
    Görüntüleyelim = UIView ()
    view.clipsToBounds = true
    view.layer.cornerRadius = 5
    view.backgroundColor = Color.main.withAlphaComponent (0.4) ile
dönüş görünümü
  } ()
}

Aynı işlevi birden çok özellik için yeniden kullanmayı planlıyorsak, make işlevlerini de kullanabiliriz.

son sınıf RecipeDetailView: UIView {
  private (set) tembel var imageView: UIImageView = self.makeImageView ()
private func makeImageView () -> UIImageView {
    let imageView = UIImageView ()
    imageView.contentMode = .scaleAspectFill
    imageView.clipsToBounds = true
    imageView döndür
  }
}

Bu aynı zamanda Akıcı Kullanım için Stresi'nin tavsiyelerine de uyuyor.

Fabrika yöntemlerinin adlarını “make” ile başlatın, örneğin, x.makeIterator ().

Kod parçacıkları

Bazı kod sözdizimini hatırlamak zor. Otomatik kod üretmek için kod parçacıklarını kullanmayı düşünün. Bu, Xcode tarafından desteklenir ve Apple mühendisleri tarafından demo olduklarında tercih edilen yoldur.

eğer #available ise (iOS 11, *) {
  viewController.navigationItem.searchController = searchController
  viewController.navigationItem.hidesSearchBarWhenScrolling = false
} Başka {
  viewController.navigationItem.titleView = searchController.searchBar
}

Birçoğunun kullanmaktan zevk aldığı bazı faydalı Snippet'lerle bir repo yaptım.

Swift'de ağ kurmak çözülmüş bir problemdir. HTTP yanıtlarını ayrıştırma, istek sıralarını işleme, parametre sorgularını işleme gibi sıkıcı ve hataya açık görevler vardır. PATCH istekleriyle ilgili hatalar gördüm, küçük harfli HTTP yöntemleri,… Sadece Alamofire'ı kullanabiliriz. Burada zaman harcamanıza gerek yok.

Bu uygulama için, çünkü basit ve gereksiz bağımlılıkları önlemek için. Doğrudan URLSession'ı kullanabiliriz. Bir kaynak genellikle URL, yol, parametreler ve HTTP yöntemini içerir.

yapı Kaynak {
  URL yapalım: URL
  izin verilen yol: String?
  izin httpMethod: String
  let parametreleri: [String: String]
}

Basit bir ağ servisi, kaynağı URLRequest'e ayrıştırabilir ve URLSession'a çalışmasını söyler.

son sınıf NetworkService: Ağ İletişimi {
  @discardableResult func fetch (kaynak: Kaynak, tamamlama: @escaping (Data?) -> Void) -> URLSessionTask? {
    guard let request = makeRequest (resource: resource) başka {
      tamamlama (sıfır)
      geri dönüş
    }
let let = session.dataTask (with: request, tamamlamaHandler: {data, _, error in
      bekçi izin ver veri = veri, hata == nil else {
        tamamlama (sıfır)
        dönüş
      }
tamamlama (veri)
    })
task.resume ()
    iade görevi
  }
}

Bağımlılık enjeksiyonu kullanın. Arayan kişinin URLSessionConfiguration belirtmesine izin ver. Burada en yaygın seçeneği sağlamak için Swift varsayılan parametresini kullanırız.

init (configuration: URLSessionConfiguration = URLSessionConfiguration.default) {
  self.session = URLSession (yapılandırma: yapılandırma)
}

Ayrıca iOS 8'den olan URLQueryItem'i de kullanıyorum. Bu öğeleri sorgulamak için parametreleri ayrıştırma ve daha az sıkıcı hale getirir.

Ağ kodu nasıl test edilir

Ağ yanıtları için bir saplama eklemek için URLProtocol ve URLCache kullanabilir ya da URLSessionConfiguration'ı değiştiren Mockingjay gibi çerçeveler kullanabiliriz.

Kendimi test etmek için protokolü kullanmayı tercih ediyorum. Protokol kullanılarak, test bir saplama tepkisi sağlamak için sahte bir istek oluşturabilir.

protokol ağı {
  @discardableResult func fetch (kaynak: Kaynak, tamamlama: @escaping (Data?) -> Void) -> URLSessionTask?
}
son sınıf MockNetworkService: Networking {
  veri ver: Veri
  init (fileName: String) {
    paket verelim = Bundle (for: MockNetworkService.self)
    url olsun = bundle.url (forResource: fileName, withExtension: "json")!
    self.data = deneyin! Veri (içerikOf: url)
  }
func fetch (kaynak: Kaynak, tamamlanma: @escaping (Data?) -> Void) -> URLSessionTask? {
    tamamlama (veri)
    geri dönüş
  }
}

Çevrimdışı destek için önbellek uygulama

Önbellek adlı bir kütüphaneye katkıda bulunur ve kullanırdım. İyi bir önbellek kütüphanesinden ihtiyacımız olan şey bellek ve disk önbelleği, hızlı erişim için bellek, kalıcılık için disk. Kaydettiğimizde, hem belleğe hem de diske kaydederiz. Yüklediğimizde, bellek önbelleği başarısız olursa, diskten yükleriz ve ardından belleği tekrar güncelleriz. Önbellek temizliği, kullanım süresi, erişim sıklığı gibi birçok gelişmiş konu var. Burada onlar hakkında bir şeyler oku.

Bu basit uygulamada, bir homegrown önbellek servis sınıfı yeterlidir ve önbelleğin nasıl çalıştığını öğrenmek için iyi bir yoldur. Swift'deki her şey Veri'ye dönüştürülebilir, bu nedenle Veriyi önbelleğe kaydedebiliriz. Swift 4 Codable, nesneyi Data'ya seri hale getirebilir.

Aşağıdaki kod bize FileManager'ın disk önbelleği için nasıl kullanıldığını gösterir.

/// Verileri hafızaya ve disk önbelleğine kaydedin ve yükleyin
son sınıf CacheService {
/// Hafızaya veri almak veya yüklemek için
  özel izin belleği = NSCache  ()
/// Önbelleğe alınmış dosyalar içeren yol URL'si (mp3 dosyaları ve resim dosyaları)
  private let diskPath: URL
/// Dosya veya dizini kontrol etmek için belirtilen bir yolda var
  private let fileManager: FileManager
/// Tüm işlemlerin seri olarak yapıldığından emin olun
  private let serialQueue = DispatchQueue (etiket: "Yemek Tarifleri")
init (fileManager: FileManager = FileManager.default) {
    self.fileManager = fileManager
    yap {
      documentDirectory = let fileManager.url ('ı deneyin)
        için: .documentDirectory,
        içinde: .userDomainMask,
        uygunsa: nil,
        oluştur: true
      )
      diskPath = documentDirectory.appendingPathComponent ("Yemek Tarifleri")
      createDirectoryIfNeeded () öğesini deneyin
    } tutmak {
      ölümcül hata()
    }
  }
func save (veri: Veri, anahtar: Dize, tamamlanma: (() -> Void)? = nil) {
    let key = MD5 (anahtar)
serialQueue.async {
      self.memory.setObject (NSData, forKey: NSString olarak anahtar)
      yap {
        data.write'ı deneyin (için: self.filePath (anahtar: anahtar))
        tamamlama? ()
      } tutmak {
        Baskı (hata)
      }
    }
  }
}

Hatalı biçimlendirilmiş ve çok uzun dosya adlarından kaçınmak için, bunları birleştirebiliriz. SwiftHash'dan MD5 kullanıyorum, bu da ölü basit kullanım sağlar let key = MD5 (key).

Önbellek nasıl test edilir

Önbellek işlemlerini eşzamansız olacak şekilde tasarladığımdan, test beklentisini kullanmamız gerekiyor. Her testten önceki durumu sıfırlamayı unutmayın; böylece önceki test durumu mevcut testle etkileşime girmez. XCTestCase'deki beklenti, asenkron kodları test etmeyi hiç olmadığı kadar kolaylaştırıyor.

sınıf CacheServiceTests: XCTestCase {
  let service = CacheService ()
func setUp () işlevini geçersiz kıl
    super.setUp ()
Deneyin? service.clear ()
  }
func testClear () {
    Beklenti verelim = self.expectation (açıklama: #function)
    let string = "Merhaba dünya"
    let = = string.data (kullanarak: .utf8)!
service.save (data: data, key: "key", tamamlanma: {
      Deneyin? self.service.clear ()
      self.service.load (anahtar: "anahtar", tamamlanma: {
        XCTAssertNil ($ 0)
        expectation.fulfill ()
      })
    })
bekle (için: [beklenti], zaman aşımı: 1)
  }
}

Uzak Görüntüler Yükleme

Ayrıca Hayali'ye katkıda bulunuyorum, bu yüzden nasıl çalıştığını biraz biliyorum. Uzak görüntüler için onu indirip önbelleğe almamız gerekir ve önbellek anahtarı genellikle uzak görüntünün URL'sidir.

Reçete uygulamamızda, NetworkService ve CacheService'e dayanan basit bir ImageService yapalım. Temel olarak bir resim sadece indirdiğimiz ve önbelleğe aldığımız bir ağ kaynağıdır. Kompozisyonu tercih ediyoruz, böylece ImageService'e NetworkService ve CacheService'i ekleyeceğiz.

/// Yerel önbelleği kontrol et ve uzaktaki resmi al
son sınıf ImageService {
private let networkService: Ağ İletişimi
  private let cacheService: CacheService
  özel var görev: URLSessionTask?
init (networkService: Ağ, cacheService: CacheService) {
    self.networkService = networkService
    self.cacheService = cacheService
  }
}

UIImageView ile genellikle UICollectionView ve UITableView hücrelerine sahibiz. Hücreler yeniden kullanıldığından, yeni bir istek yapmadan önce mevcut istek görevini iptal etmemiz gerekir.

func fetch (url: URL, tamamlanma: @escaping (UIImage?) -> Void) {
  // Varsa mevcut görevi iptal et
  Görev? .cancel ()
// Önbellekten yükleme yapmayı deneyin
  cacheService.load (anahtar: url.absoluteString, tamamlama: {[zayıf ben] cachedData in
    let data = cachedData ise, let image = UIImage (data: data) {
      DispatchQueue.main.async {
        tamamlama (görüntü)
      }
    } Başka {
      // Ağdan istekte bulunmaya çalış
      let let = Kaynak (url: url)
      self? .task = self? .networkService.fetch (kaynak: kaynak, tamamlanma: {networkData in
        let data = networkData ise, let image = UIImage (data: data) {
          // Önbelleğe kaydet
          self? .cacheService.save (veri: veri, anahtar: url.absoluteString)
          DispatchQueue.main.async {
            tamamlama (görüntü)
          }
        } Başka {
          print ("\ (url) konumuna resim yüklenirken hata oluştu")
        }
      })
öz? .resume .task ()
    }
  })
}

Görüntü yüklemeyi UIImageView için daha uygun hale getirme

URL’den uzak görüntüyü ayarlamak için UIImageView’e bir uzantı ekleyelim. Bu ImageService'i korumak ve eski istekleri iptal etmek için ilişkili nesneyi kullanıyorum. ImageService'i UIImageView'a eklemek için ilişkili nesneyi iyi kullanıyoruz. Mesele, istek tekrar tetiklendiğinde mevcut talebi iptal etmektir. Bu, görüntü görünümleri bir kaydırma listesinde görüntülendiğinde kullanışlıdır.

uzantı UIImageView {
  func setImage (url: URL, yer tutucu: UIImage? = nil) {
    eğer imageService == nil {
      imageService = ImageService (networkService: NetworkService (), cacheService: CacheService ())
    }
self.image = yer tutucu
    self.imageService? .fetch (url: url, tamamlama: {[zayıf kendini] görüntü
      öz? .image = görüntü
    })
  }
private var imageService: ImageService? {
    almak {
      dön objc_getAssociatedObject (öz, & AssociateKey.imageService) olarak? ImageService
    }
    Ayarlamak {
      objc_setAssociatedObject (
        öz,
        Ve AssociateKey.imageService,
        yeni değer,
        objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
      )
    }
  }
}

UITableView ve UICollectionView için Genel Veri Kaynağı

Hemen hemen her uygulamada UITableView ve UICollectionView kullanıyoruz ve neredeyse aynı şeyi tekrar tekrar yapıyoruz.

  • yükleme sırasında yenileme kontrolünü göster
  • veri durumunda listeyi yeniden yükle
  • başarısızlık durumunda hatayı göster.

UITableView ve UICollection çevresinde birçok sarıcı vardır. Her biri bize daha fazla güç veren ancak aynı anda kısıtlamalar uygulayan başka bir soyutlama katmanı ekler.

Bu uygulamada, bir tipi güvenli bir koleksiyon yapmak için genel bir veri kaynağı elde etmek için Adaptör kullanıyorum. Çünkü, sonuçta, ihtiyacımız olan tek şey modelden hücrelere haritalamak.

Ayrıca bu fikri temel alan Upstream'den de faydalanıyorum. UITableView ve UICollectionView etrafında dolaşmak zordur, çünkü çoğu zaman uygulamaya özeldir, bu nedenle Adaptör gibi ince bir ambalaj yeterlidir.

son sınıf Bağdaştırıcı : NSObject,
UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
  var öğe: [T] = []
  var configure: ((T, Cell) -> Boşluk)?
  var seç: ((T) -> Boşluk)?
  var cellHeight: CGFloat = 60
}

Denetleyici ve Görünüm

Storyboard'u birçok kısıtlama ve birçok sorun nedeniyle reddettim. Bunun yerine, görünümler oluşturmak ve kısıtlamaları tanımlamak için kod kullanıyorum. Takip etmesi zor değil. UIViewController’daki boyler kodunun çoğu, görünüm oluşturmak ve yerleşimi yapılandırmak içindir. Bunları manzaraya taşıyalım. Buradan daha fazlasını okuyabilirsiniz.

/// Denetleyici ve görünüm arasında ayırmak için kullanılır
sınıf BaseController : UIViewController {
  Kök = T () olsun
func loadView () işlevini geçersiz kıl
    görünüm = kök
  }
}
son sınıf RecipeDetailViewController: BaseController  {}

Çocuk Görüntüleme Denetleyicisiyle sorumlulukları işleme

View kontrol konteyneri güçlü bir konsepttir. Her görünüm denetleyicisi endişenin ayrılmasına sahiptir ve gelişmiş özellikler oluşturmak için birlikte oluşturulabilir. Ben UICollectionView yönetmek ve yemek tarifleri listesini göstermek için RecipeListViewController kullandım.

son sınıf RecipeListViewController: UIViewController {
  private (set) var collectionView: UICollectionView!
  let adapter = Adapter  ()
  private let emptyView = EmptyView (metin: "Yemek tarifleri bulunamadı!")
}

Bu RecipeListViewController embed HomeViewController var

/// Tariflerin listesini göster
son sınıf HomeViewController: UIViewController {
/// Bir tarif aldığınızda
  var select: ((Tarif) -> İptal)?
private var refreshControl = UIRefreshControl ()
  özel let tarifleriServis: TariflerServis
  private let searchComponent: SearchComponent
  private let recipeListViewController = RecipeListViewController ()
}

Kompozisyon ve Bağımlılık Enjeksiyonu

Bileşenleri oluşturmaya ve mümkün olduğunda kod oluşturmaya çalışıyorum. ImageService'in NetworkService ve CacheService'i kullandığını ve RecipeDetailViewController'ın Recipe ve RecipesService'ı kullandığını görüyoruz.

İdeal olarak nesneler kendi başlarına bağımlılıklar yaratmamalıdır. Bağımlılıklar dışarıda yaratılmalı ve kökten indirilmelidir. Bizim app kök bu AppDelegate ve AppFlowController yani bağımlılıklar buradan başlamalıdır.

App Transport Güvenliği

İOS 9’dan bu yana, tüm uygulamalar App Transport Security’yi benimsemelidir.

Uygulama Aktarım Güvenliği (ATS), bir uygulama ile arka ucu arasındaki güvenli bağlantılarda en iyi uygulamaları uygular. ATS, yanlışlıkla ifşa edilmesini önler, güvenli varsayılan davranış sağlar ve kabul edilmesi kolaydır; iOS 9 ve OS X v10.11'de varsayılan olarak açıktır. Yeni bir uygulama oluşturup oluşturmadığınızdan veya mevcut bir uygulamayı güncelleyip güncellemediğinizden bağımsız olarak ATS'yi mümkün olan en kısa sürede kabul etmelisiniz.

Bizim app, bazı resimler bir HTTP bağlantısı üzerinden elde edilir. Onu güvenlik kuralından hariç tutmamız gerekir, ancak yalnızca bu etki alanı için.

 NSAppTransportSecurity 

   NSExceptionDomains 
  
     food2fork.com 
    
       NSIncludesSubdomains 
      
       NSExceptionAllowsInsecureHTTPLoads 
      
    
  

Özel Kaydırılabilir Görünüm

Ayrıntı ekranı için UITableView ve UICollectionView'ı farklı hücre tipleriyle kullanabiliriz. Burada görünümler statik olmalıdır. UIStackView kullanarak yığın oluşturabiliriz. Daha fazla esneklik için, yalnızca UIScrollView'ü kullanabiliriz.

/// UIScrollView'da Otomatik Mizanpajı kullanarak dikey olarak düzen görünümü
son sınıf ScrollableView: UIView {
  private let scrollView = UIScrollView ()
  private let contentView = UIView ()
geçersiz kılma init (çerçeve: CGRect) {
    super.init (çerçeve: çerçeve)
scrollView.showsHorizontalScrollIndicator = false
    scrollView.alwaysBounceHorizontal = yanlış
    addSubview (scrollview)
scrollView.addSubview (contentView)
NSLayoutConstraint.activate ([
      scrollView.topAnchor.constraint (equalTo: topAnchor),
      scrollView.bottomAnchor.constraint (equalTo: bottomAnchor),
      scrollView.leftAnchor.constraint (equalTo: leftAnchor),
      scrollView.rightAnchor.constraint (equalTo: rightAnchor),
contentView.topAnchor.constraint (equalTo: scrollView.topAnchor),
      contentView.bottomAnchor.constraint (equalTo: scrollView.bottomAnchor),
      contentView.leftAnchor.constraint (equalTo: leftAnchor),
      contentView.rightAnchor.constraint (equalTo: rightAnchor)
    ])
  }
}

UIScrollView'ü kenarlara sabitleriz. ContentView üst ve alt çapasını UIScrollView'e sabitlerken, contentView'ın sol ve sağ bağlantılarını kendimize sabitleriz.

ContentView içindeki görünümlerde üst ve alt sınırlamalar vardır, bu yüzden genişlediklerinde contentView'i de genişletirler. UIScrollView, contentSize değerini belirlemek için bu contentView öğesinden Otomatik Mizanpaj bilgilerini kullanır. RecipeDetailView'de ScrollableView nasıl kullanılır?

scrollableView.setup (çiftler: [
  ScrollableView.Pair (görünüm: imageView, iç metin: UIEdgeInsets (üst: 8, sol: 0, alt: 0, sağ: 0)),
  ScrollableView.Pair (görünüm: ingredientHeaderView, iç metin: UIEdgeInsets (üst: 8, sol: 0, alt: 0, sağ: 0)),
  ScrollableView.Pair (görünüm: ingredientLabel, ek: UIEdgeInsets (üst: 4, sol: 8, alt: 0, sağ: 0)),
  ScrollableView.Pair (görünüm: infoHeaderView, iç metin: UIEdgeInsets (üst: 4, sol: 0, alt: 0, sağ: 0)),
  ScrollableView.Pair (görünüm: commandButton, ek: UIEdgeInsets (üst: 8, sol: 20, alt: 0, sağ: 20)),
  ScrollableView.Pair (görünüm: orijinalButton, ek: UIEdgeInsets (üst: 8, sol: 20, alt: 0, sağ: 20)),
  ScrollableView.Pair (görünüm: infoView, iç metin: UIEdgeInsets (üst: 16, sol: 0, alt: 20, sağ: 0))
])

Arama işlevi ekleme

İOS 8'den itibaren, arama çubuğuyla ve sonuç denetleyicisiyle varsayılan arama deneyimi elde etmek için UISearchController'ı kullanabiliriz. Takılabilir olması için arama işlevini SearchComponent’e dahil edeceğiz.

son sınıf SearchComponent: NSObject, UISearchResultsUpdating, UISearchBarDelegate {
  yemek tarifleri verHizmet: yemek tarifleriHizmet
  let searchController: UISearchController
  recipeListViewController = RecipeListViewController () işlevine izin verin
}

İOS 11'den başlayarak, UINavigationItem'de searchController adlı bir özellik var, bu da arama çubuğunu gezinti çubuğunda göstermeyi kolaylaştırıyor.

func add (viewController için: UIViewController) {
  eğer #available ise (iOS 11, *) {
    viewController.navigationItem.searchController = searchController
    viewController.navigationItem.hidesSearchBarWhenScrolling = false
  } Başka {
    viewController.navigationItem.titleView = searchController.searchBar
  }
viewController.definesPresentationContext = true
}

Bu uygulamada, şu ana kadar hidesNavigationBarDuringPresentation'ı devre dışı bırakmamız gerekiyor, çünkü oldukça sıkıcı. Umarım gelecekteki iOS güncellemelerinde çözümlenir.

Sunum içeriğini anlama

Sunum içeriğini anlamak, denetleyici sunumu için çok önemlidir. Aramada, searchResultsController kullanıyoruz.

self.searchController = UISearchController (searchResultsController: recipeListViewController)

Kaynak görünüm denetleyicisinde (arama çubuğunu eklediğimiz görünüm denetleyicisi) definesPresentationContext'i kullanmamız gerekir. Bu olmadan, tam ekrandan sunulacak searchResultsController'ı elde ederiz !!!

Bir görünüm denetleyicisini sunmak için currentContext veya overCurrentContext stilini kullanırken, bu özellik, görünüm denetleyicisi hiyerarşisinde bulunan mevcut görüntü denetleyicisinin gerçekte yeni içerik tarafından kapsandığını kontrol eder. Bağlam tabanlı bir sunum gerçekleştiğinde, UIKit sunum görünümü denetleyicisinde başlar ve görüntüleme denetleyicisi hiyerarşisini kaldırır. Bu özellik için değeri doğru olan bir görünüm denetleyicisi bulduğunda, bu görünüm denetleyicisinden yeni görünüm denetleyicisini göstermesini ister. Görünüm denetleyicisi sunum içeriğini tanımlamazsa, UIKit, pencerenin kök görünüm denetleyicisine sunumu işlemesini ister.
Bu özellik için varsayılan değer yanlıştır. UINavigationController gibi bazı sistem tarafından sağlanan görünüm denetleyicileri, varsayılan değeri true olarak değiştirir.

Arama eylemlerini yeniden başlatma

Kullanıcıların arama çubuğundaki her tuş vuruşu için arama istekleri gerçekleştirmemeliyiz. Bu nedenle, bir çeşit azaltma gerekir. DispatchWorkItem'i eylemi içine almak ve sıraya göndermek için kullanabiliriz. Daha sonra iptal edebiliriz.

son sınıf Debouncer {
  özel izin gecikme: TimeInterval
  özel var workItem: DispatchWorkItem?
init (gecikme: TimeInterval) {
    self.delay = gecikme
  }
/// Biraz gecikmeden sonra eylemi tetikle
  func programı (eylem: @escaping () -> İptal) {
    Workitem? .cancel ()
    workItem = DispatchWorkItem (block: eylem)
    DispatchQueue.main.asyncAfter (son tarih: .now () + gecikme, çalıştırma: workItem!)
  }
}

Tersine çevrilmiş beklenti ile ayrılma testi

Debouncer'ı test etmek için, ters modda XCTest beklentisini kullanabiliriz. Bununla ilgili daha fazla bilgi için Asenkron Swift kodu test edin.

Test sırasında bir durumun meydana gelmediğini kontrol etmek için, beklenmeyen durum oluştuğunda gerçekleşen bir beklenti oluşturun ve isInverted özelliğini true değerine ayarlayın. Ters beklenti yerine getirilirse testiniz derhal başarısız olur.
sınıf DebouncerTests: XCTestCase {
  func testDebouncing () {
    cancelExpectation = self.expectation (açıklama: "iptal") için izin verin
    cancelExpectation.isInverted = true
completeExpectation = self.expectation (açıklama: "tamamlandı")
    izin ver debouncer = Debouncer (gecikme: 0.3)
debouncer.schedule {
      cancelExpectation.fulfill ()
    }
debouncer.schedule {
      completeExpectation.fulfill ()
    }
bekleyin (için: [cancelExpectation, completeExpectation], zaman aşımı: 1)
  }
}

Kullanıcı arayüzünü UITest'lerle test etme

Bazen küçük yeniden düzenlemenin büyük bir etkisi olabilir. Devre dışı bırakılmış bir düğme daha sonra kullanılamaz ekranlara yol açabilir. UITest, uygulamanın bütünlüğünü ve işlevsel yönlerini sağlamaya yardımcı olur. Test bildirimsel olmalıdır. Robot desenini kullanabiliriz.

sınıf TariflerUITest'ler: XCTestCase {
  var app: XCUIApplication!
  func setUp () işlevini geçersiz kıl
    super.setUp ()
    continuAfterFailure = yanlış
    app = XCUIApplication ()
  }
  func testScrolling () {
    app.launch ()
    collectionView = app.collectionViews.element (izin verme: 0)
    collectionView.swipeUp ()
    collectionView.swipeUp ()
  }
  func testGoToDetail () {
    app.launch ()
    collectionView = app.collectionViews.element (izin verme: 0)
    firstCell = collectionView.cells.element (boundBy: 0) olsun
    firstCell.tap ()
  }
}

İşte testle ilgili makalelerimden bazıları.

  • İOS'ta Facebook ile giriş yaparak UITest'leri çalıştırma
  • O Zaman Verilen Desen İle Swift'de Test Etme

Ana iplik koruması

Kullanıcı arabirimine arka plan kuyruğundan erişmek olası sorunlara neden olabilir. Daha önce, MainThreadGuard'ı kullanmam gerekiyordu, şimdi Xcode 9'un Main Thread Checker'ı var, bunu Xcode'da etkinleştirdim.

Ana İş Parçası Denetleyicisi, arka plan iş parçacığında AppKit, UIKit ve diğer API'lerin geçersiz kullanımını algılayan Swift ve C dilleri için kullanılan bağımsız bir araçtır. Ana iş parçacığı dışındaki bir iş parçacığında kullanıcı arabirimi güncelleştirilmesi, eksik kullanıcı arabirimi güncelleştirmelerine, görsel hatalara, veri bozulmalarına ve çökmelere neden olabilecek yaygın bir hatadır.

Performansları ve sorunları ölçme

Enstrümanları uygulamayı tam olarak belirlemek için kullanabiliriz. Hızlı ölçüm için Debug Navigator sekmesine yönelebilir ve CPU, Bellek ve Ağ kullanımını görebiliriz. Aletler hakkında daha fazla bilgi edinmek için bu harika makaleye göz atın.

Bahçesi ile prototipleme

Oyun alanı prototip ve uygulamalar oluşturmak için önerilen yoldur. WWDC 2018'de, Apple, Tren modelini eğitmek için Oyun Alanını destekleyen Create ML'yi tanıttı. Swift'deki oyun alanı odaklı gelişim hakkında daha fazla bilgi edinmek için bu harika makaleye göz atın.

Buradan nereye

Bu kadarını yaptığın için teşekkürler. Umarım yararlı bir şeyler öğrendin. Bir şeyi öğrenmenin en iyi yolu sadece yapmaktır. Aynı kodu tekrar tekrar yazarsanız, bileşen olarak yapın. Bir sorun size zor anlar yaşarsa, bunu yazın. Deneyiminizi dünya ile paylaşın, çok şey öğreneceksiniz.

İOS gelişimi hakkında daha fazla bilgi edinmek için iOS geliştirmesini öğrenmek için En İyi yerler makalesini incelemenizi öneririz.

Herhangi bir sorunuz, yorumunuz veya geri bildiriminiz varsa, yorumlara eklemeyi unutmayın. Bu makaleyi yararlı bulduysanız, alkışlamayı unutmayın.

Bu yayını beğendiyseniz, diğer makalelerimi ve uygulamalarımı ziyaret etmeyi düşünün