معماری SwiftUI: نماهای ساختاری برای قابلیت استفاده مجدد و وضوح
همانطور که پروژههای SwiftUI شما رشد میکنند، نیاز به معماری بهتر SwiftUI نیز افزایش مییابد، زیرا برای مشاهده بدنهها بسیار طولانی میشود. شما با یک صفحه نمایش ساده شروع می کنید، و قبل از اینکه متوجه شوید، بدن شما شامل ده ها VStacks، HStacks و اصلاح کننده های سفارشی است. در یک نقطه، پیمایش در بدن شبیه خواندن یک رمان است.
در این مقاله به یک مثال واقع بینانه نگاه می کنیم. این یک نمای بزرگ است که میخواهیم آن را به بخشهای خواناتر تقسیم کنیم. ما تکنیکهای مختلفی را بررسی میکنیم که دیدگاههای شما را خوانا، قابل نگهداری و قابل استفاده مجدد میکند—همه بدون مهندسی بیش از حد.
قبل از شروع: هیچ راهی برای خروج وجود ندارد. از این تکنیک ها به عنوان الهام بخش و ابزاری برای اصلاح دیدگاه های خود در حین حرکت استفاده کنید.
مشکل: بدن در حال رشد
تصور کنید که ما در حال ساخت یک ساده هستیم ArticleListView برای نمایش لیستی از مقالات:
struct Article {
let title: String
let url: URL
}
struct ArticleListView: View {
let articles: [Article]
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text("Articles")
.font(.largeTitle)
.bold()
ForEach(articles, id: \.url) { article in
HStack {
VStack(alignment: .leading) {
Text(article.title)
.font(.headline)
Text(article.url.absoluteString)
.font(.subheadline)
.foregroundStyle(.secondary)
}
Spacer()
Image(systemName: "chevron.right")
.foregroundStyle(.gray)
}
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
}
}
.padding()
}
}
این دیدگاه ممکن است در ابتدا خوب به نظر برسد. اما به محض اینکه برای حالتهای خالی، نشانگرهای بارگیری یا اقدامات سلولی منطق اضافه میکنیم، همه چیز به هم میریزد. حتی در وضعیت کنونی آن، میتوانیم چند نکته را نتیجه بگیریم:

شما در واقع به تصویر ذهن من در هنگام تجزیه یک نما در معماری SwiftUI نگاه می کنید. من همیشه تلاش میکنم دیدگاههایم را اسکن و خواندن آسان کنم. مدل هایی که من می بینم تبدیل به ظروف جداگانه یا اشیاءکه ورودی برای یافتن راه حل صحیح هستند. اینکه کدام تصمیم می گیرید بستگی دارد و راه های مختلفی برای ادامه وجود دارد.
دوره 5 روزه رایگان ایمیل: The Swift Concurrency Playbook
الف دوره 5 روزه رایگان ایمیل افشای 5 اشتباه بزرگ توسعهدهندگان iOS با async/wait کار میکنند، که منجر به رد کردن App Store و پروژههای مهاجرت به جای چند روز، ماهها طول میکشد (حتی اگر سالها در حال نوشتن Swift باشید).
The Temptation: Extensions and Calculated Properties
بسیاری از توسعه دهندگان سعی می کنند با جابجایی قسمت هایی از بدن به اکستنشن، اندازه بدن را کاهش دهند. پسوندها در یک فایل یافت می شوند و اغلب طول خود فایل را افزایش می دهند. به نظر من، خوانایی بهبود نیافته است و در نهایت خطوط کد بیشتری برای اسکن داریم.
در اینجا یک نمونه از ما است ArticleListViewاما با بازدیدهای فرعی به یک برنامه افزودنی منتقل شده است:
extension ArticleListView {
var header: some View {
Text("Articles")
.font(.largeTitle)
.bold()
}
func articleRow(for article: Article) -> some View {
HStack {
VStack(alignment: .leading) {
Text(article.title)
.font(.headline)
Text(article.url.absoluteString)
.font(.subheadline)
.foregroundStyle(.secondary)
}
Spacer()
Image(systemName: "chevron.right")
.foregroundStyle(.gray)
}
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
}
}
و سپس به این یاوران بدن می گویند:
var body: some View {
VStack(alignment: .leading, spacing: 8) {
header
ForEach(articles, id: \.url, content: articleRow)
}
.padding()
}
این کار می کند، اما فقط کد را جابجا می کند – لزوماً نمای شما را قابل نگهداری یا قابل استفاده مجدد نمی کند. تنها فایده ای که می بینم خوانایی بهبود یافته بدن ما است، اما من راه حلی را ترجیح می دهم که هم خوانایی بدن من و هم قابلیت استفاده مجدد و نگهداری کد من را بهبود می بخشد. ما به یک رویکرد بهتر برای معماری بهبود یافته SwiftUI نیاز داریم.
روش بهتر: استخراج و استفاده مجدد هوشمندانه
به جای اینکه با گسترش ها و خواص محاسبه شده مانند زباله ها رفتار کنید، از آنها به صورت استراتژیک استفاده کنید.
در اینجا سه رویکرد بهتر وجود دارد:
1. نماهای سفارشی SwiftUI را استخراج کنید
اگر بخشی از نگاه شما دارد مراقبت از خودبه او بده View:
struct ArticleRow: View {
let article: Article
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(article.title)
.font(.headline)
Text(article.url.absoluteString)
.font(.subheadline)
.foregroundStyle(.secondary)
}
Spacer()
Image(systemName: "chevron.right")
.foregroundStyle(.gray)
}
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
}
}
و از آن در لیست خود استفاده کنید:
ForEach(articles, id: \.url) { article in
ArticleRow(article: article)
}
اکنون نمای اصلی شما مانند یک نمای کلی سطح بالا خوانده می شود – نه جزئیات عملکرد. هر دیدگاه در نهایت یک مسئولیت دارد. به محض اینکه حالت های بیشتری را به آن اضافه کنیم ArticleRowما می توانیم این کار را در یک مکان انجام دهیم. همچنین به ما این امکان را می دهد که به راحتی رندرهای SwiftUI را برای هر ایالت تعریف کنیم. میتوانیم #پیشنمایش نماهای SwiftUI را با استفاده از ماکروها مشاهده کنیم یا از @Previewable برای ایجاد آسان نماهای SwiftUI پویا استفاده کنیم.
2. اصلاح کننده های نمای قابل استفاده مجدد ایجاد کنید
وقتی متوجه شدید استایل تکراریآن را به عنوان یک اصلاح کننده استخراج کنید:
struct CardStyle: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
}
}
extension View {
func cardStyle() -> some View {
modifier(CardStyle())
}
}
سپس از آن در همه جا استفاده کنید:
ArticleRow(article: article)
.cardStyle()
اصلاحکنندهها زبان طراحی شما را در بین نماها ثابت میکنند—و آزمایش و استفاده مجدد از آنها فوقالعاده آسان است. تشخیص این موضوع برای من سخت تر است: چه زمانی می دانید چیزی به زبان طراحی تبدیل می شود؟
اغلب پس از اینکه برنامه خود را کمی طولانی تر توسعه دادید، متوجه خواهید شد. من اغلب متوجه میشوم که همان اصلاحکنندهها را از دید به نمایش کپی میکنم. در این موارد:
- به دنبال ترکیبی از اصلاح کننده های view باشید
- آن را با جدید خود جایگزین کنید
ViewModifier
نتیجه به شما امکان می دهد سبک برنامه خود را در یک مکان با کد قابل استفاده مجدد به روز کنید. به هر حال، برای این کار نیازی به استفاده از View modifier ندارید. شما همچنین می توانید فقط از a استفاده کنید View پسوند:
extension View {
func cardStyle() -> some View {
self.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
}
}
در هر صورت، بهبود قابلیت کشف را با استفاده از جستجوهای اعضای ثابت در زمینه های رایج در نظر بگیرید. این تکنیکی است که قابلیت کشف راه حل های معماری SwiftUI شما را حفظ می کند.
3. پسوندها را برای نمای کلی ایجاد کنید
اگر متوجه شدید که مرتباً طرحبندیهای مشابهی مینویسید، کمککنندگان معمولی را در نظر بگیرید.
به عنوان مثال، نمایش یک بخش با عنوان معمول است:
extension View {
func sectionHeader(_ title: String) -> some View {
VStack(alignment: .leading, spacing: 4) {
Text(title)
.font(.title3)
.bold()
self
}
}
}
اکنون می توانید بنویسید:
VStack {
ForEach(articles, id: \.url) { article in
ArticleRow(article: article)
}
}
.sectionHeader("Articles")
این نوع پسوند کد شما را در حین ارتقاء یک زبان رابط کاربری مشترک قابل خواندن نگه می دارد. گاهی اوقات من اینها را به یک ترجیح می دهم SectionHeader مشاهده کنید زیرا باعث کاهش padding در کد من می شود.
با این حال، من متوجه شدم که این یکی مطمئنترین است، زیرا باعث میشود اصلاحکننده view در پشت صحنه چه کاری انجام میدهد کمتر مشخص شود. با این حال، این مهم است که چه تعداد از اصلاحکنندههای نمایش کار میکنند، بنابراین این به شما بستگی دارد که آیا این را در سبک کدنویسی خود لحاظ کنید.
قانون کلی برای معماری خوب SwiftUI
اگر مطمئن نیستید که چیزی را بازیابی کنید، بپرسید:
“آیا این بخش از رابط کاربری هدف و پتانسیل روشنی برای استفاده مجدد دارد؟”
- اگر بله ← آن را جدید کنید
ViewیاViewModifier.
- اگر نه ← آن را به صورت محلی ذخیره کنید یا از یک ویژگی محاسبه شده استفاده کنید.
هدف کوچک کردن خطوط کد نیست، اما بله بهبود وضوح و قابلیت استفاده مجدد برای معماری بهتر SwiftUI.
نتیجه گیری
بدنه های بزرگ SwiftUI نشانه رشد است نه شکست. آنها اغلب به این معنی هستند که دیدگاه شما با محصول شما تکامل یافته است. اما دانستن چگونه شکستن چیزها می تواند هرج و مرج را به اجزای تمیز، قابل ترکیب و آزمایش تبدیل کند. با ترکیب دیدگاه های اختصاصی، اصلاح کننده هاو پسوندهای عمومیشما SwiftUI را می نویسید که نه تنها کوتاه تر، بلکه هوشمندتر است.
اگر میخواهید دانش SwiftUI خود را حتی بیشتر ارتقا دهید، صفحه دسته بندی SwiftUI را بررسی کنید. با من تماس بگیرید یا توییت کنید توییتر اگر نکات یا بازخورد بیشتری دارید.
متشکرم




