/TwitchClient

Small app fetching top games from Twitch

Primary LanguageSwift

TwitchClient

This repo fetches top games from Twitch and for a given game it fetches the top streams.

Requirements

You need a Client-Id (API token) to run this project. Read here about how to retrieve said Client-Id. Paste this Client-Id in the Environments.plist file.

Clean Code

This project uses SwiftLint enforce Swift style and conventions. Open the swiftlint.yml file to see the rules I have setup. One of the most strict rules is that no file can be longer than 100 lines of code. If any file exceed this limit the code will not compile.

Architectural choices

I use some industry standard pods such as Alamofire, ObjectMapper (which I prefer over SwiftyJson thanks to syntax and the new ImmutableMappable protocol).

I think writing protocols for all parts of the app doing stuff, such as API and HTTPClients make the code more modular. A good way to enforce writing protcols is to use dependency injection. I like to use Swinject for dependency injection (DI). DI gets ruined by Storyboards since we cannot use them together, if we were to use Storyboards we would need to use property injection instead, as done in my other SwiftIntro project. And since I do not like Storyboards or Xibs to begin with I tend to not use Interface Builder at all. I dislike Storyboards since I do not get human readable git history of the GUI (tens of thousands of lines of XML is at least hard for me to read...). We would probably have to setup localizable strings for our labels created in Storyboards anyway, because it is a mess to use the localization inside the Storyboards because then we do not have localization in one place (Localizable.strings). It is not easy to create reuseable components in Storyboards.

Instead I make use of Cartography for constructing UI.

For async methods I have started using RxSwift lately. But I did not like the syntax:

loadingView.show()x
gamesService.getGames(Pagination(limit: 5))
.subscribe(onNext: { games in
    self.setupTableSource(with: games)
}, onError: { error in
    log.error("Failed to fetch games, error: \(error)")
}) { 
    self.loadingView.hide()
}.addDisposableTo(bag)

So I have written an extension on RxSwift that enables this syntax instead:

loadingView.show()
bag += gamesService.getGames(Pagination(limit: 5))
.success { games in
    self.setupTableSource(with: games)
}.failure { error in
    log.error("Failed to fetch games, error: \(error)")
}.always {
    self.loadingView.hide()
}

I use amazing SwiftGen for generating enums of my Localizable.strings strings.

Non standard design choices

  1. Renamed Info.plist -> TwitchClient-Info.plist to more easily find it using CMD + SHIFT + O command since the project is using Pods and we have many Info.plist files.
  2. Renamed Supporting Files -> SupportingFiles since it is preferably to not use spaces in folders or file names
  3. Moved AppDelegate to SupportingFiles

Improvements

I added a third ViewController that got pushed when selecting a stream for a game. I thought it would be nice to be able to watch the stream. I used the url inside the channel object of the stream and loaded that in a request inside a WKWebView. But the video would not start. Even though I added allow arbitrary loads and background mode video (in Capabilities). I also tried using AVPlayerLayer and an AVPlayer to stream it, but that did not work. So I thought maybe there is another endpoint I could use. So I found this blog post mentioning access token requests. I tried it out but the loading of the playlist timed out. So I let it go, but it would have made the app more fun.