Skip to content

Instantly share code, notes, and snippets.

@seanli1
Last active May 23, 2019 03:32
Show Gist options
  • Save seanli1/92023218714d69de67e4f585f018366c to your computer and use it in GitHub Desktop.
Save seanli1/92023218714d69de67e4f585f018366c to your computer and use it in GitHub Desktop.
Add json file to your Project Navigator, and run this to upload it to Firebase easily using a demo app. Call `FirebaseHelper.uploadJsonFileToFirebase()`.
//
// FirebaseHelper.swift
//
//
// Created by Sean Li on 5/22/19.
// Copyright © 2019 Sean Li. All rights reserved.
//
import Foundation
import Firebase
protocol FirebaseUploadProtocol: Codable {
///* Use this to return an array of the string equivalents of each parameter (i.e. `var myVariable` would be `"myVariable"`, so this would `return ["myVariable1", "myVariable2"]`.
///* `keys.count` and `values.count` must match 1:1.
var keys: [String] { get }
///* Use this to return an array of the values of each parameter. (i.e. `return [myVariable1, myVariable2]`).
///* `keys.count` and `values.count` must match 1:1.
var values: [Any] { get }
}
class FirebaseHelper {
private init () {}
/// Upload local JSON data to Firebase easily. See Discussion for instructions.
/**
*Warning: This only works on the first layer (the documents immediately in collections). This does not work for nested setups.*
Preparation before you can use this function:
1. Make a struct that conforms to `FirebaseUploadProtocol`.
2. For every key value pair in the JSON file you wish to upload, create the corresponding parameters and set their values.
3. Set the computed properties via the protocol `keys` and `values`, returning an array of `[String]` and `[Any]` respectively. They must match exactly in order. These will be what get stored in Firebase.
Instructions:
1. You **must** pass in an array type (e.g. [MyObject].self) even if you are only wanting to upload one object.
2. Only enter a `document` name if you are uploading one object. Otherwise set to `nil`.
3. If you are uploading one object, `merge` can be either `true` or `false`. Otherwise set to `false`.
jsonArrayType: The individual object of which the JSON file is an array.
filename: Simply the name of the file as you see it in the Project Navigator (i.e. "upload.json").
collectionName: The name of the collection you want to upload to.
optionalDocumentName: The name of the document you wish to upload to. See Instructions for more info.
merge: If `merge` is `true`, then all new information is written to the document. All existing information is updated. If `merge` is `false`, the entire document is overwritten. See Instructions for more info.
completionEveryUpload: (optional) Execute the completion block every time information was uploaded.
*/
static func uploadJsonFileToFirebase<T>(_ jsonArrayType: [T].Type, _ filename: String, collectionName collection: String, optionalDocumentName document: String?, mergeWithExisting: Bool, _ completionEveryUpload: ((_ key: String, _ value: Any) -> Void)?) where T : FirebaseUploadProtocol {
if document == nil && mergeWithExisting == true {
print("You want to merge with existing info in Firebase. However, you also did not enter a document name.")
print("Without a document name, the names are unique by default. This means declaring to merge is pointless.")
print("Exiting because this is probably not what you wanted to do. Read the function's summary for more information.")
}
let jsonFilepath = Bundle.main.path(forResource: filename, ofType: nil)
let url = URL(fileURLWithPath: jsonFilepath!)
var data: Data?
do {
data = try Data(contentsOf: url)
} catch let err {
print("ERROR 1: ",err)
return
}
if data == nil { return }
var objects: [T]?
do {
objects = try JSONDecoder().decode(jsonArrayType, from: data!)
} catch let err {
print("ERROR 2: ",err)
return
}
if objects == nil { return }
if objects!.count > 1 && document != nil {
print("You specified a document name to use in this collection. However, there is more than one object in the JSON file.")
print("This would result in constantly overwriting the info under the same document name, leaving you with only the last object in the array.")
print("This is probably not what you want, so exiting now before accessing Firebase. Read the function's summary for more information.")
}
objects!.forEach { (object) in
var documentName: String
if let document = document {
documentName = document
} else {
documentName = firestore.collection(collection).document().documentID
}
let propertyCount = object.keys.count
for i in 0 ..< propertyCount {
firestore.collection(collection).document(documentName).setData(([object.keys[i] : object.values[i]]), merge: mergeWithExisting)
if let completion = completionEveryUpload {
completion(object.keys[i], object.values[i])
}
}
}
}
}
/// A `struct` equivalent to the JSON objects that will be used.
struct SampleObject: FirebaseUploadProtocol {
let category: String
let action: String
let description: String
let difficulty: Int
let cost: Int
let hashtags: [String]
var keys: [String] {
return [
"category",
"action",
"description",
"difficulty",
"cost",
"hashtags"
]
}
var values: [Any] {
return [
category,
action,
description,
difficulty,
cost,
hashtags
]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment