Reputation: 1703
I'm a beginner and I did this function only to called getMovieById() on my viewDidLoad. But now I need to call this function in another place, receiving something. I have to receive an imdbID to pass to my Alamofire request function and I need to return my movieDetailed, how can I do this?
func getMovieById(){
let requestURL = "https://www.omdbapi.com/?i=\(imdbID!)"
print("|getMovieIdFromAPI| URL: \(requestURL)")
Alamofire.request(requestURL).responseObject{ (response: DataResponse<Movie>) in
print("|Alamofire request| Response is: \(response)")
let movieResult = response.result.value
let movieDetailed: Movie = Movie()
if let title = movieResult?.title {
print("Title: \(title)")
self.movTitleLabel.text = title
movieDetailed.title = title
}
// Lots of irrelevant code removed here
if let movieImdbID = movieResult?.imdbID {
movieDetailed.imdbID = movieImdbID
}
if self.fromFavorite {
self.addButton.title = "Remove"
}
}//Alamofire.request
}//getMovieByID()
Just for the record, my Movie class is:
import Foundation
import ObjectMapper
import AlamofireObjectMapper
class Movie: Mappable {
var posterURL : String?
var title : String?
var runtime : String?
var director : String?
var actors : String?
var genre : String?
var plot : String?
var production : String?
var released : String?
var year : String?
var imdbID : String?
var imdbRating : String?
init() { }
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
posterURL <- map["Poster"]
title <- map["Title"]
runtime <- map["Runtime"]
director <- map["Director"]
actors <- map["Actors"]
genre <- map["Genre"]
plot <- map["Plot"]
production <- map["Production"]
released <- map["Released"]
year <- map["Year"]
imdbID <- map["imdbID"]
imdbRating <- map["imdbRating"]
}
}
Any help?
So, my MovieViewController now is this:
import UIKit
import AlamofireObjectMapper
import Alamofire
import Kingfisher
class MovieViewController: UIViewController {
@IBOutlet weak var addButton: UIBarButtonItem!
/* Outlets */
var imdbID: String?
var fromFavorite: Bool = false
var movieDelegate: MovieDelegate?
override func viewDidLoad() {
super.viewDidLoad()
getMovie(imdbID: imdbID!)
}
override func viewWillAppear(_ animated: Bool) {
DispatchQueue.main.async {
let spinnerActivity = MBProgressHUD.showAdded(to: self.view, animated: true)
spinnerActivity.label.text = "Loading";
spinnerActivity.isUserInteractionEnabled = false;
}
}
func getMovie(imdbID: String){
let requestURL = "https://www.omdbapi.com/?i=\(imdbID)"
Alamofire.request(requestURL).responseObject{ (response: DataResponse<Movie>) in
let movieResult = response.result.value
let movieDetailed: Movie = Movie()
/* Irrelevant code - If let for any item of movie */
if self.fromFavorite {
self.addButton.title = "Remove"
}
DispatchQueue.main.async {
MBProgressHUD.hide(for: self.view, animated: true)
}
self.movieDelegate?.didGetDetails(movie: movieDetailed)
}//Alamofire.request
}//getMovie()
func showAlertButton(tile: String, msg: String) {
let alert = UIAlertController(title: tile, message: msg, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
//Action for my addButton
@IBAction func addAsFavorite() {
if (!self.fromFavorite) {
saveMovieOnDB(movieID: self.imdbID!, onCompletion: {
(isFavorite) in
if isFavorite {
self.showAlertButton(tile: "Error", msg: "You already saved this movie as favorite")
}
else {
self.showAlertButton(tile: "Saved", msg: "The movie was saved in your favorites list")
}
})
}
else {
removeFromFavorites(movieID: self.imdbID!, onCompletion: {
self.showAlertButton(tile: "Removed", msg: "The movie was removed from favorites list")
})
}
}
} //MovieControler
Now, my FavoritesViewController for my CollectionView (that will be show the movies saved):
import UIKit
import Parse
import Kingfisher
protocol MovieDelegate {
func didFavorite(imdbID: String)
func didGetDetails(movie: Movie)
}
class FavoritesViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
@IBOutlet weak var favCollectionView: UICollectionView!
var movies = [Movie]()
override func viewDidLoad() {
super.viewDidLoad()
self.favCollectionView.delegate = self
self.favCollectionView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.async {
let spinnerActivity = MBProgressHUD.showAdded(to: self.view, animated: true)
spinnerActivity.label.text = "Loading";
spinnerActivity.isUserInteractionEnabled = false;
}
//Database(Parse) function
getMovieCollection(onCompletion: {
(objects) in
var moviesIDs = [String]()
var auxArray = [Movie]()
for obj: PFObject in objects! {
//Getting the IDs saved in my database
moviesIDs.append(obj["imdbID"] as! String)
}
for movieID: String in moviesIDs {
print("|Fav CollectionView| - Getting movie by id")
/* for each ID on my moviesIDs array I'll do a search by ID using getMovie() and add each movie to an array of movies that will be show on my collection view */
}
DispatchQueue.main.async {
MBProgressHUD.hide(for: self.view, animated: true);
}
})
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.movies.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FavCollectionCell", for: indexPath) as! FavoritesCollectionViewCell
let imgStg: String = movies[indexPath.row].posterURL!
let imgURL: URL = URL(string: imgStg)!
let resource = ImageResource(downloadURL: imgURL, cacheKey: imgStg)
cell.favPosterImageView.kf.setImage(with: resource)
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueFromFavorite" {
if let destination = segue.destination as? MovieViewController {
let cell = sender as! FavoritesCollectionViewCell
let indexPath = self.favCollectionView!.indexPath(for: cell)?.row
let movieSelectedID = self.movies[indexPath!].imdbID
destination.imdbID = movieSelectedID!
destination.fromFavorite = true
}
}
}
}
Problems/Questions
The app is crashing when I click on Add button. (this button is on my navigation bar and it should change his title to "Remove" once the movie is on my collection view). The console show this error:
2017-05-03 02:10:27.080 Favorite Movies[1658:36995] -[Favorite_Movies.MovieViewController addAsFavorite:]: unrecognized selector sent to instance 0x7fca7456a270 2017-05-03 02:10:27.092 Favorite Movies[1658:36995] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Favorite_Movies.MovieViewController addAsFavorite:]: unrecognized selector sent to instance 0x7fca7456a270'
Any idea of what is causing this? (Maybe is related to the next question)
Upvotes: 0
Views: 114
Reputation: 796
I am still a bit confused on what your asking because having a web call return a value synchronous is not a good idea. But I hope this helps you with what your looking for. If not comment and I will try to change.
Protocol for Communication between detail and collection
protocol MovieDelegate {
func didFavorite(movieId: String)
func didGetDetails(movie: Movie)
}
Collection ViewController
class MoviesViewController: UICollectionViewController, MovieDelegate {
let movieIds = [String]()
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedMovieId = self.movieIds[indexPath.row]
// Perform segue here
self.perfomSegue(withIdentifier: "goToDetail", sender: selectedMovieId)
}
func didFavorite(movieId: String) {
// Change collection view accordingly.
}
func didGetDetails(movie: Movie) {
// Do something with the Movie object
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? MovieDetailViewController {
destination.imdbID = sender as! String
destination.movieDelegate = self
}
}
}
Detail ViewController
class MovieDetailViewController: UIController {
var imdbID: String!
var movieDelegate: MovieDelegate?
override func viewDidLoad() {
getMovie(imdbID)
}
override func viewWillAppear() {
// Start loading indicator here
}
func getMovie(imdbID: String) {
Alamofire.request(requestURL).responseObject{
let movie = Movie()
// Fill Movie object based off the response
// Create a view based off Movie object
// Stop loading indicator
// Let the collection view know we now have details for this movie.
self.movieDelegate.didGetDetails(movie)
}
}
func didTapFavorite() {
// Let the collection view know the user favorited the movie.
self.movieDelegate.didFavorite(self.imdbID)
}
}
UPDATE:
1 The reason you are getting crashes on button is because your @IBAction needs to be.
@IBAction func addAsFavorite(sender: Any)
2 Now I understand what you want so you don't need a protocol unless you want to update when a favorite is unfavorited.
Here is what you want.
for movieID: String in moviesIDs {
print("|Fav CollectionView| - Getting movie by id")
/* for each ID on my moviesIDs array I'll do a search by ID using getMovie() and add each movie to an array of movies that will be show on my collection view */
Alamofire.request(requestURL).responseObject{ (response: DataResponse<Movie>) in
let movieResult = response.result.value
let movie: Movie = Movie()
/* Irrelevant code - If let for any item of movie */
self.movies.append(movie)
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
Upvotes: 1