This is an app that I already made for iPhone and want to make it available for the apple watch.
I have a function that uses SwiftyJSON to parse a JSON and collect various information that I use to populate my table. In willActiviate I loop through all of my rows and index them to get the information I need to display on the label. Or at least, that's what I'm trying to do. When I run it, the correct amount of labels are shown, but the labels do not display anything. I've also created loadTableData() to manually reload the tableData.
If I'm doing something completely wrong when trying to populate it, or if I'm doing something else wrong, a little point in the right direction would be appreciated.
My code:
#IBOutlet weak var earthTable: WKInterfaceTable!
private func loadTableData() {
getEarthquakeInfo { (info) in
self.earthTable.setNumberOfRows(0, withRowType: "earthquake")
self.earthTable.setNumberOfRows(info.count, withRowType: "earthquake")
}
}
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
self.loadTableData()
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
for index in 0..<self.earthTable.numberOfRows {
var currentRow = self.earthTable.rowControllerAtIndex(index) as earthquakeViewController
let time = info[self.earthTable.indexOfAccessibilityElement(currentRow)].time
let mag = info[self.earthTable.indexOfAccessibilityElement((currentRow))].mag
let title = info[self.earthTable.indexOfAccessibilityElement(currentRow)].title
currentRow.titleLabel.setText("\(title)")
currentRow.timeLabel.setText("\(time)")
currentRow.magLabel.setText("\(mag)")
}
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
}
My entire function where I parse the JSON if needed:
class InterfaceController: WKInterfaceController {
var info = [AppModel]()
func getEarthquakeInfo(completion: (results : [AppModel]) ->Void ){
DataManager.getEarthquakeDataFromFileWithSuccess {
(data) -> Void in
let json = JSON(data: data)
if var JsonArray = json.array {
JsonArray.removeAtIndex(0)
for appDict in JsonArray {
// parsing
var ids: String? = appDict["id"].stringValue
var title: String? = appDict["title"].stringValue
var time: String = appDict["time"].stringValue
var lattitude: String? = appDict["lat"].stringValue
var longitude: String? = appDict["lng"].stringValue
var north: String? = appDict["north"].stringValue
var west: String? = appDict["west"].stringValue
var mag: String? = appDict["mag"].stringValue
var depth: String? = appDict["depth"].stringValue
var timeStamp: String? = appDict["timestamp"].stringValue
// Splitting up title string into 2 parts
let newString = title!.stringByReplacingOccurrencesOfString(" ", withString: " - ", options: NSStringCompareOptions.LiteralSearch, range: nil)
var title2strings = newString.componentsSeparatedByString(" - ")
var scale = title2strings[0]
var location = title2strings[1]
// replacing M in scale string with Richter Scale
let scaleString = scale.stringByReplacingOccurrencesOfString("ML", withString: "Magnitude", options: NSStringCompareOptions.LiteralSearch, range: nil)
let scaleString2 = scaleString.stringByReplacingOccurrencesOfString("mb", withString: "Magnitude", options: NSStringCompareOptions.LiteralSearch, range: nil)
let scaleString3 = scaleString2.stringByReplacingOccurrencesOfString("Mw", withString: "Magnitude", options: NSStringCompareOptions.LiteralSearch, range: nil)
let scaleString4 = scaleString3.stringByReplacingOccurrencesOfString("MD", withString: "Magnitude", options: NSStringCompareOptions.LiteralSearch, range: nil)
let scaleString5 = scaleString4.stringByReplacingOccurrencesOfString("M ", withString: "Magnitude ", options: NSStringCompareOptions.LiteralSearch, range: nil)
//Formatting the date
var date = NSDate(dateString: time).getDatePart()
// Collecting all the information
var information = AppModel(idEarth: ids, title: title, time: date, lat: lattitude, lng: longitude, north: north!, west: west, mag: mag, depth: depth, timeStamp: timeStamp, location: location, scale: scaleString5)
self.info.append(information)
//sorting array by highest magnitude
// self.info.sort({$0.mag > $1.mag})
// returning the completion handler
completion(results: self.info)
}
}
}
}
My AppModel file that I used (if needed):
import Foundation
import WatchKit
class AppModel: NSObject, Printable {
let idEarth: String
let title: String
let time: String
let lat: String
let lng: String
let north: String
let west: String
let mag: String
let depth: String
let timeStamp: String
let scale: String
let location: String
override var description: String {
return "ID: \(idEarth), TITLE: \(title), TIME: \(time), LAT: \(lat), LNG: \(lng), NORTH: \(north), WEST: \(west), MAG: \(mag), DEPTH: \(depth), TIMESTAMP: \(timeStamp), LOCATION: \(location), SCALE: \(scale) \n"
}
init(idEarth: String?, title: String?, time: String?, lat: String?, lng: String?, north: String, west: String?, mag: String?, depth: String?, timeStamp: String?, location: String?, scale: String?) {
self.idEarth = idEarth ?? ""
self.title = title ?? ""
self.time = time ?? ""
self.lat = lat ?? ""
self.lng = lng ?? ""
self.north = north ?? ""
self.west = west ?? ""
self.mag = mag ?? ""
self.depth = depth ?? ""
self.timeStamp = timeStamp ?? ""
self.location = location ?? ""
self.scale = scale ?? ""
}
}
I think that your problem is that you are trying to populate your tableView in willActivate but you are not even sure that you already have the data (your completion handler from getEarthquakeInfo may not be reached).
You should try to set up your cells just after you set the number of rows.
And by the way, why are you setting two times the number or rows?
Try something like this
#IBOutlet weak var earthTable: WKInterfaceTable!
private func loadTableData() {
getEarthquakeInfo { (info) in
self.earthTable.setNumberOfRows(info.count, withRowType: "earthquake")
//Create cells
for index in 0..<self.earthTable.numberOfRows {
var currentRow = self.earthTable.rowControllerAtIndex(index) as earthquakeViewController
let time = info[self.earthTable.indexOfAccessibilityElement(currentRow)].time
let mag = info[self.earthTable.indexOfAccessibilityElement((currentRow))].mag
let title = info[self.earthTable.indexOfAccessibilityElement(currentRow)].title
currentRow.titleLabel.setText("\(title)")
currentRow.timeLabel.setText("\(time)")
currentRow.magLabel.setText("\(mag)")
}
}
}
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
self.loadTableData()
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
}
Related
I am using MySQL and PHP to download a restaurants menu but the user of the app should be able to add a certain amount to which item from the menu they want. Currently I am using a stepper to indicate the amount and adding that amount to a UserDefaults key which gets called when the menu is downloaded again.
This makes me have to download the menu again when I go to another viewController which sums up the order but I can't seem to filter out only them items which do have an amount.
What is a better way to add that amount to the downloaded data and how can I filter these items in my cart ViewController to only show and use the items which have an amount.
My current downloadModel, MenuModel, cellViewController (for the menu tableview) look like this:
MenuDownload.swift:
import UIKit
protocol MenuDownloadProtocol: class {
func productsDownloaded(products: NSArray)
}
class MenuDownload: NSObject {
//properties
weak var delegate: MenuDownloadProtocol!
func downloadProducts() {
let urlPath = "http://server.com/download.php" // Fake URL obviously
let url: URL = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Failed to download data")
}else {
print("Menu downloaded")
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let products = NSMutableArray()
for i in 0 ..< jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let restomenu = MenuModel()
//the following insures none of the JsonElement values are nil through optional binding
if let product = jsonElement["product"] as? String,
let price = jsonElement["price"] as? String,
let info = jsonElement["info"] as? String,
let imageurl = jsonElement["imageurl"] as? String
{
let productandprice = product + " " + "€" + price
let quantityy = UserDefaults.standard.object(forKey: productandprice) as? String
restomenu.product = product
restomenu.price = price
restomenu.info = info
restomenu.imageurl = imageurl
restomenu.quantity = quantityy
}
products.add(restomenu)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.productsDownloaded(products: products)
})
}
}
extension String {
func chopPrefix(_ count: Int = 1) -> String {
return substring(from: index(startIndex, offsetBy: count))
}
func chopSuffix(_ count: Int = 1) -> String {
return substring(to: index(endIndex, offsetBy: -count))
}
}
MenuModel.swift:
import UIKit
class MenuModel: NSObject {
//properties
var product: String?
var price: String?
var info: String?
var imageurl: String?
var quantity: String?
//empty constructor
override init()
{
}
init(product: String, price: String, info: String, imageurl: String, quantity: String) {
self.product = product
self.price = price
self.info = info
self.imageurl = imageurl
self.quantity = quantity
}
//prints object's current state
override var description: String {
return "product: \(String(describing: product)), price: \(String(describing: price)), info: \(String(describing: info)), imageurl: \(String(describing: imageurl)), quantity: \(String(describing: quantity))"
}
}
tableViewCell.swift:
import UIKit
class productTableViewCell: UITableViewCell {
#IBOutlet weak var productLabel: UILabel!
#IBOutlet weak var productImage: UIImageView!
#IBOutlet weak var cellView: UIView!
#IBOutlet weak var orderCount: UILabel!
#IBOutlet weak var stepper: UIStepper!
var amount: String?
#IBAction func stepperValueChanged(_ sender: UIStepper) {
amount = Int(sender.value).description
orderCount.text = amount
// let defaultkey = String(productLabel.text!)
UserDefaults.standard.setValue(amount, forKey: productLabel.text!)
if amount == "0"
{
orderCount.isHidden = true
UserDefaults.standard.removeObject(forKey: productLabel.text!)
}
else
{
orderCount.isHidden = false
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
EDIT: after trying filtering options and many different ways I still haven't found how to fix this. I think I'm overthinking it too much.
How would I achieve this setup in Firebase Realtime Database with Swift:
Database hierarchy
Currently, I am doing this by storing the larger element (with properties familyKey, geofences, and phoneNumbers) as a custom object. Also, the geofences property itself is an array of custom objects. I get an NSException doing this in the described fashion. How else would I go about doing this?
var tempGeofences = [GeofenceData]()
tempGeofences.append(GeofenceData(name: "Hello WOrld", latitude: 0, longitude: 0, radius: 1000))
let familyKey:String = String(Int.random(in: 1000...99999))
let uid:String = Auth.auth().currentUser!.uid
let phoneNumber = "1111111111"
let parent = Parent(phoneNumber: phoneNumber, familyKey: familyKey, geofences: tempGeofences)
databaseRef.child(uid).setValue(parent)
The NSException is thrown on this line:
databaseRef.child(uid).setValue(parent)
Parent class:
import Foundation
public class Parent {
var phoneNumber: String?
var familyKey: String?
var geofences: [GeofenceData]?
init() {
self.phoneNumber = ""
self.familyKey = ""
self.geofences = nil
}
init(phoneNumber: String?, familyKey: String?, geofences:[GeofenceData]) {
self.phoneNumber = phoneNumber
self.familyKey = familyKey
self.geofences = geofences
}
public func getPhoneNumber() -> String {
return phoneNumber!
}
public func getFamilyKey() -> String {
return familyKey!
}
public func getGeofences() -> [GeofenceData] {
return geofences!
}
// left off here, trying to send geofence object to firebase
public func toDictionary() -> Any {
return ["familyKey": familyKey, "geofences": geofences, "phoneNumber": phoneNumber]
}
}
And the GeofenceData class:
import Foundation
import Firebase
public class GeofenceData {
var name: String?
var latitude: Double?
var longitude: Double?
var radius: Float?
init() {
}
init(name: String?, latitude: Double, longitude: Double, radius: Float) {
self.name = name
self.latitude = latitude
self.longitude = longitude
self.radius = radius
}
// left off here, trying to send geofence object to firebase
public func toDictionary() -> Any {
return ["name": name, "latitude": latitude, "longitude": longitude, "radius": radius]
}
public func getName() -> String {
return name!
}
public func getLatitude() -> Double {
return latitude!
}
public func getLongitude() -> Double {
return longitude!
}
public func getRadius() -> Float {
return radius!
}
public func setName(name: String?) {
self.name = name
}
public func saveToFirebase(reference: DatabaseReference) {
let dict = ["name": name, "latitude": latitude, "longitude": longitude, "radius": radius] as Any
reference.child("geofences").child("0").setValue(dict)
}
}
Parent is not an object that Firebase recognizes so it throws an error.
The Firebase guide Reading & Writing Data shows the four types of objects that can be written; String, Number, Dictionary, Array.
One solution is to build a function into the class that returns the data you want to write.
public class Parent {
var phoneNumber: String?
var familyKey: String?
var geofences: [GeofenceData]?
init() {
self.phoneNumber = ""
self.familyKey = ""
self.geofences = nil
}
//other init functions
func getParentDict() -> [String: Any] {
let geoDict = ["name": name,
"latitude": latitude,
"longitude": longitude,
"radius": radius
]
let zeroNode = ["0": geoDict]
let dictForFirebase: [String: Any] = [
"phoneNumber": phoneNumber,
"familyKey": familyKey,
"geofences": zeroNode
]
return dictForFirebase
}
}
and in practice
var tempGeofences = [GeofenceData]()
tempGeofences.append(GeofenceData(name: "Hello WOrld", latitude: 0, longitude: 0, radius: 1000))
let familyKey:String = String(Int.random(in: 1000...99999))
let uid:String = Auth.auth().currentUser!.uid
let phoneNumber = "1111111111"
let parent = Parent(phoneNumber: phoneNumber, familyKey: familyKey, geofences: tempGeofences)
let parentDict = parent.getParentDict
databaseRef.child(uid).setValue(parentDict)
However, one concern is the child node with "0" as the key. That looks like you may be using an array. If there's a good reason that's fine but there are usually much better alternatives to using array's in NoSQL databases. See the legacy but still accurate Firebase post called Arrays Are Evil
EDIT:
Per a comment/question 'how to add another child node following the "0" node"
Assume we know the parent node, qSaEE..., lets add a "1" node
let parentNode = "qSaEE..."
let geofenceRef = firebaseRef.child(parentNode).child("geofences")
let geoDict = ["name": name,
"latitude": latitude,
"longitude": longitude,
"radius": radius
]
let oneNode = ["1": geoDict]
geofenceNode.setValue(oneNode)
I have problem adding annotations to mapView. I had success with this code:
func placeAnnotations() {
for _ in placeDetails {
let multipleAnnotations = MKPointAnnotation()
multipleAnnotations.title = place.address
multipleAnnotations.subtitle = place.phone
multipleAnnotations.coordinate = CLLocationCoordinate2D(latitude: place.lat, longitude: place.lng)
mapView.addAnnotation(multipleAnnotations)
}
}
Problem is, it is not conforming to my Place class, thus not showing custom Title, Subtitle and MKAnnotationView. This is code inside viewDidLoad(), where I'm trying to put all the annotations, but it keeps adding only last one. I understood that it overrides all the previous ones from array, but haven't found any other way/method to implement.
var placeDetails = [Place]()
var places = [Place]()
override func viewDidLoad() {
super.viewDidLoad()
downloadPlaceID {
for obj in places {
place.downloadDetails(input: obj.placeId, completed: {
self.placeDetails.append(obj)
//self.placeAnnotations()
self.mapView.addAnnotations(self.placeDetails)
})
}
}
}
And this is my class with all the data conforming to MKAnnotation
protocol, and functions, downloadPlaceID() and downloadDetails()
class Place: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var placeId: String!
var vicinity: String!
var phone: String!
var workHours: Bool!
var lat: Double!
var lng: Double!
var address: String!
var subtitle: String? {
return phone
}
var title: String? {
return address
}
var _placeId: String {
if placeId == nil {
placeId = ""
}
return placeId
}
var _vicinity: String {
if vicinity == nil {
vicinity = ""
}
return vicinity
}
var _phone: String {
if phone == nil {
phone = ""
}
return phone
}
var _workHours: Bool {
if workHours == nil {
workHours = false
}
return workHours
}
var _lat: Double {
if lat == nil {
lat = 0.0
}
return lat
}
var _lng: Double {
if lng == nil {
lng = 0.0
}
return lng
}
var _address: String {
if address == nil {
address = ""
}
return address
}
init(place: [String:Any]) {
if let ids = place["place_id"] as? String {
self.placeId = ids
}
if let vicinities = place["vicinity"] as? String {
self.vicinity = vicinities
}
self.coordinate = CLLocationCoordinate2DMake(0.0, 0.0)
}
func downloadDetails(input: String, completed: #escaping DownloadComplete) {
let details = "\(detailsBaseURL)\(detailsPlaceId)\(input)\(detailsKey)\(detailsSearchAPIKey)"
Alamofire.request(details).responseJSON { response in
let result = response.result
if let dictionary = result.value as? [String:Any] {
if let result = dictionary["result"] as? [String:Any] {
if let phoneNumber = result["formatted_phone_number"] as? String {
self.phone = phoneNumber
}
if let geometry = result["geometry"] as? [String:Any] {
if let location = geometry["location"] as? [String:Any] {
if let latitude = location["lat"] as? Double {
self.lat = latitude
}
if let longitude = location["lng"] as? Double {
self.lng = longitude
}
self.coordinate = CLLocationCoordinate2DMake(self.lat, self.lng)
}
}
if let openingHours = result["opening_hours"] as? [String:Any] {
if let openNow = openingHours["open_now"] as? Bool {
self.workHours = openNow
}
}
if let addressComponents = result["address_components"] as? [[String:Any]] {
let longName = addressComponents[1]["long_name"] as? String
let shortName = addressComponents[0]["long_name"] as? String
self.address = "\(longName!),\(shortName!)"
}
}
}
completed()
}
}
}
func downloadPlaceID (completed: #escaping DownloadComplete) {
let placeURL = URL(string: nearbyURL)
Alamofire.request(placeURL!).responseJSON { (response) in
let result = response.result
if let dictionary = result.value as? [String:Any] {
if let results = dictionary["results"] as? [[String:Any]] {
if let status = dictionary["status"] as? String {
if status == "OK" {
for obj in results {
place = Place(place: obj)
places.append(place)
}
} else {
print("jede govna")
}
}
}
}
completed()
}
There's some odd mixing of variable names and concepts here that makes your code somewhat hard to understand.
As an example, assigning a variable you call vicinities, plural, to an attribute called vicinity, singular. Or not separating your downloadDetails function from your data model.
That aside, it looks to me like you're unnecessarily adding your MKAnnotations many times to your map, by adding your array of [MKAnnotation] to your map in each loop.
I suspect you've done this because you've made it hard for yourself to know when the whole array is done updating its details.
As a quick fix, I'd suggest changing your downloadDetails function to call the completed function with the place you've just downloaded details for. Here's a really simplified, but working, version of what you are trying to do. First your Place class:
class Place: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
override init() {
coordinate = CLLocationCoordinate2DMake(0, 0)
}
func downloadDetails(completed: #escaping (Place) -> Void) {
// Instead of downloading details we are just creating random positions
self.coordinate = CLLocationCoordinate2DMake(CLLocationDegrees(arc4random_uniform(50)), CLLocationDegrees(arc4random_uniform(50)))
// Return the object you've just built
completed(self)
}
}
Now in your view controller, here I am starting with an array of 50 Place objects, getting the details for them and placing them on the map:
var places = [Place]()
for _ in 1...50 {
places.append(Place())
}
for place in places {
place.downloadDetails(completed: { (placeWithDetails) in
self.mapView.addAnnotation(placeWithDetails)
})
}
This results in the map being populated with 50 random places:
So, i'm trying to create an iOS app (i'm a beginner) that search a movie on IMDB (using OMDb API with Alamofire). The language is swift 3.
After reading a lot of tutorials I did two methods to connect to the API:
func searchMoviesOnJson(imdbTitle: String, completionHandler: #escaping (Dictionary<String, Any>?) -> ()) {
let urlByName: String = "https://www.omdbapi.com/?s=\(imdbTitle)&type=movie"
//returns a list of movies that contains the title searched
Alamofire.request(urlByName).responseJSON {
response in
switch response.result {
case .success(let value):
let moviesJSON = value
completionHandler(moviesJSON as? Dictionary<String, Any>)
case .failure(_):
completionHandler(nil)
}
}
}
func getMovieFromJson(imdbID: String, completionHandler: #escaping (Dictionary<String, String>) -> ()) {
let urlById: String = "https://www.omdbapi.com/?i=\(imdbID)"
Alamofire.request(urlById).responseJSON {
response in
if let moviesJSON = response.result.value {
completionHandler(moviesJSON as! Dictionary<String, String>)
}
}
}
Then, I did my Movie class and get stuck in my MovieDAO class (code above):
class Movie {
let poster: String?
let title: String?
let runtime: String?
let genre: String?
let director: String?
let actors: String?
let plot: String?
let released: String?
let imdbID: String?
let imdbRating: String?
init(poster: String?, title: String?, runtime: String?, genre: String?, director: String?, actors: String?, plot: String?, released: String?, imdbID: String?, imdbRating: String?) {
//checking if is nil
if let isPoster = poster {
self.poster = isPoster
} else {
self.poster = nil
}
if let isTitle = title {
self.title = isTitle
} else {
self.title = nil
}
if let isGenre = genre {
self.genre = isGenre
} else {
self.genre = nil
}
if let isRuntime = runtime {
self.runtime = isRuntime
} else {
self.runtime = nil
}
if let isDirector = director {
self.director = isDirector
} else {
self.director = nil
}
if let isActors = actors {
self.actors = isActors
} else {
self.actors = nil
}
if let isPlot = plot {
self.plot = isPlot
} else {
self.plot = nil
}
if let isReleased = released {
self.released = isReleased
} else {
self.released = nil
}
if let isImdbID = imdbID {
self.imdbID = isImdbID
} else {
self.imdbID = nil
}
if let isImdbRating = imdbRating {
self.imdbRating = isImdbRating
} else {
self.imdbRating = nil
}
}
}
I have a table view controller with a search bar and a table view, when the user type the movie title I would like to show the results in my table view.
How can I make the result of my search bar functions be the variable that my MovieDAO will receive? (Sorry if I sad something wrong, feel free to correct me, please)
My search bar test to get user's text:
func searchBar(_ searchBar: UISearchBar, textDidChange imdbTitle:String) {
print("Movie typed: \(imdbTitle)")
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
print("Movie searched: \(searchBar.text!)")
}
Any orientation, explanation, tutorial indication? Every help will be welcome!
Use ObjectMapper to map the objects https://github.com/Hearst-DD/ObjectMapper. And also use AlamofireObjectMapper to retrieve data https://github.com/tristanhimmelman/AlamofireObjectMapper .you can get the tutorials from these links.
Alamofire.request(url,method: .yourmethod).validate().responseArray{ (response:DataResponse<[yourObjectMapperClass]>) in
}
I am trying to grab a list of bars from a Firebase Database and store it in an array so I can display it in a table view.
I have configured Firebase and managed to get data in the app as String, AnyObject dictionary.
Here is my code :
struct Bar {
var latitude: Double?
var longitude: Double?
var name: String!
var phoneNumber: String?
var happyHour: String?
var url: NSURL?
var barLogo: UIImage?
var followers: Int?
var addedToFavorites: Int?
var zipCode: Double?
var area: String?
}
class ViewController: UIViewController {
var ref: FIRDatabaseReference!
var refHandle: UInt!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
refHandle = ref.observe(FIRDataEventType.value , with: {(snapshot) in
let dataDict = snapshot.value as! [String : AnyObject]
}
)
}
Here is my JSON exported from Firebase:
{
"data" : {
"bars" : {
"bar1" : {
"addedToFavorites" : 0,
"area" : "upper east",
"follwers" : 0,
"happyHour" : "m-f 16-19",
"lattitude" : 4412334,
"longitude" : 223455,
"name" : "bar1",
"phone" : 212222,
"url" : "http://www.bar1.com",
"zipCode" : 12345
},
"bar2" : {
"addedToFavorites" : 0,
"area" : "upper west",
"follwers" : 0,
"happyHour" : "f - s 20-22",
"lattitude" : 4443221,
"longitude" : 221234,
"name" : "bar 2",
"phone" : 215555,
"url" : "http://www.bar2.com",
"zipCode" : 54321
}
}
}
}
What would be the best approach for this?
I would like to scale it and download hundreds of bars, so manually grabbing the data from the dictionary and storing it into a Bar struct variable and then appending it to an array is not a path I want to go on.
I need a solution to grab all the bars and somehow adding them to an array (or any other method to display them into a tableView).
Thanks in advance.
I found a way to solve this issue :
First of all I got rid of the struct and created a class :
My class file :
import Foundation
import UIKit
class Bar {
private var _name: String!
private var _area: String!
private var _latitude: Double!
private var _longitude: Double!
private var _followers: Int!
private var _happyHour: String!
private var _phone: Double!
private var _url: String!
private var _zipCode: Double!
private var _addedToFav: Int!
var name: String! {
return _name
}
var area: String! {
return _area
}
var latitude: Double! {
return _latitude
}
var longitude: Double! {
return _longitude
}
var followers: Int! {
return _followers
}
var happyHour: String! {
return _happyHour
}
var phone: Double! {
return _phone
}
var url: String! {
return _url
}
var zipCode: Double! {
return _zipCode
}
var addedToFav: Int! {
return _addedToFav
}
init(name: String,area: String! , latitude: Double, longitude: Double, followers: Int, happyHour: String, phone: Double, url: String, zipCode: Double, addedToFav: Int) {
self._name = name
self._area = area
self._latitude = latitude
self._longitude = longitude
self._followers = followers
self._happyHour = happyHour
self._phone = phone
self._url = url
self._zipCode = zipCode
self._addedToFav = addedToFav
}
init(barData: Dictionary<String, AnyObject>) {
if let name = barData["name"] as? String {
self._name = name
}
if let area = barData["area"] as? String {
self._area = area
}
if let latitude = barData["lattitude"] as? Double {
self._latitude = latitude
}
if let longitude = barData["longitude"] as? Double {
self._longitude = longitude
}
if let followers = barData["followers"] as? Int {
self._followers = followers
}
if let happyHour = barData["happyHour"] as? String {
self._happyHour = happyHour
}
if let phone = barData["phone"] as? Double {
self._phone = phone
}
if let url = barData["url"] as? String {
self._url = url
}
if let zipCode = barData["zipCode"] as? Double {
self._zipCode = zipCode
}
if let addedToFav = barData["addedToFavorites"] as? Int {
self._addedToFav = addedToFav
}
}
}
I created a DataService class with a singleton
Data service class file:
import Foundation
import Firebase
let URL_BASE = FIRDatabase.database().reference()
class DataService {
static let ds = DataService()
private var _REF_BASE = URL_BASE
private var _REF_BARS = URL_BASE.child("data").child("bars")
var REF_BASE: FIRDatabaseReference {
return REF_BASE
}
var REF_BARS: FIRDatabaseReference {
return _REF_BARS
}
}
And my modified viewController file (i did not use a tableViewController)
class ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource {
#IBOutlet weak var myTableView: UITableView!
var baruri = [Bar]()
override func viewDidLoad() {
super.viewDidLoad()
myTableView.dataSource = self
myTableView.delegate = self
DataService.ds.REF_BARS.observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
print(snap)
if let barData = snap.value as? Dictionary<String, AnyObject> {
let bar = Bar(barData: barData)
self.baruri.append(bar)
print(self.baruri)
self.myTableView.reloadData()
}
self.myTableView.reloadData()
}
self.myTableView.reloadData()
}
self.myTableView.reloadData()
}
)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView( _ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return baruri.count
}
func tableView( _ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.myTableView.dequeueReusableCell(withIdentifier: "newCell", for: indexPath) as! NewCell
var baruriTabel: Bar!
baruriTabel = baruri[indexPath.row]
cell.barNameLBl.text = baruriTabel.name
cell.followersNrLbl.text = String(baruriTabel.followers)
cell.areaLbl.text = baruriTabel.area
cell.addedToFavoritesLbl.text = String(baruriTabel.addedToFav)
cell.happyHourLbl.text = baruriTabel.happyHour
cell.urlLbl.text = baruriTabel.url
cell.lattitudeLbl.text = String(baruriTabel.latitude)
cell.longitudeLbl.text = String(baruriTabel.longitude)
cell.phoneLbl.text = String(baruriTabel.phone)
cell.zipCode.text = String(baruriTabel.zipCode)
return cell
}
}