الگوی MVVM در SwiftUI – راهنمای کاربردی
SwiftUI یک «فریمورک اعلانی» (Declarative Framework) است که از سوی اپل برای ساخت اپلیکیشنهای مربوط به محصولات این شرکت ارائه شده است. این بدان معنی است که به جای استفاده از استوریبورد و یا تولید برنامهنویسی شده اینترفیس، میتوانید به سادگی از فریمورک SwiftUI بهره بگیرید. SwiftUI از معماری MVC پیروی نمیکند. به زبان سادهتر وقتی میخواهید یک اپلیکیشن کاملاً جدید در Xcode 11 بسازید و SwiftUI را فعال میکنید هیچ کنترلری ایجاد نمیشود. البته این به آن معنی نیست که نمیتوان از الگوی طراحی MVC در SwiftUI استفاده کرد، بلکه صرفاً به این معنا است که یک الگوی معماری متفاوتی به نام MVVM در SwiftUI برای رفع نیازهای ما در این فریمورک عملکرد بهتری ارائه میکند. الگوی MVC ساختاری سه لایه دارد و در توسعه وب با چارچوب MVC در PHP بسیار کاربردی است.
در این مقاله یک اپلیکیشن خبری کامل با استفاده از SwiftUI و الگوی طراحی MVVM ایجاد میکنیم و به این ترتیب به بررسی الگوی MVVM در SwiftUI میپردازیم.
پیادهسازی
در این اپلیکیشن خبری آخرین عناوین اخبار را از وبسایت NewsAPI.org دریافت میکنیم. ثبت نام در وبسایت NewsAPI.org برای به دست آوردن کلید API جهت ارسال درخواست به این سرویس ضروری است.
وبسرویس و مدلها
پیش از پرداختن به بخش SwiftUI ابتدا یک وبسرویس ایجاد میکنیم که مسئول بازیابی آخرین خبرها از NewsAPI خواهد بود. این وبسرویس به صورت زیر پیادهسازی میشود:
1class Webservice {
2
3 func loadTopHeadlines(url: URL, completion: @escaping ([Article]?) -> ()) {
4
5 URLSession.shared.dataTask(with: url) { data, response, error in
6
7 guard let data = data, error == nil else {
8 completion(nil)
9 return
10 }
11
12 let response = try? JSONDecoder().decode(NewsResponse.self, from: data)
13 if let response = response {
14 DispatchQueue.main.async {
15 completion(response.articles)
16 }
17 }
18
19
20 }.resume()
21
22 }
23
24}
تابع loadTopHeadlines همه مقالات را بازیابی میکند و مدل Article را پر میکند. مدل Article و NewsResponse به صورت زیر پیادهسازی شدهاند:
1import Foundation
2
3struct NewsResponse: Codable {
4 let articles: [Article]
5}
6
7struct Article: Codable {
8 let title: String
9 let description: String?
10}
مدلهای نما
این مدلها دادهها را از وبسرویس میگیرند و سپس آن را به «مدلهای نما» (View Models) که مسئول نمایش دادن آن روی صفحه هستند، تحویل میدهند. ArticleViewModel مسئول یک مقاله منفرد است که در یک سلول شامل کنترل List، نمایش خواهد یافت. پیادهسازی ArticleViewModel به صورت زیر است:
1class ArticleViewModel: Identifiable {
2
3 let id = UUID()
4
5 let article: Article
6
7 init(article: Article) {
8 self.article = article
9 }
10
11 var title: String {
12 return self.article.title
13 }
14
15 var description: String {
16 return self.article.description ?? ""
17 }
18
19
20}
ArticleViewModel با پروتکل Identifiable سازگار است، زیرا باید دادهها را به List ارائه کند. List از مشخصه id برای مطمئن شدن از یکتا بودن محتوا بهره میگیرد. سپس ArticleListViewModel را پیادهسازی میکنیم. این مدل نما مسئول بازتاب محتوای کل صفحه است. ArticleListViewModel ضمناً از وبسرویس بهره میگیرد تا همه مقالات اخیر را بازیابی کند.
1class ArticleListViewModel: BindableObject {
2
3 let didChange = PassthroughSubject<ArticleListViewModel,Never>()
4
5 init() {
6 fetchTopHeadlines()
7 }
8
9 var articles = [ArticleViewModel]() {
10 didSet {
11 didChange.send(self)
12 }
13 }
14
15 private func fetchTopHeadlines() {
16
17 guard let url = URL(string: "https://newsapi.org/v2/top-headlines?country=us&apiKey=InsertYourAPIKeyHere") else {
18 fatalError("URL is not correct!")
19 }
20
21 Webservice().loadTopHeadlines(url: url) { articles in
22
23 if let articles = articles {
24 self.articles = articles.map(ArticleViewModel.init)
25 }
26
27 }
28
29 }
30
31}
چند نکته در ArticleListViewModel وجود دارد که باید مورد اشاره قرار دهیم. ابتدا این که با پروتکل BindableObject سازگار است. این به آن معنی است که میتواند رویدادها را به مشترکان انتشار دهد و میتواند به نماهای روی صفحه اتصال یابد. تنها الزام BindableObject، الزام به پیادهسازی رویداد didChange است. رویداد didChange به مشترکان اطلاع میدهد که دادههای جدیدی موجود شده است.
در تابع fetchTopHeadlines از یک درخواست وبسرویس ایجاد میکنیم تا همه مقالات خبری را بازیابی کنیم. زمانی که همه مقالات دریافت شدند، به مشخصه articles انتساب میدهیم که یک متد به نام didSet دارد. بدین ترتیب رویداد didChange در self ارسال میشود که در این مورد ArticleListViewModel است. اکنون تنها نکتهای که مانده این است که مطمئن شویم مقالات روی صفحه نمایش یافتهاند. SwiftUI این کار را به سادگی ممکن ساخته است.
SwiftUI
استایل اعلانی ارائه شده از سوی SwiftUI موجب شده که ساخت اینترفیس بسیار آسان شود. کد کامل برای SwiftUI که در فایل ContentView.swift نوشته میشود به صورت زیر است:
1struct ContentView : View {
2
3 @ObjectBinding var model = ArticleListViewModel()
4
5 var body: some View {
6 List(model.articles) { article in
7
8 VStack(alignment: .leading) {
9
10 Text(article.title)
11 .lineLimit(nil)
12
13 Text(article.description)
14 .foregroundColor(.secondary)
15 .lineLimit(nil)
16
17 }
18
19 }
20 }
21}
نخستین نکتهای که توجه ما را جلب میکند، این است که مدل دارای علامتی به صورت ObjectBinding@ است. معنی آن این است که وقتی مدل در نهایت تنظیم شود، پس از فراخوانی ناهمگام، اقدام به رندر مجدد صفحه با اجرای مشخصه body میکند. اگر ObjectBinding@ را با State@ عوض کنید، همچنان به همین صورت عمل میکند. اما مقصود از State@ با ObjectBinding@ تفاوتهایی دارد. تصویر زیر اپلیکیشن را در عمل نمایش میدهد:
اگر این مطلب برای شما مفید بوده است، آموزشها و مطالب زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی
- آموزش برنامه نویسی Swift (سوئیفت) برای برنامه نویسی iOS
- مجموعه آموزشهای دروس علوم و مهندسی کامپیوتر
- آموزش سوئیفت (Swift) — مجموعه مقالات مجله فرادرس
- الگوریتم های مرتب سازی در سوئیفت (Swift) — به زبان ساده
==