
"ฉันดึงข้อมูลหลักออกมานี่คือวิธีที่ควรทำงาน"
- Josh Holtz
"บูติกเป็นเรื่องง่ายที่จะนำไปใช้และทำให้การคงอยู่เป็นเรื่องง่ายมันกลายเป็นส่วนเสริมครั้งแรกของฉันสำหรับทุกโครงการที่ฉันเริ่ม
- Tyler Hillsman
"บูติกกลายเป็นสิ่งที่มีค่าฉันใช้มันในทุกโครงการตอนนี้การไม่ต้องสนใจเกี่ยวกับการคงอยู่นั้นยอดเยี่ยมและค่าใช้จ่ายในการเริ่มต้นเป็นศูนย์ในทางปฏิบัติ"
- romain pouclet
หากคุณพบว่าบูติกมีค่าฉันจะขอบคุณจริง ๆ ถ้าคุณจะพิจารณาช่วยสปอนเซอร์งานโอเพ่นซอร์สของฉันดังนั้นฉันจึงสามารถทำงานในโครงการเช่นบูติกเพื่อช่วยนักพัฒนาอย่างตัวคุณเอง
บูติกเป็นห้องสมุดการคงอยู่ที่เรียบง่าย แต่ทรงพลังชุดเครื่องห่อและประเภทอสังหาริมทรัพย์ขนาดเล็กที่เปิดใช้งานการสร้างแอพที่ขับเคลื่อนด้วยรัฐที่เรียบง่ายอย่างไม่น่าเชื่อสำหรับ Swiftui, Uikit และ AppKit ด้วยหน่วยความจำสองชั้น + ดิสก์การแคชสถาปัตยกรรมบูติกให้วิธีการสร้างแอพที่อัปเดตแบบเรียลไทม์ด้วยที่เก็บข้อมูลออฟไลน์แบบเต็มในรหัสเพียงไม่กี่บรรทัดโดยใช้ API ที่เรียบง่ายอย่างไม่น่าเชื่อ บูติกถูกสร้างขึ้นบน Bodega และคุณสามารถค้นหาแอพสาธิตที่สร้างขึ้นบนยอดสถาปัตยกรรม View Model View Controller ใน repo นี้ซึ่งจะแสดงวิธีการสร้างแอพ Swiftui แบบออฟไลน์พร้อมรหัสเพียงไม่กี่บรรทัด คุณสามารถอ่านเพิ่มเติมเกี่ยวกับความคิดเบื้องหลังสถาปัตยกรรมในโพสต์บล็อกนี้สำรวจสถาปัตยกรรม MVCS
บูติกมีแนวคิดเดียวที่คุณต้องเข้าใจ เมื่อคุณบันทึกข้อมูลไปยัง Store ข้อมูลของคุณจะคงอยู่โดยอัตโนมัติสำหรับคุณและเปิดเผยเป็นอาร์เรย์ Swift ปกติ @StoredValue และ @SecurelyStoredValue wrappers ทำงานในลักษณะเดียวกัน แต่แทนที่จะเป็นอาร์เรย์ที่พวกเขาทำงานกับค่า Swift เอกพจน์ คุณจะไม่ต้องคิดถึงฐานข้อมูลทุกอย่างในแอพของคุณคืออาร์เรย์หรือค่าที่รวดเร็วโดยใช้โมเดลแอปของคุณด้วยรหัสตรงไปตรงมาที่ดูเหมือนแอพอื่น ๆ
คุณอาจคุ้นเคยกับ Store จาก Redux หรือสถาปัตยกรรมแบบคอมโพสิต แต่แตกต่างจากเฟรมเวิร์กเหล่านั้นที่คุณไม่จำเป็นต้องกังวลเกี่ยวกับการเพิ่มการกระทำหรือตัวลด ด้วยการใช้งาน Store นี้ข้อมูลทั้งหมดของคุณจะคงอยู่สำหรับคุณโดยอัตโนมัติจะไม่จำเป็นต้องใช้รหัสเพิ่มเติม สิ่งนี้ช่วยให้คุณสามารถสร้างแอพอัพเดตแบบเรียลไทม์ด้วยการสนับสนุนแบบออฟไลน์เต็มรูปแบบในลักษณะที่เรียบง่ายและตรงไปตรงมาอย่างไม่น่าเชื่อ
คุณสามารถอ่านภาพรวมระดับสูงของบูติกด้านล่าง แต่บูติกได้รับการบันทึกไว้อย่างสมบูรณ์ที่นี่
เราจะผ่านภาพรวมระดับสูงของ Store ด้านล่าง แต่ Store ได้รับการบันทึกไว้อย่างสมบูรณ์ด้วยบริบทการใช้เคสและตัวอย่างที่นี่
พื้นที่ผิวทั้งหมดของ API เพื่อให้ได้รับการสนับสนุนแบบออฟไลน์เต็มรูปแบบและการอัปเดตแบบจำลองเรียลไทม์ในแอพทั้งหมดของคุณคือสามวิธี, .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 } )
} )¹คุณสามารถมีร้านค้าได้มากหรือไม่กี่ร้านเท่าที่คุณต้องการ อาจเป็นกลยุทธ์ที่ดีที่จะมีร้านหนึ่งสำหรับรูปภาพทั้งหมดที่คุณดาวน์โหลดในแอพของคุณ แต่คุณอาจต้องการมีร้านค้าหนึ่งร้านต่อประเภทรุ่นที่คุณต้องการแคช คุณสามารถสร้างร้านค้าแยกต่างหากสำหรับการทดสอบบูติกไม่ได้กำหนดและตัวเลือกสำหรับวิธีที่คุณต้องการจำลองข้อมูลของคุณเป็นของคุณ คุณจะสังเกตเห็นว่านั่นเป็นแนวคิดจาก Bodega ที่คุณสามารถอ่านได้ในเอกสารประกอบของ Bodega ของ Bodega
²ใต้ฮูดร้านค้ากำลังทำงานเพื่อบันทึกการเปลี่ยนแปลงทั้งหมดลงในดิสก์เมื่อคุณเพิ่มหรือลบรายการ
³ใน Swiftui คุณสามารถเพิ่มประสิทธิภาพ View ของคุณด้วย $items และใช้ .onReceive() เพื่ออัปเดตและจัดการข้อมูลที่เผยแพร่โดย $items ของร้านค้า
คำเตือน การจัดเก็บภาพหรือข้อมูลไบนารีอื่น ๆ ในบูติกได้รับการสนับสนุนทางเทคนิค แต่ไม่แนะนำ เหตุผลก็คือการจัดเก็บภาพในบูติกสามารถบอลลูนได้ที่ร้านค้าในหน่วยความจำและความทรงจำของแอปของคุณเป็นผล ด้วยเหตุผลที่คล้ายกันเนื่องจากไม่แนะนำให้จัดเก็บภาพหรือ binary blobs ในฐานข้อมูลจึงไม่แนะนำให้จัดเก็บภาพหรือ binary blobs ในบูติก
เราจะผ่านภาพรวมระดับสูงของ wrapper คุณสมบัติ @Stored ด้านล่าง แต่ @Stored ได้รับการบันทึกไว้อย่างสมบูรณ์ด้วยบริบทการใช้กรณีและตัวอย่างที่นี่
นั่นเป็นเรื่องง่าย แต่ฉันต้องการแสดงให้คุณเห็นบางสิ่งที่ทำให้บูติกรู้สึกมีมนต์ขลังอย่างจริงจัง Store เป็นวิธีง่ายๆในการรับประโยชน์จากการจัดเก็บออฟไลน์และการอัปเดตเรียลไทม์ แต่ด้วยการใช้ wrapper คุณสมบัติ @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 wrappers ด้านล่าง แต่พวกเขาได้รับการบันทึกไว้อย่างเต็มที่ด้วยบริบทการใช้เคสและตัวอย่างที่นี่
Store และ @Stored ถูกสร้างขึ้นเพื่อจัดเก็บอาร์เรย์ของข้อมูลเนื่องจากแอพข้อมูลส่วนใหญ่แสดงผลในรูปแบบของอาร์เรย์ แต่บางครั้งเราจำเป็นต้องเก็บค่าส่วนบุคคลนั่นคือที่ @StoredValue และ @SecurelyStoredValue มีประโยชน์
ไม่ว่าคุณจะต้องการบันทึกข้อมูลที่สำคัญในครั้งต่อไปที่แอปของคุณเปิดตัวให้เก็บโทเค็นการรับรองความถูกต้องในพวงกุญแจหรือคุณต้องการเปลี่ยนว่าแอปดูตามการตั้งค่าของผู้ใช้อย่างไร
บ่อยครั้งที่ผู้คนจะเลือกที่จะจัดเก็บรายการแต่ละรายการเช่นนั้นใน UserDefaults หากคุณใช้ @AppStorage แล้ว @StoredValue จะรู้สึกเหมือนอยู่บ้านมันมี API ที่คล้ายกันมากพร้อมคุณสมบัติเพิ่มเติมบางอย่าง @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 ( ) wrapper คุณสมบัติ @SecurelyStoredValue สามารถทำทุกอย่างที่ @StoredValue ทำ แต่แทนที่จะจัดเก็บค่าใน UserDefaults A @SecurelyStoredValue จะยังคงอยู่ในพวงกุญแจของระบบ นี่เป็นสิ่งที่สมบูรณ์แบบสำหรับการจัดเก็บค่าที่ละเอียดอ่อนเช่นรหัสผ่านหรือโทเค็นรับรองความถูกต้องซึ่งคุณไม่ต้องการจัดเก็บใน UserDefaults
หากคุณมีคำถามใด ๆ ฉันจะถามว่าคุณโปรดดูเอกสารก่อนทั้งบูติกและ Bodega ได้รับการบันทึกไว้อย่างหนัก ด้านบนของบูติกนั้นมาพร้อมกับแอพสาธิตเพียงหนึ่งเดียว แต่สองแอพแต่ละอันมีจุดประสงค์ที่แตกต่างกัน แต่แสดงให้เห็นว่าคุณสามารถสร้างแอพบูติกที่ได้รับการสนับสนุนได้อย่างไร
ขณะที่ฉันกำลังสร้าง v1 ฉันสังเกตเห็นว่าคนที่ได้รับบูติกรักมันและคนที่คิดว่ามันอาจจะดี แต่มีคำถามเริ่มรักมันเมื่อพวกเขาเข้าใจวิธีการใช้งาน ด้วยเหตุนี้ฉันจึงพยายามเขียนเอกสารจำนวนมากเพื่ออธิบายแนวคิดและกรณีการใช้งานทั่วไปที่คุณจะพบเมื่อสร้างแอพ iOS หรือ MacOS หากคุณยังมีคำถามหรือข้อเสนอแนะฉันเปิดรับข้อเสนอแนะมากวิธีการมีส่วนร่วมจะถูกกล่าวถึงในส่วนคำติชมที่มีชื่อ aptly ของ readme นี้
บูติกมีประโยชน์มากในการสร้างแอพออฟไลน์แบบเรียลไทม์ที่มีรหัสเพียงไม่กี่บรรทัด แต่มันก็ยิ่งมีประสิทธิภาพมากขึ้นเมื่อคุณใช้สถาปัตยกรรม Model View Controller Store ที่ฉันพัฒนาขึ้นแสดงให้เห็นใน ImagesController ด้านบน MVCs รวบรวมความคุ้นเคยและความเรียบง่ายของสถาปัตยกรรม MVC ที่คุณรู้จักและชื่นชอบด้วยพลังของ Store เพื่อให้แอปของคุณมีการจัดการสถานะที่เรียบง่าย แต่กำหนดไว้อย่างดีและสถาปัตยกรรมข้อมูล
หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับวิธีการทำงานคุณสามารถอ่านเกี่ยวกับปรัชญาในโพสต์บล็อกที่ฉันสำรวจ MVCs สำหรับ Swiftui และคุณสามารถค้นหาการใช้งานอ้างอิงของแอป MVCS แบบออฟไลน์พร้อมใช้งานแบบออฟไลน์ที่ขับเคลื่อนโดยบูติกใน repo นี้
เรามีรอยขีดข่วนพื้นผิวของสิ่งที่บูติกสามารถทำได้ที่นี่ การใช้ประโยชน์จาก StorageEngine ของ Bodega คุณสามารถสร้างท่อข้อมูลที่ซับซ้อนซึ่งทำทุกอย่างตั้งแต่การแคชข้อมูลไปจนถึงการเชื่อมต่อกับเซิร์ฟเวอร์ API ของคุณ Boutique และ Bodega เป็นมากกว่าห้องสมุดพวกเขาเป็นชุดของแอปพลิเคชันที่ขับเคลื่อนด้วยข้อมูลใด ๆ ดังนั้นฉันขอแนะนำให้พวกเขาถ่ายภาพเล่นกับแอพสาธิตและแม้แต่สร้างแอพของคุณเอง!
โครงการนี้มีหลายรูปแบบของการส่งข้อเสนอแนะให้กับผู้ดูแล
หากคุณมีคำถามเกี่ยวกับบูติกเราขอให้คุณปรึกษาเอกสารก่อนเพื่อดูว่าคำถามของคุณได้รับคำตอบที่นั่นหรือไม่
โครงการนี้ได้รับการบันทึกไว้อย่างหนัก แต่ยังรวมถึงโครงการตัวอย่างหลายโครงการ
StorageEngine ที่กำหนดเองโครงการนี้จะให้บริการคุณได้ดีเป็นวิธีทดสอบประสิทธิภาพของการดำเนินงานที่คุณต้องสร้างหากคุณยังมีคำถามการปรับปรุงหรือวิธีการปรับปรุงบูติกโครงการนี้ใช้ประโยชน์จากคุณลักษณะการอภิปรายของ GitHub
หากคุณพบข้อผิดพลาดและต้องการรายงานปัญหาจะได้รับการชื่นชม
Swift Package Manager เป็นเครื่องมือสำหรับการกระจายรหัส Swift โดยอัตโนมัติและรวมเข้ากับระบบการสร้าง Swift
เมื่อคุณตั้งค่าแพ็คเกจ Swift แล้วการเพิ่มบูติกเป็นการพึ่งพานั้นง่ายพอ ๆ กับการเพิ่มลงในค่าการอ้างอิงของ Package.swift ของคุณ
dependencies: [
. package ( url : " https://github.com/mergesort/Boutique.git " , . upToNextMajor ( from : " 1.0.0 " ) )
] หากคุณไม่ต้องการใช้ SPM คุณสามารถรวมบูติกเข้ากับโครงการของคุณด้วยตนเองโดยการคัดลอกไฟล์ใน
สวัสดีฉันเป็นโจทุกที่บนเว็บ แต่โดยเฉพาะอย่างยิ่งใน Mastodon
ดูใบอนุญาตสำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการใช้บูติก
บูติกเป็นงานแห่งความรักที่จะช่วยให้นักพัฒนาสร้างแอพที่ดีขึ้นทำให้คุณง่ายขึ้นในการปลดล็อกความคิดสร้างสรรค์ของคุณและสร้างสิ่งที่น่าอัศจรรย์สำหรับตัวคุณเองและผู้ใช้ของคุณ หากคุณพบว่าบูติกมีค่าฉันจะขอบคุณจริง ๆ ถ้าคุณพิจารณาช่วยสปอนเซอร์งานโอเพ่นซอร์สของฉันดังนั้นฉันจึงสามารถทำงานในโครงการเช่นบูติกเพื่อช่วยนักพัฒนาอย่างตัวคุณเอง
ตอนนี้คุณรู้แล้วว่ามีอะไรอยู่ ในร้าน สำหรับคุณถึงเวลาเริ่มต้นแล้ว ?