So I have two custom model classes. One is a Story
and the other is Page
. The Story contains multiple properties including an array of pages
.
struct Story {
var name: String
var pages: [Page]
var tags: [String]
var likes: [String]
var isPrivate: Bool
var dictionary: [String: Any] {
return [
"name": name,
"pages": pages,
"tags": tags,
"likes": likes,
"isPrivate": isPrivate,
]
}
struct Page {
let thumbnail: String
let image: String?
let video: String?
var dictionary: [String: Any] {
return [
"thumbnail": thumbnail,
"image": image as Any,
"video": video as Any
]
}
When I upload to Firebase I basically I want the pages object to be a subcollection within the Story object. But I am confused on how I would correctly do this in such a way that I can have the models properly map and decode from the Firebase Response.
The only way I can think of doing this is to create a Story on the database, get a callback with the ID and then create the page within that. The problem is that my Story
model wouldn't then include the custom Page
model
For what it's worth, anyone winding up here via Google looking for custom data model handling + Swift + Firebase should check out this feature of Firebase using the Codable
protocol.
https://firebase.google.com/docs/firestore/manage-data/add-data#custom_objects
Specifically this gives you access to a special setData
call that looks like document("LA").setData(from: city)
.
You should not need the extra import anymore in Firebase v7.3+ so that's awesome! If you're still on v6 or older, you need it.
If you don't have access to this function in your code, it's because you need to import FirebaseFirestoreSwift
which are special extensions for Swift prior to v7.0.
First, you don't need to write to Firestore to get the auto-generated Id of that document. Firestore auto generates Ids on the client (it's a combination of timestamp and random).
Second, you may want to consider adding a property to Story
that carries the array of pages in dictionary format:
struct Page {
let thumbnail: String
let image: String?
let video: String?
var dictionary: [String: Any] {
return [
"thumbnail": thumbnail,
"image": image as Any,
"video": video as Any
]
}
}
struct Story {
var name: String
var pages: [Page]
var pagesData: [[String: Any]] // I added this for convenience
var tags: [String]
var likes: [String]
var isPrivate: Bool
var dictionary: [String: Any] {
return [
"name": name,
"pages": pages,
"tags": tags,
"likes": likes,
"isPrivate": isPrivate,
]
}
}
Then create your pages as normal:
let page1 = Page(thumbnail: "abc", image: nil, video: nil)
let page2 = Page(thumbnail: "xyz", image: nil, video: nil)
Create a story and inject the pages into it, including the datafied version:
let story = Story(name: "Story", pages: [page1, page2], pagesData: [page1.dictionary, page2.dictionary], tags: ["kiwi", "mango"], likes: ["x", "y"], isPrivate: false)
Use Firestore to generate a random identifier:
let docRef = Firestore.firestore().collection("someCollection").document()
let docId = docRef.documentID
You can now inject this identifier into every object before writing to Firestore if you need them to share it.
Create the data object that will be written to Firestore:
let data: [String: Any] = [
"name": story.name,
"pages": story.pagesData
// etc
]
In the backend, the pages
field will be an array of maps.
Then just write the data to Firestore:
docRef.setData(data) { (error) in
if let error = error {
print("🤬 \(error)")
} else {
print("👌")
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With