Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map Data From Firestore to a struct In Swift - IOS

I'm trying to map a "user" retrieved from my Firestore database to a user struct I've defined in my code but I don't think I properly understand the mapping features in swift.

How do I go about mapping the user retrieved into the struct?

Struct

struct User {
// Properties

    var firstName: String
    var lastName: String
    var userName: String
    var email: String

    init(firstName: String, lastName: String, userName: String, email: String) {
        self.firstName = firstName
        self.lastName = lastName
        self.userName = userName
        self.email = email
    }
}

Function that gets user from Firestore

func getUser(UID: String) {
    // Firebase setup
    settings.areTimestampsInSnapshotsEnabled = true
    db.settings = settings

    db.collection("users").document(UID).getDocument { (document, error) in
        if let error = error {
            print("Error: \(error.localizedDescription)")
        } else {
            print(document!.data()!)
        }
    }
}
like image 989
cabyambo Avatar asked Jan 12 '19 21:01

cabyambo


1 Answers

A queried Firestore document is of type [String: Any], so configure your initializer to accept that kind of dictionary. Because you're working with a database return, there is never a guarantee that the data will be fully intact, so I would suggest making your initializer failable, which just means that it can return nil (it can fail).

I took your example and made the email property optional. In the example below, only three of the four properties are necessary to instantiate the User object.

let info: [String: Any] = ["hasFriends": true]
let firestoreData: [String: Any] = ["firstName": "lance", "lastName": "stevenson", "userName": "lstevenson", "info": info]

struct User {
    var firstName: String
    var lastName: String
    var userName: String
    var info: [String: Any]
    var email: String?
    var hasFriends: Bool
    
    init?(data: [String: Any]) {
        guard let firstName = data["firstName"] as? String,
            let lastName = data["lastName"] as? String,
            let userName = data["userName"] as? String,
            let info = data["info"] as? [String: Any],
            let hasFriends = info["hasFriends"] as? Bool else {
                return nil
        }
        self.firstName = firstName
        self.lastName = lastName
        self.userName = userName
        self.info = info
        self.hasFriends = hasFriends
        self.email = data["email"] as? String // User will be created even if this is nil
    }
}

if let user = User(data: firestoreData) {
    print(user.hasFriends) // true
}

I would not suggest that you use Swift's mapping tool for this because you will likely be dealing with varying types of values within the same dictionary for different models. And the code to map this dictionary with those variables in an initializer would not be pretty.

like image 137
liquid LFG UKRAINE Avatar answered Nov 03 '22 11:11

liquid LFG UKRAINE