UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Arayüzümüzü tasarlama

Uygulamamız tüm fırtına görsellerini doğru bir şekilde yüklüyor, ama onlarla ilginç birşey yapmıyor; Xcode konsoluna yazdırmak derleme için faydalı, evet ama, sizi temin ederim ki bu, uygulamanızı çok satanlar arasına sokmaz!

Bunu düzeltmek için, bir sonraki amacımız görsellerin listeleneceği bir kullanıcı arayüzü oluşturmak olacak; böylece kullanıcılar içlerinden birini seçecibilirler. UIKit –yani iOS'un kullanıcı arayüzü çalışma çerçevesi– birçok dahili arayüz aracına sahiptir. Dolayısıyla biz de bunları kullanarak, kullanıcıların beklediği şekilde görünen ve çalışan güçlü uygulamalar yaratacağız.

Bu uygulama için, ana kullanıcı arayüz bileşenimiz UITableViewController olarak adlandırılıyor. Apple'ın en temel ekran tipi olan UIViewController bileşenini baz almıştır. Kaydırıldığında veya seçildiğinde veri satırlarını gösterebilme kabiliyeti ekler. Bu UITableViewController'ı Settings, Mail, Notes, Health ve daha birçok uygulamada görebilirsiniz; o kadar güçlü, esnek ve hızlıdır ki, birçok uygulamada kullanıldığını görmek şaşırtıcı değil.

Varolan ViewController ekranımız, UIViewController temellidir, ama bunun yerine UITableViewController temelli olmasını istiyoruz. Bu çok bir şey değil, ama Interface Builder (Arayüz Oluşturucu) adlı Xcode'un yeni bir parçasıyla tanışacaksınız.

Hemen Interface Builder'a geçeceğiz. Yine de önce ViewController.swift dosyasında küçük bir değişiklik yapmamız gerekli. Şu satırı bulunuz:

class ViewController: UIViewController {

Bu satır "ViewController adıyla yeni bir ekran oluştur ve onu Apple'ın kendi UIViewController ekranı üzerine inşaat et" diyor. O satırı, şu şekilde değiştirmenizi istiyorum:

class ViewController: UITableViewController {

Bu sadece küçük bir fark, ama en önemli olanı: ViewController ekranı şimdi, UIViewController yerine UITableViewController ekranından türetiliyor anlamına geliyor. Bu da bize, bir anda görebileceğiniz kadar büyük miktarda fazladan fonksiyonellik veriyor.

Sahne arkasında, UITableViewController hala UIViewController üzerine inşa ediliyor; buna "class hierarchy" (sınıf hiyerarşisi) deniyor ve fonksiyonelliği hızlı bir şekilde oluşturmanın yaygın bir yoludur.

Kodu ViewController için değiştirdik, böylece artık UITableViewController üzerine inşa ediliyor. Ama eşlenecek kullanıcı arayüzünü de değiştirmemiz gerekiyor. Kullanıcı arayüzlerini, isterseniz tamamen kod yazarak oluşturabilirsiniz; zaten birçok geliştirici de böyle yapıyor. Ama daha yaygın bir şekilde Interface Builder adlı grafiksel düzenleyiciyi kullanarak oluşturuyorlar. Interface Builder'a (genellikle sadece "IB" denir), ViewController'ın bir table view controller (tablo ekranı denetleyicisi) olduğunu söylememiz gerekiyor ki, böylece kodumuzda yaptığımız değişikliğe uygun olsun.

Bu noktaya kadar, tamamen ViewController.swift adlı dosyada çalıştık, artık şimdi Main.storyboard dosyasını seçmek için project navigator'ünü (proje kılavuzu) kullanmanızı rica ediyorum. Storyboard'lar, uygulamanız için arayüzleri içerir ve tek bir ekran üzerinde hepsini veya bir kısmını görselleştirmenizi sağlar.

Main.storyboard'ı seçtiğinizde, Interface Builder görsel düzenleyicisine geçeceksiniz; aşağıdaki gibi bir ekran görmelisiniz:

Single View (Tek Arayüzlü) Uygulama şablonu size şekillendirmeniz için büyük, boş bir kanvas sağlıyor.

Bu büyük beyaz boşluk, uygulamalar çalıştığında oluşan büyük beyaz ekranı üreten şeydir. Eğer bu boşluğa yeni bir bileşen sürükleyip bırakırsanız, uygulama çalıştığında görünür hale gelirler. Ama tabi yapmak istediğimiz şey bu değil; aslında, biz bu büyük boşluğu da hiç istemiyoruz, o yüzden onu sileceğiz.

Interface Builder içindeki elemanları görüntülemenin, seçmenin, düzenlemenin ve silmenin en iyi yolu, dokümanın ana hatlarını kullanmaktır. Ama bu gizlenmiş olarak gelecektir; ilk iş onu görünür hale getirmek olsun. Editor menüsüne gidin ve Show Document Outline'ı (Doküman'ın Ana Hatlarını Göster) seçin; muhtemelen üstten üçüncü seçenek. Eğer bunun yerine Hide Document Outline'ı (Doküman'ın Ana Hatlarını Gizle) görüyorsanız, dokümanın ana hatları zaten görünür halde demektir.

Dokümanın ana hatları, storyboard'ınızdaki tüm ekranlarınızda kullanılan bileşenlerin hepsini gösterir. Orada “View Controller Scene” (Ekran Denetleyicisi Sahnesi) seçeneğini gördüyseniz, lütfen seçin. Ardından da silmek için klavyenizdeki Backspace (Geri Silme) tuşuna basın.

Sıkıcı eski UIViewController yerine, kodumuzda yaptığımız değişikliklerle eşleşmesi için eğlenceli yeni bir UITableViewController istiyoruz. Bir tane oluşturmak için, nesne kütüphanesini açılmasını sağlayacak olan Cmd+Shift+L tuşlarına basınız. Alternatif olarak, eğer tuştakımı kestirmelerinden hoşlanmıyorsanız, bunun yerine View menüsüne gidip, Libraries > Show'u seçebilirsiniz.

Nesne kütüphanesi, Xcode penceresi üzerinde kayar ve sürükleyip çıkarabileceğiniz ve içinizden geçtiği gibi yeniden düzenleyebileceğiniz grafiksel bileşenler seçkisini içerir. Oldukça çok bileşen barındırır, o yüzden "Filter" kutusuna birkaç harf girerek seçkiyi daraltmayı kullanışlı bulabilirsiniz.

Tavsiye: Birşeyi sürükleyip çıkardıktan sonra da nesne kütüphanesinin açık kalmasını istiyorsanız, Alt+Cmd+Shift+L tuşlarını kullanın; böylece yeri değiştirilebilen, ölçülendirilebilir bir pencereye sahip olursunuz.

Tam da şimdi, Table View Controller olarak adlandırdığmız bir bileşen istiyoruz. Eğer "Filter" kutusuna "table" sözcüğünü tuşlarsanız, Table View Controller, Table View ve Table View Cell seçeneklerini göreceksiniz. Hepsi farklı şeylerdir, o yüzden sarı arkaplana sahip simgesi olan Table View Controller'i seçtiğinizden emin olun.

Table View Controller bileşenine tıklayın ve ardından bir önceki görüntü denetleyicisinin olduğu yerde bulunan büyük açık boşluğun içine onu sürükleyin. Storyboard kanvası üzerine tablo ekranı denetleyicisini bıraktığınızda, aşağıdakine benzer bir ekrana dönüşecek:

Orijinal görüntü denetleyicisini silip, yeni bir tablo ekranı denetleyicisi ile değiştirdiğinizde, Xcode bu şekilde görünür.

Kullanıcı arayüzü için son dokunuşlar

Burada işimiz bitmeden önce, birkaç küçük değişiklik daha yapmamız gerekiyor.

İlk olarak, bu storyboard'taki tablo ekranı denetleyicisinin, ViewController.swift içindeki kodda geçen görüntü denetleyicisi ile aynı olduğunu Xcode'a söylememiz gerekiyor. Bunun için, Alt+Cmd+3 tuşlarına basarak (veya View > Utilities > Show Identity Inspector'u seçerek) identity inspector'ı (kimlik denetmeni) etkinleştiriyoruz ve ardından en üstte "Class" adlı kutuyu arıyoruz. Orada açık gri renkli bir metin olarak “UITableViewController” yazılı olacak. Ama eğer sağındaki ok işaretine tıklarsanız, “ViewController” sözcüğünü içeren bir aşağı açılır menü göreceksiniz. Şimdi lütfen onu seçin.

İkinci olarak, bu yeni tablo ekranı denetleyicisinin uygulama ilk çalıştığında neyi göstermesi gerektiğini Xcode'a söylemeliyiz. Bunun için, Alt+Cmd+4 tuşlarına basarak (veya View > Utilities > Show Attributes Inspector'ı seçerek) attributes inspector'ı (nitelik denetmeni) etkinleştiriyoruz ve ardından “Is Initial View Controller” (Başlangıç Ekran Denetleyicisi'dir) onay kutucuğunu bulup, seçili olduğundan emin oluyoruz.

Üçüncü olarak, yeni tablo ekranı denetleyicisinin içine bakmak için doküman ana hatlarını kullanmak istiyorum. İçerisinde bir "Table View" olduğunu göreceksiniz ki, sıra sıra "Cell" (hücre) içerir. Bir tablo ekranı hücresi, bir veri satırı göstermekten sorumludur ve biz de her bir hücrede bir görsel adını görüntüleyeceğiz.

O zaman lütfen attributes inspector'daki "Cell"i seçin, identifier (tanımlayıcı) olarak işaretlenmiş metin alanı içine "Picture" yazısını girin. Hazır buradayken, attributes inspector'ın üst tarafındaki Style seçeneğini değiştirin; artık Custom yazılı olması gerekli, ama lütfen onu Basic olarak değiştirin.

Son olarak, tüm bu tablo ekranı denetleyicisini başka birşeyin içine yerleştireceğiz. Bu öyle birşey olmalı ki, yapılandırmamıza veya üzerinde düşünmemize gerek kalmasın, ama aynı zamanda da iOS üzerinde son derece yaygın olan bir kullanıcı arayüzü olsun; sanırım hemen tanıyacaksınız. Adına navigation controller (kılavuz denetleyicisi) deniyor ve hali hazırda Settings ve Mail gibi uygulamalarda görüyorsunuz; ekranın en üstünde gri bir bar sunar ve iOS üzerinde ekranlar arasında dolaştığınızda oluşan sağdan sola kaydırma animasyonunu yapmaktan sorumludur.

Tablo ekranı denetleyicimizi, bir kılavuz denetleyicisi içine yerleştirmek için, ihtiyacımız olan tek şey Editor menüsüne gitmek ve Embed In > Navigation Controller'ı seçmektir. Interface Builder, sahip olduğunuz ekran denetleyicisini sağa hareket ettirecek ve onun çevresine bir kılavuz denetleyicisi ekleyecektir; şimdi tablo ekranınızın üzerinde uyarlanmış bir gri bar görmeniz gerekiyor. Aynı zamanda “Is Initial View Controller” özelliğini de kılavuz denetleyicisine taşır.

Tam bu noktada çalışmanızın sonuçlarını görebilmek için yeterince yol almış bulunuyorsunuz: Xcode'un Başlat butonuna veya biraz daha seçkin hissetmek istiyorsanız Cmd+R tuşlarına basın şimdi. Kodunuz çalıştığında, düz beyaz boşluğun geniş, boş bir tablo ekranı ile yer değiştirdiğini göreceksiniz. Eğer tıklarsanız veya çekiştirirseniz, içinde henüz herhangi bir veri olmamasına rağmen, ekranda kaydığını ve umduğunuz gibi sektiğini göreceksiniz. Aynı zamanda en üstte gri kılavuz barını da görmeniz gerekli; bu daha sonra önemli hale gelecek.

Birçok satırı gösterme

Sonraki adım, tablo ekranının veri göstermesini sağlamak. Kesin bir şekilde, her bir satırda "nssl" görsellerinin listesini göstermesini istiyoruz. Apple'ın UITableViewController veri tipi birçok şey için ön tanımlı davranış sağlar, ama ön tanımlı olarak orada hiç satırın olmadığını da söyler.

ViewController ekranımız UITableViewController üzerine kurulmuştur ve gereken değişikliklerin yapılabilmesini sağlayan Apple'ın tablo ekranının ön tanımlı davranışını geçersiz kılacaktır. Sadece istediğiniz bit'leri geçersiz kılmanız gerekiyor; ön tanımlı değerler tamamen duyarlıdır.

Tablonun satırlarımızı göstermesi için, iki davranışı geçersiz kılmamız gerekiyor: Kaç tane satır gösterilecek ve her satır ne içerecek. Bu, özellikle adlandırılmış iki metot yazılarak yapılır, ama Swift diline yeni olduğunuz için, başta biraz garip görünebilir. Herkesin takip edebildiğinden emin olmak için, bunu ağırdan alacağım; sonuçta bu ilk projemiz!

Tabloda kaç tane satır görünmesi gerektiğini ayarlayan metotla başlayalım. viewDidLoad()'un sonuna bu kodu ekleyin; eğer "numberof" diye yazmaya başlarsanız, Xcode'un sizin için işin çoğunu yapan kod tamamlayıcısını kullanabilirsiniz:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return pictures.count
}

Not: Bu "viewDidLoad()'un sonundan sonra" ile kastedilen şey, kapalı parantezden sonradır.

Bu metot, ilk başta kafa karışıklığına yol açan üç defa "table view" sözcüklerini içeriyor. O yüzden ne anlama geldiklerini iyice anlayalım.

  • override anahtar sözcüğü, metodun zaten tanımlanmış olduğu ve varolan davranışını geçersiz kılacak yeni bir davranış kazandıracağımız anlamına geliyor. Eğer geçersiz kılmasaydınız, o zaman daha önceden tanımlanan metot çalıştırılacak ve bu örnekte herhangi bir satır olmadığını söyleyecekti.
  • func anahtar sözcüğü yeni bir fonksiyon veya yeni bir metot başlatıyor; Swift dili bu anahtar sözcüğünü her iki anlamda da kullanıyor. Teknik olarak konuşmak gerekirse, bir metot, bir class'ın içindeki bir fonksiyondur; tam olarak bizim ViewController gibi. Başka bir fark yoktur aralarında.
  • metodun adından sonra ise tableView geliyor. Bu kulağa pek kullanışlı gelmiyor, ama Apple'ın metotları tanımlama yolu, kullanışlı bir şekilde isimlendirilen parametrelerin içine aktarılacak bilgiyi sağlamaktır. Bu durumda ise, ilk önce aktarılacak olan, kodu tetikleyen tablo ekranıdır. Bir tablo ekranı, sonuca vardığınız gibi, görsellerimizin adlarını içerecek olan ve aşağı yukarı kaydırılabilen şeydir; iOS içindeki bir çekirdek bileşendir.
  • Belirtildiği gibi, ardından kodu tetikleyecek olan tablo ekranı tableView: UITableView gelir. Ama bu aynı anda iki bilgi içerir: metodun içindeki tablo ekranına göndermek için kullanacağımız isim tableView ve neyin ne olduğunu açıklayacak bit olan UITableView veri tipi.
  • metodun en önemli kısmı ise ardından geliyor: numberOfRowsInSection section: Int. Bu, metodun aslında ne işe yaradığını açıklıyor. Bir tablo ekranı içereceğini biliyoruz, çünkü metodun adı bu, ama numberOfRowsInSection parçası asıl işi yapan kısım: Bu kod, iOS tablo ekranında kaç satır olduğunu bilmek istediğinde tetiklenecektir. Tıpkı Contacts (Kişiler) uygulamasında ilk harfine göre isimlerin ayrıldığı gibi, tablo ekranının da bölümlere ayrılabileceği section vardır. Bizim sadece bir bölümümüz var, o yüzden bu sayıyı göz ardı edebiliriz. Int kısmı ise, 3, 30, ve 35678 rakamları gibi "bu bir tam sayı olacak" anlamına geliyor.
  • Son olarak -> Int ifadesi, "bu metot, bir tam sayı döndürmek zorunda" anlamına geliyor; yani tabloda gösterilecek satırların sayısını.

Bıraktığım bir şey daha vardı ama bunu özellikle es geçtim: Swift kariyerinizin bu noktasında biraz kafa karıştırıcı bir şey. Oradaki _ ifadesine dikkat ettiniz mi? Bu bir alttan çizgi. metodun çağrılma şeklini değiştiriyor. Bunu açıklayabilmek için basit bir fonksiyon veriyorum:

func doStuff(thing: String) {
    // do stuff with "thing"
}

İçi boş, çünkü ne içerdiğinin bir önemi yok. Bunun yerine, onu nasıl çağırdığımıza odaklanalım. Şu şekilde çağırılıyor:

doStuff(thing: "Hello")

doStuff() fonksiyonunu çağırdığınızda, thing parametresinin adını yazmanız gerekiyor. Bu bir Swift özelliği ve kodu daha okunabilir kılıyor. Yine de bazen, ilk parametre genellikle metot isminin içinde derlendiğinden, bir isme sahip olması aslında çok da bir anlam ifade etmiyor.

Bu şekilde olduğunda, aşağıdaki gibi alttan çizgi karakteri kullanabilirsiniz:

func doStuff(_ thing: String) {
    // do stuff with "thing"
}

Bu, "fonksiyonu çağırdığımda thing yazmak istemiyorum, ama fonksiyonun içinde aktarılan değeri kullanmak için thing kullanmak istiyorum" anlamına geliyor.

Bizim tablo ekranı metotumuzda olan şey de budur. metot tableView() olarak adlandırıldı, çünkü onun ilk parametresi çalıştığınız "table view" (tablo ekranı). tableView(tableView: someTableView) şeklinde yazmak çok bir anlam ifade etmezdi, o yüzden alttan çizgi kullanımı, tableView(someTableView) yazabileceğiniz anlamına geliyor.

Swift metotlarının nasıl göründüğünü ve çalıştığını anlamak kolaymış gibi yapmayacağım. Ama eğer anlamadıysanız, yapmanız gereken en iyi şey kaygılanmamaktır, çünkü birkaç saatlik kodlamadan sonra bunlar daha doğal hale gelecek.

En azından, onların isimlerini (tableView) ve parametrelerini kullanarak bu metotlara ulaşıldığını bilmeniz gerekiyor. İsimleri olmayan parametrelere alttan çizgi ile başvurulabilir: _. Böylece ona tam adını vermek için, yazdığınız metot tableView(_:numberOfRowsInSection:) olarak ifade ediliyor. Evet, biliyorum, çoğu kişi genellikle tam da bu yüzden, örneğin "numberOfRowsInSection metodundaki gibi", önemli bir bit hakkında konuşuyor.

metodun içine sadece bir satır kod yazdık: return pictures.count. Bu, "dizimizdeki görsellerin sayısını geri döndür" anlamına geliyor. Böylece, oradaki görsel sayısı kadar tablo satırı olmasını istiyoruz.

Hücreleri kuyruktan çıkarmak

Uygulamanın bu aşamasını tamamlamak için yazmamız gereken iki metodun ilki bu. İkincisi ise, her satırın neye benzeyeceğini belirlemek. Bunun için bir önceki metottakine benzer bir isimlendirme kullanılır. Şimdi bu kodu ekleyin:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Picture", for: indexPath)
    cell.textLabel?.text = pictures[indexPath.row]
    return cell
}

Tekrar parçalara ayıralım ve nasıl çalıştığını daha iyi görelim.

İlk olarak, override func tableView(_ tableView: UITableView ifadesi, bir önceki metotla özdeştir: metot adı tableView() ifadesidir ve bir tablo ekranına ilk parametresi olarak aktarılacaktır. _ ifadesi ise harici olarak bir isim göndermesine gerek olmadığını söylüyor, çünkü metot adıyla aynı.

İkincisi, metot adının önemli bir parçası olan cellForRowAt indexPath: IndexPath ifadesidir. metot cellForRowAt çağırıyor ve bir satır gerektiğinde çağırılmış olacak. Gösterilecek satır parametrede belirleniyor: IndexPath tipinde bir indexPath. Bu hem bölüm numarasını, hem de satır numarasını içeren bir veri tipidir. Bizim sadece bir bölümümüz var, dolayısıyla onu göz ardı edip sadece satır numarası olarak kullanabiliriz.

Üçüncüsü ise, -> UITableViewCell ifadesi, bu metodun bir tablo ekranı hücresi döndürmek zorunda olduğu anlamına gelir. Hatırlarsanız, Interface Builder içinde bir adet oluşturmuş ve ona "Picture" tanımlayıcısını vermiştik. Şimdi bunu kullanmak istiyoruz.

Biraz iOS sihrinin işin içine girdiği yer işte burası: Eğer Settings uygulamasına bakarsanız, telefonunuzun ekran ölçülerine bağlı olarak aynı anda yaklaşık 12 satırın sığdığını göreceksiniz.

CPU (işlemci) zamanlaması ve RAM (bellek) alanı kazanmak için, iOS sadece çalışmak için gerektiği kadar satır oluşturur. Bir satır, ekranın üstüne kayıp yok olduğunda, iOS onu atacak ve alttan gelen ve geri dönüşüme hazır olarak yeniden kullanılmak için kuyrukta bekleyen yeni satırın içine yerleştirecek. Bu, saniyede yüzlerce satırı kaydırabileceğiniz anlamına gelir ve iOS tembel davranarak yeni tablo ekranı hücreleri oluşturmaktan kaçınabilir; kısaca varolanı geri dönüştürerek kullanır.

Bu fonksiyonellik doğrudan iOS içinde gerçekleştirilir ve şu satırdaki kodumuzun yaptığı da tam olarak budur:

let cell = tableView.dequeueReusableCell(withIdentifier: "Picture", for: indexPath)

Bu, tablodan geri dönüştürülen bir hücreyi kuyruktan çıkartarak, cell adında yeni bir sabit oluşturur. Dönüştürmek istediğimiz hücre tipinin tanımlayıcısını (identifier) ona vermek zorundayız, yani Interface Builder'da verdiğimiz aynı ismi girmeliyiz: "Picture". Talep edilen dizin yolunu (index path) aynı şekilde geçiyoruz; bu, tablo ekranı tarafından dahili olarak kullanılır.

Bu, çalışabileceğimiz bir tablo ekranı hücresinden, ekran bilgisi olarak bize dönecektir. İsterseniz (daha fazlası daha sonra!), kendinize özel tablo ekranı hücresi oluşturabilirsiniz, ama biz bir metin etiketine sahip olan dahili Temel stili kullanacağız. Burası ikinci satırın işe dahil olduğu yer: Hücrenin metin etiketine dizimizdeki görselin adıyla aynı metin burada veriliyor. Kodumuza tekrar bakalım:

cell.textLabel?.text = pictures[indexPath.row]

cell değişkeni, textLabel olarak adlandırılan bir özelliğe sahip, ama bu bir optional: Burada bir metin etiketi olabilir de, olmayabilir de; örneğin kendiniz tasarlamışsanız. Bir metin etiketi olup olmadığını anlamak için ayrıca kontroller yazmaktansa, Swift dili –textLabel? şeklinde– bir soru işareti kullanmamıza izin veriyor. Bu "eğer orada gerçekten bir metin varsa şunu yap, ama eğer yoksa hiçbir şey yapma" anlamına geliyor.

pictures adlı dizimizden doğru görselin adı olacak etiket metnini ayarlamak istiyoruz ve bu kod tam olarak bunu yapıyor. indexPath.row ifadesi, yüklenmesi için bizden istenen satır sayısını içerecek. Böylece onu, pictures dizisinden ilgili görseli okumak için kullanacağız ve hücrenin metin etiketine yerleştireceğiz.

Metottaki son satır return cell ifadesidir. Bu metodun bir tablo ekranı hücresi döndürmesi beklendiğini hatırlayın. Dolayısıyla, oluşturduğumuz birini geri göndermemiz gerekiyor; işte bu da return cell ifadesinin yaptığı iş.

Bu iki küçük sevimli metotla, kodunuzu tekrar çalıştırıp, nasıl göründüğüne bakabilirsiniz. Her şey yolunda giderse, her birinde farklı bir görsel adı yazan 10 tane tablo ekranı hücresi görmelisiniz şimdi. Eğer herhangi birinin üzerine tıklarsanız, rengi griye dönecektir, ama başka bir şey olmayacaktır. Şimdi bunu düzeltelim...

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.