حالة تطبيق النمذجة باستخدام كائنات تخزين في SwiftUI

أريد أن أتحدث هذا الأسبوع عن نمذجة طبقة بيانات في SwiftUI. لقد انتهيت بالفعل من العمل على طلبي الأول ، والذي أقوم بإنشائه باستخدام SwiftUI فقط. الآن يمكنني مشاركة طريقة إنشاء طبقة نموذج باستخدام كائنات المتجر التي استخدمتها عند تطوير تطبيق NapBot.

كائن مخزن


كائنات المتجر مسؤولة عن الحفاظ على الحالة وتوفير الإجراء لتغييرها. يمكنك الحصول على أكبر عدد ممكن من كائنات المتجر ، ويفضل أن تكون بسيطة ومسؤولة عن جزء صغير من حالة التطبيق الخاص بك. على سبيل المثال ، قد يكون لديك SettingsStore لحفظ حالة إعدادات المستخدم و TodoStore لحفظ المهام المخصصة.

لإنشاء كائن Store ، يجب عليك إنشاء فئة تتوافق مع بروتوكول ObservableObject . يسمح بروتوكول ObservableObject لـ SwiftUI بمراقبة تغييرات البيانات والرد عليها. لمعرفة المزيد حول ObservableObject ، ألقِ نظرة على مقالة " إدارة تدفق البيانات في SwiftUI ". دعونا نلقي نظرة على مثال بسيط لكائن SettingsStore .

import Foundation import Combine final class SettingsStore: ObservableObject { let objectWillChange = PassthroughSubject<Void, Never>() @UserDefault(Constants.UserDefaults.sleepGoal, defaultValue: 8.0) var sleepGoal: Double @UserDefault(Constants.UserDefaults.notifications, defaultValue: true) var isNotificationsEnabled: Bool private var didChangeCancellable: AnyCancellable? override init() { super.init() didChangeCancellable = NotificationCenter.default .publisher(for: UserDefaults.didChangeNotification) .map { _ in () } .receive(on: DispatchQueue.main) .subscribe(objectWillChange) } } 

في مثال التعليمة البرمجية أعلاه ، لدينا فئة SettingsStore ، والتي توفر الوصول إلى إعدادات المستخدم. نستخدم أيضًا didChangeNotification لإخطار SwiftUI كلما قام المستخدم بتغيير الإعدادات الافتراضية.

الاستخدام المطول


دعنا ننظر إلى استخدام آخر لكائن المتجر عن طريق إنشاء تطبيق Todo بسيط. نحن بحاجة إلى إنشاء كائن مخزن يخزن قائمة المهام ويوفر إجراءات لتغييرها ، على سبيل المثال ، حذفها وتصفيتها.

 import Foundation import Combine struct Todo: Identifiable, Hashable { let id = UUID() var title: String var date: Date var isDone: Bool var priority: Int } final class TodosStore: ObservableObject { @Published var todos: [Todo] = [] func orderByDate() { todos.sort { $0.date < $1.date } } func orderByPriority() { todos.sort { $0.priority > $1.priority } } func removeCompleted() { todos.removeAll { $0.isDone } } } 

هناك فئة TodosStore يتوافق مع بروتوكول ObservableObject. يوفر TodosStore العديد من الإجراءات لتغيير حالته ، يمكننا استخدام هذه الأساليب من وجهات نظرنا. بشكل افتراضي ، يقوم SwiftUI بتحديث العرض في كل مرة يتغير فيها الحقل المنشور . هذا هو السبب في أن مجموعة عناصر Todo تم تعيينها على أنها Published . بمجرد إضافة أو إزالة عناصر من هذا الصفيف ، سيقوم SwiftUI بتحديث العرض المشترك في TodosStore .

يمكنك الآن إنشاء طريقة عرض تعرض قائمة من المهام وإجراءات مثل وضع علامة على مهمة مكتملة وحذف وتغيير ترتيب عرض المهام. لنبدأ بإنشاء طريقة عرض تعرض عنوان المهمة ومفتاحًا لتمييز المهمة على أنها مكتملة.

 import SwiftUI struct TodoItemView: View { let todo: Binding<Todo> var body: some View { HStack { Toggle(isOn: todo.isDone) { Text(todo.title.wrappedValue) .strikethrough(todo.isDone.wrappedValue) } } } } 

في المثال أعلاه ، تم استخدام Binding لتوفير مرجع ، على سبيل المثال الوصول إلى نوع القيمة. بمعنى آخر ، امنح حق الوصول للكتابة إلى عنصر ما يجب عمله. لا يملك TodoItemView مثيل بنية Todo ، لكن لديه حق الوصول للكتابة إلى TodoStore من خلال Binding .

 import SwiftUI struct TodosView: View { @EnvironmentObject var store: TodosStore @State private var draft: String = "" var body: some View { NavigationView { List { TextField("Type something...", text: $draft, onCommit: addTodo) ForEach(store.todos.indexed(), id: \.1.id) { index, _ in TodoItemView(todo: self.$store.todos[index]) } .onDelete(perform: delete) .onMove(perform: move) } .navigationBarItems(trailing: EditButton()) .navigationBarTitle("Todos") } } private func delete(_ indexes: IndexSet) { store.todos.remove(atOffsets: indexes) } private func move(_ indexes: IndexSet, to offset: Int) { store.todos.move(fromOffsets: indexes, toOffset: offset) } private func addTodo() { let newTodo = Todo(title: draft, date: Date(), isDone: false, priority: 0) store.todos.insert(newTodo, at: 0) draft = "" } } 

الآن لدينا TodosView ، وهو عنصر يستخدم مكون القائمة لعرض المهام. يوفر مكون القائمة أيضًا إعادة ترتيب وحذف. شيء آخر مثير للاهتمام هو وظيفة المفهرسة () . هذه الوظيفة تقوم بإرجاع مجموعة من العناصر مع مؤشراتها. نستخدمها للوصول إلى العناصر الموجودة في المتجر من خلال Binding. هنا هو المصدر الكامل لهذا التمديد.

 import Foundation struct IndexedCollection<Base: RandomAccessCollection>: RandomAccessCollection { typealias Index = Base.Index typealias Element = (index: Index, element: Base.Element) let base: Base var startIndex: Index { base.startIndex } var endIndex: Index { base.endIndex } func index(after i: Index) -> Index { base.index(after: i) } func index(before i: Index) -> Index { base.index(before: i) } func index(_ i: Index, offsetBy distance: Int) -> Index { base.index(i, offsetBy: distance) } subscript(position: Index) -> Element { (index: position, element: base[position]) } } extension RandomAccessCollection { func indexed() -> IndexedCollection<Self> { IndexedCollection(base: self) } } 

البيئة هي المرشح المثالي لتخزين كائنات المتجر. يمكن للبيئة تقسيمها بين طرق عرض متعددة دون تطبيق صريح من خلال طريقة init . لمعرفة المزيد حول فوائد البيئة في SwiftUI ، ألق نظرة على مقالة " ميزات البيئة في SwiftUI ".

لجميع الناس، لقطات

استنتاج


ناقش هذا المقال طريقة لنمذجة حالة التطبيق باستخدام كائنات تخزين متعددة. أنا حقا أحب بساطة هذا النهج ومدى سهولة توسيع نطاق التطبيق الخاص بك عن طريق إضافة المزيد من كائنات المتجر. أتمنى أن تستمتعوا بهذا المقال.

Source: https://habr.com/ru/post/ar467675/


All Articles