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
}
}
Related
I have problem with use data from firebase after get them. I written function getData() in model, use delegate to call them on UITableViewController and set data to TableView.
But when I create new array to get data from func getData(), this array is nil.
This is my model:
import Foundation
import Firebase
protocol myDelegate: class {
func didFetchData(datas: [Book])
}
class Book {
var Id: String?
var Author: String?
var ChapterCount: Int?
var CoverPhoto: String?
var Genre: String?
var Image: String?
var Intro: String?
var Like: Int?
var Name: String?
var Status: String?
var UpdateDay: String?
var UploadDay: String?
var View: Int?
var ref: DatabaseReference!
weak var delegate: myDelegate?
init()
{
}
init(Id: String,Author: String,Image: String,Name: String,Status: String,UpdateDay: String,View: Int)
{
self.Id = Id
self.Author = Author
self.Image = Image
self.Name = Name
self.Status = Status
self.UpdateDay = UpdateDay
self.View = View
}
func getListBook() {
ref = Database.database().reference()
ref.child("Book").observe(.value, with: { snapshot in
var newNames: [Book] = []
let value = snapshot.value as? NSDictionary
for nBook in value! {
let val = nBook.value as? NSDictionary
self.Name = val?["Name"] as? String ?? ""
self.Author = val?["Author"] as? String ?? ""
self.View = val?["View"] as? Int ?? 0
self.Status = val?["Status"] as? String ?? ""
self.Id = val?["Id"] as? String ?? ""
self.Image = val?["Image"] as? String ?? ""
self.UpdateDay = val?["UpdateDay"] as? String ?? ""
newNames.append(Book(Id: self.Id!, Author: self.Author!, Image: self.Image!, Name: self.Name!, Status: self.Status!, UpdateDay: self.UpdateDay!, View: self.View!))
}
self.delegate?.didFetchData(datas: newNames)
})
}
}
And there is class UITableViewController:
import Firebase
class ListStoryTableView: UITableViewController, myDelegate {
var ref: DatabaseReference!
var book = Book()
var listBook: [Book] = []
func didFetchData(datas: [Book]) {
listBook = datas
}
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib.init(nibName: "ListStoryTableViewCell", bundle: nil)
self.tableView.register(nib, forCellReuseIdentifier: "ListStoryTableViewCell")
book.delegate = self
book.getListBook()
print("\(listBook)") //this is return 0
}```
One solution would be to change-remove your protocol implementation and use a completion block in your getListBook func. Delete myDelegate reference from your ListStoryTableView and do the following change:
func getListBook(completion: #escaping (_ books: [Book]) -> Void) {
ref = Database.database().reference()
ref.child("Book").observe(.value, with: { snapshot in
var newNames: [Book] = []
let value = snapshot.value as? NSDictionary
for nBook in value! {
let val = nBook.value as? NSDictionary
self.Name = val?["Name"] as? String ?? ""
self.Author = val?["Author"] as? String ?? ""
self.View = val?["View"] as? Int ?? 0
self.Status = val?["Status"] as? String ?? ""
self.Id = val?["Id"] as? String ?? ""
self.Image = val?["Image"] as? String ?? ""
self.UpdateDay = val?["UpdateDay"] as? String ?? ""
newNames.append(Book(Id: self.Id!, Author: self.Author!, Image: self.Image!, Name: self.Name!, Status: self.Status!, UpdateDay: self.UpdateDay!, View: self.View!))
}
completion(newNames)
})
}
and then in your viewDidLoad or any other function you use the following to fetch your data:
book.getListBook { books in
listBook = books
tableView.reloadData()
}
I hope that helps you.
func didFetchData(datas: [Book]) {
listBook = datas
print("\(listBook)")
}
print listBook in this function and you will have the data..
I am trying to parse json data in my table view cell and its not parsing and not showing the cells . I am attaching all my code please tell me whats the mistake i am doing .. I am not getting any error but the cells are not showing. I have made separate class and functions for json parsing and storing it in an array .
//
// ViewController.swift
// WorkingFeed2
//
// Created by keshu rai on 08/08/17.
// Copyright © 2017 keshu rai. All rights reserved.
//
import UIKit
import Alamofire
import MediaPlayer
class ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource{
var post : PostData!
var posts = [PostData]()
typealias DownloadComplete = () -> ()
var arrayOfPostData : [String] = []
#IBOutlet weak var feedTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
feedTable.dataSource = self
feedTable.delegate = self
}
func downloadPostData(completed: #escaping DownloadComplete) {
Alamofire.request("https://new.example.com/api/posts/get_all_posts").responseJSON { response in
let result = response.result
if let dict = result.value as? Dictionary<String,AnyObject> {
if let successcode = dict["STATUS_CODE"] as? Int {
if successcode == 1 {
if let postsArray = dict["posts"] as? [Dictionary<String,AnyObject>]
{
for obj in postsArray
{
let post = PostData(postDict: obj)
self.posts.append(post)
print(obj)
}
// self.posts.remove(at: 0)
self.feedTable.reloadData()
}
}
}
}
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 419
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : ImageTableViewCell = self.feedTable.dequeueReusableCell(withIdentifier: "contentViewReuse") as! ImageTableViewCell
let post = posts[indexPath.row]
print(post)
cell.configureCell(post : post)
return cell
}
}
This is my PostData Class.
import Foundation
class PostData {
var _profileImageURL : String?
var _fullName : String?
var _location : String?
var _title : String?
var _postTime : String?
var _likes : Int?
var _comments : Int?
var _mediaType : String?
var _contentURL : String?
var _content : String?
var _plocation : String?
var profileImageURL : String
{
if _profileImageURL == nil {
_profileImageURL = ""
}
return _profileImageURL!
}
var fullName : String
{
if _fullName == nil {
_fullName = ""
}
return _fullName!
}
var location : String {
if _location == nil {
_location = ""
}
return _location!
}
var title : String {
if _title == nil {
_title = ""
}
return _title!
}
var postTime : String {
if _postTime == nil {
_postTime = ""
}
return _postTime!
}
var likes : Int {
if _likes == nil {
_likes = 0
}
return _likes!
}
var comments : Int {
if _comments == nil {
_comments = 0
}
return _comments!
}
var mediaType : String {
if _mediaType == nil {
_mediaType = ""
}
return _mediaType!
}
var contentURL : String {
if _contentURL == nil {
_contentURL = ""
}
return _contentURL!
}
var content : String {
if _content == nil {
_content = ""
}
return _content!
}
var pLocation : String {
if _plocation == nil {
_plocation = ""
}
return _plocation!
}
init(postDict : Dictionary<String , AnyObject>)
{
if let postsArray = postDict["posts"] as? [Dictionary<String,AnyObject>]
{
for i in 1..<postsArray.count
{
let fullName1 = postsArray[i]["full_name"] as? String
self._fullName = fullName1
let profileImageURL1 = postsArray[i]["profile_pic"] as? String
self._profileImageURL = profileImageURL1
let location1 = postsArray[i]["user_city"] as? String
self._location = location1
let title1 = postsArray[i]["title"] as? String
self._title = title1
let postTime1 = postsArray[i]["order_by_date"] as? String
self._postTime = postTime1
let likes1 = postsArray[i]["liked_count"] as? Int
self._likes = likes1
let comments1 = postsArray[i]["comment_count"] as? Int
self._comments = comments1
let mediaType1 = postsArray[i]["media_path"] as? String
self._mediaType = mediaType1
let contentURL1 = postsArray[i]["media_path"] as? String
self._contentURL = contentURL1
let content1 = postsArray[i]["content"] as? String
self._content = content1
let plocation1 = postsArray[i]["location"] as? String
self._plocation = plocation1
}
}
}
}
This is my PostDataTableViewCell code.
import UIKit
import Alamofire
class PostDataTableViewCell: UITableViewCell {
#IBOutlet weak var profileImage: UIImageView!
#IBOutlet weak var titlePost: UILabel!
#IBOutlet weak var profileFullName: UILabel!
#IBOutlet weak var profileUserLocation: UILabel!
#IBOutlet weak var likeBtn: UIButton!
var buttonAction: ( () -> Void) = {}
var pressed = false
#IBAction func likeBtnPressed(_ sender: Any) {
if !pressed {
let image = UIImage(named: "Like-1.png") as UIImage!
likeBtn.setImage(image, for: .normal)
pressed = true
} else {
let image = UIImage(named: "liked.png") as UIImage!
likeBtn.transform = CGAffineTransform(scaleX: 0.15, y: 0.15)
UIView.animate(withDuration: 2.0,
delay: 0,
usingSpringWithDamping: 0.2,
initialSpringVelocity: 6.0,
options: .allowUserInteraction,
animations: { [weak self] in
self?.likeBtn.transform = .identity
},
completion: nil)
likeBtn.setImage(image, for: .normal)
pressed = false
}
}
#IBAction func commentBtnPressed(_ sender: Any) {
print("Commented")
}
#IBAction func shareBtnPressed(_ sender: Any) {
self.buttonAction()
}
#IBAction func readContentBtnPressed(_ sender: Any) {
print("Read")
}
#IBOutlet weak var contentPostLabel: UILabel!
#IBOutlet weak var contentTypeView: UIView!
#IBOutlet weak var likeAndCommentView: UIView!
#IBOutlet weak var numberOfLikes: UILabel!
#IBOutlet weak var numberOfComments: UILabel!
#IBOutlet weak var postLocation: UILabel!
#IBOutlet weak var postTimeOutlet: UILabel!
func configureCell(post : PostData)
{
titlePost.text = "\(post.title)"
profileFullName.text = "\(post.fullName)"
profileUserLocation.text = "\(post.location)"
numberOfLikes.text = "\(post.likes) Likes"
numberOfComments.text = "\(post.comments) Comments"
postLocation.text = "\(post.pLocation)"
postTimeOutlet.text = "\(post.postTime)"
let url = URL(string: post.profileImageURL)
let data = try? Data(contentsOf: url!)
profileImage.image = UIImage(data: data!)
contentPostLabel.text = "\(post.content)"
if post.mediaType == "image"
{
let url1 = URL(string: post.contentURL)
let data1 = try? Data(contentsOf: url1!)
let image = UIImage(data: data1!)
let imageToView = UIImageView(image: image!)
imageToView.frame = CGRect(x: 0, y: 0, width: 375 , height: 250)
imageToView.contentMode = UIViewContentMode.scaleToFill
contentTypeView.addSubview(imageToView)
}
else if post.mediaType == "null"
{
print("Status")
}
else if post.mediaType == "video"
{
print("Video")
}
else if post.mediaType == "youtube"
{
print("youtube")
}
}
}
Most likely the issue occurs because you are trying to parse the value for key posts twice, once in PostData and once in ViewController.
First of all in Swift 3 a JSON dictionary is [String:Any], secondly – as already mentioned in my comment – private backing variables are nonsense in Swift.
The class PostData can be reduced to
class PostData {
let profileImageURL : String
let fullName : String
let location : String
let title : String
let postTime : String
let likes : Int
let comments : Int
let mediaType : String
let contentURL : String
let content : String
let plocation : String
init(postDict : [String:Any])
{
fullName = postDict["full_name"] as? String ?? ""
profileImageURL = postDict["profile_pic"] as? String ?? ""
location = postDict["user_city"] as? String ?? ""
title = postDict["title"] as? String ?? ""
postTime = postDict["order_by_date"] as? String ?? ""
likes = postDict["liked_count"] as? Int ?? 0
comments = postDict["comment_count"] as? Int ?? 0
mediaType = postDict["media_path"] as? String ?? ""
contentURL = postDict["media_path"] as? String ?? ""
content = postDict["content"] as? String ?? ""
plocation = postDict["location"] as? String ?? ""
}
}
I've got sample structure that looks like this:
I want to add every item to the array of items I've created. So as you can see the downloadListData function can only download information from Apples, because I don't know how to get to Apples, Bread and Eggs in the same time without writing a lot of code. I was trying loops and arrays and it didn't work. Moreovery I was analyzing examples on the Internet and I didn't get the answer which worked in my app.
ListItem.swift:
import Foundation
import Firebase
struct ListItem{
var name : String!
var addedBy : String!
var completed : Bool!
init(name: String, addedBy: String, completed: Bool){
self.name = name
self.addedBy = addedBy
self.completed = completed
}
}
part of my ViewController.swift:
import UIKit
import Firebase
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var rootRef : FIRDatabaseReference!
var listDataRef : FIRDatabaseReference!
var refHandle: UInt!
var listItemsDownload = [ListItem]()
//test vars
var user : String!
#IBOutlet weak var plusButton: UIButton!
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.delegate = self
tableView.dataSource = self
user = "test#me.com"
rootRef = FIRDatabase.database().reference()
listDataRef = rootRef.child("listData")
downloadListData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listItemsDownload.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? CellData{
print("cellForRowAt")
let items = listItemsDownload[indexPath.row]
// testing the tableView
cell.nameLabel?.text = items.name
}
return cell
} else{
print("else")
return CellData()
}
}
func downloadListData(){
print(" ### DOWNLOAD ###")
self.listDataRef.observe(FIRDataEventType.value, with: { snapshot in
var downloadedName : String!
var downloadedUser : String!
var downloadedComplete : Bool!
if let dict = snapshot.value as? Dictionary<String, Any>{
if let apples = dict["Apples"] as? Dictionary<String, Any>{
if let name = apples["name"] as? String{
downloadedName = name
}
if let user = apples["addedBy"] as? String{
downloadedUser = user
}
if let completed = apples["completed"] as? Bool{
downloadedComplete = completed
}
let item = ListItem(name: downloadedName, addedBy: downloadedUser, completed: downloadedComplete)
self.listItemsDownload.append(item)
self.tableView.reloadData()
}
}
})
}
So probably I have to change only this line to get to different values and not only Apples (if let apples = dict["Apples"] as? Dictionary<String, Any>{
Just use a separate method which you can call with your different keys like this:
func downloadListData(){
print(" ### DOWNLOAD ###")
self.listDataRef.observe(FIRDataEventType.value, with: { snapshot in
addAllItemsFromSnapshotWithKey(snapshot, key: "Apples")
addAllItemsFromSnapshotWithKey(snapshot, key: "Bread")
addAllItemsFromSnapshotWithKey(snapshot, key: "Eggs")
// You need to reload your table view on the main thread, since it's an asynchronous call to firebase
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
}
func addAllItemsFromSnapshotWithKey(_ snapshot: FIRDataSnapshot, key: String) {
var downloadedName : String!
var downloadedUser : String!
var downloadedComplete : Bool!
if let dict = snapshot.value as? Dictionary<String, Any>{
if let values = dict[key] as? Dictionary<String, Any> {
if let name = values["name"] as? String{
downloadedName = name
}
if let user = values["addedBy"] as? String{
downloadedUser = user
}
if let completed = values["completed"] as? Bool{
downloadedComplete = completed
}
let item = ListItem(name: downloadedName, addedBy: downloadedUser, completed: downloadedComplete)
self.listItemsDownload.append(item)
}
}
}
Update for a more scalable solution. Just loop through all keys
func downloadListData(){
print(" ### DOWNLOAD ###")
self.listDataRef.observe(FIRDataEventType.value, with: { snapshot in
var downloadedName : String!
var downloadedUser : String!
var downloadedComplete : Bool!
if let dict = snapshot.value as? Dictionary<String, Any>{
for key in dict.keys {
if let values = dict[key] as? Dictionary<String, Any> {
if let name = values["name"] as? String{
downloadedName = name
}
if let user = values["addedBy"] as? String{
downloadedUser = user
}
if let completed = values["completed"] as? Bool{
downloadedComplete = completed
}
let item = ListItem(name: downloadedName, addedBy: downloadedUser, completed: downloadedComplete)
self.listItemsDownload.append(item)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
})
}
I have used Alamofire and SwiftyJSON To Populate the UiCollectionview and its working fine but didSelectItemAtIndexPath function shows array out of index thought I have printed the array count and it's not empty
any suggestion
here is my code:-
The model
import Foundation
class ProductModel {
private var _ProductItemId: String!
private var _ProductMainCategory: String!
private var _ProductCategoryId: String!
private var _ProductName: String!
private var _ProductItemNo: String!
private var _ProductAvalaibility: String!
private var _ProductSeoDesc: String!
private var _ProductImageURL: String!
private var _ProductBrand_ID: String!
private var _ProductCat_name: String!
//Level 1
private var _ProductTotalQuantity : String!
private var _Productprice : String!
private var _ProductSalePrice : String!
private var _ProductWeightName : String!
private var _ProductCode : String!
var ProductItemId : String {
return _ProductItemId
}
var ProductMainCategory : String {
return _ProductMainCategory
}
var ProductCategoryId : String {
return _ProductCategoryId
}
var ProductName : String {
return _ProductName
}
var ProductItemNo : String {
return _ProductItemNo
}
var ProductAvalaibility : String {
return _ProductAvalaibility
}
var ProductSeoDesc : String {
return _ProductSeoDesc
}
var ProductImageURL : String {
return _ProductImageURL
}
var ProductBrand_ID: String {
return _ProductBrand_ID
}
var ProductCat_name: String {
return _ProductCat_name
}
//Level 1
var ProductTotalQuantity : String {
return _ProductTotalQuantity
}
var Productprice : String {
return _Productprice
}
var ProductSalePrice : String {
return _ProductSalePrice
}
var ProductWeightName : String {
return _ProductWeightName
}
var ProductCode : String {
return _ProductCode
}
//Initilizer
init(ProductImageURL : String, ProductName : String, Productprice : String, ProductSalePrice : String)
{
self._ProductName = ProductName
self._ProductImageURL = ProductImageURL//
//Level 1
self._Productprice = Productprice//
self._ProductSalePrice = ProductSalePrice//
}
My CollectionView Delegates and Data sources
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ProductCell", forIndexPath: indexPath)as? ProductCell {
let _prod: ProductModel!
_prod = prod [indexPath.row]
cell.configureCell(_prod)
return cell
}
else{
return UICollectionViewCell()
}
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let prodDetail: ProductModel!
prodDetail = prod[indexPath.row] //error Array index out of range
print(prodDetail.Productprice)
performSegueWithIdentifier("productDetailSegue", sender: prodDetail)
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//if inSearchMode{
//return filteredProd.count
// }
return prod.count
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
Calling API and Parsing
Alamofire.request(.POST, "http://www.picknget.com/webservice/index.php/Home/filter_grocery_product_practice/", parameters: parameterDictionary as? [String : AnyObject])
.responseJSON { response in
if let value = response.result.value {
let json = JSON(value)
print(json)
if let _statusCode = json["status"].string {
// print("the ststus code is ", _statusCode)
if (_statusCode == "1"){
self.parseJSON(json)
}
if (_statusCode == "0"){
SwiftSpinner.hide({
self.callAlert("OOP's", _msg: "No More Product is available in this section right now")
})
}
}
//print ("json result ", json)
}
}.responseString { response in
//print("response ",response.result.value)
}
}
func parseJSON(json: JSON) {
for result in json["cat"].arrayValue {
let name = result["Name"]
let aString: String = "\(result["ImageURL"])"
let product_Image_Url = aString.stringByReplacingOccurrencesOfString("~", withString: "http://www.picknget.com", options: NSStringCompareOptions.LiteralSearch, range: nil)
let price = result["cat_price"][0]["Price"].string
let SalePrice = result["cat_price"][0]["SalePrice"].string
let product = ProductModel(ProductImageURL: "\(product_Image_Url)", ProductName: "\(name)", Productprice: "\(price!)", ProductSalePrice: "\(SalePrice!)")
prod.append(product)
}
print("########")
print(prod.count)
dispatch_async(dispatch_get_main_queue(),{
self.productCollect.reloadData()
});
}
According your comments, I believe the issue is related to how you set the obtained Products for the Collection View.
It's very likely that the function parseJSON executes on a secondary thread. This is actually, the same execution context of the completion handler of method responseJSON.
Within function parseJSON you have this statement:
prod.append(product)
Here, prod should not be a global variable or not a member variable of the view controller! Make it a local variable in function parseJSON!
Your view controller should have a property of this array as well, e.g. products. This serves as the "model" of the view controller. It will be accesses only from the main thread.
In parseJSON assign the view controller the products as follows:
func parseJSON(json: JSON) {
var tmpProducts: [Product] = []
for result in json["cat"].arrayValue {
let name = result["Name"]
let aString: String = "\(result["ImageURL"])"
let product_Image_Url = aString.stringByReplacingOccurrencesOfString("~", withString: "http://www.picknget.com", options: NSStringCompareOptions.LiteralSearch, range: nil)
let price = result["cat_price"][0]["Price"].string
let SalePrice = result["cat_price"][0]["SalePrice"].string
let product = ProductModel(ProductImageURL: "\(product_Image_Url)", ProductName: "\(name)", Productprice: "\(price!)", ProductSalePrice: "\(SalePrice!)")
tmpProducts.append(product)
}
dispatch_async(dispatch_get_main_queue(),{
self.products = tmpProducts // assuming `self` is the ViewController
self.productCollect.reloadData()
});
}
Note: you need to change your Data Source Delegates accordingly, e.g. accessing the "model" (self.products.count) etc.
Hi guys i have been stuck in this problem for nearly 2 week
The Problem is that i am getting the error fatal Error Unexpectedly while unwrapping an optional value while i am loading comments..
I am trying to load JSON data For the Comments
in PopularShotsCollectionViewController
import UIKit
import FMMosaicLayout
private let reuseIdentifier = "Cell"
class PopularShotsCollectionViewController: UICollectionViewController {
private var shots : [Shot] = [Shot](){
didSet{
self.collectionView?.reloadData()
}
}
var API_URL = Config.SHOT_URL
var shotPages = 1
var shot : Shot!
var comments : [Comment] = [Comment]()
var tableView : UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let layout : FMMosaicLayout = FMMosaicLayout()
self.collectionView?.collectionViewLayout = layout
// Do any additional setup after loading the view.
self.title = "Popular"
self.API_URL = Config.POPULAR_URL
self.loadShots()
self.loadComments()
}
func loadComments(){
// When i debug shot.commentsUrl does not return nil
DribbleObjectHandler.getComments(shot.commentsUrl) { (comments) -> Void in
self.comments = comments
}
}
But i am getting this error:
The code for the getCommentsMethod
class func getComments(commentsUrl : String, completion:(([Comment]) -> Void)) {
var comments = [Comment]()
let url = commentsUrl + "&access_token=" + Config.ACCESS_TOKEN
HttpService.getJSON(url) { (jsonData) -> Void in
for commentData in jsonData {
let comment = Comment(data: commentData as! NSDictionary)
comments.append(comment)
}
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0), { () -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completion(comments)
})
})
}
}
Code for the HttpService
import Foundation
import UIKit
import Alamofire
class HttpService {
class func getJSON(url: String, callback:((NSArray) -> Void)) {
let nsURL = NSURL(string: url)!
Alamofire.request(.GET, nsURL).response { (request, response, data, error) -> Void in
if error != nil{
print("error")
}
if data != nil {
let jsonData = (try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSArray
print(jsonData)
callback(jsonData)
}
}
}
}
If you need more Code to solve the problem i am more than happy
And in PopularShotsCollectionViewController i have been loading shots also
with the same method
func loadShots(){
DribbleObjectHandler.getShots(API_URL) { (shots) -> Void in
self.shots = shots
}
}
but i get no error and it works perfectly
and code for the get Shots is same.. Just we access The Shot class in loadShots and we access The Comment Class in loadComments
The Comment Class
import Foundation
import UIKit
class Comment {
var id : Int!
var body : String!
var date : String!
var user : User!
init(data : NSDictionary){
self.id = data["id"] as! Int
let bodyHTML = Utils.getStringFromJSON(data, key: "body")
self.body = Utils.stripHTML(bodyHTML)
let dateInfo = Utils.getStringFromJSON(data, key: "created_at")
self.date = Utils.formatDate(dateInfo)
if let userData = data["user"] as? NSDictionary {
self.user = User(data: userData)
}
}
}
Code for the Shot Class
import Foundation
class Shot: DribbbleBase {
var imageUrl : String!
var htmlUrl : String!
var commentsUrl : String!
var bucketsUrl : String!
var likesUrl : String!
var attachmentUrl : String!
var reboundUrl : String!
var title : String!
var date : String!
var description : String!
var commentCount : Int!
var viewsCount : Int!
var likesCount : Int!
var bucketsCount : Int!
var attachmentsCount : Int!
var reboundCount : Int!
var user : User!
override init(data: NSDictionary) {
super.init(data: data)
self.commentCount = data["comments_count"] as! Int
self.likesCount = data["likes_count"] as! Int
self.viewsCount = data["views_count"] as! Int
self.bucketsCount = data["buckets_count"] as! Int
self.attachmentsCount = data["attachments_count"] as! Int
self.reboundCount = data["rebounds_count"] as! Int
self.commentsUrl = Utils.getStringFromJSON(data, key: "comments_url")
self.bucketsUrl = Utils.getStringFromJSON(data, key: "buckets_url")
self.likesUrl = Utils.getStringFromJSON(data, key: "likes_url")
self.title = Utils.getStringFromJSON(data, key: "title")
self.attachmentUrl = Utils.getStringFromJSON(data, key: "attachments_url")
self.reboundUrl = Utils.getStringFromJSON(data, key: "rebounds_url")
let dateInfo = Utils.getStringFromJSON(data, key: "created_at")
self.date = Utils.formatDate(dateInfo)
let desc = Utils.getStringFromJSON(data, key: "description")
self.description = Utils.stripHTML(desc)
let images = data["images"] as! NSDictionary
self.imageUrl = Utils.getStringFromJSON(images, key: "normal")
//let tags = data["tags"] as! NSArray
if let userData = data["user"] as? NSDictionary {
self.user = User(data: userData)
}
}
}
and Code for DribbbleBase
import Foundation
class DribbbleBase {
var id: Int
init(data: NSDictionary){
self.id = data["id"] as! Int
}
}
Please help me.. And Please Bear in mind i am fairly new to Swift
Thanks in Advance
Aryan
you declared shot property in your PopularShotsCollectionViewController and you didn't initialize it so shot is be nil. Then you tried to access commentsUrl property of shot which is nil in the loadComments method. You should instantiate shot in the init or view did load method of your view controller