SwiftUI: What is @AppStorage property wrapper SwiftUI: What is @AppStorage property wrapper swift swift

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