skip to Main Content

Hey in my app I am trying to store whether the User wants a location to be its favourite. In order to make the change in this UserDefaults update the view I need to use @AppStorage. However, at the moment I only get the ERROR "No exact matches in call to initializer" Do i need to use Data instead of the [String: String], if yes how? Does anyone know how to fix this? Thanks in advance

import SwiftUI

struct SpotDetailView: View {
    @State var spot: WindApp //knows which spot it's at
    var spotname:String
    //@State var favourites: [String:String] = UserDefaults.standard.object(forKey: "heart") as? [String:String] ?? [:]
    @AppStorage("heart") var favourites: [String: String] = [:]

        
    var body: some View {
        
        ZStack {
            
            List {
                SpotMapView(coordinate: spot.locationCoordinate)
                    .cornerRadius(8)
                ForEach(0..<9) { i in
                    SingleDayView(spot: spot, counter: (i * 8))
                    }
            }
            .navigationTitle("(spot.spot)")
            VStack {
                Spacer()
                HStack {
                    Spacer()
                    Button(action: {
                        if favourites[spot.spot] == "heart" {
                            favourites[spot.spot] = "heart.fill"
                            UserDefaults.standard.set(favourites, forKey: "heart")
                        }
                        else if favourites[spot.spot] == "heart.fill" {
                            favourites[spot.spot] = "heart"
                            UserDefaults.standard.set(favourites, forKey: "heart")
                        }
                        else {
                            favourites[spot.spot] = "heart.fill"
                            UserDefaults.standard.set(favourites, forKey: "heart")
                        }
                
                    }, label: {
                        Image(systemName: favourites[spot.spot] ?? "heart")
                    })
                    .frame(width: 20, height: 20)
                }
            }
        }
    }
}

3

Answers


  1. You will have to use Data. @AppStorage currently only supports:

    • Int
    • Double
    • String
    • Bool
    • URL
    • Data

    See this answer for sample implementation and further details

    Login or Signup to reply.
  2. You can make Dictionary [String:String] conform to RawRepresentable.

    extension Dictionary: RawRepresentable where Key == String, Value == String {
        public init?(rawValue: String) {
            guard let data = rawValue.data(using: .utf8),  // convert from String to Data
                let result = try? JSONDecoder().decode([String:String].self, from: data)
            else {
                return nil
            }
            self = result
        }
    
        public var rawValue: String {
            guard let data = try? JSONEncoder().encode(self),   // data is  Data type
                  let result = String(data: data, encoding: .utf8) // coerce NSData to String
            else {
                return "{}"  // empty Dictionary resprenseted as String
            }
            return result
        }
    
    }
    
    Login or Signup to reply.
  3. I would recomend storing your [String: String] object as Data.

    Since [String: String] conforms to Codable out of the box this is an easy task.

    This is an example of how you could implement it and also get Combine support.
    Notice that MyUserDefaultsStorage is not only for this value but you can add additional values you would like to store in AppStorage.

    struct MyUserDefaultsStorage {
    
        var favorites: CurrentValueSubject<[String: String], Never> = .init([:])
    
        @AppStorage("stars")
        private var favoritesData = Data()
    
        private let decoder = JSONDecoder()
        private let encoder = JSONEncoder()
    
        private var cancellables = Set<AnyCancellable>()
    
        init() {
            do {
                favorites.value = try decoder.decode([String: String].self, from: favoritesData)
            } catch {
                print("No saved data")
            }
    
            favorites
                .receive(on: RunLoop.main)
                .removeDuplicates()
                .encode(encoder: encoder)
                .replaceError(with: Data())
                .assign(to: .favoritesData, on: self)
                .store(in: &cancellables)
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search