How can I save a Date in AppStorage within Swift?

I am trying to save a Date as a default value inside AppStorage but I can't get it to work. This is my code:

@AppStorage("CREATED_AT") var created_at = Date()

The error that I get it this:

No exact matches in call to initializer 

How can I store a Date value that I can update later on?

Possibly helpful: @AppStorage with Date in SwiftUI | Apple Developer Forums

1 Like

I think it's better not to pollute Date with a somewhat arbitrary raw representation, everywhere. Instead, you can do something like this—unfortunately, copying and pasting for all necessary overloads, to match the AppStorage API. :face_exhaling:

@AppStorage.Converter("🗝️") var date = .now
import SwiftUI

public extension AppStorage {
  /// A way to use `AppStorage` with more types,
  /// via conversion to and from the limited supported types.
  typealias Converter<Converted> = StorageConverter<Self, Converted>
}

// MARK: - StorageDynamicProperty
extension AppStorage: StorageDynamicProperty { }

// MARK: - Double
public extension AppStorage<Double>.Converter where Storage == AppStorage<Double> {
  init(
    wrappedValue: Converted,
    _ key: String,
    store: UserDefaults? = nil,
    fromStorage: @escaping FromStorage,
    toStorage: @escaping ToStorage
  ) {
    self.init(
      storage: .init(wrappedValue: toStorage(wrappedValue), key, store: store),
      fromStorage: fromStorage, toStorage: toStorage
    )
  }
}

public extension AppStorage<Double>.Converter<Date> {
  init(
    wrappedValue: Converted,
    _ key: String,
    store: UserDefaults? = nil
  ) {
    self.init(
      wrappedValue: wrappedValue,
      key,
      store: store,
      fromStorage: Date.init(timeIntervalSinceReferenceDate:),
      toStorage: \.timeIntervalSinceReferenceDate
    )
  }
}
import SwiftUI

@propertyWrapper public struct StorageConverter<Storage: StorageDynamicProperty, Converted> {
  public typealias ToStorage = (Converted) -> Storage.Value
  public typealias FromStorage = (Storage.Value) -> Converted

  public var wrappedValue: Converted {
    get { fromStorage(storage.wrappedValue) }
    nonmutating set { storage.wrappedValue = toStorage(newValue) }
  }

  public var projectedValue: Binding<Converted> {
    .init(
      get: { wrappedValue },
      set: { wrappedValue = $0 }
    )
  }

  // MARK: internal
  init(
    storage: Storage,
    fromStorage: @escaping FromStorage,
    toStorage: @escaping ToStorage
  ) {
    self.storage = storage
    self.toStorage = toStorage
    self.fromStorage = fromStorage
  }

  // MARK: private
  private let storage: Storage
  private let fromStorage: FromStorage
  private let toStorage: ToStorage
}

public protocol StorageDynamicProperty<Value>: DynamicProperty {
  associatedtype Value
  var wrappedValue: Value { get nonmutating set }
}

// MARK: - DynamicProperty
extension StorageConverter: DynamicProperty { }
5 Likes

Thank you for both of your help :slightly_smiling_face:

1 Like

That link is now redirecting improperly.

I ended up using a set/get:

    @AppStorage("storedStartTime") var storedStartTime = Date.now.timeIntervalSinceReferenceDate
    var startTime: Date {
        set {storedStartTime = newValue.timeIntervalSinceReferenceDate}
        get {return Date(timeIntervalSinceReferenceDate: storedStartTime)}
    }

Can you elaborate? I cannot detect a problem with that link, but please let me know if something needs to be fixed.

Seems to be working now. Sorry.