swifty و userdefaults
قم بتخزين أزواج القيمة الرئيسية باستمرار عبر عمليات إطلاق تطبيقك.
يستخدم UserDefaults أسفله ولكنه يكشف عن واجهة آمنة من النوع مع الكثير من وسائل الراحة الجميلة.
يتم استخدامه في الإنتاج من قبل جميع تطبيقاتي (4 ملايين مستخدم+).
UserDefaults .@AppStorageCodable .Toggle . أضف https://github.com/sindresorhus/Defaults في علامة التبويب "Swift Package Manager" في XCode.
Int(8/16/32/64)UInt(8/16/32/64)DoubleCGFloatFloatStringBoolDateDataURLUUIDRangeClosedRangeCodableNSSecureCodingColor 1 (Swiftui)Color.Resolved 1 (Swiftui)NSColorUIColorNSFontDescriptorUIFontDescriptor تدعم الإعدادات الافتراضية أيضًا الأنواع المذكورة أعلاه ملفوفة في Array ، Set ، Dictionary ، Range ، ClosedRange ، وحتى ملفوف في أنواع متداخلة. على سبيل المثال ، [[String: Set<[String: Int]>]] .
لمزيد من الأنواع ، راجع مثال التعداد أو المثال Codable أو الاستخدام المتقدم. لمزيد من الأمثلة ، راجع الاختبارات/DefaultStests.
يمكنك بسهولة إضافة دعم لأي نوع مخصص.
إذا كان هناك نوع يتوافق مع كل من NSSecureCoding و Codable ، فسيتم استخدام Codable للتسلسل.
وثائق API.
تعلن عن مفاتيح الافتراضات مقدمًا مع نوع وقيمة افتراضية.
يجب أن يكون الاسم الرئيسي هو ASCII ، ولا يبدأ بـ @ ، ولا يمكن أن يحتوي على نقطة ( . ).
import Defaults
extension Defaults . Keys {
static let quality = Key < Double > ( " quality " , default : 0.8 )
// ^ ^ ^ ^
// Key Type UserDefaults name Default value
} يمكنك بعد ذلك الوصول إليه كفرقة على العمولة Defaults :
Defaults [ . quality ]
//=> 0.8
Defaults [ . quality ] = 0.5
//=> 0.5
Defaults [ . quality ] += 0.1
//=> 0.6
Defaults [ . quality ] = " ? "
//=> [Cannot assign value of type 'String' to type 'Double']يمكنك أيضًا إعلان مفاتيح اختيارية عندما لا ترغب في إعلان القيمة الافتراضية مقدمًا:
extension Defaults . Keys {
static let name = Key < Double ? > ( " name " )
}
if let name = Defaults [ . name ] {
print ( name )
} القيمة الافتراضية هي بعد nil .
يمكنك أيضًا تحديد قيمة افتراضية ديناميكية. قد يكون ذلك مفيدًا عندما تتغير القيمة الافتراضية أثناء عمر التطبيق:
extension Defaults . Keys {
static let camera = Key < AVCaptureDevice ? > ( " camera " ) { . default ( for : . video ) }
} enum DurationKeys : String , Defaults . Serializable {
case tenMinutes = " 10 Minutes "
case halfHour = " 30 Minutes "
case oneHour = " 1 Hour "
}
extension Defaults . Keys {
static let defaultDuration = Key < DurationKeys > ( " defaultDuration " , default : . oneHour )
}
Defaults [ . defaultDuration ] . rawValue
//=> "1 Hour"(هذا يعمل طالما أن القيمة الخام للعنونة هي أي من الأنواع المدعومة)
struct User : Codable , Defaults . Serializable {
let name : String
let age : String
}
extension Defaults . Keys {
static let user = Key < User > ( " user " , default : . init ( name : " Hello " , age : " 24 " ) )
}
Defaults [ . user ] . name
//=> "Hello" لا يُطلب منك إرفاق مفاتيح إلى Defaults.Keys .
let isUnicorn = Defaults . Key < Bool > ( " isUnicorn " , default : true )
Defaults [ isUnicorn ]
//=> true@Default في View يمكنك استخدام @Default Property Wrapper للحصول على/تعيين عنصر Defaults وأيضًا يتم تحديث العرض عندما تتغير القيمة. هذا مشابه لـ @State .
extension Defaults . Keys {
static let hasUnicorn = Key < Bool > ( " hasUnicorn " , default : false )
}
struct ContentView : View {
@ Default ( . hasUnicorn ) var hasUnicorn
var body : some View {
Text ( " Has Unicorn: ( hasUnicorn ) " )
Toggle ( " Toggle " , isOn : $hasUnicorn )
Button ( " Reset " ) {
_hasUnicorn . reset ( )
}
}
} لاحظ أنه @Default ، وليس @Defaults .
لا يمكنك استخدام @Default في ObservableObject . من المفترض أن تستخدم في View .
@ObservableDefault في @Observable باستخدام Macro @ObservableDefault ، يمكنك استخدام Defaults داخل الفئات @Observable التي تستخدم إطار المراقبة. القيام بذلك أمر بسيط مثل استيراد DefaultsMacros وإضافة سطرين إلى خاصية (لاحظ أن إضافة @ObservationIgnored مطلوبة لمنع الاشتباكات باستخدام @Observable ):
مهم
ستزداد أوقات البناء عند استخدام وحدات الماكرو.
تعتمد وحدات الماكرو السريعة على حزمة swift-syntax . هذا يعني أنه عندما تقوم بتجميع الكود الذي يتضمن وحدات الماكرو كتبعيات ، يجب عليك أيضًا تجميع swift-syntax . من المعروف على نطاق واسع أن القيام بذلك له تأثير خطير في وقت البناء ، وعلى الرغم من أنها مشكلة يتم تتبعها (انظر swift-syntax #2421) ، إلا أنه لا يوجد حل في الوقت الحالي.
import Defaults
import DefaultsMacros
@ Observable
final class UnicornManager {
@ ObservableDefault ( . hasUnicorn )
@ ObservationIgnored
var hasUnicorn : Bool
} Toggle هناك أيضًا غلاف SwiftUI.Toggle يسهل إنشاء تبديل استنادًا إلى مفتاح Defaults ذات قيمة Bool .
extension Defaults . Keys {
static let showAllDayEvents = Key < Bool > ( " showAllDayEvents " , default : false )
}
struct ShowAllDayEventsSetting : View {
var body : some View {
Defaults . Toggle ( " Show All-Day Events " , key : . showAllDayEvents )
}
}يمكنك أيضًا الاستماع إلى التغييرات:
struct ShowAllDayEventsSetting : View {
var body : some View {
Defaults . Toggle ( " Show All-Day Events " , key : . showAllDayEvents )
// Note that this has to be directly attached to `Defaults.Toggle`. It's not `View#onChange()`.
. onChange {
print ( " Value " , $0 )
}
}
} extension Defaults . Keys {
static let isUnicornMode = Key < Bool > ( " isUnicornMode " , default : false )
}
// …
Task {
for await value in Defaults . updates ( . isUnicornMode ) {
print ( " Value: " , value )
}
} على النقيض من الملاحظة الرئيسية لـ UserDefaults ، تتلقى هنا كائن تغيير قوي.
extension Defaults . Keys {
static let isUnicornMode = Key < Bool > ( " isUnicornMode " , default : false )
}
Defaults [ . isUnicornMode ] = true
//=> true
Defaults . reset ( . isUnicornMode )
Defaults [ . isUnicornMode ]
//=> false هذا يعمل مع Key مع اختياري أيضًا ، والذي سيتم إعادة تعيينه مرة أخرى إلى nil .
التغييرات التي Defaults.publisher() Defaults.observe() في Defaults.withoutPropagation
let observer = Defaults . observe ( keys : . key1 , . key2 ) {
// …
Defaults . withoutPropagation {
// Update `.key1` without propagating the change to listeners.
Defaults [ . key1 ] = 11
}
// This will be propagated.
Defaults [ . someKey ] = true
}UserDefaults مع السكرهذا يعمل أيضًا:
extension Defaults . Keys {
static let isUnicorn = Key < Bool > ( " isUnicorn " , default : true )
}
UserDefaults . standard [ . isUnicorn ]
//=> trueUserDefaults المشتركة let extensionDefaults = UserDefaults ( suiteName : " com.unicorn.app " ) !
extension Defaults . Keys {
static let isUnicorn = Key < Bool > ( " isUnicorn " , default : true , suite : extensionDefaults )
}
Defaults [ . isUnicorn ]
//=> true
// Or
extensionDefaults [ . isUnicorn ]
//=> trueUserDefaults عندما تقوم بإنشاء Defaults.Key ، فإنه يقوم تلقائيًا بتسجيل القيمة default مع UserDefaults العادية. هذا يعني أنه يمكنك الاستفادة من القيمة الافتراضية ، على سبيل المثال ، الروابط في Builder الواجهة.
extension Defaults . Keys {
static let isUnicornMode = Key < Bool > ( " isUnicornMode " , default : true )
}
print ( UserDefaults . standard . bool ( forKey : Defaults . Keys . isUnicornMode . name ) )
//=> trueلاحظ أن
Defaults.Keyذات القيمة الافتراضية الديناميكية لن تقوم بتسجيل القيمة الافتراضية فيUserDefaults.
DefaultsDefaults.Keys النوع: class
يخزن المفاتيح.
Defaults.Key Defaults.Keys.Key Defaults . Key < T > ( _ name : String , default : T , suite : UserDefaults = . standard ) النوع: class
إنشاء مفتاح مع قيمة افتراضية.
تتم كتابة القيمة الافتراضية إلى UserDefaults الفعلية ويمكن استخدامها في مكان آخر. على سبيل المثال ، مع ربط منشئ الواجهة.
Defaults.Serializable public protocol DefaultsSerializable {
typealias Value = Bridge . Value
typealias Serializable = Bridge . Serializable
associatedtype Bridge : Defaults . Bridge
static var bridge : Bridge { get }
} النوع: protocol
يمكن استخدام الأنواع التي تتوافق مع هذا البروتوكول مع Defaults .
يجب أن يحتوي النوع على bridge متغير ثابت يجب أن يشير إلى مثيل من النوع الذي يتوافق مع Defaults.Bridge .
Defaults.Bridge public protocol DefaultsBridge {
associatedtype Value
associatedtype Serializable
func serialize ( _ value : Value ? ) -> Serializable ?
func deserialize ( _ object : Serializable ? ) -> Value ?
} النوع: protocol
Bridge مسؤول عن التسلسل والخروج.
لديها نوعان من القيمة المرتبطة Value وقابلة Serializable .
Value : النوع الذي تريد استخدامه.Serializable : النوع المخزن في UserDefaults .serialize : تم تنفيذه قبل التخزين إلى UserDefaults .deserialize : تم تنفيذها بعد استرداد قيمتها من UserDefaults . Defaults.AnySerializable Defaults . AnySerializable < Value : Defaults . Serializable > ( _ value : Value ) النوع: class
نوع الغلاف المسترجع للقيم Defaults.Serializable .
get<Value: Defaults.Serializable>() -> Value? : استرجاع القيمة أي نوع هو Value من userDefaults.get<Value: Defaults.Serializable>(_: Value.Type) -> Value? : حدد Value التي تريد استردادها. هذا يمكن أن يكون مفيدًا في بعض الحالات الغامضة.set<Value: Defaults.Serializable>(_ newValue: Value) : تعيين قيمة جديدة لـ Defaults.AnySerializable . Defaults.reset(keys…) النوع: func
أعد ضبط المفاتيح المعطاة إلى قيمها الافتراضية.
يمكنك أيضًا تحديد مفاتيح السلسلة ، والتي يمكن أن تكون مفيدة إذا كنت بحاجة إلى تخزين بعض المفاتيح في مجموعة ، حيث لا يمكن تخزين Defaults.Key في مجموعة لأنها عامة.
Defaults.removeAll Defaults . removeAll ( suite : UserDefaults = . standard ) النوع: func
قم بإزالة جميع الإدخالات من جناح UserDefaults المعطى.
Defaults.withoutPropagation(_ closure:)تنفيذ الإغلاق دون إثارة الأحداث تغيير.
لن تنتشر أي تغييرات مفتاح Defaults التي تم إجراؤها داخل الإغلاق إلى مستمعي الأحداث Defaults ( Defaults.observe() و Defaults.publisher() ). قد يكون هذا مفيدًا لمنع عودة لا حصر لها عندما تريد تغيير مفتاح في رد الاتصال الاستماع إلى التغييرات لنفس المفتاح.
@Default(_ key:) احصل على/تعيين عنصر Defaults وأيضًا يتم تحديث عرض Swiftui عند تغيير القيمة.
Defaults.CollectionSerializable public protocol DefaultsCollectionSerializable : Collection , Defaults . Serializable {
init ( _ elements : [ Element ] )
} النوع: protocol
Collection يمكنها تخزينها في UserDefaults الأصلية.
يجب أن يكون لديه init init(_ elements: [Element]) للسماح Defaults عن إزالة التالي.
Defaults.SetAlgebraSerializable public protocol DefaultsSetAlgebraSerializable : SetAlgebra , Defaults . Serializable {
func toArray ( ) -> [ Element ]
} النوع: protocol
SetAlgebra التي يمكن تخزينها في UserDefaults الأصلية.
يجب أن يكون لها وظيفة func toArray() -> [Element] للسماح Defaults عن التسلسل.
على الرغم من أن Defaults لديها بالفعل دعم مدمج للعديد من الأنواع ، فقد تحتاج إلى أن تكون قادرًا على استخدام النوع المخصص الخاص بك. سيوضح لك الدليل أدناه كيفية جعل النوع المخصص الخاص بك يعمل مع Defaults .
struct User {
let name : String
let age : String
}Defaults.Bridge struct UserBridge : Defaults . Bridge {
typealias Value = User
typealias Serializable = [ String : String ]
public func serialize ( _ value : Value ? ) -> Serializable ? {
guard let value else {
return nil
}
return [
" name " : value . name ,
" age " : value . age
]
}
public func deserialize ( _ object : Serializable ? ) -> Value ? {
guard
let object ,
let name = object [ " name " ] ,
let age = object [ " age " ]
else {
return nil
}
return User (
name : name ,
age : age
)
}
}User يتوافق مع Defaults.Serializable . يجب أن يكون الجسر الثابت هو الجسر الذي أنشأناه أعلاه. struct User {
let name : String
let age : String
}
extension User : Defaults . Serializable {
static let bridge = UserBridge ( )
} extension Defaults . Keys {
static let user = Defaults . Key < User > ( " user " , default : User ( name : " Hello " , age : " 24 " ) )
static let arrayUser = Defaults . Key < [ User ] > ( " arrayUser " , default : [ User ( name : " Hello " , age : " 24 " ) ] )
static let setUser = Defaults . Key < Set < User>> ( " user " , default : Set ( [ User ( name : " Hello " , age : " 24 " ) ] ) )
static let dictionaryUser = Defaults . Key < [ String : User ] > ( " dictionaryUser " , default : [ " user " : User ( name : " Hello " , age : " 24 " ) ] )
}
Defaults [ . user ] . name //=> "Hello"
Defaults [ . arrayUser ] [ 0 ] . name //=> "Hello"
Defaults [ . setUser ] . first ? . name //=> "Hello"
Defaults [ . dictionaryUser ] [ " user " ] ? . name //=> "Hello" قد تكون هناك مواقف تريد استخدامها [String: Any] مباشرة ، ولكن Defaults تحتاج إلى قيمها لتتوافق مع Defaults.Serializable . تساعد Defaults.AnySerializable type-eraser.
Defaults.AnySerializable متاح فقط للقيم التي تتوافق مع Defaults.Serializable .
تحذير: يجب استخدام اختبار النوع فقط عندما لا تكون هناك طريقة أخرى للتعامل معها لأنه يتمتع بأداء أسوأ بكثير. يجب استخدامه فقط في أنواع ملفوفة. على سبيل المثال ، ملفوفة في Array أو Set أو Dictionary .
Defaults.AnySerializable ExpressibleByStringLiteral ExpressibleByIntegerLiteral ExpressibleByFloatLiteral ExpressibleByBooleanLiteral ExpressibleByNilLiteral ExpressibleByArrayLiteral ExpressibleByDictionaryLiteral
مما يعني أنه يمكنك تعيين هذه الأنواع البدائية مباشرة:
let any = Defaults . Key < Defaults . AnySerializable > ( " anyKey " , default : 1 )
Defaults [ any ] = " ? " get and setبالنسبة لأنواع أخرى ، سيتعين عليك تعيينها مثل هذا:
enum mime : String , Defaults . Serializable {
case JSON = " application/json "
case STREAM = " application/octet-stream "
}
let any = Defaults . Key < Defaults . AnySerializable > ( " anyKey " , default : [ Defaults . AnySerializable ( mime . JSON ) ] )
if let mimeType : mime = Defaults [ any ] . get ( ) {
print ( mimeType . rawValue )
//=> "application/json"
}
Defaults [ any ] . set ( mime . STREAM )
if let mimeType : mime = Defaults [ any ] . get ( ) {
print ( mimeType . rawValue )
//=> "application/octet-stream"
} Array أو Set أو Dictionary Defaults.AnySerializable تدعم أيضًا الأنواع المذكورة أعلاه ملفوفة في Array ، Set ، Dictionary .
فيما يلي مثال [String: Defaults.AnySerializable] :
extension Defaults . Keys {
static let magic = Key < [ String : Defaults . AnySerializable ] > ( " magic " , default : [ : ] )
}
enum mime : String , Defaults . Serializable {
case JSON = " application/json "
}
// …
Defaults [ . magic ] [ " unicorn " ] = " ? "
if let value : String = Defaults [ . magic ] [ " unicorn " ] ? . get ( ) {
print ( value )
//=> "?"
}
Defaults [ . magic ] [ " number " ] = 3
Defaults [ . magic ] [ " boolean " ] = true
Defaults [ . magic ] [ " enum " ] = Defaults . AnySerializable ( mime . JSON )
if let mimeType : mime = Defaults [ . magic ] [ " enum " ] ? . get ( ) {
print ( mimeType . rawValue )
//=> "application/json"
}لمزيد من الأمثلة ، راجع الاختبارات/defaultsanyserializableTests.
Codable الغامض قد يكون لديك نوع يتوافق مع Codable & NSSecureCoding أو التعداد Codable & RawRepresentable . بشكل افتراضي ، تفضل Defaults المطابقة Codable واستخدام CodableBridge لتسلسلها في سلسلة JSON. Defaults.PreferRawRepresentable كنت ترغب في إجراء تسلسلها كبيانات NSSecureCoding أو استخدام القيمة الخام للعدو RawRepresentable ، فيمكنك أن تتوافق مع Defaults.PreferNSSecureCoding .
enum mime : String , Codable , Defaults . Serializable , Defaults . PreferRawRepresentable {
case JSON = " application/json "
}
extension Defaults . Keys {
static let magic = Key < [ String : Defaults . AnySerializable ] > ( " magic " , default : [ : ] )
}
print ( UserDefaults . standard . string ( forKey : " magic " ) )
//=> application/json application/json لم أضفوا Defaults.PreferRawRepresentable "application/json"
يمكن أن يكون هذا مفيدًا أيضًا إذا كنت تتوافق مع نوع لا تتحكم فيه مع Defaults.Serializable القابلة للتطبيق لأن النوع يمكن أن يتلقى المطابقة Codable في أي وقت ومن ثم قد يتغير التمثيل المخزن ، مما قد يجعل القيمة غير قابلة للقراءة. من خلال تحديد الجسر الذي يجب استخدامه بشكل صريح ، يمكنك التأكد من أن التمثيل المخزن سيبقى دائمًا كما هو.
Collection المخصصةCollection وجعل عناصرها تتوافق مع Defaults.Serializable . struct Bag < Element : Defaults . Serializable > : Collection {
var items : [ Element ]
var startIndex : Int { items . startIndex }
var endIndex : Int { items . endIndex }
mutating func insert ( element : Element , at : Int ) {
items . insert ( element , at : at )
}
func index ( after index : Int ) -> Int {
items . index ( after : index )
}
subscript ( position : Int ) -> Element {
items [ position ]
}
}Bag تتوافق مع Defaults.CollectionSerializable . extension Bag : Defaults . CollectionSerializable {
init ( _ elements : [ Element ] ) {
self . items = elements
}
} extension Defaults . Keys {
static let stringBag = Key < Bag < String > > ( " stringBag " , default : Bag ( [ " Hello " , " World! " ] ) )
}
Defaults [ . stringBag ] [ 0 ] //=> "Hello"
Defaults [ . stringBag ] [ 1 ] //=> "World!"SetAlgebra مخصصSetAlgebra الخاص بك وجعل عناصرها تتوافق مع Defaults.Serializable & Hashable struct SetBag < Element : Defaults . Serializable & Hashable > : SetAlgebra {
var store = Set < Element > ( )
init ( ) { }
init ( _ store : Set < Element > ) {
self . store = store
}
func contains ( _ member : Element ) -> Bool {
store . contains ( member )
}
func union ( _ other : SetBag ) -> SetBag {
SetBag ( store . union ( other . store ) )
}
func intersection ( _ other : SetBag ) -> SetBag {
var setBag = SetBag ( )
setBag . store = store . intersection ( other . store )
return setBag
}
func symmetricDifference ( _ other : SetBag ) -> SetBag {
var setBag = SetBag ( )
setBag . store = store . symmetricDifference ( other . store )
return setBag
}
@ discardableResult
mutating func insert ( _ newMember : Element ) -> ( inserted : Bool , memberAfterInsert : Element ) {
store . insert ( newMember )
}
mutating func remove ( _ member : Element ) -> Element ? {
store . remove ( member )
}
mutating func update ( with newMember : Element ) -> Element ? {
store . update ( with : newMember )
}
mutating func formUnion ( _ other : SetBag ) {
store . formUnion ( other . store )
}
mutating func formSymmetricDifference ( _ other : SetBag ) {
store . formSymmetricDifference ( other . store )
}
mutating func formIntersection ( _ other : SetBag ) {
store . formIntersection ( other . store )
}
}SetBag يتوافق مع Defaults.SetAlgebraSerializable extension SetBag : Defaults . SetAlgebraSerializable {
func toArray ( ) -> [ Element ] {
Array ( store )
}
} extension Defaults . Keys {
static let stringSet = Key < SetBag < String > > ( " stringSet " , default : SetBag ( [ " Hello " , " World! " ] ) )
}
Defaults [ . stringSet ] . contains ( " Hello " ) //=> true
Defaults [ . stringSet ] . contains ( " World! " ) //=> true بعد Defaults V5 ، لا تحتاج إلى استخدام Codable لتخزين القاموس ، ويدعم Defaults تخزين القاموس أصلاً. للاطلاع على أنواع الدعم Defaults ، راجع أنواع الدعم.
SwiftyUserDefaults ؟إنها مستوحاة من تلك الحزمة والحلول الأخرى. الفرق الرئيسي هو أن هذه الوحدة لا ترحم القيم الافتراضية وتأتي بدعم قابل للترميز.
سابق
لا يمكنك استخدام Color.accentColor . ↩ ↩ 2