iOS geliştiricisinin metamorfozu: tuist

👋 pbxproj, xcodeproj, xcworkspace 👋 yallah gitignore'a

·

7 min read

iOS geliştiricisinin metamorfozu: tuist

Çalıştığım şirketlerde en çok dalga geçtiğimiz program bu çekiç logolu program oldu: internet 'meme'leri yaptık paylaştık, Slack'e garip ekran görüntüleri gönderdik, thread'lerde kah güldük kah eğlendik. Visual Studio'ya ve Intellij'ci Android ya da backend yazan iş arkadaşlarımızın bıyık altından bakışlarına henüz hiç geri vole vuramadık. Xcode update'lerinden umudu çoktan kestik, yeter ki daha çok bozulmasın, elimizdekinden olmayalım. Sürekli crash olan, kod kaybı yaşatan, devasa büyük, xib'ten unarchive etmesi bile en az yarım saatinizi alan hantal bir uygulama bu. Ha [AppCode]( AppCode: Smart Swift/Objective-C IDE for iOS & macOS ...jetbrains.com › o... ) daha mı iyi? Yok onu da denedik gördük, aynısının başka bir rengi. Ne yapalım, zevk almaya bakacağız. En az zararla kodumuzu yazabilirsek, o da iyi.

Xcode bu kadar kötü olunca, Xcode projeleri, yani o çift tıklayıp açtığımız .xcodeproj ya da .xcworkspace klasörleri ve içindeki dosyaların yapıları da bir garip olmuş tabii ki. Android'çiler XML ile UI yapıyor diye dalga geçiyoruz, o zaman kendimize dönüp koskoca projeyi böyle standart olmayan bir formatta ayağa kaldırmak nedir demek, çuvaldızı biraz kendimize de batırmamız gerekir.

Aşağıda biraz sonra tuist ile oluşturacağım projenin pbxproj dosyasından bir örnek. 🤮

/* Begin PBXBuildFile section */
        1F44150A659AA0555426C718 /* ProjemUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70CE45157BA22C3F39682339 /* ProjemUI.framework */; };
        4117A4AB964C06CD27106B77 /* ProjemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043163F177A27A253712F23C /* ProjemTests.swift */; };
        5193730CDE078B6405F2A976 /* Assets+Projem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 
{ ... }
{ ... }
        B2B4032F0086DB46D82FAAC3 /* ProjemKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEC96509FC38E0CCF15225BB /* ProjemKitTests.swift */; };
        C8FA70ECEE39C955495655C4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01BD1BAB864AF3BE9C68D81D /* Assets.xcassets */; };
        FBB6AC91D22F871F1246666C /* ProjemApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D786B0E0E85998BF253D881C /* ProjemApp.swift */; };
        FE4B22D8761B836D3CCF1B43 /* ProjemUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 70CE45157BA22C3F39682339 /* ProjemUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */

Bu tip tanınmayan bir formatla çalışmanın herhalde en kötü tarafı, merge conflict'ler ya da Code Review sürecinde gördüğümüz garip rastgele kod değişiklikleridir. İş arkadaşınızla proje dosyasının birbirine yakın yerlerini mi değiştiriyorsunuz, conflict çıkması çok olası ve bu conflict'i iyi çözemezsen proje açılmaz öyle kalırsın, al bakalım şimdi o merge işlemini geri, başka bir rastgele çözüm dene, bunları yaşadık gördük. Zaten bu yüzden vazgeçmedik mi yine XML formunda saklanan Storyboard'lardan, XIB'lerden?

2015'te, Apple, Swift Package Manager (SPM) projesini open source yapınca tüm iOS'çular kodu incelemeye başladık, en azından swift dilinin kendisi gibi c++ ile değil swift ile yazıldığı için biraz anlıyorduk, anlamanın ötesine gidenler projeye katkı yaptılar, projedeki bazı inovasyonları kendi projelerinde kullandılar, o dönem bir SPM dependency kambriyen patlaması yaşandı, binlerce SPM projesi ortaya çıktı ve GitHub'da paylaşıldı. Bugün bu repolar bir index'te toplanıyor.

Birkaç meraklı ekip SPM projesinin içerisinde, Xcode projesi oluşturan kısımla ilgilendi. Bildiğiniz gibi SPM package'ları bir Xcode projesine ihtiyaç duymuyor. Xcode'dan Open Project diyip SPM projenizin klasörünü gösterince proje olmasa da Xcode'da açabiliyorsunuz. Ancak siz yine de proje ile çalışacağım derseniz o zaman --generate-project diyip o berbat pbxproj dosyasını yaratıp Xcode'da eskisi gibi açabiliyorsunuz.

İşte bahsettiğim meraklı ekip open source SPM projesindeki bu generator kısmını aldı ve hızlıca yeni bir proje yarattılar. XcodeGen, xcake ve konumuz tuist böylece hayata gelmiş oldu.

Diğer alternatifleri geçiyorum, tuist kısaca, Cocoa projenizi Xcode projesi olmadan yönetmenizi sağlıyor. Bunu Android'çilerin yıllardır aşina olduğu tarzda bir manifest dosyası üzerinden yapıyor. Siz yalnızca root klasörünüzde bir Project.swift dosyası yaratıyorsunuz, bunun içerisinde Xcode projesinde yaptığınız ayarları giriyorsunuz ardından $ tuist generate komutu ile Xcode projeniz oluşturuluyor, zaten bundan sonrası aynı eskisi gibi, kodunuzu build ediyorsunuz ve aynen devam.

Bu durumda kar ediyorsunuz çünkü artık o bahsettiğimiz .xcodeproj ya da .xcworkspace klasörlerinizi .gitignore'a ekliyorsunuz, bu veda demek: artık code review sürecinde bu saçma dosyanın içeriğini görmeyeceksiniz demek, iş arkadaşınız hızlıca geçmek zorunda kalmayacak, yani artık baş ağrısı yok. Her proje ayarı, zaten aşina olduğumuz, ustası olduğumuz swift diliyle yazılıyor. Zaten bu yüzden Cocoapods'tan da SPM'e geçmiştik, ruby de neydi? İnsanın evi gibi yoktu.

Bu noktadan sonrası artık yaparak öğrenme. tuist'i kullanmanın birkaç yolu var, biz binary'sini repomuzda saklıyoruz, CI'da onu çalıştırıyoruz. Kodu clone'ladığımızda localimizde de bu binary'yi kullanıyoruz böylece büyük bir ekipte herkesin aynı versiyonda olduğundan ve aynı binary'yi kullandığından emin olabiliyoruz. Bu tip kısıtlar büyük bir ekipte çalışmanın en önemli şartı olabilir.

tuist'in nasıl kurulacağını burada yazmayacağım, çünkü zaten README'sinde yazıyor. Sadece ilk projenizi ayağa kaldırmanızı sağlayacağım, ardından aradan çekileceğim.

tuist'i lokal mac'inize kurduktan sonra, terminalinizden Desktop'a gidin ve aşağıdaki komutları çalıştırın.

mkdir Projem
cd Projem
tuist init --platform ios --template swiftui
tuist generate --no-open

Bu komutlar taze Projem klasörünüzde bir Xcode projesi oluşturacaktır. Ben şu anda 3.4.0 versiyonundayım, bu versiyonda, Projem klasöründe aşağıdaki Project.swift dosyası oluştu (commentleri attım).

import ProjectDescription
import ProjectDescriptionHelpers
import MyPlugin

let localHelper = LocalHelper(name: "MyPlugin")
let project = Project.app(name: "Projem",
                          platform: .iOS,
                          additionalTargets: ["ProjemKit", "ProjemUI"])

Farkedildiği üzere burada Projem adında bir iOS projesi yaratılıp altına ProjemKit ve ProjemUI adında iki adet target eklenmiş durumda. Yeni bir target eklemek için ["ProjemKit", "ProjemUI"] array'ine birşey ekleyin ve tuist generate komutunu tekrar çalıştırın.

Burada biraz trick yapılmış, yani ilk kullanıcı etkilensin diye birçok şey abstract edilmiş. Asıl target'ların oluşturulduğu yere gidelim.

open Tuist/ProjectDescriptionHelpers/Project+Templates.swift

Buradaki commentleri ve kodu inceleyin, fikir verecektir.

Ekstralar: Herhangi bir tuist alt komutunun yanına -h yazarak alt komutla ilgili yardım alabilirsiniz.

tuist -h
tuist generate -h
tuist cache -h

Halihazırdaki bir projeyi tuist’e çevirmek

Birçoğunuzun senaryosu bu olacaktır. Bizim 30-40 feature’lü, 15 dk’da build olan projemizi tamamen tuist’e geçirmek birkaç ayımızı aldı. İşin güzel yanı proje modüler olunca modül modül ilerliyorsunuz bu da migration’ı kolaylaştırıyor.

Migration için en iyi başlangıç, Xcode’da el ile yaptığınız ayarları xcconfig dosyalarına çevirmek. Dökümantasyonda bu işlem de anlatılmış, tuist migration tool’u gayet iş görüyor, yetmediği yerlerde iş başa düşüyor ama olacak o kadar.

tuist için open source contributor olmak

tuist open source projesinin readme'sini incelerseniz orada ismimi görürsünüz. Buraya girmek için gerçekten hiç çaba sarfetmedim. Çalıştığım şirketin projesine tuist'in cache ve test özelliklerini eklemek için uğraştığımda, bu komutların bizim projemizde çalışmadığını farkettim. Ardından araştırmaya başladım, ilk yaptığım iş tuist'i lokalime klonlamak ve aynı komutları çalıştırırken tuist'i debug etmek oldu. Bu aşamada birkaç bug buldum. Hemen GitHub issue'sü açmak yerine problemi çözmeye çalıştım. Birkaç tanesini çözünce bunları Pull Request'ler açıp ekiple paylaştım. Slack gruplarına girdim tartıştım, derken birkaç PR'ım merge oldu ve beni eklediler.

Projeye katkı yaparken zorlandığım konu Xcode ve tuist versiyonları arasındaki uyumsuzluk oldu, ayrıca tuist'i debug modda ayağa kaldırmak için yine aynı ekip tarafından geliştirilip tuist repo içine koyulan fourier adlı ruby scriptini de çalıştırmak gerekti. Ruby'nin kendisini ve gem'lerini kurmak bir işkence zaten, özellikle proxy arkasındaysanız. Bunu da komuta --http-proxy argümanıyla ilgili proxy'nin linkini vererek çözdüm.

Bu yazıda yalnızca Xcode'un proje dosyasından dem vurarak sizi motive etmeye çalıştım ancak projeyi tuist'e taşımanın çok fazla artı tarafı var. Bunlardan benim için en önemlisi ise tuist cache. Eğer projenizi modülerleştirdiyseniz her modülü izole bir biçimde generate edebiliyorsunuz, bununla da kalmıyor bu modülün dependency'lerini cache'leyip build zamanlarını dramatik ölçüde kısaltabiliyorsunuz. Örneğin 10 dk süren bir incremental build oluyor sana 15 sn. Tabii deneyimli okuyucu Computer Science'ın en büyük iki probleminden ikincisinin cache yönetimi olduğunu biliyor olacaktır.

Diğeri tuist test. Bu da projedeki tüm testleri bir kez çalıştırdıktan sonra, eğer herhangi bir modülün içindeki dosyaya ve dependency'lerine dokunmadıysanız o modülün testlerini atlıyor böylece yine zaman tasarrufu sağlıyorsunuz.

Eğer mikro architecture (μFeature) yapısından yararlanıyorsanız, tuist yeni feature eklemeniz için scaffold özelliğiyle bir çok boilerplate kodu kendi yaratıyor size de sadece Project.swift dosyalarını modifiye etmek kalıyor. Bunu biz ekibimizde çok kullanıyoruz.

Bir başka baba özellik ise tuist graph. Projenin yapısını grafik halinde çıktı veriyor. Aşağıda benim oyunumun çıktısından bir örnek.

graph.png

Bu kadar bilgiden sonra okuyucuyu tuist'in dökümantasyonuna yönlendireceğim. Pek sağlam bir docs sayılmaz ancak baya fikir veriyor. Docs içinde özellikle μFeature kısmını her iOS developer'a tavsiye ederim.

Sevgili dostlar, bu tip trendleri takip etmenin hep yararını gördüm. Belki bazınıza bunlar overengineering gibi gelecektir. Evet özellikle küçük bir projede gerçekten öyle ama işlerin büyüyeceği gün buna geçmeniz projenin sustainability'si açısından kesin yararlı olacaktır. Ben yeni projelerime size'larından bağımsız tuist ile başlıyorum, hem familiarity açısından hem de ekip en son hangi özelliği eklemiş öğrenmek açısından hep yararını gördüm.

Eğer kendi ekibinize tuist dersi vermemi isterseniz bana LinkedIn'den ulaşınız, yardım ederim.

Referanslar

Did you find this article valuable?

Support Erk Ekin by becoming a sponsor. Any amount is appreciated!