data with request completionhandler not always called - ios

I am making an app where you can search for movies with the API of http://www.omdbapi.com/.
The problem I am having is with the completion handler of dataTaskWithRequest. If you click on one of the collectionView cell, you will go to the detailView of that selected movie. However it doesn't work all the time. I get an error saying: unexpectedly found nil while unwrapping. And that's because it doesn't go in the completion handler of dataTaskWithRequest but goes straight to the detailVC and try passing data in the title label, genre label, etc but there is no data.
I hope you guys know what the problem is, because I have tried and I don't see what the problem is.
Or, does this problem occurs because of something before? Because first I retrieve data from http://www.omdbapi.com/ using "by search" instead of "by ID". And from there I retrieve the ID and from that ID I retrieve data for my detailVC.
Here is my code:
// Go to detail view of selected movie
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let selectedMovieId = self.IDs[indexPath.row]
chosenMovieId = selectedMovieId
self.performSegueWithIdentifier("showDetail", sender: self)
}
// Preparations before going to the detail view of selected movie
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
_ = self.movieInfoFrom(searchMovie: chosenMovieId, segue: segue)
}
}
func movieInfoFrom(searchMovie movieId: String, segue: UIStoryboardSegue) {
let movieUrlString = "http://www.omdbapi.com/?i=\(movieId)&y=&plot=full&r=json"
let url = NSURL(string: movieUrlString)
print(movieUrlString)
let urlRequest = NSURLRequest(URL: url!)
let urlSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let urlTask = urlSession.dataTaskWithRequest(urlRequest) { (data, response, error) in
if error == nil {
// Convert data to JSON
let swiftyJSON = JSON(data: data!)
let title = swiftyJSON["Title"].string!
let runTime = swiftyJSON["Runtime"].string!
let genre = swiftyJSON["Genre"].string!
let plot = swiftyJSON["Plot"].string!
let rating = swiftyJSON["imdbRating"].string!
let year = swiftyJSON["Year"].string!
let poster = swiftyJSON["Poster"].string
self.infoResult = ["\(title)", "\(runTime)", "\(genre)", "\(plot)", "\(rating)", "\(year)"]
print("\(self.infoResult)")
let destinationVC = segue.destinationViewController as! MovieDetailController
destinationVC.movieDetails = self.infoResult
destinationVC.moviePoster = poster
}
}
urlTask.resume()
}

I tried to fix your code and explain with some comments:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let selectedMovieId = self.IDs[indexPath.row]
chosenMovieId = selectedMovieId
self.movieInfoFrom(searchMovie: chosenMovieId)
}
// Preparations before going to the detail view of selected movie
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
let destinationVC = segue.destinationViewController as! MovieDetailController
destinationVC.movieDetails = self.infoResult
destinationVC.moviePoster = poster
}
}
func movieInfoFrom(searchMovie movieId: String) {
let movieUrlString = "http://www.omdbapi.com/?i=\(movieId)&y=&plot=full&r=json"
let url = NSURL(string: movieUrlString)
print(movieUrlString)
let urlRequest = NSURLRequest(URL: url!)
let urlSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
// This is asynchronously, you can put a loading here
let urlTask = urlSession.dataTaskWithRequest(urlRequest) { (data, response, error) in
// Got response, stop loading here
if error == nil {
// Convert data to JSON
let swiftyJSON = JSON(data: data!)
let title = swiftyJSON["Title"].string!
let runTime = swiftyJSON["Runtime"].string!
let genre = swiftyJSON["Genre"].string!
let plot = swiftyJSON["Plot"].string!
let rating = swiftyJSON["imdbRating"].string!
let year = swiftyJSON["Year"].string!
// You can save the poster as local variable
let poster = swiftyJSON["Poster"].string
self.infoResult = ["\(title)", "\(runTime)", "\(genre)", "\(plot)", "\(rating)", "\(year)"]
print("\(self.infoResult)")
// This should be call on main thread
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("showDetail", sender: self)
}
}
}
urlTask.resume()
}

Try this code, with safe optionals
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
chosenMovieId = self.IDs[indexPath.row]
movieInfoFrom(searchMovie: chosenMovieId)
}
func movieInfoFrom(searchMovie movieId: String) {
let movieUrlString = "http://www.omdbapi.com/?i=\(movieId)&y=&plot=full&r=json"
let url = NSURL(string: movieUrlString)
print(movieUrlString)
let urlRequest = NSURLRequest(URL: url!)
let urlSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let urlTask = urlSession.dataTaskWithRequest(urlRequest) { (data, response, error) in
if error == nil && data != nil {
// Convert data to JSON
let swiftyJSON = JSON(data: data!)
let title = swiftyJSON["Title"].string ?? ""
let runTime = swiftyJSON["Runtime"].string ?? ""
let genre = swiftyJSON["Genre"].string ?? ""
let plot = swiftyJSON["Plot"].string ?? ""
let rating = swiftyJSON["imdbRating"].string ?? ""
let year = swiftyJSON["Year"].string ?? ""
let poster = swiftyJSON["Poster"].string
self.infoResult = ["\(title)", "\(runTime)", "\(genre)", "\(plot)", "\(rating)", "\(year)"]
print("\(self.infoResult)")
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("showDetail", sender: poster)
}
}
}
urlTask.resume()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail", let destinationVC = segue.destinationViewController as? MovieDetailController {
destinationVC.movieDetails = self.infoResult
destinationVC.moviePoster = sender as? String
}
}

Related

how to get item id when collectionView cell is selected

I just want to pass the item id to the next view controller when the item of collectionView is selected.
here I store the data that I get from API
here's some code -->
var posts = [[String: Any]]()
func apicall() {
let Url = String(format: "http:example.com")
guard let serviceUrl = URL(string: Url) else { return }
var request = URLRequest(url: serviceUrl)
request.httpMethod = "POST"
request.setValue("Application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
do {
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]{
self.posts = (json["data"] as? [[String : Any]])!
DispatchQueue.main.async() {
self.collectionView.reloadData()
}
}
} catch {
print(error)
}
}
}.resume()
}
now I get the data and I want to pass the item id of that item which is selected only
#IBAction func onClickNext(_ sender: Any) {
let controller = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController") as! secondViewController
self.navigationController?.pushViewController(controller, animated: true)
}
here the code of the didSelectItemAt index path
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! secondCollectionViewCell
}
Always get the data from the model, the data source array, never from the view
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let item = self.posts[indexPath.item]
let id = item["id"]
// do things with id
}
Well at that moment if you have the selection enabled the collection view will be able to return to you the IndexPath for all selected cells in the collection view.
Please take a look to this property on the UICollectionView
var indexPathsForSelectedItems: [IndexPath]? { get }
apple documentation for indexPathForSelectedItems
then at your #IBAction func just do this
#IBAction func onClickNext(_ sender: Any) {
// logic to grab the id from self.posts using the selected indexPaths ie.
let selectedItems = self.collectionView.indexPathsForSelectedItems ?? []
let ids = selectedItems.compactMap { self.posts[$0.row] }
let controller = self.storyboard?.instantiateViewController(withIdentifier:
"secondViewController") as! secondViewController
controller.selectedIds = ids // all the selected ids
self.navigationController?.pushViewController(controller, animated: true)
}
so something like that you should do, i have no idea how the data structure looks like inside your self.posts property but the above code gives you an idea. To simplify this try to run below code in a playground and see the result.
import UIKit
let posts: [String] = ["Carrot_Post", "Pencil_Post", "Dish_Post", "Data_Post",
"iOS_Post", "Kitties_Post", "VideoGamesPost", "Bitcoin_Post"]
let selected: [Int] = [1, 3, 0, 5]
let items: [String] = selected.compactMap({ posts[$0] })
print(items) // output: ["Pencil_Post", "Data_Post", "Carrot_Post", "Kitties_Post"]
Hope that helps with your problem.

Can I fix this with the using of API Spotify in iOS app?

I have this error:
unexpectedly found nil while unwrapping an Optional value"
at this line: vc.mainPreviewURL = posts[indexPath!].previewURL
HOW CAN I FIX THIS PLEASE?
This is all my code,thanks in advance ;)
class TableViewController: UITableViewController {
var posts = [post]()
var names = [String]()
var searchURL = "https://api.spotify.com/v1/search?q=Shawn+Mendes&type=track"
var oAuthToken = "BQCvqHzNOHyHgUTKvw43PdXV4yZs9jHvdIPsn3XbXNE5Jbg0zwNrpfwh81VMeuK5LQeRel0djaJT1IyLa1T9YzQmDypC5LkMD5z_NDzeAWRcEvH4fMc_nn50X2R_i8a38AMrjfMS8qPNhGYoHjAe8sFvjBSwQOereRr2RrEbmXc8JMGq7-Aq-ttalp87DuCRVy8mt8wVt8Muenihus8hXrctT071x7he2j_eGHJSWp7WoA5fOyk9xhzkxU_p_3Hkab6x6rbYCM4SFX9WlDtb5h_jikfehT-15Mjol_PmnRYo9WPnaCLKTs3AOblDlNk"
typealias JSONStandard = [String: AnyObject]
override func viewDidLoad() {
super.viewDidLoad()
callAlamo(url: searchURL,token: oAuthToken)
}
func callAlamo(url : String,token: String){
Alamofire.request(searchURL, method: .get, parameters: ["q":"Shawn Mendes", "type":"track"], encoding: URLEncoding.default, headers: ["Authorization": "Bearer "+oAuthToken]).responseJSON { response in
self.parseData(JSONData: response.data!)
print(response)
}
}
func parseData(JSONData:Data){
do {
var readableJSON = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as! JSONStandard
print(readableJSON)
if let tracks = readableJSON["tracks"] as? JSONStandard{
if let items = tracks["items"] as? [JSONStandard] {
for i in 0..<items.count{
let item = items[i]
print(item)
let name = item["name"] as! String
let previewURL = item["preview_url"] as? String
if let album = item["album"] as? JSONStandard {
if let images = album["images"] as? [JSONStandard]{
let imageData = images [0]
let mainImageURL = URL(string: imageData["url"] as! String)
let mainImageData = NSData(contentsOf: mainImageURL!)
let mainImage = UIImage(data: mainImageData as! Data)
posts.append(post.init(mainImage: mainImage! ,name: name,previewURL: previewURL))
self.tableView.reloadData()
}
}
}
}
}
}
catch {
print(error)
}
}
override func prepare(for segue: UIStoryboardSegue,sender: Any?){
let indexPath = self.tableView.indexPathForSelectedRow?.row
let vc = segue.destination as! AudioVC
vc.image = posts[indexPath!].mainImage
vc.mainSongTitle = posts[indexPath!].name
vc.mainPreviewURL = posts[indexPath!].previewURL
}
}
Swift 3X
Change this line ...
override func prepare(for segue: UIStoryboardSegue,sender: Any?){
if let indexPath = self.tableView.indexPathForSelectedRow{
let vc = segue.destination as! AudioVC
vc.image = posts[indexPath.row].mainImage
vc.mainSongTitle = posts[indexPath.row].name
vc.mainPreviewURL = posts[indexPath.row].previewURL
}
}
You should declare your segue manual and call it from inside your didSelectRowAt function.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "yourSegue",sender: indexPath.row)
}
override func prepare(for segue: UIStoryboardSegue,sender: Any?){
if let segue.identifier == "yourSegue" {
let vc = segue.destination as! AudioVC
let index = sender as! Int
vc.image = posts[index].mainImage
vc.mainSongTitle = posts[index].name
vc.mainPreviewURL = posts[index].previewURL
}
}

Firebase Retrieve Image as Url pass tableView as Image (Swift)

I'm trying to retrieve my data as URL and pass my another viewController.
Here is my retrieve data's code:
private func loadPlaces() {
let ref = FIRDatabase.database().reference()
ref.child("places").queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
let images = snapshot.value as! [String : AnyObject]
// self.places.removeAll()
for (_, value) in images {
let userToShow = historicalPlaces()
if let img = value["imagePath"] as? String,
let name = value["name"] as? String,
let information = value["information"] as? String
{
userToShow.historyImage = img
userToShow.historyName = name
userToShow.information = information
self.places.append(userToShow)
}
}
self.tableView.reloadData()
})
// ref.removeAllObservers()
}
In this code I'm using extension which can read URL.
Extension code here.
extension UIImageView {
func downloadImage(from imgURL: String!) {
let url = URLRequest(url: URL(string: imgURL)!)
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
self.image = UIImage(data: data!)
}
}
task.resume()
}
}
and here I print my images and labels to viewController.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "historyTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? HistoryTableViewCell else {
fatalError("The dequeued cell is not an instance of historyTableViewCell.")
}
let place = places[indexPath.row]
cell.nameLabel.text = place.historyName
cell.photoImageView.downloadImage(from: place.historyImage!)
return cell
}
and this is the cell control code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
switch(segue.identifier ?? "") {
case "ShowDetail":
guard let historyDetail = segue.destination as? selectedPlaceViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
guard let selectedPlace = sender as? HistoryTableViewCell else {
fatalError("Unexpected sender: \(sender)")
}
guard let indexPath = tableView.indexPath(for: selectedPlace) else {
fatalError("The selected cell is not being displayed by the table")
}
let Place = places[indexPath.row]
historyDetail.selectedPlaces = Place
default:
fatalError("Unexpected Segue Identifier; \(segue.identifier)")
}
}
now whenever I click the cell which has view and label print it's images and labels to another viewController.
I try something like this:
import UIKit
class selectedPlaceViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var photoImageView: UIImageView!
var selectedPlaces: historicalPlaces?
override func viewDidLoad() {
super.viewDidLoad()
nameTextField.delegate = self
if let history = selectedPlaces {
navigationItem.title = history.historyName
nameTextField.text = history.historyName
// photoImageView.downloadImage(from: selectedPlaces [IndexPath.init(row: 0, section: 0)].historyImage!)
}
}
}
In this code I can retrieve labels from another viewController but I can't retrieve images. How can I retrieve Images from another viewCell URL to image.

How to get json data for passing it by segue

I try to get json data while URLSession.dataTask is running to set segue .(each json data as sender)
So First, I made my own Class Array productList = [Product]().
Next, I call getJsonData() and inside of that, I set URLSession.dataTask method. So I got Parsed json data. However, When I try to save that json data(append each data to productList) from dataTask completionHandler, it cannot save correctly.(result productList is [])
I want to pass parsed json data by segue. How can I do this?
edited --
class MainVC: UITableViewController {
var productList = [Product]()
override func viewDidLoad() {
super.viewDidLoad()
getJsonData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as? ItemCell {
let product = productList[indexPath.row]
cell.configureCell(product)
return cell
} else {
return UITableViewCell()
}
}
func getJsonData() {
let url = URL(string: "http://demo7367352.mockable.io")
let request = URLRequest(url: url!)
let defaultSession = URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: request, completionHandler: { (data, response, error) in
do {
guard let data = data, error == nil else {
print("network request failed: error = \(error)")
return
}
guard let rawItem = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
if let fineItem = rawItem["goods"] as? [[String:Any]] {
for item in fineItem {
let eachProduct = Product(title: "", price: 0)
let title = item["TITLE"]
let price = item["PRICE"]
let regDate = item["REGDATE"]
let description = item["DESCRIPTION"]
let iconURL = item["ICON_URL"]
let images = item["IMAGES"]
if let title = title as? String {
eachProduct.title = title
}
if let price = price as? String {
eachProduct.price = Int(price)!
}
DispatchQueue.main.async(execute: {
self.productList.append(eachProduct)
self.tableView.reloadData()
})
}
}
} catch {
print("error trying to convert data to JSON")
return
}
})
task.resume()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToProductDetail" {
if let controller = segue.destination as? DetailVC, let indexPath = tableView.indexPathForSelectedRow {
}
}
}
}
Right now, I can parse datas from URLSession DataTask. I want to implement segue of tableView to show detail. But productList is empty. So I cannot use prepareForSegue with productList[indexPath.row].
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToProductDetail" {
if let controller = segue.destination as? DetailVC, let indexPath = tableView.indexPathForSelectedRow {
controller.product = productList[indexPath.row] // productList is nil.
}
}
}
You need to implement prepare(for:sender:) and pass the data there:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let controller = segue.destination as? MySecondViewController, indexPath = tableView.indexPathForSelectedRow {
controller.product = productList[indexPath.row]
}
}
The exact syntax will vary (what is the class name of your destination view controller) and you'd have to declare that product property in the destination, and the destination's viewDidLoad would need to use that property, but hopefully it illustrates the basic idea.
Some additional observations:
I'd suggest you inspect rawItem and make sure it's a dictionary that has a key called goods and that the value associated with that key is really an array of dictionaries. Without seeing your JSON, it's impossible to say what exactly is going wrong.
Also, consider:
if let fineItem = rawItem["goods"] as? [[String:Any]] {
...
}
If that fails, you'll never know. I might instead suggest:
guard let fineItem = rawItem["goods"] as? [[String:Any]] else {
print("goods not found or wrong type")
return
}
...
BTW, and unrelated to your problem at hand, it's a little dangerous to be mutating productList directly in the completion handler of your data task. Don't asynchronously mutate arrays from one thread that are read from another thread. Arrays are not thread-safe. The data task completion handler should build a local array and only when it's done, inside where you're dispatching the reload to the main queue, you should insert code to replace the productList with your local array before the table is reloaded.
Also, you're currently calling reloadData inside the parsing loop. You'd generally call it at the end of the parsing loop. Right now, if your data set had 100 rows, you'd be reloading the table 100 times.
The reference to data! is a bit dangerous. If you have no internet connection, data will be nil and your code will crash. I'd suggest:
guard let data = data, error == nil else {
print("network request failed: error = \(error)")
return
}
And then you can replace the data! reference with data.
You aren't posting all of your code, but I believe that your mistake is that you are performing an asynchronous task and then immediately calling print on the array being modified. I wouldn't expect the array to be populated until the task is complete.
Does your tableView actually populate with results? Are you printing out the JSON to ensure that your data is matching properly? Are the errors being printed?
Edit:
To pass your data along the segue you need to retrieve your destinationViewController as a variable and pass the information to it. There is a method called prepareForSegue that allows you to handle the preliminary state before your action happens.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ExampleVC
vc.setProducts(productList)
}
Something like that. Obviously changing your class and variable names
I solved my Problem and here is my final code.
class MainVC: UITableViewController {
var productList = [Product]()
override func viewDidLoad() {
super.viewDidLoad()
getJsonData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as? ItemCell {
let product = productList[indexPath.row]
cell.configureCell(product)
return cell
} else {
return UITableViewCell()
}
}
// parsing
func getJsonData() {
let url = URL(string: "http://demo7367352.mockable.io")
let request = URLRequest(url: url!)
let defaultSession = URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: request, completionHandler: { (data, response, error) in
do {
guard let data = data, error == nil else {
print("network request failed: error = \(error)")
return
}
guard let rawItem = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
guard let fineItem = rawItem["goods"] as? [[String:Any]] else {
print("goods not found or wrong type")
return
}
for item in fineItem {
let eachProduct = Product(title: "", price: 0)
let title = item["TITLE"]
let price = item["PRICE"]
if let title = title as? String {
eachProduct.title = title
}
if let price = price as? String {
eachProduct.price = Int(price)!
}
self.productList.append(eachProduct)
}
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
} catch {
print("error trying to convert data to JSON")
return
}
})
task.resume()
}
// segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToProductDetail" {
if let controller = segue.destination as? DetailVC, let indexPath = tableView.indexPathForSelectedRow {
controller.product = productList[indexPath.row]
}
}
}
}

Send Firebase data from Main View Controller to Detailed View Controller

I was able to create a UICollection View feed similar to Instagram but I am not sure how to select the cells and go to a more detailed view controller. Here are what my main view controller looks like. '
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "details", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "details" {
if let indexPaths = self.collectionView!.indexPathsForSelectedItems{
let vc = segue.destination as! MainViewController
let indexPath = indexPaths[0] as NSIndexPath
let post = self.posts[indexPath.row] as! [String: AnyObject]
let Booked = post["title"] as? String
let Authors = post["Author"] as? String
let ISBNS = post["ISBN"] as? String
let Prices = post["Price"] as? String
let imageNames = post["image"] as? String
vc.Booked = Booked
vc.Authors = Authors
vc.ISBNS = ISBNS
vc.Prices = Prices
vc.imageNames = imageNames
print(indexPath.row)
} }}
Here is what my database looks like:
//Detailed View Controller
FIRDatabase.database().reference().child("posts").child(self.loggedInUser!.uid).observeSingleEvent(of: .value, with: { (snapshot:FIRDataSnapshot) in
if let dictionary = snapshot .value as? [String: AnyObject] {
self.BookTitle.text = dictionary["title"] as? String
self.Author.text = dictionary["Author"] as? String
self.ISBN.text = dictionary["ISBN"] as? String
self.Price.text = dictionary["Price"] as? String
self.Image.image = ["image"] as? UIImage
}
})
Above is my detailed view controller. However, when I click the cells, my information is not passed
You need to give segue from cell instead of view.Like shown in image below
Then modify your code as shown below:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// No Need to call this performSegue(withIdentifier: "details", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "details" {
if let indexPaths = self.collectionView!.indexPathsForSelectedItems{
let vc = segue.destination as! MainViewController
let cell = sender as UICollectionViewCell
let indexPath = self.collectionView!.indexPathForCell(cell)
let post = self.posts[indexPath.row] as! [String: AnyObject]
let Booked = post["title"] as? String
let Authors = post["Author"] as? String
let ISBNS = post["ISBN"] as? String
let Prices = post["Price"] as? String
let imageNames = post["image"] as? String
vc.Booked = Booked
vc.Authors = Authors
vc.ISBNS = ISBNS
vc.Prices = Prices
vc.imageNames = imageNames
print(indexPath.row)
} }}
then in DetailsViewController code will be like below (no need to reference firebase again as you already have all info) :
self.BookTitle.text = self.Booked
self.Author.text = self.Author
self.ISBN.text = self.ISBN
self.Price.text = self.Price
if let stringImage = self.imageNames as? String {
let imageRef = storage.reference(forURL: "gs://gsignme-14416.appspot.com/images/\(stringImage)")
imageRef.data(withMaxSize: 25 * 1024 * 1024, completion: { (data, error) -> Void in
if error == nil {
self.Image.image = UIImage(data: data!)
}else {
print("Error downloading image:" )
}
Write code in viewDidLoad.
This seems to be a duplicate. A great example of passing data between view controllers can be found here: Passing Data between View Controllers.
Structuring your data is also important. If you have a Post object received from your Firebase Database, you should create a local Post object and reference that. On the UICollectionViewCell, you can use the selected indexPath to get the Post designated to that cell.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "details" {
if let indexPaths = self.collectionView!.indexPathsForSelectedItems{
let vc = segue.destination as! MainViewController
let cell = sender as! UICollectionViewCell
let indexPath = self.collectionView!.indexPath(for: cell)
let post = self.posts[(indexPath?.row)!] as! [String: AnyObject]
let Booked = post["title"] as? String
let Authors = post["Author"] as? String
let ISBNS = post["ISBN"] as? String
let Prices = post["Price"] as? String
if let imageNames = post["image"] as? String {
let imageRef = storage.reference(forURL: "gs://gsignme-14416.appspot.com/images/")
imageRef.data(withMaxSize: 25 * 1024 * 1024, completion: { (data, error) -> Void in
if error == nil {
let image = UIImage(data: data!)
}else {
print("Error downloading image:" )
}
vc.Booked = Booked
vc.Authors = Authors
vc.ISBNS = ISBNS
vc.Prices = Prices
vc.imageNames = imageNames
print(indexPath?.row)
})} } }}

Resources