I am trying to practice mapping custom objects using swift & replace all manual mapping by using Codable. This concept is new to me but seems very worthwhile. In this project I want to fetch the user and make sure that all their data is stored in their User Document on firebase (including the custom object 'FavouriteItems'.
My User Model struct is:
import SwiftUI
import FirebaseFirestoreSwift
struct UserModel: Identifiable, Codable{
#DocumentID var id: String?
var username : String
var pic : String
var bio: String
var uid : String
var isVerified: Bool
var favouriteItems: [FavouriteItems]
}
My struct for an Item is:
import SwiftUI
import FirebaseFirestoreSwift
import Firebase
struct Item: Identifiable, Codable {
#DocumentID var id: String?
var item_name: String
var item_type: String
var item_image: String
var item_details: String
var item_uid : String
var didFavourite: Bool? = false
var isFavourite: Bool = false
}
My struct for FavouriteItems is:
import SwiftUI
import FirebaseFirestoreSwift
//MARK: Make Hashable (See Tag Example)?
struct FavouriteItems: Identifiable, Codable {
#DocumentID var id: String?
var item: Item
}
My fetch user function is (edited):
func fetchUser(uid: String, completion: #escaping (UserModel) -> ()){
let db = Firestore.firestore()
let docRef = db.collection("Users").document(uid).getDocument(as: UserModel.self) { result in
switch result {
case .success(let user):
// A `UserModel` value was successfully initialized from the DocumentSnapshot.
print("UserModel: \(user)")
DispatchQueue.main.async {
completion(user)
}
case .failure(let error):
// A `UserModel` value could not be initialized from the DocumentSnapshot.
print("Error decoding UserModel: \(error)")
// handle errors todo
}
}
}
I am getting errors in the fetchUser code including:
Cannot convert value of type 'UserModel.Type' to expected argument type 'FirestoreSource'
Contextual closure type '(DocumentSnapshot?, Error?) -> Void' expects 2 arguments, but 1 was used in closure body
Incorrect argument label in call (have 'as:_:', expected 'source:completion:')
All of these errors occur on the line:
let docRef = db.collection("Users").document(uid).getDocument(as: UserModel.self) { result in
Here is my Podfile:
Can I show a sheet using a String?
Please let me know if there's a way around it.
#State var selectedContactInfo: String?
.sheet(item: $selectedContactInfo){ item in MessageView(recipient: selectedContactInfo!) }
I'm receiving this error:
Instance method 'sheet(item:onDismiss:content:)' requires that
'String' conform to 'Identifiable'
String could become Identifiable
Its id could be self because String is Hashable
extension String: Identifiable {
public var id: String {self}
}
I am getting data in [Any] format and I want to convert data into the format mentioned below
struct Details {
var image: UIImage?
var title: String?
var subTitle: String?
}
I am trying to build an application which receives a JSON Object from an API endpoint, which then I want to list out in the view. I've watched a lot of videos on this topic, but in each video they use very simplistic JSON Objects as examples and therefore the code they write doesn't really seem to transfer over, giving me errors no matter how I try to format it. The code is as follows
import SwiftUI
import Combine
import Foundation
public struct ActivityModel: Codable, Identifiable {
public let id: Int
public let name: String
public let activity_desc: String?
}
public struct ActivitiesModel2: Codable {
public let location: String
public let popular: [String:ActivityModel]
}
public struct ActivitiesModel: Codable {
public let activities: ActivitiesModel2
}
public class ActivityFetcher: ObservableObject {
var activities: ActivitiesModel?
init(){
guard let url = URL(string: "https://mywebsite.com/api/loadapi") else { return }
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
do {
if let d = data {
let decodedLists = try JSONDecoder().decode(ActivitiesModel.self, from: d)
DispatchQueue.main.async {
self.activities = decodedLists
}
} else {
print("No Data")
}
} catch {
print("Error")
}
}.resume()
}
}
struct ActivityGuestView: View {
let networkingServiceGeneral = NetworkingServiceGeneral()
#ObservedObject var viewRouter: ViewRouter
#ObservedObject var fetcher = ActivityFetcher()
var body: some View {
// This is where my issues start
List(fetcher.activities?.activities.popular) { result in
VStack {
Text(result.name)
Text(result.activity_desc)
.font(.system(size: 11))
.foregroundColor(Color.gray)
}
}
}
}
This code, as I put it, gives me 5 errors. They are the following;
- Initializer 'init(_:rowContent:)' requires that '(key: String, value: ActivityModel)' conform to Identifiable
- Initializer 'init(_:rowContent:)' requires that '[String : ActivityModel]' conform to 'RandomAccessCollection'
-Value of optional type '[String : ActivityModel]?' must be unwrapped to a value of type '[String : ActivityModel]'
- Coalesce using '??' to provide a default when the optional value contains 'nil'
- Force-unwrap using '!' to abort execution if the optional value contains 'nil'
Some of these errors have options to fix it, but when I press fix it adds code but doesn't actually fix the error so I figured to just include them anyways. I'm still fairly new to Swift, but I know what some of it is asking, particularly the conforming to Identifiable, but it says that struct ActivitiesModel does not conform to identifiable when I try to add the tag, and the JSON Object doesn't have an ID for that section, so I can't ask the ID to make it identifiable.
Any help would be greatly appreciated, this has kind of been a wall right now.
Here's the JSON
"activities": {
"location": "Dallas",
"popular": {
"10": {
"id": 38,
"name": "Adventure Landing Dallas",
"activity_desc": "Aquatic complex chain with additional land attractions including mini-golf, laser tag & go-karts.",
},
"12": {
"id": 40,
"name": "Jumpstreet",
"activity_desc": "None provided.",
},
}
}
}
You have a couple of issues here.
First, dictionaries and Lists aren't compatible; You really need an array. As the errors say, the item that is supplied to a List needs to confirm to Identifiable and RandomAccessCollection. A dictionary conforms to neither and you can't really make it do so.
Your second issue is that fetcher.activities is an optional and the List initialiser can't accept an optional. The compiler is suggesting a couple of alternatives - Supply a default value using the nil coalescing operator (??) or force unwrap using ! (Which will crash, since you know that fetcher.activities is going to be nil initially.
The root cause of the first problem is that your JSON has variable keys rather than a simple array of activities. This is not a great idea and you should really put some work in on the server side to eliminate the meaningless numeric key and have a simple array, as per my comment, however you have indicated that you can't do this.
Given this, another approach is to expose the dictionary values as an array using a computed property:
public struct ActivitiesModel2: Codable {
public let location: String
private var popular: [String:ActivityModel]
public var popularActivities: [ActivityModel] {
get {
return Array(self.popular.values)
}
}
}
While we are here, we can fix that un-Swifty activity_desc with a CodingKeys enumeration:
public struct ActivityModel: Codable, Identifiable {
public let id: Int
public let name: String
public let activityDesc: String?
enum CodingKeys: String, CodingKey {
case id = "id"
case name = "name"
case activityDesc = "activity_desc"
}
}
Now, you can re-write your view to use the new computed property and to handle the optional:
struct ActivityGuestView: View {
let networkingServiceGeneral = NetworkingServiceGeneral()
#ObservedObject var viewRouter: ViewRouter
#ObservedObject var fetcher = ActivityFetcher()
var body: some View {
// This is where my issues start
List(fetcher.activities?.activities.popularActivities ?? []) { result in
VStack {
Text(result.name)
Text(result.activity_desc)
.font(.system(size: 11))
.foregroundColor(Color.gray)
}
}
}
}
This question already has answers here:
How do I make an enum Decodable in Swift?
(9 answers)
Closed 2 years ago.
I have a nested an enum inside a struct that I want to conform to Codable. How do I make the enum codable and therefore make the struct codable?
Here is an example of what I have:
struct Person: Codable {
var firstName: String
var lastName: String
var favoriteColor: Color
enum Color {
case blue, red, green, yellow, pink, purple
}
}
Then, I get two errors :
Type 'Person' does not conform to protocol 'Decodable'
Type 'Person' does not conform to protocol 'Encodable'
How can I fix this problem?
Edit
I have also tried conforming Color to Codable. Xcode adds these protocol stubs:
init(from decoder: Decoder) throws {
<#code#>
}
func encode(to encoder: Encoder) throws {
<#code#>
}
What would I do with this?
struct Person: Codable {
var firstName: String
var lastName: String
var favoriteColor: Color
}
enum Color: String, Codable {
case blue, red, green, yellow, pink, purple
}