
"핵심 데이터를 찢어 버렸습니다. 이것이 작동하는 방식입니다."
- 조쉬 홀츠
"부티크는 엄청나게 구현하기 쉽고 지속성을 산들 바람으로 만듭니다. 그것은 내가 시작한 모든 프로젝트에 처음으로 추가되었습니다.
- 타일러 힐즈 만
"부티크는 귀중해졌습니다. 나는 지금 모든 사이드 프로젝트에서 사용합니다. 지속성에 관심을 갖지 않아도되며 시작하는 데 드는 비용은 실제로 0입니다."
- Romain Pouclet
부티크가 귀중한 것을 발견했다면 오픈 소스 작업을 스폰서로 돕는 것을 고려한다면 정말 감사하겠습니다.
Boutique는 간단하지만 강력한 지속성 라이브러리, Swiftui, Uikit 및 Appkit을위한 엄청나게 간단한 상태 중심 앱을 구축 할 수있는 소규모 부동산 포장지 및 유형입니다. 듀얼 레이어 메모리 + 디스크 캐싱 아키텍처 부티크는 엄청나게 간단한 API를 사용하여 몇 줄의 오프라인 스토리지로 실시간으로 업데이트하는 앱을 빌드하는 방법을 제공합니다. 부티크는 Bodega 꼭대기에 제작 되었으며이 repo의 Model View Controller Store Architecture 위에 구축 된 데모 앱을 찾을 수 있으며,이 리브레는 몇 줄의 코드로 오프라인으로 준비된 Swiftui 앱을 만드는 방법을 보여줍니다. MVCS 아키텍처를 탐구하는이 블로그 게시물에서 건축의 사고에 대한 자세한 내용을 읽을 수 있습니다.
부티크에는 이해해야 할 개념이 하나뿐입니다. 데이터를 Store 에 저장하면 데이터가 자동으로 유지되며 일반적인 신속한 배열로 노출됩니다. @StoredValue 및 @SecurelyStoredValue 속성 포장지는 같은 방식으로 작동하지만 배열 대신 Singular Swift 값으로 작동합니다. 데이터베이스에 대해 생각할 필요가 없습니다. 앱의 모든 것은 다른 앱처럼 보이는 간단한 코드를 사용하여 앱 모델을 사용하는 일반적인 신속한 배열 또는 값입니다.
Redux 또는 Composable Architecture의 Store 에 익숙 할 수도 있지만 해당 프레임 워크와 달리 작업이나 리더러 추가에 대해 걱정할 필요가 없습니다. 이 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의 StorageEngine 문서에서 읽을 수있는 Bodega의 개념입니다.
² 후드 아래에서 상점은 품목을 추가하거나 제거 할 때 디스크에 모든 변경 사항을 저장하는 작업을 수행하고 있습니다.
³ Swiftui에서는 $items 으로 View 에 전원을 공급하고 .onReceive() 사용하여 상점의 $items 에 의해 게시 된 데이터를 업데이트하고 조작 할 수 있습니다.
부티크에 이미지 또는 기타 이진 데이터를 저장 하는 것은 기술적으로 지원되지만 권장되지 않습니다. 그 이유는 부티크에 이미지를 저장하면 메모리 내 상점을 팽팽하게 만들 수 있고 그 결과 앱의 메모리가 발생하기 때문입니다. 데이터베이스에 이미지 나 이진 블로브를 저장하는 것이 권장되지 않는 비슷한 이유로 부티크에 이미지 나 이진 블로브를 저장하는 것이 좋습니다.
아래 @Stored 속성 래퍼에 대한 높은 수준의 개요를 살펴 보지만 @Stored 컨텍스트, 사용 사례 및 예제로 완전히 문서화되어 있습니다.
그것은 쉬웠지만 부티크를 마법처럼 느끼게하는 것을 보여 드리고 싶습니다. 이 Store 오프라인 스토리지 및 실시간 업데이트의 이점을 얻는 간단한 방법이지만 @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 집에서 바로 느끼게되면 몇 가지 추가 기능이있는 매우 유사한 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 ( ) @SecurelyStoredValue 속성 래퍼는 @StoredValue 가하는 모든 작업을 수행 할 수 있지만 UserDefaults 에 값을 저장하는 대신 @SecurelyStoredValue 시스템의 키 체인에 항목이 지속됩니다. 이는 UserDefaults 에 저장하고 싶지 않은 암호 또는 인증 토큰과 같은 민감한 값을 저장하는 데 적합합니다.
궁금한 점이 있으면 먼저 문서를보십시오. 부티크와 Bodega는 모두 매우 많이 문서화되어 있습니다. 이 부티크 외에도 하나의 데모 앱이 아니라 두 개의 데모 앱이 제공되며 각각 다른 목적을 제공하지만 부티크 지원 앱을 구축 할 수있는 방법을 보여줍니다.
내가 V1을 건설 할 때 나는 부티크를받은 사람들이 그것을 좋아한다는 것을 알았고, 그것이 좋을지도 모르지만 질문을받은 사람들은 그것을 사용하는 방법을 이해하면 그것을 좋아하는 것을 좋아했습니다. 그 때문에 나는 iOS 또는 MacOS 앱을 구축 할 때 발생할 수있는 개념과 일반적인 사용 사례를 설명하는 많은 문서를 작성하려고했습니다. 여전히 질문이나 제안이 있다면 피드백에 매우 열려있는 경우,이 readme의 적절한 이름의 피드백 섹션에서 기여하는 방법에 대해 논의합니다.
Boutique는 몇 줄의 코드만으로 실시간 오프라인으로 준비된 앱을 구축하는 데 매우 유용하지만 위의 ImagesController 에서 보여준 Model View Controller Store Architecture를 사용할 때 더욱 강력합니다. MVCS는 Store 의 힘을 알고 알고 사랑하는 MVC 아키텍처의 친숙 함과 단순성을 모아서 간단하지만 잘 정의 된 상태 관리 및 데이터 아키텍처를 제공합니다.
어떻게 작동하는지에 대한 자세한 내용은 Swiftui 용 MVC를 탐색하는 블로그 게시물에서 철학에 대해 읽을 수 있으며이 리베리크에서 Boutique로 구동되는 오프라인으로 준비된 실시간 MVCS 앱의 참조 구현을 찾을 수 있습니다.
우리는 여기서 부티크가 할 수있는 일의 표면을 긁었습니다. Bodega의 StorageEngine 활용하면 캐싱에서 데이터 캐싱에서 API 서버와 인터페이스에 이르기까지 모든 작업을 수행하는 복잡한 데이터 파이프 라인을 구축 할 수 있습니다. Boutique와 Bodega는 라이브러리 이상이며 모든 데이터 중심 애플리케이션을위한 프리미티브 세트이므로 샷을 제공하고 데모 앱을 사용하고 자신만의 앱을 구축하는 것이 좋습니다!
이 프로젝트는 관리자에게 여러 가지 형태의 피드백을 제공합니다.
부티크에 대한 질문이 있으면 먼저 문서를 참조하여 질문에 대한 답변이 있는지 확인하십시오.
이 프로젝트는 많이 문서화되어 있지만 여러 샘플 프로젝트도 포함되어 있습니다.
StorageEngine 에서 작업하는 경우이 프로젝트는 구축 해야하는 작업의 성능을 테스트하는 방법을 제공합니다.여전히 질문, 강화 또는 부티크를 개선하는 방법이 있다면이 프로젝트는 Github의 토론 기능을 활용합니다.
버그를 발견하고 문제를보고하고 싶다면 감사 할 것입니다.
Swift 패키지 관리자는 Swift 코드의 배포를 자동화하기위한 도구이며 Swift 빌드 시스템에 통합됩니다.
빠른 패키지를 설정하면 부티크를 종속성으로 추가하는 것은 Package.swift 의 종속성 값에 추가하는 것만 큼 쉽습니다.
dependencies: [
. package ( url : " https://github.com/mergesort/Boutique.git " , . upToNextMajor ( from : " 1.0.0 " ) )
] SPM을 사용하지 않으려면 파일을 복사하여 부티크를 프로젝트에 수동으로 통합 할 수 있습니다.
안녕하세요, 저는 웹의 모든 곳에서 Joe, 특히 Mastodon에 있습니다.
부티크를 사용하는 방법에 대한 자세한 내용은 라이센스를 참조하십시오.
Boutique는 개발자가 더 나은 앱을 구축 할 수 있도록 돕기 위해 사랑의 노력으로 창의성을 높이고 자신과 사용자에게 놀라운 것을 쉽게 만들 수 있습니다. 부티크가 귀중하다고 판단되면 오픈 소스 작업을 스폰서로 돕는 것을 고려한다면 정말 감사하겠습니다.
이제 당신을 위해 무엇이 있는지 알았으니 시작할 시간입니까 ?