SwiftUI: What is @AppStorage property wrapper
AppStorage
@AppStorage
is a convenient way to save and read variables from UserDefaults and use them in the same way as @State
properties. It can be seen as a @State
property which is automatically saved to (and read from) UserDefaults
.
You can think of the following:
@AppStorage("emailAddress") var emailAddress: String = "sample@email.com"
as an equivalent of this (which is not allowed in SwiftUI and will not compile):
@State var emailAddress: String = "sample@email.com" { get { UserDefaults.standard.string(forKey: "emailAddress") } set { UserDefaults.standard.set(newValue, forKey: "emailAddress") }}
Note that @AppStorage
behaves like a @State
: a change to its value will invalidate and redraw a View.
By default @AppStorage
will use UserDefaults.standard
. However, you can specify your own UserDefaults
store:
@AppStorage("emailAddress", store: UserDefaults(...)) ...
Unsupported types (e.g., Array
):
As mentioned in iOSDevil's answer, AppStorage
is currently of limited use:
types you can use in @AppStorage are (currently) limited to: Bool, Int, Double, String, URL, Data
If you want to use any other type (like Array
), you can add conformance to RawRepresentable
:
extension Array: RawRepresentable where Element: Codable { public init?(rawValue: String) { guard let data = rawValue.data(using: .utf8), let result = try? JSONDecoder().decode([Element].self, from: data) else { return nil } self = result } public var rawValue: String { guard let data = try? JSONEncoder().encode(self), let result = String(data: data, encoding: .utf8) else { return "[]" } return result }}
Demo:
struct ContentView: View { @AppStorage("itemsInt") var itemsInt = [1, 2, 3] @AppStorage("itemsBool") var itemsBool = [true, false, true] var body: some View { VStack { Text("itemsInt: \(String(describing: itemsInt))") Text("itemsBool: \(String(describing: itemsBool))") Button("Add item") { itemsInt.append(Int.random(in: 1...10)) itemsBool.append(Int.random(in: 1...10).isMultiple(of: 2)) } } }}
Useful links:
Disclaimer: iOS 14 Beta 2
In addition to the other useful answers, the types you can use in @AppStorage are (currently) limited to: Bool, Int, Double, String, URL, Data
Attempting to use other types (such as Array) results in the error: "No exact matches in call to initializer"
Re-implementation for iOS 13 and without SwiftUI
In additon to pawello2222 answer, here. is the reimplementation of the AppStorage that I named it as UserDefaultStorage
:
@propertyWrapperstruct UserDefaultStorage<T: Codable> { private let key: String private let defaultValue: T private let userDefaults: UserDefaults init(key: String, default: T, store: UserDefaults = .standard) { self.key = key self.defaultValue = `default` self.userDefaults = store } var wrappedValue: T { get { guard let data = userDefaults.data(forKey: key) else { return defaultValue } let value = try? JSONDecoder().decode(T.self, from: data) return value ?? defaultValue } set { let data = try? JSONEncoder().encode(newValue) userDefaults.set(data, forKey: key) } }}
This wrapper can store/restore any kind of codable into/from the user defaults. Also, it works in iOS 13 and it doesn't need to import SwiftUI
.
Usage
@UserDefaultStorage(key: "myCustomKey", default: 0)var myValue: Int
Note that it can't be used directly as a State