Yirmi kişilik bir iOS ekibinde project.pbxproj dosyası, kimsenin sahiplenmediği ama herkesin her gün değiştirdiği tek dosyadır. Ekip elli kişiye çıktığında haftada birkaç kez bozulur, yüz kişide ise birinin tam zamanlı işi haline gelir. O noktada toplantıya üç öneri gelir: "her şeyi Swift package yapalım", "Tuist'e geçelim", "büyük şirketler Bazel kullanıyor". Üçü de bir yerde doğru, üçü de yanlış ekipte pahalı bir hata.
Bu yazıda seçenekleri Ekim 2026 itibarıyla araçların bugünkü haliyle karşılaştırıyoruz ve hangi organizasyonda hangisini seçtiğimizi söylüyoruz. Örnek olarak hayali bir seyahat uygulaması kullanacağız: Rota, 60 mühendis, 90 modül, iki haftalık release train.
Elle bakılan .xcodeproj nerede acıtır
Merge conflict'ler
Klasik tablo şu: iki kişi aynı gün aynı target'a birer dosya ekler, ikisi de PBXBuildFile ve PBXFileReference bölümlerine satır yazar.
<<<<<<< HEAD
7A1F0B3C2D8E4A1100C3B9F1 /* FareRulesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1F0B3B2D8E4A1100C3B9F1 /* FareRulesView.swift */; };
=======
7A22C4D12D8E51F400A7E6B2 /* SeatMapViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A22C4D02D8E51F400A7E6B2 /* SeatMapViewModel.swift */; };
>>>>>>> feature/seat-map
Çözümü basittir (iki satırı da tut), ama dört ayrı bölümde tekrarlanır ve biri yanlış çözüldüğünde derleme yine geçer. Hata bir hafta sonra "dosya projede görünüyor ama target'a dahil değil" şeklinde ortaya çıkar.
Bu sorunun büyük kısmını Apple çözdü. Xcode 16 ile gelen buildable folder'lar (pbxproj içinde PBXFileSystemSynchronizedRootGroup) proje dosyasına yalnızca klasör yolunu yazar, içindeki dosyaları tek tek saymaz. Release note'taki ifadeyle amaç proje dosyasındaki değişiklikleri azaltmak ve version control conflict'lerinden kaçınmaktır. Mevcut bir group'u Project navigator'da "Convert to Folder" ile dönüştürebilirsiniz. Herhangi bir araca geçmeden önce yapılacak ilk iş budur ve çoğu ekipte dosya kaynaklı conflict'leri neredeyse bitirir.
Buildable folder'ın çözmediği şeyler de pbxproj'un içinde durmaya devam eder: target tanımları, build phase'ler, target dependency'leri, package referansları ve en önemlisi build setting'ler.
Target'lar arasında kayan build setting'ler
Büyük projelerde sık gördüğümüz tablo: 40 framework target'ı, hepsi aynı şablondan kopyalanmış, üç yıl sonra hiçbiri birbirine benzemiyor. Ölçmesi kolaydır:
# Her target'ın Debug ayarlarından ilgilendiklerimizi yan yana dök
for target in $(xcodebuild -list -json -project Rota.xcodeproj | jq -r '.project.targets[]'); do
xcodebuild -showBuildSettings -json -project Rota.xcodeproj \
-target "$target" -configuration Debug 2>/dev/null \
| jq -r --arg t "$target" '.[0].buildSettings
| [$t, .SWIFT_VERSION, .IPHONEOS_DEPLOYMENT_TARGET,
.SWIFT_STRICT_CONCURRENCY, .ENABLE_TESTABILITY] | @tsv'
done | column -t
Rota gibi bir projede çıktı genellikle şuna benzer:
BookingFeature 6.0 17.0 complete YES
SearchFeature 5.0 16.0 minimal YES
PaymentsKit 5.0 17.0 NO
DesignSystem 6.0 15.0 targeted YES
Kimse PaymentsKit için testability'yi neden kapattığını hatırlamaz, çünkü pbxproj yorum satırı kabul etmez ve git blame size "Xcode'un önerdiği ayarlara güncellendi" commit'ini gösterir. Ayarın sebebi iki yıl önce ayrılan birinin kafasında kalmıştır.
Onboarding
Yeni gelen mühendis yeni bir modül açmak ister. Elle bakılan projede bu, wiki'deki 14 adımlı bir sayfadır ve sayfa en son iki Xcode sürümü önce güncellenmiştir. Sonuç, şablondan sapmış bir target daha olur.
Xcode'un son sürümlerinde değişenler
Karar verirken üç değişikliği hesaba katın. Buildable folder'lar (Xcode 16) dosya conflict'lerini azalttı. Explicitly built modules Xcode 16'da geldi, Xcode 26'da Swift target'ları için varsayılan oldu. Xcode 26 ayrıca opt-in bir compilation cache getirdi: "Enable Compilation Caching" ayarı, aynı girdiler tekrar derlendiğinde önceki sonucu cache'ten verir ve Apple'ın notuna göre en çok branch değiştirirken ve clean build'de işe yarar. Bu son madde önemli, çünkü "binary cache istiyorsak proje üretimine geçmek zorundayız" cümlesi artık eskisi kadar doğru değil.
Seçenek 1: ince proje artı yerel Swift package'lar
Uygulama projesi yalnızca app target'ını, extension'ları, entitlement'ları ve signing'i tutar. Geri kalan her şey yerel package'larda yaşar. Apple'ın kendi dokümantasyonunun önerdiği yol da budur.
// swift-tools-version: 6.2
// Packages/Features/Package.swift
import PackageDescription
let package = Package(
name: "Features",
defaultLocalization: "tr",
platforms: [.iOS(.v17)],
products: [
.library(name: "BookingFeature", targets: ["BookingFeature"]),
.library(name: "SearchFeature", targets: ["SearchFeature"]),
],
dependencies: [
.package(path: "../Core"),
],
targets: [
.target(
name: "BookingFeature",
dependencies: [
.product(name: "Networking", package: "Core"),
.product(name: "DesignSystem", package: "Core"),
],
resources: [.process("Resources")],
swiftSettings: [.swiftLanguageMode(.v6)]
),
.testTarget(name: "BookingFeatureTests", dependencies: ["BookingFeature"]),
.target(name: "SearchFeature", dependencies: ["BookingFeature"]),
]
)
Güçlü tarafları açık. Manifest Swift'tir, yorum yazılır, code review'dan geçer. Dosya eklemek hiçbir manifest değişikliği gerektirmez. Araç Apple'ındır, Xcode beta'sının ilk gününde çalışır. swift build ve swift test ile Xcode'suz doğrulama yapabilirsiniz. Ve hiçbir üçüncü parti araca bağımlı değilsiniz.
Sınırlar ise modül sayısı arttıkça görünür olur.
Build setting kontrolü dardır. Bir package target'ına swiftSettings, cSettings ve linkerSettings verebilirsiniz, xcconfig bağlayamazsınız. unsafeFlags kullanan bir target'ın ürünleri, dokümantasyonun ifadesiyle başka package'lar tarafından kullanılmaya uygun olmaktan çıkar (yerel path bağımlılıklarında pratikte nasıl davrandığını kendi toolchain sürümünüzde deneyin). Run script phase yoktur, yerine build tool plugin yazarsınız. Static mi dynamic mi link edileceğine çoğu durumda Xcode karar verir.
Resource'lar Bundle.module üzerinden gelir. Bu, kodun package içinde mi yoksa bir framework target'ında mı derlendiğine göre farklı bundle araması demektir ve ileride başka bir yapıya geçerken dokunmanız gereken yerlerden biridir.
Scheme ve indexing davranışı büyük package'larda zorlaşır. Xcode her package ürünü için otomatik scheme üretir, 90 modülde scheme listesi kullanılmaz hale gelir. Proje açılışında ve branch değişiminde package resolution çalışır. Tek bir dev Package.swift içinde 90 target tutarsanız manifest'in kendisi yeni conflict noktası olur, 30 ayrı package'a bölerseniz her birinin Package.swift dosyasında platform ve Swift ayarlarını tekrar edersiniz. Bu dengeyi 100 modüllü SPM yapısı yazısında ayrıntılı ele aldık.
Graph araçları sınırlıdır. swift package show-dependencies yalnızca package seviyesinde çalışır. Target grafiği için swift package describe --type json çıktısını kendiniz işlersiniz. "Bu PR hangi modülleri etkiliyor, yalnızca onların testini koş" sorusunun hazır bir cevabı yoktur, kendiniz yazarsınız.
Buna rağmen, 20 ile 60 mühendis arasında ve tek uygulamalı ekiplerde ilk tercihimiz çoğunlukla budur.
Seçenek 2: projeyi üretmek
Proje üretiminin fikri basit: .xcodeproj bir build çıktısıdır, git'e girmez. Kaynak, okunabilir bir manifest'tir.
Tuist
Tuist'te manifest'ler Swift'tir (Project.swift, Workspace.swift, Tuist.swift) ve ortak kod Tuist/ProjectDescriptionHelpers altında bir modül olarak derlenir. Platform ekibinin kuralları tam burada yaşar:
// Tuist/ProjectDescriptionHelpers/Target+Feature.swift
import ProjectDescription
extension Target {
/// Rota'daki her feature modülü bu fonksiyondan geçer.
/// Ayarlar tek bir xcconfig'ten gelir, sebebi orada yorum olarak yazılıdır.
public static func feature(
_ name: String,
dependencies: [TargetDependency] = []
) -> [Target] {
let settings: Settings = .settings(configurations: [
.debug(name: "Debug", xcconfig: "Config/Module.xcconfig"),
.release(name: "Release", xcconfig: "Config/Module.xcconfig"),
])
return [
.target(
name: name,
destinations: .iOS,
product: .staticFramework,
bundleId: "com.example.rota.\(name)",
deploymentTargets: .iOS("17.0"),
buildableFolders: [.folder("Modules/\(name)/Sources")],
dependencies: dependencies,
settings: settings
),
.target(
name: "\(name)Tests",
destinations: .iOS,
product: .unitTests,
bundleId: "com.example.rota.\(name)Tests",
deploymentTargets: .iOS("17.0"),
buildableFolders: [.folder("Modules/\(name)/Tests")],
dependencies: [.target(name: name)],
settings: settings
),
]
}
}
// Project.swift
import ProjectDescription
import ProjectDescriptionHelpers
let project = Project(
name: "Rota",
targets: [
.target(
name: "Rota",
destinations: .iOS,
product: .app,
bundleId: "com.example.rota",
deploymentTargets: .iOS("17.0"),
infoPlist: "App/Info.plist",
buildableFolders: [.folder("App/Sources"), .folder("App/Resources")],
dependencies: [
.target(name: "BookingFeature"),
.target(name: "SearchFeature"),
]
),
]
+ Target.feature("BookingFeature", dependencies: [
.target(name: "Networking"),
.external(name: "Alamofire"), // Tuist/Package.swift içinde tanımlı
])
+ Target.feature("SearchFeature", dependencies: [.target(name: "BookingFeature")])
+ Target.feature("Networking")
)
Günlük akış tuist install (harici package'ları çözer) ve tuist generate komutlarından ibarettir. buildableFolders kullandığınız için yeni dosya eklemek yeniden üretim gerektirmez. Yeniden üretim, manifest değiştiğinde gerekir.
Karşılığında aldıklarınız:
- Graph komutları.
tuist graphgrafiği dot, json, png ya da svg olarak verir,tuist inspect dependencies --only implicitise beyan edilmemiş import'ları statik analizle bulur ve bulursa sıfırdan farklı kodla çıkar. CI'a koyulacak komut budur. (EskienforceExplicitDependenciesseçeneği bu komut lehine deprecated edildi.) Grafiği nasıl okuyacağınızı dependency graph yazısında anlattık. - Module cache.
tuist cachetarget'ları hash'ler,.xcframeworkolarak derler vetuist generate BookingFeaturedediğinizde üzerinde çalışmadığınız modülleri binary ile değiştirir. Dokümantasyon bunun geliştirme akışı için olduğunu, release build'i için kaynakla (--cache-profile none) üretmeniz gerektiğini açıkça söylüyor. - Selective testing.
tuist testaynı hash'leri kullanarak son başarılı koşudan beri etkilenmeyen test target'larını atlar. Bugün yalnızca üretilmiş projelerde destekleniyor. - Xcode cache. Xcode 26'nın compilation cache'ini uzak bir depoya bağlar. Bunun için proje üretimi gerekmez, herhangi bir Xcode projesinde çalışır.
Neyin açık kaynak, neyin ücretli olduğu konusu toplantıda mutlaka sorulur, o yüzden net olalım. CLI ve proje üretimi MIT lisanslıdır ve hesap gerektirmez. Sunucu tarafının kaynak kodu aynı depoda durur ama MIT değildir (Fair Core License, yani source-available). Kendi altyapınızda barındırmak, dokümantasyona göre ücretli lisans ve Enterprise planı ister. Barındırılan serviste yazı tarihinde üretilmiş projeler, selective testing ve build insights tüm planlarda sınırsız görünüyor. Kullanıma göre faturalanan kalemler cache'ten indirilen veri (egress), cache istek sayısı ve test insights. Rakamları yazmıyoruz çünkü değişir, karar öncesi fiyat sayfasına bakın ve bir haftalık CI trafiğinizle kendi tahmininizi çıkarın.
Bedeli de var. Tuist haftalık release çıkarır ve sunucusu yalnızca son üç ayın CLI sürümlerini destekler, yani cache kullanıyorsanız CLI'ı en az çeyrekte bir güncellemek zorundasınız. Harici package'ları kendi XcodeProj tabanlı entegrasyonuyla target'a çevirir. Bu, cache'in çalışmasını sağlayan şeydir, ama yeni SwiftPM özelliklerinin desteği Xcode'un yerleşik entegrasyonundan sonra gelebilir (Tuist'in kendi dokümantasyonu da bunu söyler). Ve manifest Swift olduğu için, yeterince yaratıcı bir platform ekibi helper'ların içine kimsenin okuyamadığı bir DSL kurabilir. 400 satırlık AppContainer'ın proje tanımı versiyonu.
XcodeGen
XcodeGen aynı fikrin sade halidir: tek bir YAML spec, MIT lisanslı bir CLI, sunucu yok, hesap yok.
# project.yml
name: Rota
options:
bundleIdPrefix: com.example.rota
deploymentTarget:
iOS: "17.0"
defaultSourceDirectoryType: syncedFolder # Xcode 16 buildable folder'ları
configFiles:
Debug: Config/Project.xcconfig
Release: Config/Project.xcconfig
packages:
Core:
path: Packages/Core
settingGroups:
module:
SWIFT_VERSION: "6.0"
SWIFT_STRICT_CONCURRENCY: complete
targetTemplates:
Feature:
type: framework.static
platform: iOS
sources: [Modules/${target_name}/Sources]
settings:
groups: [module]
targets:
Rota:
type: application
platform: iOS
sources: [App]
dependencies:
- target: BookingFeature
- target: SearchFeature
BookingFeature:
templates: [Feature]
dependencies:
- package: Core
product: Networking
SearchFeature:
templates: [Feature]
dependencies:
- target: BookingFeature
xcodegen generate ile proje çıkar. settingGroups ve targetTemplates ayar kaymasını bitirir, syncedFolder desteği vardır, include ile spec'i dosyalara bölebilirsiniz. Olmayanlar: binary cache yok (--use-cache bayrağı yalnızca spec değişmediyse yeniden üretimi atlar), selective testing yok, graph komutu yok. YAML'da döngü ve koşul olmadığı için 90 modülde spec uzar. Yazı tarihinde son sürüm 2.46.0 (Temmuz 2026) ve Tuist'e göre daha seyrek güncellenen bir topluluk projesi.
Bizim için XcodeGen'in yeri belli: conflict ve tutarlılık sorununu çözmek istiyorsunuz, cache'e ihtiyacınız yok ve gerektiğinde iki saatte geri dönebilmek istiyorsunuz.
Seçenek 3: Xcode'un build sisteminden çıkmak
Bazel (rules_apple, rules_swift ve Xcode projesini üreten rules_xcodeproj ile) hermetik build, uzak cache ve uzak execution verir. rules_xcodeproj indexing, debugging ve Xcode Previews desteğini listeliyor ve odaklı proje üretebiliyor. Kimler geçmeli: Android, backend ve iOS'un aynı monorepo'da yaşadığı, şirkette zaten bir Bazel ekibi olan ve iOS build'ine tam zamanlı en az birkaç mühendis ayırabilen organizasyonlar. Kimler geçmemeli: gerekçesi "build yavaş" olan herkes. Bazel'e geçtiğinizde her Xcode sürümünde kural setlerinin uyumunu beklersiniz, her üçüncü parti SDK'nın entegrasyonunu kendiniz yazarsınız ve yeni gelen her iOS mühendisine Starlark öğretirsiniz. 200 mühendisin altında, yalnızca iOS için bunu önermiyoruz. Xcode 26'nın compilation cache'i ve Tuist'in module cache'i, bu ekiplerin Bazel'den beklediği kazancın büyük kısmını çok daha düşük bakım maliyetiyle verir.
Kararı belirleyen ölçütler
Modül sayısı ilk ölçüttür. 30 target'ın altında hiçbir aracın getirisi maliyetini karşılamaz. 30 ile 100 arasında yerel package'lar rahat taşır. 100'ün üstünde odaklı proje üretimi (yalnızca çalıştığınız modüller kaynak, gerisi binary) Xcode'un açılış ve indexing süresinde hissedilir fark yaratır. Modül sayısının kendisinin doğru olup olmadığı ayrı bir sorudur, onu modülerleşmenin maliyeti yazısında tartıştık.
CI dakikaları ikinci ölçüttür ve ölçülebilir. Son 30 günün PR pipeline'larında harcanan toplam dakikayı ve bunun ne kadarının "değişmemiş modülü yeniden derlemek ve test etmek" olduğunu çıkarın. Bu oran düşükse cache size bir şey kazandırmaz. Yüksekse önce Xcode 26 compilation cache'ini mevcut projenizde açıp ölçün, yetmezse module cache ve selective testing'e bakın.
Build tooling'e kimin bakacağı üçüncü ölçüttür. Adı konmuş en az iki kişi yoksa (biri izne çıkar) proje üretimine geçmeyin. Bazel için bu sayı bir ekiptir.
Release train'i olan ekiplerde kural tektir: release, cache'siz ve kaynaktan üretilmiş projeyle, sabitlenmiş araç sürümüyle çıkar. Tuist ya da XcodeGen sürümünü depoda pin'leyin, release branch'inde araç güncellemesi yapmayın.
Previews ve geliştirici deneyiminde yerel package'lar en az sürprizli olandır. Üretilmiş projelerde buildable folder kullanıyorsanız günlük akış neredeyse aynıdır, fark branch değiştirince generate çalıştırmaktır. Binary'ye çevrilmiş bir modülün içine adım adım debug edemezsiniz, o modülü odağa alıp yeniden üretirsiniz.
Son ölçüt üçüncü parti riskidir. Proje tanımınız başkasının aracına bağlıysa çıkış yolunuzu bilmelisiniz. Hem Tuist'te hem XcodeGen'de çıkış aynıdır: son üretilen .xcodeproj dosyasını git'e commit edersiniz ve elle bakılan projeye dönmüş olursunuz. Tuist'in CLI'ı MIT lisanslı olduğu için üretim tarafı şirketin akıbetinden bağımsız çalışmaya devam eder. Bağımlılık asıl cache ve selective testing'de oluşur, CI süreniz onlara göre şekillendikten sonra servis kesilirse pipeline'ınız yavaşlar ama durmaz.
Proje dosyası üretilen bir çıktıysa, ondan vazgeçmek bir commit'tir. Build sisteminden vazgeçmek bir projedir.
Hükmümüz
| Durum | Seçimimiz | Hüküm |
|---|---|---|
| 20 kişiden az, 30 target'tan az, tek uygulama | Düz Xcode projesi, buildable folder, xcconfig | Araç eklemeyin |
| 20 ile 60 mühendis, 30 ile 100 modül, tek uygulama | İnce proje artı yerel package'lar | İlk tercih |
| Aynı ölçek, ama çok sayıda app, extension ve white-label target | XcodeGen | Package'lar app target'larını çözmez, spec çözer |
| 60 ile 200 mühendis, 100+ modül, CI kuyruğu şikâyet konusu | Tuist ile üretim, module cache, selective testing | Platform ekibi varsa evet |
| Aynı ölçek, platform ekibi yok | Yerel package'lar ve Xcode compilation cache | Önce sahip bulun, sonra araç |
| Derleme çıktısı şirket dışına çıkamıyor | Tuist üretimi (hesapsız) ve yerel cache, ya da Enterprise self-host | Maliyeti hukukla birlikte hesaplayın |
| Çok dilli monorepo, mevcut Bazel ekibi, 200+ mühendis | Bazel ve rules_xcodeproj |
Yalnızca bu koşulların hepsi varsa |
Düz Xcode projesinde kalacağımız durumlar da var. Group'ları buildable folder'a çevirip ayarları xcconfig'e taşıdıktan sonra pbxproj conflict'i ayda birkaç taneye iniyorsa sorun çözülmüştür. Build tooling'e bakacak kimse yoksa yeni bir araç yalnızca yeni bir sahipsiz dosya ekler. Projeyi başka bir ekibe ya da müşteriye devredecekseniz, karşı tarafın bilmediği bir aracı da devretmiş olursunuz. Ve her Xcode beta'sını ilk gün denemek zorundaysanız (platform SDK'sı geliştiren ekipler gibi), aranıza giren her katman bir gün gecikme demektir.
Geri alınabilir bir geçiş
Geçişin tek kuralı: eski proje, yenisi bir ekipte iki hafta sorunsuz çalışana kadar yerinde kalır.
Birinci adım araçtan bağımsızdır ve tek başına da değerlidir: build setting'leri pbxproj'dan çıkarıp xcconfig'e taşıyın. Tuist bunun için tuist migration settings-to-xcconfig komutunu verir, elle de yapılabilir. Sonuçta ayarların sebebi nihayet yazılabilir olur:
// Config/Module.xcconfig
SWIFT_VERSION = 6.0
IPHONEOS_DEPLOYMENT_TARGET = 17.0
SWIFT_STRICT_CONCURRENCY = complete
// PaymentsKit'te kapalıydı: 3DS SDK'sının eski sürümü testability ile link hatası veriyordu.
// SDK 4.x'e geçildi, artık gerekmiyor. Tekrar kapatacaksanız sebebini buraya yazın.
ENABLE_TESTABILITY[config=Debug] = YES
İkinci adımda yeni projeyi eskisinin yanında üretin. Tuist'in kendi geçiş rehberi, çakışmayı önlemek için proje adına -Tuist eki koymanızı önerir. Mevcut Rota.xcodeproj git'te durur, Rota-Tuist.xcodeproj .gitignore'dadır.
Üçüncü adımda iki projenin build setting'lerini karşılaştırın ve bunu gözle yapmayın, diff alın:
#!/bin/bash
# compare-settings.sh <target> <configuration>
set -euo pipefail
dump() {
xcodebuild -showBuildSettings -json \
-project "$1" -target "$2" -configuration "$3" 2>/dev/null \
| jq -S '.[0].buildSettings
| with_entries(select(.key
| test("DIR$|_PATH$|ROOT$|^PROJECT_|^BUILD_|^TARGET_BUILD") | not))'
}
diff <(dump Rota.xcodeproj "$1" "$2") <(dump Rota-Tuist.xcodeproj "$1" "$2") \
&& echo "$1 ($2): aynı"
Yol içeren anahtarları eliyoruz çünkü proje adı farklı. Kalan her fark ya bilinçli bir düzeltmedir (PR açıklamasına yazılır) ya da bir hatadır. Bu script'i her target ve her configuration için CI'da koşturun.
Dördüncü adımda CI iki projeyi de derler ve test eder. Bu, geçiş boyunca CI süresini artırır, geçici olduğunu bilerek kabul edin.
Beşinci adımda tek bir ekip yeni projeye geçer. Seçeceğiniz ekip en çok modül ekleyen ekip olsun, sorunları en hızlı onlar bulur. İki hafta boyunca şunları izleyin: preview'lar çalışıyor mu, debug oturumu açılıyor mu, archive eski projeyle aynı binary boyutunu ve aynı entitlement'ları üretiyor mu, o ekibin pbxproj kaynaklı conflict sayısı sıfıra indi mi.
Altıncı adımda herkes geçer ve eski .xcodeproj git'ten silinir. Geri dönmek gerekirse üretilen projeyi commit edersiniz, iş biter. Cache ve selective testing'i bu noktadan sonra, ayrı bir karar olarak açın. İkisini aynı anda devreye alırsanız bir sorun çıktığında hangisinden kaynaklandığını ayıramazsınız.
Karar toplantısı için kontrol listesi
- Group'lar buildable folder'a çevrildi ve son 30 günün pbxproj conflict sayısı tekrar ölçüldü.
- Target'lar arası build setting farkları dökümü çıkarıldı, kaç ayarın sebebinin bilinmediği sayıldı.
- Modül sayısı ve bir yıl sonraki beklenen sayı yazıldı.
- Son 30 günün PR CI dakikaları ve bunun ne kadarının değişmemiş modüllere gittiği ölçüldü.
- Xcode 26 compilation cache mevcut projede denendi, sonucu not edildi.
- Build tooling'in sahibi olacak en az iki kişinin adı yazıldı.
- Seçilen aracın lisansı, ücretli kalemleri ve derleme çıktısının nereye gittiği hukuk ve güvenlik ile konuşuldu.
- Araç sürümünün nasıl pin'leneceği ve release branch'inde nasıl davranılacağı belirlendi.
- Çıkış planı yazıldı: üretilen projeyi commit edip elle bakıma dönmek kaç saat sürer.
- Pilot ekip, iki haftalık pilotun başarı ölçütleri ve eski projenin silineceği tarih belirlendi.
Bu konuda desteğe mi ihtiyacınız var?
Uygulamanızda bu kontrollerin doğru kurulup kurulmadığını birlikte inceleyelim. Güvenlik denetimi ya da danışmanlıkla buradayız.
Kaynaklar
- Apple · Xcode 16 Release Notes
- Apple · Xcode 26 Release Notes
- Apple · Organizing your code with local packages
- Apple · Bundling resources with a Swift package
- Tuist · tuist/tuist deposu ve lisans
- Tuist · Module cache
- Tuist · Xcode cache
- Tuist · Migrate an Xcode project
- XcodeGen · Project Spec
- MobileNativeFoundation · rules_xcodeproj
Yorumlar ve sorular
Katılmadığınız bir nokta ya da projenizde takıldığınız bir yer mi var? Yazın, konuşalım.