Creating a Marvel iOS App from scratch.. Tools, pods, tricks of the trade and more .. Part 1

Today i'm starting a series of posts that will show how to create an iOS app from scratch, using many different pods and tools that will make your life easier. This project will be divided in several posts that cover many important topics. You can usually find this information elsewhere but it is usually splitted in different unrelated tutorials, my approach here is to convey all within a single project, you can find the source code here. There will be different tags from different points in time, covering first part, second and so on.

Roadmap ..

  • How to create an network layer using Moya + RxSwift.
  • How to parse the response using a Json -> Model mapping framework
  • How to avoid massive view controllers, using external datasources and delegates. This will keep TableView | CollectionView boilerplate code where it should.
  • How to handle storyboards and view controllers using Enums with SwiftGen, making everything much safer.
  • A better way to handle tableView and collectionView cells using reusable
  • How to handle image downloads using Kingfisher
  • Handle specifics security needs of Marvel's api using CryptoSwift.
  • Using cocoapods-keys to keep sensitive data and keys away from your repository.

Later posts will address:

  • How to test your app's layers( Network, models, viewControllers, datasources, etc).
  • How to generate a coverage report.
  • Automate your development pipeline with Fastlane.
  • How to configure continuous integration for Github projects with Travis
  • Creating PR rules for your repository using Danger
  • Skecth for developers! How we can create "bootstraped" designs.

The app ..

For this project I've chosen to build a Marvel app, using marvel's api. This first part is all gathering under a tag called v0.1. You just need to clone the repo and switch to tag v0.1.

Disclaimer: **The layout will be enhanced a lot in a later post, when we talk about sketch and design for developers, for now it is very simple as you can see bellow.

First things first .. The Podfile

So let me first layout here what pods we're going to use in this first part.

There is something special about this Podfile it is using as mentioned above cocoapods-keys plugin, this will allow us to set in our keychain the values of our keys on the first time we run pod install. After this configuration step, we can inject in our code the keys without ever need to add them to our repository. Not only that but it will also allow you guys to use your own Marvel's api keys, which can be generated here. How nice is that ? Thanks orta ..

plugin 'cocoapods-keys', {
:project => "Marvel",
:keys => [
"MarvelApiKey",
"MarvelPrivateKey"
]}

After api keys creation and project setup lets move on to next part.

Network Layer with Moya

Moya is an amazing pod that can manage all the boilerplate and complexity of having to create your own network abstraction layer from project to project. Altough it is not mandatory to use it with RxSwift, for the sake of this tutorial, i'm going to combine the two.

That's it, super easy way to define your api settings, endpoint's paths, parameters, methods and more. The stored property sampleData will be use later on a post about tests.
 Also worth mention is the MarvelAPIConfig struct and authParameters method, both are needed to conform with marvel api's mandatory needs. The authParameters is merged with user created params using a nice pod called Dollar, that have lots of useful methods.

After creating the api settings we still need to create a Moya provider to use the api, this can be done anywhere, although is highly recommended to create another abstraction to handle this kind of logic.

There is a lot going on here, i've created two generic methods(one for array, other for object) to abstract how to interact with the api and handle the response. Later this methods can be used to make specific api calls like:

func characters(query: String? = nil, completion: @escaping ([Character]?) -> Void) {        
requestArray(.characters(query),
type: Character.self, completion: completion)
}

Disclamer ** I know that i'm not using even 1% of RxSwift, i am only using it to unwrap the api response(*This api has 2 layers of wrapper objects, that i dont really care about, for this tutorial purpose at least), parse it into models and handle success and error flow. Many would argue, and they are right in doing so, that i should return an Observable list that can, later on, be combined with other api calls, filtered and so on, instead of handle this inside the api manager and providing a completion function.

The thing is, there is no right or wrong here, everything is a trade off, the project's needs do not ask for such implementation, so let's keep things simple.

Now we can make calls to the api, without worrying about any implementation detail, simple as that:

let apiManager = MarvelAPIManager()
apiManager.characters(query: query) { characters in }

This explanations are not intended to be a complete how-to guide, they are much more like a guideline, an example. It is mandatory for anyone following along to clone the repo and experiment with it.

External Datasources + Reusable ..

Next, we are going to take a look at datasources and how we can extract a lot of code from our view controllers. Let's create a protocol to define a contract, so we can repeat this pattern throughout the project.

Now we can easily implement any datasource, in a standard manner, all we need is to conform with the protocol.

You guys can notice methods like register(cellType: ) and dequeReusableCell they all come from an amazing pod called reusable, that can really improve the way we handle TableView and CollectionView cells in our code. Actually it is pretty easy to implement similar behavior using protocols, all we need to do is:

Of course the reusable pod do a few more things as well, but you got the picture. Just for the sake of sanity, I do think that we need to use the pod instead of implement ourselves, don’t reinvent the wheel!

Downloading images ..

More often than not we can find ourselves re-creating the same code again and again, one of those repetitive patterns has to do with image download and how to load it inside an imageView. In the example bellow you can notice a simple interface called download(image:), which takes a string param containing the image url path.

This interface is placed under an extension of UIImageView, a simple solution, that can help to centralize behavior. There are of course many other ways, better ones in fact, to handle this situation, i am just trying to show a creative way to use swift extensions.

SwiftGen and the Storyboards enum ..

If you are like me, and kind of dislike segues(*to say the least), you may find yourself repeting the same code recipe every time you need to navigate between one view controller to another. Instantiate the storyboard, recover the controller using a hardcoded string and so on. Things like that tend to get very mess and easy to produce unexpected behaviors and errors.

However, it does not have to be like that for sure. Tools like swiftGen can help us to create a Storyboard enum and enable us the same behavior, with much less or no pain at all. All we need to do is add a run build phase to our project with the following code.

$PODS_ROOT/SwiftGen/bin/swiftgen storyboards -t swift3 $SOURCE_ROOT --output $SOURCE_ROOT/Marvel/Resources/Storyboard.swift --sceneEnumName Storyboard

After that we only need to add that generated Storyboard.swift file to our project and handle the problem like a boss.

Making it short… #not! That ship has sailed …

This is the end of the first part of this series of posts. I am still experimenting with this format, so it is possible that this content may change a little, maybe going in the screencast direction.

It is absolutely important for those following this steps to clone the project and play with it, i can't convey all that information in a single post otherwise would be huge, and unreadable.

As always any thoughts, doubts or feedback are more than welcome. =)

Ps: If you like this post, share it on twitter, recommend it on medium, or both =). This really helps me to reach more people. Thanks a lot ..

Updated: ** You can check the second part of this tutorial here.