0

I am retrieving the data from Firebase, then I convert the array received of type FIRDataSnapshot into an array of dictionaries of type [String:Any]. How can I output this array of dictionaries to a .txt file in my Xcode-project in an ordered list?

The output printed in the txt file should be in this format:

Booking 1:
EmailAddress: johnmm@gmail.com
PhoneNumber: 94949392
.
.
Booking 4:
EmailAddress: chris@gmail.com
PhoneNumber: 202583963

At the bottom of my question you will find the raw data how it looks in finalDictionary

I think the the logic should be like this: 1. loop through the elements of finalDictionary 2. take each element of the array, assign a count to it + break line

let finalDictionary = [String:Any]()

func startObservingDB() {

  dbRef.child(FullData.uid!).observe(.value, with: { (snapshot: FIRDataSnapshot) in

        // an instance of FireBaseData holding all bookings under currentUid
        var newBookingInfo = [FireBaseData]()

//iterate over all children under /FullData.uid! path
   for customer in snapshot.children {

      //the customer node starting with cus...
        let customerObject = customer as! FIRDataSnapshot

       //customer key
          self.customerKey = customerObject.key
             print("this is the Stripe customer that can be charged \(customerObject.key)")

   //now iterate over each booking which is located under customerObject in Firebase
     for booking in customerObject.children {


         // after each iteration through snapshot.children, create an instance of FireBaseData with  'booking' for the current iteration & assign it to bookingItem
            var bookingItem = FireBaseData(snapshot: booking as! FIRDataSnapshot)

        //assign key of the parent to each booking
                bookingItem.Key = self.customerKey

            // append the bookingItem after each iteration to newBookingInfo array
            newBookingInfo.append(bookingItem)

         } // end of  for booking in myCustomer
    } // end of  for customer in snapshot.children

         //assign newBookingInfo to global variable bookingInfo so it can be used globally within the class
         self.bookingInfo = newBookingInfo

  // sort the array in place so that the most recent date will appear first
 self.bookingInfo.sort(by: {(DateAndTimeObject_1,DateAndTimeObject_2) -> Bool in

     DateAndTimeObject_1.TimeStampDateAndTime > DateAndTimeObject_2.TimeStampDateAndTime
  })


  // convert bookingInfo of type FIrebaseData to Array of Dictionaries
   let arrayOfDictionary = self.bookingInfo.flatMap { $0.toAnyObject() as? [String:Any] }

  finalDictionary =  arrayOfDictionary
   print("arrayOfDictionary is \(arrayOfDictionary)")

 //I can write a string to a file in Xcode project using the code snippet 
 //below, but this does not solve my problem, namely to output all elements 
 //of the array as specified above with a break space after each element 
//so that they can be in a human readable format. 

 let fileName = "myBookings"
  let dir = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)

  // If the directory was found, we write a file to it
     if let fileURL = dir?.appendingPathComponent(fileName).appendingPathExtension("txt") {

     // Write to the file
     let outString = "Write this text to the file"
     do {
         try outString.write(to: fileURL, atomically: true, encoding: .utf8)
     } catch {
         print("Failed writing to URL: \(fileURL), Error: " + error.localizedDescription)
    }   
}




          self.tableView.reloadData()
      }, withCancel: { (Error:Any) in
        print("Error firebase \(Error)")
    })
  } // end of startObservingDB()



func exportBookings(){

 let path = URL(string: "file:///Users/bogdanbarbulescu/Desktop/Test.txt")

        //writing
        do {  
            try (finalDictionary).description.write(to: path!, atomically: false, encoding: .utf8)
        }
        catch let error { print("The error is \(error.localizedDescription)") 
   }
}

The output of print("arrayOfDictionary is (arrayOfDictionary)") is :

arrayof Dict is [["EmailAddress": johnmm@gmail.com, "PhoneNumber": 94949392, "PaymentID": ch_1AVK6KLCZ34Ur7XGsbROjWhe, "BookingAmount": 37, "BookingCompleted": 0, "NoteInstructions": Take the dog out please, "insideCabinets": true, "BookingNumber": 883924391, "BookingStatusAdmin": 0, "insideFridge": true, "FrequencyName": Every 2 weeks, "EntryInstructions": keys under carpet, "FullName": James, "SuppliesAmount": 0, "FirebaseUserID": 7b1eRsa9QWhdtA5EwyD2FiPZHSh2, "TimeStampDateAndTime": 1497790800, "PostCode": W3 7RZ, "SelectedBathRow": 2, "FrequecyAmount": 22, "laundryWash": false, "StreetAddress": 11 High St, "CostToRescheduleClient": 0, "BookingStatusClient": 1, "TimeStampBookingSavedInDB": 1497618179, "FlatNumber": Conely, "SelectedBedRow": 1, "interiorWindows": 0, "CostToCancelClient": 0, "DoormanOption": Hidden-Key, "DateAndTime": Sun, 18 Jun 2017 14:00, "insideOven": true, "SuppliesName": I have cleaning supplies],

[“EmailAddress": johnmm@gmail.com, "PhoneNumber": 07476953923, "PaymentID": ch_1ATTRHLCZ34Ur7XGL2CGN1I3, "BookingAmount": 63, "BookingCompleted": 0, "NoteInstructions": No, "insideCabinets": true, "BookingNumber": 173009560, "BookingStatusAdmin": 1, "insideFridge": true, "FrequencyName": Every 2 weeks, "EntryInstructions": simbasdada, "CostToRescheduleAdmin": 0, "FullName": John Luch, "SuppliesAmount": 5, "FirebaseUserID": 7b1eRsa9QWhdtA5EwyD2FiPZHSh2, "TimeStampDateAndTime": 1497632400, "PostCode": IG11 6PP, "SelectedBathRow": 2, "FrequecyAmount": 33, "laundryWash": true, "StreetAddress": High St, "CostToRescheduleClient": 0, "BookingStatusClient": 1, "TimeStampBookingSavedInDB": 1497177419, "FlatNumber": Flat 11, "SelectedBedRow": 2, "interiorWindows": 0, "DoormanOption": Hidden-Key, "DateAndTime": Fri, 16 Jun 2017 18:00, "insideOven": false, "SuppliesName": Bring cleaning supplies]]

bibscy
  • 2,598
  • 4
  • 34
  • 82
  • 2
    In iOS there is not Desktop. So I guess the answer is, you can't. – dasdom Jun 16 '17 at 13:32
  • @dasdom Well, I can make a path to Desktop and then use `write(toFile:atomically:)` in order to write a string from Xcode to Desktop. It works, but I don't know how to order the elements from the array. Basically, the function above takes a string and writes it to a path. how can I take an array of dictionaries, convert it to string, then write it in an ordered list to Desktop? – bibscy Jun 16 '17 at 13:36
  • `from Xcode to Desktop` Xcode is only the code editor, it's not your app. If you're able to write from *your app* to your Mac's desktop, then it means that your app is not an iOS app, it's a macOS app. // Please explain and give more details. – Eric Aya Jun 16 '17 at 13:41
  • @EricAya Even if the deployment target is Iphone, I am not planning to host this app in AppStore, it's for my own test purposes. http://imgur.com/a/HJr7H Now, I do understand that publishing this app to AppStore, I would not be able to send anything to a local path such as Desktop. What I want to do in Xcode is send the data from this array `arrayOfDictionary` to a file located on Desktop when a button is hit in the simulator. The data should be formatted and ordered as per my question above. – bibscy Jun 16 '17 at 13:51
  • You didn't understand what I said and you're confused about a few things. I will try to explain in a few words. Listen carefully. ;) 1- Stop saying "in Xcode" or "from Xcode", this makes no sense. Xcode is only the code editor where you create the app, it's not the app. 2- From the *app* when it's compiled, you can write to local storage. If an iOS app, you write in the phone. If a macOS app, you write in the Mac. This has nothing to do with Xcode or with the App Store. 3- You cannot access you Mac's desktop from an iOS app. At least, not just like that (you would need network access). – Eric Aya Jun 16 '17 at 13:55
  • 4- If you just want to export some stuff to the desktop, then make a macOS app, not an iOS one. It's trivial to do this from a Mac app. – Eric Aya Jun 16 '17 at 13:55
  • @EricAya thank you for the explanation. What if I decided to write the output to a file in Xcode? How would I go about that? I found a tutorial, but it's not in Swift https://www.youtube.com/watch?v=An9rJGR8swY – bibscy Jun 16 '17 at 14:12
  • @EricAya Basically this answers 50% of my question, but I don't know how to order the dictionaries as I specified in my original question. https://stackoverflow.com/questions/24097826/read-and-write-data-from-text-file . – bibscy Jun 16 '17 at 14:19
  • @bibscy - Do you just want EmailAddress and PhoneNumber from each record? Or do you want all the key/value pairs? – DonMag Jun 16 '17 at 14:43
  • `I don't know how to order the dictionaries` You're out of luck for this one, dictionaries have no defined order (changes everytime, depends on the memory used). :) If you want order, make an array. An array of dictionaries, or better: an array of *objects* (classes or structs) made from the content. – Eric Aya Jun 16 '17 at 14:45
  • @DonMan I want all key/value pairs. At the top of the question I wrote just few key/value pairs to avoid making the question too verbose. – bibscy Jun 16 '17 at 14:46
  • @bibscy That YouTube video you looked at, they're using Xcode; an editor, to write a C++ program which runs locally in their computer. This way, the program is able to write to the computer's Desktop folder. *What if I decided to write the output to a file in Xcode?* Again, like what Eric said, quit saying writing to Xcode. Xcode has nothing to do with writing to a file or your app's execution. Once your app is on the phone, you'll be saving data in the phone's storage. What you'll need is some sort database that'll save the data in the phone. – eshirima Jun 16 '17 at 15:18
  • I can only see two options to achieve what you want. 1: Create a database that'll store this info in the phone. I'd recommend using [Realm](https://realm.io/) because they provide a [browser](https://github.com/realm/realm-browser-osx) you could use to view the contents of the database; which is inside the phone and not Xcode, from your computer. 2: [Create a text file](https://www.youtube.com/watch?v=e2N0kV5YQ18) in the phone and store your info there. – eshirima Jun 16 '17 at 15:25
  • @eshirima I prefer the 2nd option Create a text file in the phone and store your info there. Now, how can I save the data to this txt file so that it appears ordered, namely each dictionary on a new line? – bibscy Jun 16 '17 at 15:30
  • That's one way or you could just [convert the dictionary to JSON](https://stackoverflow.com/a/29628000/4962554). I would recommend doing this first and printing out your results to get a feel of how your data will look like before writing. Should u choose this option, then make sure to make your iphone file be of JSON type – eshirima Jun 16 '17 at 15:38

1 Answers1

1

If all you want to do is output your Array of Dictionaries in an "orderly / readable" format, you can use this:

if let orderedKeys = finalDictionary.first?.keys {

    var outputString = ""
    var i = 1

    for d in finalDictionary {
        outputString += "Booking \(i):\n"
        for k in orderedKeys {
            outputString += k + ": " + (d[k] ?? "(no value)") + "\n"
        }
        outputString += "\n"
        i += 1
    }

    print(outputString)
    // or, write outputString to a text file

}

Note: this is just quick code... not necessarily optimal or 100% error free, and assumes all records have the same keys. More of a "here's a direction to go" kinda thingy :)

Edit: If the dictionaries may have variable sets of keys, you can do this:

var outputString = ""
var i = 1
for d in finalDictionary {
    outputString += "Booking \(i):\n"
    for k in d.keys {
        outputString += k + ": " + (d[k] ?? "(no value)") + "\n"
    }
    outputString += "\n"
    i += 1
}

print(outputString)

Edit 2: probably a little "Swiftier" way of doing it:

outputString = ""
i = 1

finalDictionary.forEach {
    outputString += "Booking \(i):\n"
    $0.forEach { outputString += "\($0): \($1)\n" }
    outputString += "\n"
    i += 1
}

print(outputString)

Because Dictionaries are unordered, you may end up with:

Booking 1:
EmailAddress: johnmm@gmail.com
PhoneNumber: 94949392
FullName: John
.
Booking 2:
SomeOtherKey: Some Value
FullName: Dave
EmailAddress: dave@gmail.com
.
Booking 3:
FullName: Chris
PhoneNumber: 202583963
SomeOtherKey: Some Other Value
EmailAddress: chris@gmail.com

and so on.

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Thanks for your help. However, the dictionaries will not always have the same keys as the user can decide to write new data. – bibscy Jun 16 '17 at 15:38
  • finalDictionary is of type [String:Any], on this line `outputString += k + ": " + (d[k] ?? "(no value)") + "\n"` I get error Binary operator + cannot be applied to operands of type String and Any – bibscy Jun 16 '17 at 17:22
  • That's kinda basic Swift data-to-string stuff... try replacing that line with `outputString += "\(k): \(d[k] ?? "(no value)")\n"` – DonMag Jun 16 '17 at 17:36