
"لقد انفصلت عن البيانات الأساسية ، هذه هي الطريقة التي يجب أن تعمل بها"
- جوش هولتز
"من السهل تنفيذ البوتيك بشكل يبعث على السخرية ويجعل الثبات نسيمًا. لقد أصبح أول إضافة لي إلى كل مشروع أبدأ.
- تايلر هيلسمان
"أصبح بوتيك لا يقدر بثمن ، وأنا أستخدمه في كل مشروع من الجانبية الآن. عدم الاهتمام بالثبات أمر رائع وتكلفة البدء هي صفرًا عمليًا."
- رومان بوكليت
إذا وجدت قيمة بوتيك ، فسأقدر ذلك حقًا إذا كنت تفكر في مساعدة رعاية عملي مفتوح المصدر ، حتى أتمكن من الاستمرار في العمل في مشاريع مثل بوتيك لمساعدة المطورين مثلك.
Boutique هي مكتبة ثبات بسيطة ولكنها قوية ، وهي مجموعة صغيرة من أغلفة الممتلكات والأنواع التي تمكن من بناء تطبيقات بسيطة بشكل لا يصدق من أجل Swiftui و Uikit و Appkit. من خلال Boutique من ذاكرة الذاكرة المزدوجة + Disk Caching Architecture يوفر وسيلة لبناء التطبيقات التي تحديث في الوقت الفعلي مع تخزين كامل في وضع عدم الاتصال في عدد قليل من الأسطر من التعليمات البرمجية باستخدام واجهة برمجة تطبيقات بسيطة بشكل لا يصدق. تم تصميم Boutique على قمة Bodega ، ويمكنك العثور على تطبيق تجريبي تم تصميمه فوق بنية متجر Controller Model في هذا الريبو الذي يوضح لك كيفية صنع تطبيق Swiftui جاهز للإنترنت في بضعة أسطر فقط من التعليمات البرمجية. يمكنك قراءة المزيد حول التفكير وراء الهندسة المعمارية في منشور المدونة هذا لاستكشاف بنية MVCS.
البوتيك يحتوي فقط على مفهوم واحد تحتاج إلى فهمه. عند حفظ البيانات إلى Store ، سيتم استمرار بياناتك تلقائيًا من أجلك ويتعرض لك كصفيف سريع منتظم. تعمل @StoredValue و @SecurelyStoredValue Properments على نفس الطريقة ، ولكن بدلاً من صفيف ، يعملون مع قيم سريعة فريدة. لن تضطر أبدًا إلى التفكير في قواعد البيانات ، كل شيء في التطبيق الخاص بك هو صفيف أو قيمة عادية باستخدام نماذج التطبيق الخاصة بك ، مع رمز مباشر يشبه أي تطبيق آخر.
قد تكون على دراية Store من Redux أو الهندسة المعمارية القابلة للتكامل ، ولكن على عكس تلك الأطر التي لن تحتاج إلى القلق بشأن إضافة الإجراءات أو المخفضات. مع تطبيق هذا Store ، يتم استمرار جميع بياناتك بالنسبة لك تلقائيًا ، لا يوجد رمز إضافي مطلوب. يتيح لك ذلك إنشاء تطبيقات في الوقت الفعلي مع الدعم الكامل في وضع عدم الاتصال بطريقة بسيطة ومباشرة بشكل لا يصدق.
يمكنك قراءة نظرة عامة عالية المستوى على البوتيك أدناه ، ولكن تم توثيق بوتيك بالكامل هنا.
سنخوض نظرة عامة عالية المستوى على Store أدناه ، ولكن تم توثيق Store بالكامل مع السياق وحالات الاستخدام والأمثلة هنا.
المساحة السطحية بأكملها من واجهة برمجة التطبيقات لتحقيق الدعم الكامل للإنترنت وتحديثات نموذج الوقت الفعلي عبر تطبيقك بالكامل هي ثلاث طرق ، .insert() ، .remove() ، و .removeAll() .
// Create a Store ¹
let store = Store < Animal > (
storage : SQLiteStorageEngine . default ( appendingPath : " Animals " ) ,
cacheIdentifier : . id
)
// Insert an item into the Store ²
let redPanda = Animal ( id : " red_panda " )
try await store . insert ( redPanda )
// Remove an animal from the Store
try await store . remove ( redPanda )
// Insert two more animals to the Store
let dog = Animal ( id : " dog " )
let cat = Animal ( id : " cat " )
try await store . insert ( [ dog , cat ] )
// You can read items directly
print ( store . items ) // Prints [dog, cat]
// You also don't have to worry about maintaining uniqueness, the Store handles uniqueness for you
let secondDog = Animal ( id : " dog " )
try await store . insert ( secondDog )
print ( store . items ) // Prints [dog, cat]
// Clear your store by removing all the items at once.
store . removeAll ( )
print ( store . items ) // Prints []
// You can even chain commands together
try await store
. insert ( dog )
. insert ( cat )
. run ( )
print ( store . items ) // Prints [dog, cat]
// This is a good way to clear stale cached data
try await store
. removeAll ( )
. insert ( redPanda )
. run ( )
print ( store . items ) // Prints [redPanda]وإذا كنت تقوم ببناء تطبيق Swiftui ، فلن تضطر إلى تغيير شيء ما ، فقد تم صنع بوتيك مع Swiftui في الاعتبار. (ولكن يعمل بشكل جيد في Uikit و Appkit بالطبع.)
// Since items is a @Published property
// you can subscribe to any changes in realtime.
store . $items . sink ( { items in
print ( " Items was updated " , items )
} )
// Works great with SwiftUI out the box for more complex pipelines.
. onReceive ( store . $items , perform : {
self . allItems = $0 . filter ( { $0 . id > 100 } )
} )¹ يمكنك الحصول على أكبر عدد أو عدد قليل من المتاجر كما تريد. قد تكون استراتيجية جيدة أن يكون لديك متجر واحد لجميع الصور التي تقوم بتنزيلها في التطبيق الخاص بك ، ولكن قد ترغب أيضًا في الحصول على متجر واحد لكل طراز تريد ذاكرة التخزين المؤقت. يمكنك حتى إنشاء متاجر منفصلة للاختبارات ، فإن Boutique ليس وصولاً واختيار كيفية وصولك إلى تصميم بياناتك. ستلاحظ أيضًا أن هذا مفهوم من Bodega يمكنك قراءته في وثائق Bodega للتخزين.
² تحت الغطاء ، يقوم المتجر بعمل توفير جميع التغييرات في القرص عند إضافة العناصر أو إزالةها.
³ في Swiftui ، يمكنك حتى تشغيل View مع $items واستخدام .onReceive() لتحديث ومعالجة البيانات المنشورة بواسطة $items من المتجر.
يتم دعم تحذير الصور أو البيانات الثنائية الأخرى في البوتيك من الناحية الفنية ولكن لا ينصح بها. والسبب هو أن تخزين الصور في Boutique CAN CAN Balloon Up in-Semory ، وذاكرة تطبيقك نتيجة لذلك. لأسباب مماثلة لأنه لا ينصح بتخزين الصور أو النقط الثنائية في قاعدة بيانات ، لا ينصح بتخزين الصور أو النقط الثنائية في البوتيك.
سنخوض نظرة عامة على مستوى عالٍ من غلاف الخصائص @Stored أدناه ، ولكن تم @Stored بالكامل مع السياق وحالات الاستخدام والأمثلة هنا.
كان ذلك سهلاً ، لكني أريد أن أريكم شيئًا يجعل البوتيك يشعر بالسحر. يعد Store وسيلة بسيطة للحصول على فوائد تخزين الخطوط غير المتصلة بالإنترنت والتحديثات الحقيقية ، ولكن باستخدام ملف Property @Stored ، يمكننا تخزين أي خاصية في الذاكرة وعلى القرص مع سطر واحد فقط من التعليمات البرمجية.
extension Store where Item == RemoteImage {
// Initialize a Store to save our images into
static let imagesStore = Store < RemoteImage > (
storage : SQLiteStorageEngine . default ( appendingPath : " Images " )
)
}
final class ImagesController : ObservableObject {
/// Creates a @Stored property to handle an in-memory and on-disk cache of images. ⁴
@ Stored ( in : . imagesStore ) var images
/// Fetches `RemoteImage` from the API, providing the user with a red panda if the request succeeds.
func fetchImage ( ) async throws -> RemoteImage {
// Hit the API that provides you a random image's metadata
let imageURL = URL ( string : " https://image.redpanda.club/random/json " ) !
let randomImageRequest = URLRequest ( url : imageURL )
let ( imageResponse , _ ) = try await URLSession . shared . data ( for : randomImageRequest )
return RemoteImage ( createdAt : . now , url : imageResponse . url , width : imageResponse . width , height : imageResponse . height , imageData : imageResponse . imageData )
}
/// Saves an image to the `Store` in memory and on disk.
func saveImage ( image : RemoteImage ) async throws {
try await self . $images . insert ( image )
}
/// Removes one image from the `Store` in memory and on disk.
func removeImage ( image : RemoteImage ) async throws {
try await self . $images . remove ( image )
}
/// Removes all of the images from the `Store` in memory and on disk.
func clearAllImages ( ) async throws {
try await self . $images . removeAll ( )
}
} هذا كل شيء ، هذا هو حقا. هذه التقنية تتجه بشكل جيد للغاية ، ومشاركة هذه البيانات عبر العديد من المشاهدات هي بالضبط كيف تتماسك البوتيك من التطبيقات البسيطة إلى المعقدة دون إضافة تعقيد API. من الصعب تصديق أن تطبيقك الآن يمكنه تحديث حالته في الوقت الفعلي من خلال التخزين الكامل في وضع عدم الاتصال بفضل سطر واحد فقط من التعليمات البرمجية. @Stored(in: .imagesStore) var images
⁴ (إذا كنت تفضل فصل المتجر من نموذج العرض أو وحدة التحكم أو كائن المدير ، فيمكنك ضخ المتاجر في كائن مثل هذا.)
final class ImagesController : ObservableObject {
@ Stored var images : [ RemoteImage ]
init ( store : Store < RemoteImage > ) {
self . _images = Stored ( in : store )
}
} سنخوض نظرة عامة على مستوى عالٍ على أغلفة خاصية @StoredValue و @SecurelyStoredValue أدناه ، ولكنها موثقة بالكامل مع السياق ، وحالات الاستخدام ، والأمثلة هنا.
تم إنشاء Store و @Stored لتخزين مجموعة من البيانات لأن معظم تطبيقات البيانات تأتي في شكل صفيف. لكن في بعض الأحيان ، نحتاج إلى تخزين قيمة فردية ، حيث يكون @StoredValue و @SecurelyStoredValue مفيدًا.
سواء كنت بحاجة إلى حفظ جزء مهم من المعلومات في المرة القادمة التي يتم فيها تشغيل تطبيقك ، أو تخزين رمز المصادقة في سلسلة المفاتيح ، أو تريد تغيير كيفية ظهور التطبيق بناءً على إعدادات المستخدم ، فإن تكوينات التطبيق هذه هي القيم الفردية التي سترغب في استمرارها.
غالبًا ما يختار الأشخاص تخزين العناصر الفردية مثل تلك الموجودة في UserDefaults . إذا كنت قد استخدمت @AppStorage ، فسيشعر @StoredValue في المنزل ، فهو يحتوي على واجهة برمجة تطبيقات مشابهة للغاية مع بعض الميزات الإضافية. سينتهي الأمر إلى تخزين @StoredValue في UserDefaults ، ولكنه يعرض أيضًا publisher حتى تتمكن من الاشتراك بسهولة في التغييرات.
// Setup a `@StoredValue has the same API.
@ StoredValue ( key : " hasHapticsEnabled " )
var hasHapticsEnabled = false
// You can also store nil values
@ StoredValue ( key : " lastOpenedDate " )
var lastOpenedDate : Date ? = nil
// Enums work as well, as long as it conforms to `Codable` and `Equatable`.
@ StoredValue ( key : " currentTheme " )
var currentlySelectedTheme = . light
// Complex objects work as well
struct UserPreferences : Codable , Equatable {
var hasHapticsEnabled : Bool
var prefersDarkMode : Bool
var prefersWideScreen : Bool
var spatialAudioEnabled : Bool
}
@ StoredValue ( key : " userPreferences " )
var preferences = UserPreferences ( )
// Set the lastOpenedDate to now
$lastOpenedDate . set ( . now )
// currentlySelected is now .dark
$currentlySelectedTheme . set ( . dark )
// StoredValues that are backed by a boolean also have a toggle() function
$hasHapticsEnabled . toggle ( ) يمكن لركب خاصية خاصية @SecurelyStoredValue أن يفعل كل ما يفعله @StoredValue ، ولكن بدلاً من تخزين القيم في UserDefaults ، فإن @SecurelyStoredValue سوف يستمر في سلسلة مفاتيح النظام. يعد هذا مثاليًا لتخزين القيم الحساسة مثل كلمات المرور أو الرموز المميزة التي لا ترغب في تخزينها في UserDefaults .
إذا كانت لديك أي أسئلة ، أود أن أطرح عليك إلقاء نظرة على الوثائق أولاً ، يتم توثيق بوتيك وبوديجا بشكل كبير. علاوة على ذلك ، لا يأتي هذا البوتيك مع تطبيق واحد ولكن اثنين من التطبيقات التجريبية ، كل منهما يخدم غرضًا مختلفًا ولكنه يوضح كيف يمكنك إنشاء تطبيق مدعوم من قبل البوتيك.
بينما كنت أقوم ببناء v1 ، لاحظت أن الأشخاص الذين حصلوا على بوتيك أحبوا ذلك ، والأشخاص الذين اعتقدوا أنه قد يكون جيدًا ولكنهم قد نمت أسئلة لأحبها بمجرد أن يفهموا كيفية استخدامه. بسبب ذلك ، سعت إلى كتابة الكثير من الوثائق التي تشرح المفاهيم وحالات الاستخدام الشائعة التي ستواجهها عند إنشاء تطبيق iOS أو MacOS. إذا كان لا يزال لديك أسئلة أو اقتراحات ، فأنا منفتح جدًا على التعليقات ، تتم مناقشة كيفية المساهمة في قسم التغذية المرتدة المسمى على هذا القراءة.
يعد Boutique مفيدًا جدًا من تلقاء نفسه لبناء تطبيقات جاهزة للإنترنت في الوقت الفعلي مع بضعة أسطر من التعليمات البرمجية ، ولكنها أكثر قوة عند استخدام بنية متجر Controller Model التي طورتها ، وقد أظهرت في ImagesController أعلاه. يجمع MVCS بين الألفة وبساطة بنية MVC التي تعرفها وتحبها بقوة Store ، لمنح تطبيقك إدارة الدولة البسيطة ولكن المحددة جيدًا.
إذا كنت ترغب في معرفة المزيد حول كيفية عمله ، يمكنك أن تقرأ عن الفلسفة في منشور مدونة حيث أستكشف MVCs لـ Swiftui ، ويمكنك العثور على تطبيق مرجعي لتطبيق MVCs في الوقت الفعلي غير المتصلة بالإنترنت مدعوم من بوتيك في هذا الريبو.
لقد خدشنا سطح ما يمكن أن يفعله هنا. الاستفادة من StorageEngine Bodega ، يمكنك إنشاء خطوط أنابيب بيانات معقدة تقوم بكل شيء من تخزين البيانات إلى التواصل مع خادم API الخاص بك. يعد Boutique و Bodega أكثر من المكتبات ، وهما مجموعة من البدائية لأي تطبيق يعتمد على البيانات ، لذلك أقترح منحهم لقطة ، واللعب مع التطبيق التجريبي ، وحتى بناء تطبيق خاص بك!
يوفر هذا المشروع أشكالًا متعددة من تقديم التعليقات للمحفرين.
إذا كان لديك سؤال حول بوتيك ، نطرح عليك أولاً استشارة الوثائق لمعرفة ما إذا كان قد تم الرد على سؤالك هناك.
تم توثيق هذا المشروع بشكل كبير ولكنه يشمل أيضًا مشاريع عينة متعددة.
StorageEngine مخصص ، فسيخدمك هذا المشروع طريقة لاختبار أداء العمليات التي تحتاجها.إذا كان لا يزال لديك سؤال أو تعزيز أو وسيلة لتحسين بوتيك ، فإن هذا المشروع يستفيد من ميزة مناقشات جيثب.
إذا وجدت خطأ وترغب في الإبلاغ عن مشكلة ، فسيكون موضع تقدير.
يعد Swift Package Manager أداة لأتمتة توزيع الكود السريع ويتم دمجه في نظام البناء السريع.
بمجرد إعداد حزمة Swift الخاصة بك ، فإن إضافة البوتيك كاعتماد سهل مثل إضافته إلى قيمة التبعيات الخاصة Package.swift الخاصة بك.
dependencies: [
. package ( url : " https://github.com/mergesort/Boutique.git " , . upToNextMajor ( from : " 1.0.0 " ) )
] إذا كنت تفضل عدم استخدام SPM ، فيمكنك دمج بوتيك في مشروعك يدويًا عن طريق نسخ الملفات فيه.
مرحبًا ، أنا جو في كل مكان على الويب ، ولكن خاصة على Mastodon.
راجع الترخيص لمزيد من المعلومات حول كيفية استخدام البوتيك.
Boutique هو عمل حب لمساعدة المطورين على بناء تطبيقات أفضل ، مما يسهل عليك إلغاء قفل إبداعك وجعل شيء مدهش لنفسك ومستخدميك. إذا وجدت قيمة بوتيك ، فسأقدر ذلك حقًا إذا كنت تفكر في مساعدة رعاية عملي مفتوح المصدر ، حتى أتمكن من الاستمرار في العمل في مشاريع مثل بوتيك لمساعدة المطورين مثلك.
الآن بعد أن عرفت ما الذي يخبئك ، فقد حان الوقت للبدء ؟