iOS Trakt Client - Keep track of your favorite TV shows and movies on your iPhone. (Under development)

Overview

CouchTracker

Keep track of your favorite movies and tv shows on your iPhone

CircleCI codecov Quality Gate Status Twitter FOSSA Status Tuist Badge

Setup for development

You will need

  • Xcode 11.2.1
  • Swift 5.1.2

Run the following commands

  • git clone [email protected]:pietrocaselani/CouchTracker.git

  • cd CouchTracker && sh setup.sh

  • tuist generate

  • bundle exec pod install

  • open CouchTracker.xcworkspace

  • This project uses the Trakt API, TMDB API and TVDB API

  • To run the app, please create a file at CouchTrackerApp/Utils/Secrets.swift with yours API keys like this

enum Secrets {
  enum Trakt {
    static let clientId = "API_KEY"
    static let clientSecret = "API_KEY"
    static let redirectURL = "API_KEY"
  }

  enum TMDB {
    static let apiKey = "API_KEY"
  }

  enum TVDB {
    static let apiKey = "API_KEY"
  }

  enum Bugsnag {
    static let apiKey = "API_KEY"
  }
}

Project structure

The project is split into a few frameworks

  • CouchTrackerCore: It's a macOS framework that has all the code that is not UI (Views, ViewControllers). This framework shouldn't have dependencies that only work on iOS.

  • CouchTrackerCore-iOS: It's the iOS version of CouchTrackerCore. You won't find any files here, all files are on CouchTrackerCore.

  • CouchTrackerCoreTests: The test bundle for CouchTrackerCore. Tests run directly on the macOS, there is no need to use the iOS simulator to run those tests.

  • CouchTrackerPersistence: Here you will find entities and data sources and other things related to the persistence layer of the app. I moved this layer to another framework with the idea of changing the persistence tool in the future. Right now Realm is being used.

  • CouchTrackerApp: Here you will find all the code related to UI and dependent on UIKit, so things like Views, ViewControllers. It's possible to import this framework into CouchTrackerPlayground.playground to see a preview of screens since the use of storyboards is being avoided.

  • CouchTracker: It's the app itself. You will only find the AppDelegate here.

License

FOSSA Status

Comments
  • Episode aired yesterday doesn't appear as

    Episode aired yesterday doesn't appear as "1 remaining"

    Maybe, it's just a date/timezone configuration.

    supergirl_bug

    Here is the API response

    for watched show progress:

    {
        "aired": 43,
        "completed": 42,
        "last_watched_at": "2017-05-27T23:00:15.000Z",
        "seasons": [
            {
                "number": 1,
                "aired": 20,
                "completed": 20,
                "episodes": [
                    {
                        "number": 1,
                        "completed": true,
                        "last_watched_at": "2015-10-27T18:52:45.000Z"
                    },
                    {
                        "number": 2,
                        "completed": true,
                        "last_watched_at": "2015-11-06T03:56:12.000Z"
                    },
                    {
                        "number": 3,
                        "completed": true,
                        "last_watched_at": "2015-11-12T02:09:24.000Z"
                    },
                    {
                        "number": 4,
                        "completed": true,
                        "last_watched_at": "2015-11-19T16:18:02.000Z"
                    },
                    {
                        "number": 5,
                        "completed": true,
                        "last_watched_at": "2015-11-26T03:39:29.000Z"
                    },
                    {
                        "number": 6,
                        "completed": true,
                        "last_watched_at": "2015-12-02T16:42:56.000Z"
                    },
                    {
                        "number": 7,
                        "completed": true,
                        "last_watched_at": "2015-12-10T01:27:04.000Z"
                    },
                    {
                        "number": 8,
                        "completed": true,
                        "last_watched_at": "2015-12-19T17:41:51.000Z"
                    },
                    {
                        "number": 9,
                        "completed": true,
                        "last_watched_at": "2016-01-27T23:00:49.000Z"
                    },
                    {
                        "number": 10,
                        "completed": true,
                        "last_watched_at": "2016-01-27T23:00:55.000Z"
                    },
                    {
                        "number": 11,
                        "completed": true,
                        "last_watched_at": "2016-01-28T22:36:21.000Z"
                    },
                    {
                        "number": 12,
                        "completed": true,
                        "last_watched_at": "2016-02-06T21:13:49.000Z"
                    },
                    {
                        "number": 13,
                        "completed": true,
                        "last_watched_at": "2016-02-13T19:53:58.000Z"
                    },
                    {
                        "number": 14,
                        "completed": true,
                        "last_watched_at": "2016-02-28T18:46:29.000Z"
                    },
                    {
                        "number": 15,
                        "completed": true,
                        "last_watched_at": "2016-03-14T03:56:57.000Z"
                    },
                    {
                        "number": 16,
                        "completed": true,
                        "last_watched_at": "2016-03-26T03:12:17.000Z"
                    },
                    {
                        "number": 17,
                        "completed": true,
                        "last_watched_at": "2016-03-26T20:48:01.000Z"
                    },
                    {
                        "number": 18,
                        "completed": true,
                        "last_watched_at": "2016-05-14T01:49:01.000Z"
                    },
                    {
                        "number": 19,
                        "completed": true,
                        "last_watched_at": "2016-05-20T01:36:35.000Z"
                    },
                    {
                        "number": 20,
                        "completed": true,
                        "last_watched_at": "2016-05-21T19:38:53.000Z"
                    }
                ]
            },
            {
                "number": 2,
                "aired": 22,
                "completed": 22,
                "episodes": [
                    {
                        "number": 1,
                        "completed": true,
                        "last_watched_at": "2016-10-30T22:43:40.000Z"
                    },
                    {
                        "number": 2,
                        "completed": true,
                        "last_watched_at": "2016-11-01T00:59:15.000Z"
                    },
                    {
                        "number": 3,
                        "completed": true,
                        "last_watched_at": "2016-11-02T01:48:20.000Z"
                    },
                    {
                        "number": 4,
                        "completed": true,
                        "last_watched_at": "2016-11-06T17:49:08.000Z"
                    },
                    {
                        "number": 5,
                        "completed": true,
                        "last_watched_at": "2016-11-15T17:04:41.000Z"
                    },
                    {
                        "number": 6,
                        "completed": true,
                        "last_watched_at": "2016-11-16T23:46:13.000Z"
                    },
                    {
                        "number": 7,
                        "completed": true,
                        "last_watched_at": "2016-11-27T16:03:17.000Z"
                    },
                    {
                        "number": 8,
                        "completed": true,
                        "last_watched_at": "2016-11-29T01:00:00.000Z"
                    },
                    {
                        "number": 9,
                        "completed": true,
                        "last_watched_at": "2017-01-29T02:05:20.000Z"
                    },
                    {
                        "number": 10,
                        "completed": true,
                        "last_watched_at": "2017-02-12T03:33:25.000Z"
                    },
                    {
                        "number": 11,
                        "completed": true,
                        "last_watched_at": "2017-02-12T20:47:46.000Z"
                    },
                    {
                        "number": 12,
                        "completed": true,
                        "last_watched_at": "2017-03-10T00:33:24.000Z"
                    },
                    {
                        "number": 13,
                        "completed": true,
                        "last_watched_at": "2017-03-11T04:46:38.000Z"
                    },
                    {
                        "number": 14,
                        "completed": true,
                        "last_watched_at": "2017-03-11T19:46:20.000Z"
                    },
                    {
                        "number": 15,
                        "completed": true,
                        "last_watched_at": "2017-03-11T21:03:01.000Z"
                    },
                    {
                        "number": 16,
                        "completed": true,
                        "last_watched_at": "2017-03-28T02:12:24.000Z"
                    },
                    {
                        "number": 17,
                        "completed": true,
                        "last_watched_at": "2017-04-03T02:32:17.000Z"
                    },
                    {
                        "number": 18,
                        "completed": true,
                        "last_watched_at": "2017-05-01T22:52:27.000Z"
                    },
                    {
                        "number": 19,
                        "completed": true,
                        "last_watched_at": "2017-05-12T01:38:08.000Z"
                    },
                    {
                        "number": 20,
                        "completed": true,
                        "last_watched_at": "2017-05-13T00:30:06.000Z"
                    },
                    {
                        "number": 21,
                        "completed": true,
                        "last_watched_at": "2017-05-21T00:33:15.000Z"
                    },
                    {
                        "number": 22,
                        "completed": true,
                        "last_watched_at": "2017-05-27T23:00:15.000Z"
                    }
                ]
            },
            {
                "number": 3,
                "aired": 1,
                "completed": 0,
                "episodes": [
                    {
                        "number": 1,
                        "completed": false,
                        "last_watched_at": null
                    }
                ]
            }
        ],
        "hidden_seasons": [],
        "next_episode": {
            "season": 3,
            "number": 1,
            "title": "Girl of Steel",
            "ids": {
                "trakt": 2605191,
                "tvdb": 6124745,
                "imdb": "tt6403442",
                "tmdb": 1337377,
                "tvrage": 0
            }
        },
        "last_episode": {
            "season": 2,
            "number": 22,
            "title": "Nevertheless, She Persisted",
            "ids": {
                "trakt": 2492115,
                "tvdb": 6043967,
                "imdb": "tt5827214",
                "tmdb": 1311089,
                "tvrage": 0
            }
        }
    }
    

    and for episode summary:

    {
        "season": 3,
        "number": 1,
        "title": "Girl of Steel",
        "ids": {
            "trakt": 2605191,
            "tvdb": 6124745,
            "imdb": "tt6403442",
            "tmdb": 1337377,
            "tvrage": 0
        },
        "number_abs": 43,
        "overview": "Kara deals with the loss of Mon-El by focusing all her energy on being Supergirl and the mysterious new threat against National City. Alex confesses a secret to Maggie about their impending nuptials. A citizen of National City has a mysterious connection to Kara, and Lena makes a bold move.",
        "rating": 8.08503,
        "votes": 294,
        "first_aired": "2017-10-10T00:00:00.000Z",
        "updated_at": "2017-10-10T12:09:22.000Z",
        "available_translations": [
            "bg",
            "bs",
            "cs",
            "da",
            "de",
            "el",
            "en",
            "es",
            "fa",
            "fr",
            "he",
            "hu",
            "id",
            "it",
            "ko",
            "lt",
            "nl",
            "pl",
            "pt",
            "ro",
            "ru",
            "sv",
            "th",
            "tr",
            "uk",
            "zh"
        ],
        "runtime": 45
    }
    
    bug Shows | Watched Backlog 
    opened by ghost 3
  • ListMovies networking

    ListMovies networking

    Since we did not decide exactly what movies we want to show, I decide to show trending movies, ok?

    Modules are now separated by folders. All networking is done using moya. Requests are cached on memory level using Carlos (the lib ๐Ÿ˜).

    Yes, I know, the PR is too big, but this is what happen when we commit all the dependencies! @arctouch-brunopinheiro lets change the CI to Jenkins?

    opened by ghost 3
  • Configuring Tuist

    Configuring Tuist

    Tuist is a tool to manage Xcode projects very similar to SPM, but works for iOS projects!

    Since this project has more than a few frameworks, I think Tuist will help avoid conflicts on the CouchTracker.xcodeproj/project.pbxproj file and manage the dependencies between the frameworks.

    Now the project configuration lives on the file Project.swift, located at root of the project. It's already a big file, but I will try to figure out a better way to keep things organized.

    Right now, there are still some things that need to be done

    • [x] Add the playground to the project
    • [x] Add missing Build Phases (Swiftlint, swiftformat and maybe more)
    • [x] Check if the "Autobump" still works
    • [ ] Create schemes to group all tests
    • [x] Add Debug flag again
    • [ ] Try to fix the copy of duplicated resources (Skipping duplicate build file in Copy Bundle Resources build phase: /Users/distiller/project/CouchTrackerCore/Resources/en.lproj/CouchTrackerCore.strings (in target 'CouchTrackerCore'))

    About the duplicated strings resources -> https://github.com/tuist/tuist/issues/361

    opened by pietrocaselani 2
  • Configure project, dependencies and app/tests architecture

    Configure project, dependencies and app/tests architecture

    Sorry for the big PR. Please ignore all files on Pods folder and anything related to xcode project and workspace.

    I made a contract just to show how I am thinking to implement the app modules, its a mix on MVP and Viper. I also implement some tests on Interactor and Presenter.

    opened by ghost 2
  • Add Sync framework

    Add Sync framework

    The idea here is to create a separated framework to handle the synchronization between the APIs (Trakt, TMDB, TVDB) and the persistence layer of the app.

    opened by pietrocaselani 1
  • Enum plus plus

    Enum plus plus

    This PR adds Sourcery/Stencil templates to give Swift enum more powers! Now we have the protocol EnumClosures, that when implemented in an enum will create closures for each enum case.

    Using the following enum as example:

    enum ViewState<T> {
        case start(data: T)
        case loading
        case completed(count: Int, message: String)
    }
    

    you can implement EnumClosures, by doing extension ViewState: EnumClosures, and Sourcery will generate the following code:

    extension ViewState {
        internal func onStart(_ fn: (T) -> Void) {
            guard case let .start(data) = self else { return }
            fn(data)
        }
        internal func onLoading(_ fn: () -> Void) {
            guard case .loading = self else { return }
            fn()
        }
        internal func onCompleted(_ fn: (Int, String) -> Void) {
            guard case let .completed(count, message) = self else { return }
            fn(count, message)
        }
    }
    

    There is also the EnumProperties that will generate accessors for each case. So using the same enum, ViewState, you can extend ViewState to use EnumProperties by doing extension ViewState: EnumProperties which will generate the following code:

    extension ViewState {
        internal var isStart: Bool {
            guard case .start = self else { return false }
            return true
        }
        internal var isLoading: Bool {
            guard case .loading = self else { return false }
            return true
        }
        internal var isCompleted: Bool {
            guard case .completed = self else { return false }
            return true
        }
    
        internal var start: T? {
            guard case let .start(data) = self else { return nil }
            return (data)
        }
        internal var completed: (count: Int, message: String)? {
            guard case let .completed(count, message) = self else { return nil }
            return (count, message)
        }
    }
    

    And by doing that, we are now able to write such code:

    func render(viewState: ViewState<User>) {
        viewState.onLoading {
            // show loading indicator
        }
    
        viewState.onStart { user in
            // show the user
        }
    
        viewState.onCompleted { (count, message) in
            // show the cout and message
        }
    
        // Or
    
        view.loading = viewState.isLoading
        view.user = viewState.start
        view.countAndMessage = viewState.completed
    }
    
    opened by pietrocaselani 1
  • Add license scan report and status

    Add license scan report and status

    Your FOSSA integration was successful! Attached in this PR is a badge and license report to track scan status in your README.

    Below are docs for integrating FOSSA license checks into your CI:

    opened by fossabot 1
  • Bug on Trakt login

    Bug on Trakt login

    Sometimes, after a successful login, the screen to authorize the app never shows e after that, future attempts to log in results on a blank screen in the WebView.

    opened by pietrocaselani 0
Releases(0.0.1-53)
Owner
Pietro Caselani
Pietro Caselani
Forecast App is an ios application built on top of omdb movie api for batman lovers to see their favorite batman movies

Catbon-Movie-App Forecast App is an ios application built on top of omdb movie api for batman lovers to see their favorite batman movies, users can al

GuruKing 0 Dec 6, 2021
Swift iOS app to track when your favorite tv show airs next ๐Ÿ“†

Upcoming TV Swift iOS app to track when your favorite tv show airs next ?? Requirements iOS 13 TMdB API key Credits TMdB Contact github.com/dkhamsing

null 7 Sep 10, 2022
Mahmoud-Abdelwahab 5 Nov 23, 2022
With EconoApp you can keep track of economic information such as GDP, GDP per capita

EconoApp With EconoApp you can keep track of economic information such as GDP, GDP per capita, inflation and more. As simple as picking the country an

Vinicius Vieira 4 Feb 9, 2022
Reading List is an iOS app for iPhone and iPad which helps users track and catalog the books they read

Reading List Reading List is an iOS app for iPhone and iPad which helps users track and catalog the books they read. Reading List v2 As of version 2.0

Andrew Bennet 281 Jan 15, 2022
Swift iPhone and iPad Client for Polls API, using Hyperdrive API client

Polls Client This is a Swift iPhone and iPad client for the Polls API - a simple application allowing users to view polls and vote in them. Polls is a

Apiary 34 Jul 15, 2022
Simple iOS app to keep all your Warranties tagged!

Warrenty-Tag A simple iOS app to keep all your Warranties tagged! Description Ever find it difficult to get hold of all your warranties and bills? or

BISHAL KUMAR 1 Dec 21, 2022
๐ŸŽฎ Favorite your games filter and see the upcoming games and ! Swift + Combine = ๐Ÿ’œ Hacktoberfest ๐ŸŽƒ ๐Ÿ‘พ

โœจ Revill is App to list games and search best games โœจ Design in Swift UI + Combine โœจ The idea is develop this app in Hacktober Fest Expected To Do Des

Vinicius Mangueira 21 Dec 17, 2022
A Currency Converter & Calculator IOS application to check, convert and calculate to popular currencies to your favorite ones.

A Currency Converter & Calculator IOS application to check, convert and calculate to popular currencies to your favorite ones.

CCC 64 Jan 1, 2023
A basic twitter app to view, compose, favorite, and retweet tweets

A basic twitter app to view, compose, favorite, and retweet tweets

Kaya Yeboah 0 Nov 4, 2021
A Weather information can be searched by a city name and added into a favorite list

A Weather information can be searched by a city name and added into a favorite list. The list can be refreshed with pull to refresh. Move func exists.

Mehmet Can Seyhan 0 Dec 13, 2021
Fast iOS app to browse and search movies, tv, actors, credits

Fast iOS app to browse and search movies, tv, actors, credits

null 14 Dec 30, 2022
MovieZine - An iOS app for all about latest movies using TDMB api and swiftUI

MovieZine MovieZine ia an iOS app for "all about latest movies" using TDMB api a

Aditya Tyagi 3 Apr 23, 2022
MovieAppSwiftUI - The Application is using TMDB API and Server API to reduce user searching movies resources time with search engine

MovieAppSwiftUI The Application is using TMDB API and Server API to reduce user searching movies resources time with search engine.This Application is

JackSon_tm.m 5 Oct 29, 2022
iOS App by which the user can display a list of characters from the Harry Potter Movies.

iOS App by which the user can display a list of characters from the Harry Potter Movies. They can display a list of all characters, students, staff or view characters by house.

Matthew Hollyhead 0 Nov 7, 2021
Movies is a collection of a few UI/UX ideas that came up whilst developing an iOS app

Movies Introduction: Movies is a collection of a few UI/UX ideas that came up whilst developing an iOS app called Wattmo You'll find tableviews, detai

Kevin Mindeguia 861 Nov 19, 2022
๐Ÿ‘ถ๐Ÿป An iOS app to help you track sleep, feedings and diaper change for your baby

?? Maby Maby is an iOS/watchOS open-source app to help you keep track of your baby's sleep, feedings, diaper changes and more to avoid the constant qu

Fran Gonzรกlez 2 Aug 25, 2022
SwiftUI movies list using The Movie Database (TMDB) API

MyMoviesList About MyMovieList is an application that uses The Movie Database (TMDB) API and is built with SwiftUI. It demo of some SwiftUI (& Combine

Arnaud 1 Dec 5, 2021
Avo Keepr enables you to track your avocados so they don't end up uneaten in the garbage

Avo Keepr Open source repo for the Avo Keepr app. Avo Keepr on the App Store Screenshots: App Store Listing: Avo Keepr enables you to track your avoca

Dave Jacobsen 5 Dec 11, 2022