When starting the process of rebuilding our apps, we were confronted with a problem that most old, big apps have. The logic in our apps responsible for transforming raw data to something on the screen was completely contained in our ViewControllers and Views. This means that these classes were huge, hard to read and even harder to understand. This is a common issue with iOS apps that results mostly from the M(odel) (V)iew (C)ontroller design pattern that is often used.

MVVM

For our new app we decided on the MVVM (Model/View/ViewModel) design pattern as the core of our codebase. This design pattern is meant to remove the logic of an app from the View and ViewController. Instead a new entity is introduced called a ViewModel. This ViewModel describes the elements that should appear on the screen. You could consider it a code-representation of the UI. This ViewModel is then passed to a View. The View then only has to read the ViewModel and modify the UI as specified.

However, this approach still does not take into account one of the problems we had: Where should the main business logic of the app be located? The original Model should still be transformed into a ViewModel for the View to be read. We decided to tackle this problem by introducing yet another entity: a Mapper. a Mapper is a function or class that contains all the logic necessary to transform a Model into a ViewModel. These Mapper functions mostly take a Model as a parameter but they can also be made more flexible. For instance, you might need multiple kinds of Models to generate a single ViewModel or you might want to be able to specify what kind of ViewModel has to be created.

Testing

The core functionality of our app is getting data from an API and transforming this data into something the user understands. Because our business logic that transforms our data is now contained in a single Mapper class or Mapper function, testing this logic becomes extremely easy: we write unit tests that cover our Mapper classes. An added bonus is that our ViewModels are a very precise description of our UI state, which means we don’t have to write as many (slow and cumbersome) UI tests.

When we started rebuilding our apps, we decided to focus heavily on stability and testing. Our old app has quite a few crashes in it and we believe the only way to prevent such error-prone apps is to put a lot of emphasis on testing from the start. In Xcode 7 Apple gave developers the opportunity to easily write UI tests. These are tests that automatically run through the interface of your app to test specific screens or components.

Once we started writing these kinds of tests it became apparent that although the tools Apple gave us were very easy to use, they weren’t leading to nicely structured and easily readable tests. Take this simple 1-action test for example:

Swift

1

2

3

4

functestExample(){

letapp=XCUIApplication()

app.buttons[“Search”].tap()

}

Looks simple enough right? The application opens up and we tap a button with the text “Search”. This would work well for a single test that has to tap this button. However if there are a lot of tests that use this button this hardcoded “Search” string becomes a problem. What if we decide to rename this button? All the tests would fail and we would have to spend some time to rename all the individual references to the “Search” button. It sure would be nice if there was only one reference to this button that our tests could use.(more…)

So we’re building this awesome new app but after spending time revamping our result list we found out it just wasn’t performing well enough. We were using a UICollectionView but calculating the position of all the elements in each item in the collection was just too slow. We found out about AsyncDisplayKit (ASDK).

ASDK offers an abstraction over UIView and CALayer that allows you to perform the calculation of frames to background threads instead of the main thread. This dramatically speeds up the rendering of complex UI’s. After implementing ASDK our list is now super smooth and doesn’t lag at all.

Here are some things to take into consideration when implementing ASDK:

Actively maintained. The ASDK library is under very active development. It was originally built to power facebook’s Paper app and it seems mature enough to use in live projects.

Nodes, not Views. ASDK uses a concept called “Nodes”. Nodes are essentially UI elements and can be considered an abstraction of a View or a Layer. You use these nodes to define your UI and ASDK will asynchronously create views or layers to represent this UI.

LayoutSpec, not Auto Layout. Because ASDK uses Nodes to define UI elements, it doesn’t support things like Auto Layout or Constraints. Instead, ASDK uses something called a “LayoutSpec”. There are a number of different ways to layout nodes. To get an idea of the kind of layout you can define, have a look at the *LayoutSpec Class References.

Example node

Here’s an example of a very simple custom ASDK node. All this node does is display a label (called an “ASTextNode” within the bounds of the node. The LayoutSpec for this node also defines an inset of 10 pixels all around the text node.

Other resources

We have a pretty standard app. It’s a list of items and if you click one of these items, you go to a detail page. This kind of list / detail paradigms are used very often. For instance in mail apps, note apps etc. It is not difficult to build such an app, but it is difficult to build such an app well. Once you start making a more complex UI you often run into performance issues. We found ourselves in such a situation last week.

UICollectionView

The list of houses we implemented in the funda app is backed by a UICollectionView. A UICollectionView is a a very flexible way of building a list because the size and layout of the items in the list can be customised. This allows us to build a simple vertical list at first but morph it into a grid once we convert our iPhone-only app to an iPad format. Unfortunately this flexibility comes at a price. Having dynamically sized cells and rich content within these cells is very expensive. Every time a cell is configured with the content of a house we make new labels, download a new image and place all these elements at their correct location. Most of these actions are done on the main thread by default and thus slow down or halt the actual scrolling of the list. Additionally, we used Auto Layout to layout all the elements in the cell and this was certainly not helping our performance either.

Enter ASyncDisplayKit

While searching for a solution for this performance issue I ran across AsyncDisplayKit. This library made by facebook makes it possible to construct and layout views (they call it “nodes”) in a background thread. By doing all the heavy lifting in the background, the main thread of the app remains responsive and the list scrolls very smoothly.

So that’s where we are right now. I made an initial attempt to integrate ASDK in our current architecture but it proved to be quite tricky. Since our collection cells have a dynamic height there

Hi there. You might know me and I might now know you. But there’s a chance that if you’ve reached this page you are somewhat interested in iOS development, Swift development or both. Let me introduce myself first.

I’m Roel, a 30-something iOS developer from The Netherlands. I work at funda, the largest real estate website in our country. Together with about a 100 colleagues (half of those are developers) we develop and maintain a website containing most of the for-sale and for-rent properties in The Netherlands.

Besides a website, we also have iOS, Android and Windows Phone apps. We have a dedicated “mobile” team that maintains these apps and an API that supports it. This team is currently on a mission to rebuild our apps from the ground up with the latest technology, making use of the lessons we learned the last few years.

This blog will be a place for me to share some of the choices we make during this rebuild process and some of the technical challenges we face. These can range from simple snippets of code to posts on architecture and iOS development philosophy.