{"id":20740067,"url":"https://github.com/rcasanovan/imovies","last_synced_at":"2026-06-08T14:32:20.118Z","repository":{"id":243133248,"uuid":"147230609","full_name":"rcasanovan/iMovies","owner":"rcasanovan","description":"A simple app to search movies using  the themoviedb.org api","archived":false,"fork":false,"pushed_at":"2018-12-21T19:13:07.000Z","size":5190,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-11T11:54:20.259Z","etag":null,"topics":["cocoapods","protocol","realm","swift","viper","viper-architecture","viper-pattern-architecture"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rcasanovan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-03T16:42:28.000Z","updated_at":"2019-01-26T19:09:27.000Z","dependencies_parsed_at":"2024-06-06T22:40:59.761Z","dependency_job_id":"a9a2c9aa-963f-4a0a-87b8-517378f92d1b","html_url":"https://github.com/rcasanovan/iMovies","commit_stats":null,"previous_names":["rcasanovan/imovies"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rcasanovan/iMovies","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rcasanovan%2FiMovies","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rcasanovan%2FiMovies/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rcasanovan%2FiMovies/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rcasanovan%2FiMovies/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rcasanovan","download_url":"https://codeload.github.com/rcasanovan/iMovies/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rcasanovan%2FiMovies/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34067348,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cocoapods","protocol","realm","swift","viper","viper-architecture","viper-pattern-architecture"],"created_at":"2024-11-17T06:27:19.622Z","updated_at":"2026-06-08T14:32:20.099Z","avatar_url":"https://github.com/rcasanovan.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# iMovies\n\nThis is a project to create a simple app to search movies\n\n## 🚨 Important note 🚨\n\nThis project is using cocoapods but a gitignore file is there so the third-party libraries are not part of the repo. Please be sure to run the **pod install** command before running the project.\n\nIf you have any doubt about cocoapods you can check the reference [here](https://cocoapods.org).\n\nTo run the project you just need to add your API key in EndPoint swift file\n\n```swift\nstatic let apiKey: String = \"ADD YOUR API KEY HERE\"\n```\n\n## Project Architecture \n![alt tag](https://github.com/rcasanovan/iMovies/blob/master/Images/projectArchitecture.jpeg?raw=true)\n\nReferences:\n* [Viper architecture](https://www.objc.io/issues/13-architecture/viper/)\n* [Viper for iOS](https://medium.com/@smalam119/viper-design-pattern-for-ios-application-development-7a9703902af6)\n\n## How did I implement VIPER?\n\nBasically I have a protocol file for each scene in the app. This file defines the interaction between each layer as following:\n\n* View - Presenter: protocols to notify changes and to inject information to the UI.\n* Presenter - Interactor: protocols to request / receive information to / from the interator.\n* Presenter - Router: protocol to define the transitions between scenes (I skiped this protocols for the demo because I have only a scene there).\n\nWhith this protocols file is really easy to know how each layer notify / request / information to the other ones so we don't have any other way to communicate all the layers.\n\nAnother important point is because I'm using protocols it's really easy to define mocks views / presenters / interactors / routers for testing.\n\n```swift\n// View / Presenter\nprotocol IMSearchViewInjection : class {\n    func loadMovies(_ movies: [IMMovieViewModel], fromBeginning: Bool, totalResults: UInt)\n    func loadSuggestions(_ suggestions: [IMSuggestionViewModel])\n    func showProgress(_ show: Bool)\n    func showMessageWith(title: String, message: String, actionTitle: String)\n}\n\nprotocol IMSearchPresenterDelegate : class {\n    func searchMovie(_ movie: String)\n    func loadNextPage()\n    func getSuggestions()\n    func suggestionSelectedAt(index: NSInteger)\n}\n\n// Presenter / Interactor\n\ntypealias IMGetMoviesCompletionBlock = (Result\u003cIMMoviesResponse?\u003e) -\u003e Void\ntypealias IMGetSuggestionsCompletionBlock = ([IMSuggestionViewModel]) -\u003e Void\n\nprotocol IMSearchInteractorDelegate : class {\n    func shouldGetMovies() -\u003e Bool\n    func clearSearch()\n    func getMoviesWith(movie: String, completion: @escaping IMGetMoviesCompletionBlock)\n    func saveSearch(_ search: String)\n    func getAllSuggestions(completion: @escaping IMGetSuggestionsCompletionBlock)\n    func updateResultResponse(_ response: IMMoviesResponse?)\n}\n```\n\n## Use cases\n\n1. As​ ​a​ ​user​ ​at​ ​the​ ​search​ ​screen, when​ ​I​ ​enter​ ​a​ ​name​ ​of​ ​a​ ​movie​ ​(e.g.​ ​\"Batman\",​ ​\"Rocky\")​ ​in​ ​the​ ​search​ ​box​ ​and tap​ ​on​ ​\"search​ ​button\" then​ ​I​ ​should​ ​see​ ​a​ ​new​ ​list​ ​view​ ​with​ ​the​ ​following​ ​rows:\n* Movie​ ​Poster\n* Movie​ ​name\n* Release​ ​date\n* Full​ ​Movie​ ​Overview\n\n2. As​ ​a​ ​user​ ​at​ ​the​ ​Movie​ ​List​ ​Screen, when​ ​I​ ​scroll​ ​to​ ​the​ ​bottom​ ​of​ ​list then​ ​next​ ​page​ ​should​ ​load​ ​if​ ​available\n\n3. As​ ​a​ ​user​ ​at​ ​the​ ​search​ ​screen, when​ ​I​ ​enter​ ​a​ ​name​ ​of​ ​a​ ​movie​ ​that​ ​doesn’t​ ​exist​ ​in​ ​the​ ​search​ ​box​ ​and​ ​tap​ ​on \"search​ ​button\", then,​ ​an​ ​alert​ ​box​ ​should​ ​appear​ ​and​ ​display​ ​an​ ​error​ ​message.\n\nAll​ ​types​ ​of​ ​error​ ​should​ ​be​ ​handled\n\n4. As​ ​a​ ​user​ ​at​ ​the​ ​search​ ​screen, when​ ​I​ ​tap​ ​and​ ​focus​ ​into​ ​the​ ​search​ ​box then​ ​an​ ​auto​ ​suggest​ ​list​ ​view​ ​will​ ​display​ ​below​ ​the​ ​search​ ​box​ ​showing​ my​ ​last 10​ ​successful​ ​queries​ ​(exclude​ ​suggestions​ ​that​ ​return​ ​errors)\n\nSuggestions​ ​should​ ​be​ ​persisted.\n\n5. As​ ​a​ ​user​ ​at​ ​the​ ​search​ ​screen​ ​with​ ​the​ ​auto​ ​suggest​ ​list​ ​view​ ​shown, when​ ​I​ ​select​ ​a​ ​suggestion then​ ​the​ ​search​ ​results​ ​of​ ​the​ ​suggestion​ ​will​ ​be​ ​shown.\n\n## First at all. Where is the data came from?\n\nI'm using the api from **themoviedb.org** (you can check the api documentation [here](https://www.themoviedb.org/documentation/api)).\n\nYou just need to create an account to have access to the api. Once you do it you'll able to get information for movies in a JSON format like this:\n\n```json\n{\n  \"page\": 1,\n  \"total_results\": 102,\n  \"total_pages\": 6,\n  \"results\": [\n    {\n      \"vote_count\": 3107,\n      \"id\": 268,\n      \"video\": false,\n      \"vote_average\": 7.1,\n      \"title\": \"Batman\",\n      \"popularity\": 15.817,\n      \"poster_path\": \"/kBf3g9crrADGMc2AMAMlLBgSm2h.jpg\",\n      \"original_language\": \"en\",\n      \"original_title\": \"Batman\",\n      \"genre_ids\": [\n        14,\n        28\n      ],\n      \"backdrop_path\": \"/2blmxp2pr4BhwQr74AdCfwgfMOb.jpg\",\n      \"adult\": false,\n      \"overview\": \"The Dark Knight of Gotham City begins his war on crime with his first major enemy being the clownishly homicidal Joker, who has seized control of Gotham's underworld.\",\n      \"release_date\": \"1989-06-23\"\n    }\n    ]\n}\n```\n\nThis is an example of the api call:\nhttp://api.themoviedb.org/3/search/movie?api_key=2696829a81b1b5827d515ff121700838\u0026query=batman\u0026page=1\n\nIn order to get the images you can use this url:\nhttp://image.tmdb.org/t/p/w92/2DtPSyODKWXluIRV7PVru0SSzja.jpg​\n\n(Poster size: size:​ ​w92,​ ​w185,​ ​w500, w780)\n\n## Data models\n\n### Network data models\n\nThese includes the following models:\n\n```swift\nstruct IMMoviesResponse: Decodable {\n    let page: UInt\n    let total_results: UInt\n    let total_pages: UInt\n    let results: [IMSingleMovieResponse]\n}\n\nstruct IMSingleMovieResponse: Decodable {\n    let id: UInt\n    let vote_average: Float\n    let poster_path: String?\n    let title: String\n    let overview: String?\n    let release_date: String\n}\n```\n\nI'm using a Swift Standard Library decodable functionality in order to manage a type that can decode itself from an external representation (I really ❤ this from Swift).\n\n**Why some properties are optionals?**\n\nWell I discovered that some movies doesn't have a poster path or an overview (it's strange I know 🤷‍♂) so it's better to manage these fields are optionals.\n\n**Are more properties there??**\n\nObviously the response has more properties for each movie. I decided to use only these ones.\n\nReference: [Apple documentation](https://developer.apple.com/documentation/swift/swift_standard_library/encoding_decoding_and_serialization)\n\n\n### Suggestions data model\n\nThis model is used for the movies suggestions (last 10​ ​successful​ ​queries​ - exclude​ ​suggestions​ ​that​ ​return​ ​errors)\n\n```swift\nclass IMSearchSuggestion: Object {\n    @objc dynamic var suggestionId: String?\n    @objc dynamic var suggestion: String = \"\"\n    @objc dynamic var timestamp: TimeInterval = NSDate().timeIntervalSince1970\n    \n    override class func primaryKey() -\u003e String? {\n        return \"suggestionId\"\n    }\n}\n```\n\nAs I'm using Realm for this it's important to define a class to manage each model in the database. In this case we only have one model (IMSearchSuggestion)\n\nReference: [Realm](https://realm.io/docs/swift/latest)\n\n### Managers\n\nI think using managers is a good idea but be careful!. Please don't create managers as if the world were going to end tomorrow.\n\nI'm using only 3 here:\n\n#### IMSearchSuggestionsManager\n\nUsed to manage all the suggestions (last 10​ ​successful​ ​queries​)\n\n#### IMMovieImageManager\n\nUsed to manage the images (create the urls to retrieve the images)\n\n\n#### IMNetworkManager\n\nUsed to manage the reachability\n\n## How it looks like?\n\n### Search results\n![alt tag](https://github.com/rcasanovan/iMovies/blob/master/Images/01.png?raw=true)\n![alt tag](https://github.com/rcasanovan/iMovies/blob/master/Images/07.png?raw=true)\n\n### Recent searchs\n![alt tag](https://github.com/rcasanovan/iMovies/blob/master/Images/03.png?raw=true)\n\n### Handling errors and states\n![alt tag](https://github.com/rcasanovan/iMovies/blob/master/Images/04.png?raw=true)\n![alt tag](https://github.com/rcasanovan/iMovies/blob/master/Images/05.png?raw=true)\n![alt tag](https://github.com/rcasanovan/iMovies/blob/master/Images/06.png?raw=true)\n\n## What's left in the demo?\n\n* Realm migration process: It would be nice to add a process to migrate the realm database to a new model (just in case you need to add a new field into the database)\n* Localizable files: This demo doesn't include the localizable files to translate the app to different languages.\n\n\n## Programming languages \u0026\u0026 Development tools\n\n* Swift 4.1\n* Xcode 9.4.1\n* [Cocoapods](https://cocoapods.org) 1.5.3\n* Minimun iOS version: 9.0\n\n## Third-Party Libraries\n\n* [Haneke](https://github.com/Haneke/Haneke) (1.0): A lightweight zero-config image cache for iOS\n* [EDStarRating](https://github.com/erndev/EDStarRating) (1.1): A configurable star rating control for OSX and iOS, similar to those found in iTunes and the App Store.\n* [RealmSwift](https://github.com/realm/realm-cocoa) (3.7.6): A mobile database that runs directly inside phones, tablets or wearables\n* [SVProgressHUD](https://github.com/SVProgressHUD/SVProgressHUD) (2.2.5): A clean and lightweight progress HUD for your iOS and tvOS app.\n* [ReachabilitySwift](https://github.com/ashleymills/Reachability.swift) (4.2.1): Replacement for Apple's Reachability re-written in Swift with callbacks.\n\n## Support \u0026\u0026 contact\n\n### Email\n\nYou can contact me using my email: ricardo.casanova@outlook.com\n\n### Twitter\n\nFollow me [@rcasanovan](http://twitter.com/rcasanovan) on twitter.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frcasanovan%2Fimovies","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frcasanovan%2Fimovies","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frcasanovan%2Fimovies/lists"}