Swift/Firebase Converting the snapshots (JSON) into Excel - json

does anyone here knows of a good method to convert Firebase data into excel format? Ideally I would like to do this by function call, such that I can pull the JSON database from Firebase and convert them into excel format.
Taking my JSON from Firebase as an example:
Snap (-KQc-qqY3qc6ZQgQQ4EF) {
DOB = "01/09/2016";
purpose = "No purpose";
name = "Adam";
timeOfVisit = 1803;
userID = LV6aANvLfAcTXTDpsZr5R4viUnE2;
}
When a user taps a 'export to excel' button, I would like to grab the JSON file from Firebase and convert it to excel file. Subsequently I would like to send this file to the user via email.
#IBAction func exportToExcel(sender: AnyObject) {
let dataSnapshot: FIRDataSnapshot! = self.database[indexPath.row]
let databaseValues = dataSnapshot.value as! Dictionary< String, AnyObject>
let name = databaseValues["name"] as! String!
let time = databaseValues["timeOfVisit"] as! Int!
let DOB = databaseValues["DOB"] as! String!
let purpose = databaseValues["purpose"] as! String!
// parse above variables into individual rows in excel file
}

Related

Creating Decode Path from JSON Data in Swift that Includes Numbers and Hyphens?

this is relatively new to me and I've searched high and low but have been unsuccessful in finding a similar scenario.
I have retrieved some JSON Data from an API URL and have successfully decoded and output various values from this data as strings by parsing the data to a separate sheet and using structs and constants with the 'Decodable' value set. The problem I have is that one of the containers in the Json data is a hyphenated date in this format dates['2020-11-04'] so swift will not let me create a struct with this name (also this looks like an array but there are no square brackets when viewing the unformatted JSON data in a web browser).
Here is the full path to the date I want to output as a string and the URL being used (copied from a web browser using JSON Viewer Pro):
dates['2020-11-04'].countries.Afghanistan.date
https://api.covid19tracking.narrativa.com/api/2020-11-04
Here is the sheet containing my Structs and constants to decode the data:
import Foundation
//I understand the below name will not work but i've included it to show my presumed process
struct CovidData: Decodable {
let dates: dates[2020-11-04]
}
//Once again the below struct name does not work but i've included it as an example of my presumed process.
struct dates[2020-11-04]: Decodable {
let countries: countries
}
struct countries: Decodable {
let Afghanistan: Afghanistan
}
struct Afghanistan: Decodable {
let date: String
}
Here is my management sheet with my API call and JSON Parse:
import Foundation
protocol CovidDataManagerDelegate {
func didUpdateCovidData(_ covidDataManager: CovidDataManager, covid: CovidModel)
}
struct CovidDataManager {
var delegate: CovidDataManagerDelegate?
let covidURL = "https://api.covid19tracking.narrativa.com/api/2020-11-04"
func getData() {
let urlString = covidURL
performRequest(with:urlString)
}
func performRequest(with urlString: String){
if let url = URL(string: urlString){
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Error")
return
}
if let safeData = data {
if let covid = parseJSON(safeData){
self.delegate?.didUpdateCovidData(self, covid: covid)
}
}
}
task.resume()
}
func parseJSON(_ covidData: Data) -> CovidModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(CovidData.self, from: covidData)
let date = decodedData.dates['2020-11-04'].countries.Afghanistan.date
let covid = CovidModel(date: date)
print(date)
return covid
} catch {
print("Error with JSON Parse")
return nil
}
}
}
}
I have not included my UI update sheet as mentioned before the call and decode is working perfectly fine when decoding data with a JSON path made up entirely of strings it is only this container with additional symbols and numbers I am stumped with.
Hopefully I've supplied enough information and apologies if some of the terminology isn't accurate, this is still quite new to me.
Thanks!

Conver FIRTimestamp to JSON

I'm having problems converting a document to Firebase, but I can not convert the FIRTimestamp data.
let json = try? JSONSerialization.data(withJSONObject: d.data(), options: .prettyPrinted)
Error
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (FIRTimestamp)'
To remove FIRTimestamp from JSON
struct leadDocument: Codable {
let state: String
let details: String
}
let dataDescription = document.data() // your json response or value
var leadData = dataDescription
_ = leadData.removeValue(forKey: "serverTimeStamp") // remove FIRTimestamp
let requestData = try! JSONSerialization.data(withJSONObject: leadData, options: JSONSerialization.WritingOptions.prettyPrinted) as NSData?
let results = try JSONDecoder().decode(leadDocument.self, from: requestData! as Data)
OR covert FIRTimestamp to JSON
let db = Firestore.firestore()
let settings = db.settings
settings.areTimestampsInSnapshotsEnabled = true
db.settings = settings
let timestamp: Timestamp = document.get("serverTimeStamp") as! Timestamp
let date: Date = timestamp.dateValue()
print(date)
If you're trying to serialize the contents of a FIRTimestamp, you should either:
Convert it to a NSDate with dateValue, and serialize that instead
Convert it to seconds (and nanoseconds if desired) using the linked methods
When you deserialize those values, you may to convert them back into a FIRTimestamp with one of its constructors.

Swift unable to pull information from other Swift files unless in #IBAction

I'm currently working with the latest version of Swift. In a nutshell, I'm pulling information from a webpage and storing said information into an array. Here is how I did that (forgive the indenting..):
class TransactionData {
var transactions: [Transaction] = []
init() {
getTransactionData()
}
func getTransactionData() {
let jsonUrl = "php file with json"
let session = NSURLSession.sharedSession()
let shotsUrl = NSURL(string: jsonUrl)
let task = session.dataTaskWithURL(shotsUrl!) {
(data, response, error) -> Void in
do {
let jsonData: NSArray = (try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers) as? NSArray)!
for var index = 0; index < jsonData.count; ++index {
let orderID: String = jsonData[index]["orderID"] as! String
let orderDate: String = jsonData[index]["orderDate"] as! String
let orderType: String = jsonData[index]["orderType"] as! String
let paymentType: String = jsonData[index]["paymentType"] as! String
let itemName: String = jsonData[index]["itemName"] as! String
let itemPrice: String = jsonData[index]["itemPrice"] as! String
let itemTaxes: String = jsonData[index]["itemTaxes"] as! String
let orderModifications: String = jsonData[index]["orderModifications"] as! String
let orderVariations: String = jsonData[index]["orderVariations"] as! String
let transaction = Transaction(orderID: orderID, orderDate: orderDate, orderType: orderType, paymentType: paymentType, itemName: itemName, itemPrice: itemPrice, itemTaxes: itemTaxes, orderModifications: orderModifications, orderVariations: orderVariations)
self.transactions.append(transaction)
}
} catch _ {
// Error
}
}
task.resume()
}
When I want to call the information, I use this:
let transactionData = TransactionData()
for transaction in transactionData.transactions {
print("\(transaction)")
}
The only time information gets pulled through to a ViewController is when I'm using an IBAction. If I try anywhere else, it doesn't read the information through. For example, I'm trying to pull information from the online website to pass into a TableViewController. It just won't pull the information.
Any ideas?
You need to establish a connection between your ViewControllers so they can pass data between them. One common way is through a delegate protocol that is set during a segue between screens. Here is a tutorial on it.
To expand on this a little bit, it sounds like you have a class Transactions that is called by your ViewController to load data. If you then try to access a new instance of that in your TableViewController, you don't have the data, because a new instance is being created. There are two ways to avoid this issue:
Pass data (or a reference to your Transactions) from VC -> TVC via delegate.
Use a singleton pattern for your data model so that all can access it.
To avoid concurrency issue, I would suggest doing the former.

How to parse the data from JSON iOS

I am building an app in iOS using SWIFT and i have also been using swiftyJSON to make this project a little easier.
func parseJSON(){
let path : String = NSBundle.mainBundle().pathForResource("jsonFile", ofType: "json") as String!
let url : String = "http://www.thegoodsite.org/attend/api.php?users_id=1"
let nsurly = NSURL(string: url)
let jsonData = NSData(contentsOfURL: nsurly!) as NSData!
let readableJSON = JSON(data: jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil)
var Name = readableJSON
numberOfRows = readableJSON["People"].count //Ignore this for the question
NSLog("\(Name)")
}
I am loading this data from a url so if going to include a picuture of the data im getting back in the console.
CLICK THIS LINK TO SEE IMAGE OF WHAT THE CONSOLE SAYS
So what code do i need to add to get the email to come out as text.
var Name = readableJSON ["users","email"]
However when I do that to the code I seems not to get any data at all.
How can I edit this code to get the email like I want?
let users = readableJSON["users"]
let user = users[0] // get info of the first user, you should check more here
let email = user["email"]
Or (as #nhgrif's cmt):
if let users = readableJSON["users"], user = users.first, email = user["email"]
I play the code,then i found the result of Json if Dictionary, so use the var Name = readableJSON["users"]!![0]["email"]

Can you save json data without being archived

I want to save json data into a file without being archived. This way I can open the file and check if everything is okay
// valid json data
if NSJSONSerialization.isValidJSONObject(jsonData) {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
let documentsDirectory = paths[0] as! String
let path = documentsDirectory.stringByAppendingPathComponent("test.json")
println(path) // if I open the file from finder, all the data is archived
// try 1: the jsonData is archived
if NSKeyedArchiver.archiveRootObject(jsonData, toFile: path) {
println("saved: true");
}
let stringTest = "asd"
stringTest.writeToFile(path, atomically: true, encoding: NSStringEncoding.allZeros, error: nil)
// the string is also archived
}
I also try
var jsonNSData = NSKeyedArchiver.archivedDataWithRootObject(jsonData)
var string = NSString(data: jsonNSData, encoding: NSUTF8StringEncoding)
println(string) // returns nil
You can do it by casting your JSON to NSDictionary and then just write it to user documents directory:
var someJsonAsDict: [String: AnyObject] = ["foo": "string", "bar": NSNumber(integer: 5)]
if let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true) as? [String],
let documentsDirectory = paths.first
{
let filePath = documentsDirectory.stringByAppendingPathComponent("myPlistFile")
let fileManager = NSFileManager.defaultManager()
//IMPORTANT - someJsonAsDict cannot have primite types(they must be boxed in Appropriate NSNumber, NSValue, etc)
(someJsonAsDict as NSDictionary).writeToFile(filePath, atomically: true)
//print path of simulator so you can read it from terminal
println("target path is \(filePath)")
}
If you happen to have Array as most outer object of JSON, just replace:
someJsonAsDict as NSDictionary
with appropriate array method:
someJsonAsDict as NSArray
All this will create plist file with the JSON. If you happen to need it in raw json style(as like txt) you can use plutil(installed with Xcode command line tools):
plutil -convert json myPlistFile.plist -o MyTxtFile.json
maybe you can use NSUserDefaults to archive value. like var save = NSUserdefaults.userdefaults()then var ValueSaved = save.setObject(stringTest, "StringTest") this is the easiest mode to archive a file.. but it doesn't archive in a file :\