Skip to content

Commit

Permalink
Here we have a good version of the CouchTrackerSync framework
Browse files Browse the repository at this point in the history
* Only one entry point, the function `setupSyncModule(trakt:)`
* To ensure the statement above, the tests run in DEBUG mode without importing the framework using `@testable`
* I am using snapshot testing because the JSON is big, there is a lot of data about episodes, seasons and the tv show
* I will probrably use the snapshot testing library for screenshot tests... excited for this
  • Loading branch information
Pietro Caselani committed Jan 8, 2020
1 parent 324aace commit 955d050
Show file tree
Hide file tree
Showing 15 changed files with 11,740 additions and 2,986 deletions.
33 changes: 33 additions & 0 deletions CouchTrackerSync/APIFunctions.swift
@@ -0,0 +1,33 @@
import TraktSwift
import RxSwift

func syncWatchedShows(extended: [Extended]) -> Single<[BaseShow]> {
return trakt.sync.rx.request(.watched(type: .shows, extended: extended)).map([BaseShow].self)
}

func watchedProgress(options: WatchedProgressOptions, showIds: ShowIds) -> Single<BaseShow> {
return trakt.shows.rx.request(
.watchedProgress(
showId: showIds.realId,
hidden: options.countSpecials,
specials: options.countSpecials,
countSpecials: options.countSpecials
)
).map(BaseShow.self)
}

func seasonsForShow(showIds: ShowIds, extended: [Extended]) -> Single<[Season]> {
return trakt.seasons.rx
.request(.summary(showId: showIds.realId, extended: extended))
.map([Season].self)
}

func genresForMoviesAndShows() -> Single<Set<Genre>> {
let genresForType: (GenreType) -> Single<[Genre]> = { type in
trakt.genres.rx.request(.list(type)).map([Genre].self)
}

return Single.zip(genresForType(.movies), genresForType(.shows)) { (movies, shows) in
Set(movies + shows)
}
}
3 changes: 3 additions & 0 deletions CouchTrackerSync/CouchTrackerSync-Main.swift
@@ -0,0 +1,3 @@
import TraktSwift

//public func setupSyncModule(createTrakt: () -> TraktSwift) -> Sync
72 changes: 35 additions & 37 deletions CouchTrackerSync/CouchTrackerSync.swift
Expand Up @@ -5,63 +5,52 @@ import Moya
typealias TraktShow = TraktSwift.Show
typealias DomainShow = CouchTrackerSync.Show

struct ShowDataForSyncing: DataStruct {
let progressShow: BaseShow
let show: TraktShow
let seasons: [Season]

var showIds: ShowIds {
return show.ids
}
}

public enum SyncError: Error, EnumPoetry {
public enum SyncError: Error {
case showIsNil
case missingEpisodes(showIds: ShowIds, baseSeason: BaseSeason, season: Season)
}

struct WatchedProgressOptions: Hashable {
let hidden: Bool
let specials: Bool
let countSpecials: Bool
public struct WatchedProgressOptions: Hashable {
public let hidden: Bool
public let specials: Bool
public let countSpecials: Bool

init(hidden: Bool = false, specials: Bool = false, countSpecials: Bool = false) {
public init(hidden: Bool = false, specials: Bool = false, countSpecials: Bool = false) {
self.hidden = hidden
self.specials = specials
self.countSpecials = countSpecials
}
}

struct SyncOptions: Hashable {
let watchedProgress: WatchedProgressOptions
public struct SyncOptions: Hashable {
public static let defaultOptions = SyncOptions()

public let watchedProgress: WatchedProgressOptions

init(watchedProgress: WatchedProgressOptions = WatchedProgressOptions()) {
public init(watchedProgress: WatchedProgressOptions = WatchedProgressOptions()) {
self.watchedProgress = watchedProgress
}
}

func startSync(options: SyncOptions) -> Observable<WatchedShow> {
return syncMain(options)
}

private func syncMain(_ options: SyncOptions) -> Observable<WatchedShow> {
let genresObservable = Current.genres()
func startSync(_ options: SyncOptions = SyncOptions()) -> Observable<WatchedShow> {
let genresObservable = Current.genres().asObservable()

let showAndSeasonsObservable = Current.syncWatchedShows([.full, .noSeasons])
.asObservable()
.flatMap { Observable.from($0) }
.flatMap { watchedProgress(options: options.watchedProgress, baseShow: $0) }
.flatMap { seasonsForShow(showData: $0) }

return Observable.zip(showAndSeasonsObservable, genresObservable).map(createWatchedShow(showData:allGenres:))
}

private func watchedProgress(options: WatchedProgressOptions, baseShow: BaseShow) -> Observable<ShowDataForSyncing> {
guard let show = baseShow.show else { return Observable.error(SyncError.showIsNil) }
private func watchedProgress(options: WatchedProgressOptions, baseShow: BaseShow) -> Single<ShowDataForSyncing> {
guard let show = baseShow.show else { return Single.error(SyncError.showIsNil) }
return Current.watchedProgress(options, show.ids)
.map { ShowDataForSyncing(progressShow: $0, show: show, seasons: []) }
}

private func seasonsForShow(showData: ShowDataForSyncing) -> Observable<ShowDataForSyncing> {
private func seasonsForShow(showData: ShowDataForSyncing) -> Single<ShowDataForSyncing> {
return Current.seasonsForShow(showData.showIds, [.full, .episodes])
.map { ShowDataForSyncing(progressShow: showData.progressShow, show: showData.show, seasons: $0) }
}
Expand All @@ -76,10 +65,10 @@ private func createWatchedShow(showData: ShowDataForSyncing, allGenres: Set<Genr
let showGenres = genresFromSlugs(allGenres: allGenres, slugs: showData.show.genres ?? [])

let watchedSeasons = try createWatchedSeasons(showIds: showData.showIds,
baseSeasons: showData.progressShow.seasons ?? [],
seasons: showData.seasons)
baseSeasons: showData.progressShow.seasons ?? [],
seasons: showData.seasons)

let show = mapTraktShowToDomainShow(traktShow: showData.show, genres: showGenres, seasons: watchedSeasons)
let show = mapTraktShowToDomainShow(traktShow: showData.show, genres: showGenres, seasons: watchedSeasons, progressShow: showData.progressShow)

return createWatchedShow(show: show, progressShow: showData.progressShow)
}
Expand Down Expand Up @@ -129,20 +118,29 @@ private func createWatchedEpisode(showIds: ShowIds, baseEpisode: BaseEpisode, ep
return WatchedEpisode(episode: episode, lastWatched: baseEpisode.lastWatchedAt)
}

private func mapTraktShowToDomainShow(traktShow: TraktShow, genres: [Genre], seasons: [WatchedSeason]) -> DomainShow {
private func mapTraktShowToDomainShow(traktShow: TraktShow, genres: [Genre], seasons: [WatchedSeason], progressShow: BaseShow) -> DomainShow {
let nextEpisode = progressShow.nextEpisode.flatMap { findEpisodeOnSeasons(seasons: seasons, episode: $0) }
let lastEpisode = progressShow.lastEpisode.flatMap { findEpisodeOnSeasons(seasons: seasons, episode: $0) }
let completed = progressShow.completed

let watched = DomainShow.Watched(completed: completed, lastEpisode: lastEpisode)

return DomainShow(ids: traktShow.ids,
title: traktShow.title,
overview: traktShow.overview,
network: traktShow.network,
genres: genres,
status: traktShow.status,
firstAired: traktShow.firstAired,
seasons: seasons)
seasons: seasons,
aired: progressShow.aired,
nextEpisode: nextEpisode,
watched: watched)
}

private func createWatchedShow(show: DomainShow, progressShow: BaseShow) -> WatchedShow {
let nextEpisode = progressShow.nextEpisode.flatMap { findEpisodeOnShow(show: show, episode: $0) }
let lastEpisode = progressShow.lastEpisode.flatMap { findEpisodeOnShow(show: show, episode: $0) }
let nextEpisode = progressShow.nextEpisode.flatMap { findEpisodeOnSeasons(seasons: show.seasons, episode: $0) }
let lastEpisode = progressShow.lastEpisode.flatMap { findEpisodeOnSeasons(seasons: show.seasons, episode: $0) }

return WatchedShow(show: show,
aired: progressShow.aired,
Expand All @@ -152,7 +150,7 @@ private func createWatchedShow(show: DomainShow, progressShow: BaseShow) -> Watc
lastWatched: progressShow.lastWatchedAt)
}

private func findEpisodeOnShow(show: DomainShow, episode: TraktSwift.Episode) -> WatchedEpisode? {
let season = show.seasons.first { $0.number == episode.season }
private func findEpisodeOnSeasons(seasons: [WatchedSeason], episode: TraktSwift.Episode) -> WatchedEpisode? {
let season = seasons.first { $0.number == episode.season }
return season?.episodes.first { $0.episode.number == episode.number }
}
20 changes: 18 additions & 2 deletions CouchTrackerSync/Models/Show.swift
Expand Up @@ -9,9 +9,22 @@ public struct Show: Hashable, Codable {
public let status: Status?
public let firstAired: Date?
public let seasons: [WatchedSeason]
public let aired: Int?
public let nextEpisode: WatchedEpisode?
public let watched: Watched

public init(ids: ShowIds, title: String?, overview: String?, network: String?,
genres: [Genre], status: Status?, firstAired: Date?, seasons: [WatchedSeason]) {
public struct Watched: Hashable, Codable {
public let completed: Int?
public let lastEpisode: WatchedEpisode?

public init(completed: Int?, lastEpisode: WatchedEpisode?) {
self.completed = completed
self.lastEpisode = lastEpisode
}
}

public init(ids: ShowIds, title: String?, overview: String?, network: String?, genres: [Genre], status: Status?,
firstAired: Date?, seasons: [WatchedSeason], aired: Int?, nextEpisode: WatchedEpisode?, watched: Watched) {
self.ids = ids
self.title = title
self.overview = overview
Expand All @@ -20,5 +33,8 @@ public struct Show: Hashable, Codable {
self.status = status
self.firstAired = firstAired
self.seasons = seasons
self.aired = aired
self.nextEpisode = nextEpisode
self.watched = watched
}
}
11 changes: 11 additions & 0 deletions CouchTrackerSync/ShowDataForSyncing.swift
@@ -0,0 +1,11 @@
import TraktSwift

struct ShowDataForSyncing: DataStruct {
let progressShow: BaseShow
let show: TraktSwift.Show
let seasons: [Season]

var showIds: ShowIds {
return show.ids
}
}
72 changes: 15 additions & 57 deletions CouchTrackerSync/SyncEnvironment.swift
@@ -1,70 +1,28 @@
import TraktSwift
import RxSwift

private let trakt = Trakt(builder: TraktBuilder {
$0.clientId = Secrets.Trakt.clientId
$0.clientSecret = Secrets.Trakt.clientSecret
$0.redirectURL = Secrets.Trakt.redirectURL
$0.callbackQueue = DispatchQueue(label: "NetworkQueue", qos: .default)
})

struct APIEnviront {
public struct SyncEnvironment {
public static let live = SyncEnvironment()

public var syncWatchedShows: ([Extended]) -> Single<[BaseShow]> = syncWatchedShows(extended:)
public var watchedProgress: (WatchedProgressOptions, ShowIds) -> Single<BaseShow> = watchedProgress(options:showIds:)
public var seasonsForShow: (ShowIds, [Extended]) -> Single<[Season]> = seasonsForShow(showIds:extended:)
public var genres: () -> Single<Set<Genre>> = genresForMoviesAndShows
}

struct DatabaseEnv {
}
var trakt: Trakt = { badTrakt! }()

struct SyncEnvironment {
var syncWatchedShows: ([Extended]) -> Observable<[BaseShow]>
var watchedProgress: (WatchedProgressOptions, ShowIds) -> Observable<BaseShow>
var seasonsForShow: (ShowIds, [Extended]) -> Observable<[Season]>
var genres: () -> Observable<Set<Genre>>
}
var badTrakt: Trakt?

extension SyncEnvironment {
static let live = SyncEnvironment(
syncWatchedShows: syncWatchedShows(extended:),
watchedProgress: watchedProgress(options:showIds:),
seasonsForShow: seasonsForShow(showIds:extended:),
genres: genresForMoviesAndShows
)
public func setupSyncModule(trakt: Trakt) -> (SyncOptions) -> Observable<WatchedShow> {
badTrakt = trakt
return { options in
startSync(options)
}
}

#if DEBUG
var Current = SyncEnvironment.live
public var Current = SyncEnvironment.live
#else
let Current = SyncEnvironment.live
public let Current = SyncEnvironment.live
#endif

private func syncWatchedShows(extended: [Extended]) -> Observable<[BaseShow]> {
return trakt.sync.rx.request(.watched(type: .shows, extended: extended)).map([BaseShow].self).asObservable()
}

private func watchedProgress(options: WatchedProgressOptions, showIds: ShowIds) -> Observable<BaseShow> {
return trakt.shows.rx.request(
.watchedProgress(
showId: showIds.realId,
hidden: options.countSpecials,
specials: options.countSpecials,
countSpecials: options.countSpecials
)
).map(BaseShow.self).asObservable()
}

private func seasonsForShow(showIds: ShowIds, extended: [Extended]) -> Observable<[Season]> {
return trakt.seasons.rx
.request(.summary(showId: showIds.realId, extended: extended))
.map([Season].self)
.asObservable()
}

private func genresForMoviesAndShows() -> Observable<Set<Genre>> {
let genresForType: (GenreType) -> Single<[Genre]> = { type in
trakt.genres.rx.request(.list(type)).map([Genre].self)
}

return Single.zip(genresForType(.movies), genresForType(.shows)) { (movies, shows) in
Set(movies + shows)
}.asObservable()
}
15 changes: 0 additions & 15 deletions CouchTrackerSync/Trakt/TraktProvider.swift

This file was deleted.

0 comments on commit 955d050

Please sign in to comment.