Here is my JSON Structure (Ignore the username to email part of it, that is for a test)
{
"Username" : {
"Username" : {
"email" : "test@gmail.com"
}
},
"uid_0" : {
"Song List" : [ "The National Anthem" ]
}
}
I am making a very simplistic app using Swift and Xcode. Basically, the user signs into their account after they register. From there the user is able to choose their favorite songs from a band's discography into an empty array. The user is then able to click a button that segues them to a table view controller that contains the songs they added. The way the user adds songs is through a view controller that contains labels that have the song names and buttons next to each song name. When the user clicks the button the label is added to the array.
I want to be able to save the array to firebase so that each user will have their own favorite songs. I have done a decent amount of research and can not find anything to steer me in the right direction as this is incredibly simple. The labels have no keys and are literally just labels being added to an array.
I have the firebase storage and database pods installed as well and I am able to upload images to fire base and have the user save that image to their specific account but how to do the same thing for an array? Here is my code for the array so you can get an idea of how it works.
import UIKit
import Firebase
import FirebaseStorage
import FirebaseAuth
import LocalAuthentication
import FirebaseDatabase
//var refSongs: DatabaseReference! {
// return Database.database().reference()
//}
var list = [String]()
class SongsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var ref: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
self.ref = Database.database().reference()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
@IBAction func saveSongs(_ sender: Any) {
//upload array to firebase storage
//let defaults = UserDefaults.standard.set(list, forKey: "KeySave")
//addSongs()
// create add list reference in fire database
// let addListRef = refSongs.child("Song List").childByAutoId()
// let addList1 = list
// addListRef.setValue(addList1)
let ref = self.ref.child("uid_0").child("Song List")
// let songArray = [list] *** Removed this
ref.setValue(list) //changed to list
retriveList()
}
func retriveList() {
let ref = self.ref.child("uid_0").child("Song List")
ref.observeSingleEvent(of: .value, with: {snapshot in
var mySongArray = [String]()
for child in snapshot.children {
let snap = child as! DataSnapshot
let song = snap.value as! String //!! Program crashes here!!
mySongArray.append(song)
}
print(mySongArray)
})
}
// add labels to array
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
cell.textLabel?.text = list[indexPath.row]
return (cell)
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete
{
list.remove(at: indexPath.row)
myTableView.reloadData()
}
}
override func viewDidAppear(_ animated: Bool) {
myTableView.reloadData()
}
@IBOutlet weak var myTableView: UITableView!
}
Writing an array to Firebase is incredibly easy and Firebase does most of the work for you.
Suppose the user selects three songs and now you want to store them in the users playlist
let ref = self.ref.child("uid_0").child("playlist")
let songArray = ["Us and Them", "Get Back", "Children of the Sun"]
ref.setValue(songArray)
will result in
uid_0
playlist
0: "Us and Them"
1: "Get Back"
2: "Children of the Sun"
Now the user selects another song to add to the playlist and you want to save it. Oh wait.. snap. Firebase treats the array as one object so you have to read the node, delete that node and re-write the node with the new data.
Now suppose the user wants to query for the title of song #2. Can't do that either! Firebase Can't query inside an array.
Ok, so you have 100 users and want to add a feature to show the users whose favorite song is Children of the Sun. Can't do that either.
What if the user wants to have multiple playists?
Basically Arrays Are Evil and limited and there are usually much better options for storing data.
That being said, arrays can be useful in some cases - two options are shown here.
Here's an example
song_library
song_0
title: "Us and Them"
artist: "Pink Floyd"
play_count: 100
total_likes: 167
song_1
title: "Get Back"
artist: "Beatles"
play_count: 50
total_likes: 87
song_2
title: "Children of the Sun"
artist: "Billy Thorpe"
play_count: 98
total_likes: 1050
song_3
title: "21st Century Man"
artist: "Billy Thorpe"
play_count: 36
total_likes: 688
song_4
title: "East of Edens Gate"
artist: "Billy Thorpe"
play_count: 45
total_likes: 927
uid_0
playlist_0
song_1: 2
song_2: 0
song_4: 1
//or use an array here
//0: song_2
//1: song_4
//2: song_0
playlist_1
song_3: 0
song_4: 1
With this structure, you can re-use songs in playlists, the sequence can be easily modified, songs and be removed or added and data duplication is significantly reduced as the playlists just keep references to the actual song data.
EDIT:
To read that node back into an array, here's one option
let ref = self.ref.child("uid_0").child("playlist")
ref.observeSingleEvent(of: .value, with: { snapshot in
var mySongArray = [String]()
for child in snapshot.children {
let snap = child as! DataSnapshot
let song = snap.value as! String
mySongArray.append(song)
}
print(mySongArray)
})
Note there are other options but this guarantees the order.
Edit 2
This is to help the OP define their class vars correctly.
class ViewController: UIViewController {
var ref: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
self.ref = Database.database().reference()