U23r
U23r

Reputation: 1703

Swift - Return for this function

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?


Update

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

  1. 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)

  1. How can I call my getMovie() in that for - in my CollectionView? (for each id that I get from my database I should search for the movie by id, returning the movie details).

Upvotes: 0

Views: 114

Answers (1)

Nicholas Mata
Nicholas Mata

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

Related Questions