Typically when you create mobile app there is a team/person that is responsible for design, animations and something that is called User Experience. They investigate what is the best navigation that fits given app, they care about good look of the app or even they prototype animations. So as a developer you can fall into trap that UX is not part of your job because it was planned by someone else. But there are some parts you can improve on your own while developing mobile app.
Networking is something that user can’t notice at first sight. But wait, really? It’s the other way round. You can have the most beautiful design and the best animations hovewer if you don’t create good networking experience the overall feeling of your app can be poor.
Networking Experience- it sounds good, but what do I mean?
Networking is a huge part of development. There are plenty of patterns how to create a clean networking architecture. On iOS we have very handy libraries like Alamofire or Moya that help us build faster communication between app and our API. It does not matter which pattern or library you decide to use — you need to remember few things. In case of networking, user experience focuses mainly on data response time. So let me give you few tips how to make loading data more friendly.
Keep user informed on what is going on.
Is it still loading? There is nothing more annoying than app that get stuck on some action or view entry without any information what is going on. If you are debugging an app while developing, you exactly now that data is loading or that an error occurred, even your tester at some point knows that it takes time to load that view. So if you know this, why users should not? It’s very simple to create a state you can use across your views and present informative message to the user that the app is loading, refreshing or failed to load data. So let me show you what I am using in my apps.
It’s a simple enum that I’m holding in my ViewModels (or Controllers depending on architecture). Every view that is based on data that comes from API needs to implement each state. In UITableView or UICollectionView it works very well with DZNEmptyDataSet framework which allows you to show a message with title, subtitle or image that there are no cells to show. It looks like this:
Let user perform loading once again.
Data loading can fail because of many reasons. It’s uncomfortable for users, so give them at least an option to retry without going out and entering the view again. Place some retry button in a place where user expects content that failed to load. Pull to refresh or refresh button is also important.
Foundation framework provides you great tools to cache responses from server. URLSession supports response caching, you can even set caching policy for each NSURLRequest, that’s nice (Please remember that your API needs to support Cache-Control headers). And for me, it’s mandatory. But there is something more that you can do.
What if you could use your cache while being offline?
I recently had the pleasure of working on app that was a simple client presenting some sets of objects fetched from api. These sets are shared between views. We decided that some of that data should be available offline, but there is no sense in creating a database for it because it can’t be modified while device is offline. So we decided to record responses from api and hold them as jsons on disk. As new data comes we, just simply replace whole response. But we can go a little bit further, if we already have data (maybe even not so new) we can show it to the user immediately and then wait for fresh API response. Since we are using RxSwift it was simple to achieve this without modifying code in views and controllers. What we actually did we wasinjecting cached data into the signal that is providing API responses.
In this section I will show you how to implement offline caching using Moya and RxSwift.
Moya is using a protocol called TargetType. We define an additional protocol CachableTarget and we are extending CachableTarget for each object that conforms to TargetType protocol to use its path as cacheKey.
Then we need to record every response from server that we are interested in. Moya allows us to create a Plugin which is next layer in our networking stack. So let’s create a plugin that will grab response and save it to disk. We are caching only endpoints that have cachingEnabled property set to true and we are not caching failure responses.
After that we are ready to use our cached responses in signals that our views are driven by. We create struct API that will hold default implementations of requests for each target. Then we are extending API to support our cache.
And this is how ls that our views are driven by. We create struct API that will hold default implementations of requests for each target. Then we are extending API to support our cache.
And this is how final call of some example API looks.
I understand that this article shows just a part of the implementation that’s why I created a simple example project you can explore on github.
This way of caching improves the speed how which users can go across your app without even knowing that data is loaded from internet. Of course you need reasonably choose which endpoints you want to cache and what needs to be loaded every time.
Please always remember that user experience is not only about design and fancy animations. The way you implement networking (and core functionalities) is also a big part of it.
If you want to know how to safely store JSON files in iOS app stay tuned because that’s the subject for my next article.