<file_sep># TheMovieDatabase

List of tasks completed
1. Catalog of most popular movies -> Done
2. Detail page of selected movie -> Done
3. Fullscreen plyback of trailer on click of "Watch Trailer" -> Done
4. Offile mode handling -> Done
5. iPad and iPhone, Portrait and Landscape support -> Done
<file_sep>//
//  PopularMovieCollectionViewCell.swift
//  TheMovieDatabase
//
//  Created by <NAME> on 03/08/19.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import UIKit
import Kingfisher

class PopularMovieCollectionViewCell: UICollectionViewCell {
    
    @IBOutlet private weak var posterView: UIImageView!
    @IBOutlet private weak var movieTitle: UILabel!
    @IBOutlet private weak var ratings: UILabel!
    
    var movie: Results! {
        didSet {
            let url = URL(string: Constants.API.URLS.ImageDownloadURL + (movie.poster_path ?? ""))
            posterView.kf.setImage(with: url)
            movieTitle.text = movie.title
            ratings.text = "Rating: \(movie.vote_average ?? 0.0)"
        }
    }
    
}
<file_sep>//
//  Constants.swift
//  TheMovieDatabase
//
//  Created by <NAME> on 03/08/19.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import UIKit

//Constants file contains all constants used in the application
struct Constants {
    struct API {
        //Base URL String
        static let BaseURL = "https://api.themoviedb.org/3"
        //The Movie Database Key
        static let APIKey = "e7d3538e958b374dc44ab98a864965a0"
        
        struct URLS {
            //Popularity movie URL String
            static let PopularMoviesURL = Constants.API.BaseURL + "/movie/popular?api_key=" + Constants.API.APIKey
            //Image download URL
            static let ImageDownloadURL = "https://image.tmdb.org/t/p/w500"
            //Detail Page API
            static let MovieDetailURL = Constants.API.BaseURL + "/movie/"
        }
    }
}
<file_sep>//
//  BaseNavigationViewController.swift
//  TheMovieDatabase
//
//  Created by <NAME> on 04/08/19.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import UIKit

class BaseNavigationViewController: UINavigationController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }
    
    //Overrriding trait collection to have portrait view on iPad
    override func overrideTraitCollection(forChild childViewController: UIViewController) -> UITraitCollection? {
            if view.bounds.width < view.bounds.height {
                let traitCollections = [UITraitCollection(horizontalSizeClass: .compact), UITraitCollection(verticalSizeClass: .regular)]
                return UITraitCollection(traitsFrom: traitCollections)
            } else {
                let traitCollections = [UITraitCollection(horizontalSizeClass: .unspecified), UITraitCollection(verticalSizeClass: .unspecified)]
                return UITraitCollection(traitsFrom: traitCollections)
            }
    }

}
<file_sep>//
//  DetailHeaderTableViewCell.swift
//  TheMovieDatabase
//
//  Created by <NAME> on 04/08/19.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import UIKit

class DetailHeaderTableViewCell: UITableViewCell {
    
    @IBOutlet private weak var posterImage: UIImageView!
    @IBOutlet private weak var movieTitle: UILabel!
    
    var movie: Movie! {
        didSet {
            let url = URL(string: Constants.API.URLS.ImageDownloadURL + (movie.backdrop_path ?? ""))
            posterImage.kf.setImage(with: url) { result in
                self.setNeedsLayout()
            }
            movieTitle.text = movie.title
        }
    }

    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
    }

}
<file_sep># Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'TheMovieDatabase' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for TheMovieDatabase
  pod 'Alamofire'
  pod 'Kingfisher'
  pod 'Toast-Swift'
  pod 'YoutubeDirectLinkExtractor'

  target 'TheMovieDatabaseTests' do
    inherit! :search_paths
    # Pods for testing
  end

end
<file_sep>//
//  ArrayExtensions.swift
//  TheMovieDatabase
//
//  Created by <NAME> on 03/08/19.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import UIKit

extension Array {
    //Create genre string from IDs
    func genre() -> String? {
        if self.count == 0 { return "" }
        let genreIDs = [28: "Action",
        12: "Adventure",
        16: "Animation",
        35: "Comedy",
        80: "Crime",
        99: "Documentary",
        18: "Drama",
        10751: "Family",
        14: "Fantasy",
        36: "History",
        27: "Horror",
        10402: "Music",
        9648: "Mystery",
        10749: "Romance",
        878: "Science Fiction",
        10770: "TV Movie",
        53: "Thriller",
        10752: "War",
        37: "Western"]
        var txt = ""
        for itm in (self as! [Genres]) {
            let value = genreIDs[itm.id!]
            txt += value!
            if itm.id! != (self.last! as! Genres).id! {
                txt += ", "
            }
        }
        return txt
    }
}
<file_sep>//
//  HomeDataTableViewCell.swift
//  TheMovieDatabase
//
//  Created by <NAME> on 04/08/19.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import UIKit

class HomeDataTableViewCell: UITableViewCell {
    
    @IBOutlet weak var titleTxt: UILabel!
    @IBOutlet weak var subTxt: UILabel!

    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
    }

}
<file_sep>//
//  HomeViewController.swift
//  TheMovieDatabase
//
//  Created by <NAME> on 03/08/19.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import UIKit
import Toast_Swift

class HomeViewController: UIViewController {
    
    private var currentIdx: Int = 1
    private var moviesList: MoviesBase!
    
    @IBOutlet private weak var collectionView: UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()
        getPopularMovies()
    }
    
    override func refreshView() {
        if AlamofireManager.isConnectedToInternet {
            getPopularMovies()
        }
    }
    
    //API call to get popular movies
    func getPopularMovies() {
        let  url = URL(string: Constants.API.URLS.PopularMoviesURL + "&page=\(currentIdx)")
        AlamofireManager.getRequest(url: url!) { [weak self] (data, error) in
            if let err = error {
                self?.view.makeToast(err)
                return
            }
            let jsonDecoder = JSONDecoder()
            if let model = try? jsonDecoder.decode(MoviesBase.self, from: data!) {
                if self?.moviesList == nil { self?.moviesList = model }
                else if (self?.moviesList.page)! < model.page! { self?.moviesList.update(data: model) }
                self?.collectionView.reloadData()
            }
        }
    }

}

extension HomeViewController: UICollectionViewDataSource, UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
         return moviesList?.results?.count ?? 0
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Movie", for: indexPath) as! PopularMovieCollectionViewCell
        cell.movie = moviesList.results![indexPath.item]
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if indexPath.item == (moviesList.results?.count ?? 0) - 1 {
            if currentIdx < (moviesList.total_pages ?? 0) {
                currentIdx += 1
                getPopularMovies()
            }
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let vc = MovieDetailViewController.create()
        vc.movieID = moviesList.results![indexPath.item].id
        navigationController?.pushViewController(vc, animated: true)
    }
    
}
<file_sep>//
//  AlamofireManager.swift
//  TheMovieDatabase
//
//  Created by <NAME> on 03/08/19.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import Alamofire

class AlamofireManager: NSObject {
    //Check connectivity
    class var isConnectedToInternet:Bool {
        return NetworkReachabilityManager()!.isReachable
    }
    //API Get Method calls
    //@url: url to fetch data
    //@completion: Return Data received from API response and error message
    static func getRequest(url: URL, completion: ((Data?, String?) -> ())?) {
        if !isConnectedToInternet { completion?(nil, "You are not connected to the internet."); return }
        Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default)
            .responseData { (response) in
                if let error = response.error {
                    completion?(nil, error.localizedDescription)
                    return
                }
                if let data = response.result.value {
                    completion?(data, nil)
                }
        }
    }
    
    static let shared = AlamofireManager()
    //Reachability Manager
    let reachabilityManager = Alamofire.NetworkReachabilityManager(host: "www.apple.com")
    
    //Observing changes in reachability
    func startNetworkReachabilityObserver() {
        reachabilityManager?.listener = { status in
            switch status {
            case .notReachable:
                break
            case .unknown, .reachable(.ethernetOrWiFi), .reachable(.wwan) :
                UIApplication.getTopViewController()?.refreshView()
                break
            }
        }
        reachabilityManager?.startListening()
    }
    
}
<file_sep>//
//  UIViewControllerExtension.swift
//  TheMovieDatabase
//
//  Created by <NAME> on 05/08/19.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import UIKit

extension UIViewController {
    @objc func refreshView() {
        //Overrides for reachability update
    }
}
<file_sep>//
//  MovieDetailViewController.swift
//  TheMovieDatabase
//
//  Created by <NAME> on 03/08/19.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import UIKit
import AVKit
import Toast_Swift
import YoutubeDirectLinkExtractor

class MovieDetailViewController: UIViewController {
    
    private var movie: Movie!
    var movieID: Int!
    
    @IBOutlet weak var tableView: UITableView!
    
    //Creating new instance of view controller
    static func create() -> MovieDetailViewController {
        let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MovieDetailViewController") as! MovieDetailViewController
        return vc
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        getMovieDetails()
    }
    
    override func refreshView() {
        if AlamofireManager.isConnectedToInternet {
            getMovieDetails()
        }
    }
    
    //API call to fetch movie details
    func getMovieDetails() {
        let  url = URL(string: Constants.API.URLS.MovieDetailURL + "\(movieID ?? 0)?api_key=" + Constants.API.APIKey + "&append_to_response=videos")
        AlamofireManager.getRequest(url: url!) { [weak self] (data, error) in
            if let err = error {
                self?.view.makeToast(err)
                return
            }
            let jsonDecoder = JSONDecoder()
            if let model = try? jsonDecoder.decode(Movie.self, from: data!) {
                self?.movie = model
                self?.tableView.reloadData()
            }
        }
    }
    
    //Play video
    //@str : youtube url
    func playVideo(str: String) {
        let url = URL(string: str)
        let player = AVPlayer(url: url!)
        let vc = AVPlayerViewController()
        vc.player = player
        if #available(iOS 11.0, *) {
            vc.entersFullScreenWhenPlaybackBegins = true
        } else {
            // Fallback on earlier versions
        }
        present(vc, animated: true) {
            vc.player?.play()
        }
    }
    
    //Action to watch trailer button
    @IBAction func watchTrailer() {
        if movie == nil {
            self.view.makeToast("Please reselect the movie")
            return
        }
        if !AlamofireManager.isConnectedToInternet {
            self.view.makeToast("You are not connected to the internet.")
            return
        }
        //Used YouTubeDirectLinkExtractor Library to fetch youtube url
        if let videos = movie.videos?.results, videos.count > 0, let key = videos[0].key {
            YoutubeDirectLinkExtractor().extractInfo(for: .id(key), success: { [weak self] (info) in
                self?.playVideo(str: info.highestQualityPlayableLink!)
            }) { (error) in
                DispatchQueue.main.async {
                    self.view.makeToast(error.localizedDescription)
                }
            }
        } else {
            self.view.makeToast("No video links available")
        }
    }
    
}

extension MovieDetailViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return movie == nil ? 0 : 4
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        switch indexPath.row {
        case 0:
            let cell = tableView.dequeueReusableCell(withIdentifier: "Movie") as! DetailHeaderTableViewCell
            cell.movie = movie
            return cell
        case 1:
            let cell = tableView.dequeueReusableCell(withIdentifier: "Data") as! HomeDataTableViewCell
            cell.titleTxt?.text = "Genres"
            cell.subTxt?.text = movie.genres?.genre()
            return cell
        case 2:
            let cell = tableView.dequeueReusableCell(withIdentifier: "Data") as! HomeDataTableViewCell
            cell.titleTxt?.text = "Date"
            cell.subTxt?.text = movie.release_date
            return cell
        case 3:
            let cell = tableView.dequeueReusableCell(withIdentifier: "Data") as! HomeDataTableViewCell
            cell.titleTxt?.text = "Overview"
            cell.subTxt?.text = movie.overview
            return cell
        default:
            return UITableViewCell()
        }
    }
    
}
<file_sep>//
//  Helper.swift
//  TheMovieDatabase
//
//  Created by <NAME> on 03/08/19.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import UIKit

class Helper: NSObject {
    
    
}
