A Hungry Reflection on “How to Isolate Client-Server Interaction Logic in iOS Applications”

Since winter is coming, food thoughts begin to rise into a delicious fervor. Do I like food? Oh, yes! Yes, I do. Yet, reflections on the How to Isolate Client-Server Interaction Logic in iOS Applications need to happen. So, what’re you gonna do, right?

We’ll set this blog post on fire with food and iOS architecture geekiness. So now, hear me out. This blog post combines the desire for food and reflections about the iOS article into a tasty combination. Even if you don’t absolutely love this approach, there’s always this funny two minute video:

Since I am a skinny Finney and get full quickly, no food is mentioned at the south end of this blog post as opposed to up here in the north.

iOS Architecture Menu

Taking what the article offers and my own background, here are some typical things you’ll often see on the iOS architecture menu whether in formal object form, or via Protocol Oriented Programming mixins / traits, or just thrown in there spaghetti code style:

  • View Controllers – Ideally kept from becoming massive cookie monsters with too much code in them
  • Presenters, Model Views, or Presentation Models. In general, they have presentation logic separate from the Model objects and views.
  • Services – Work with different shared parts of the app, put things together, and interact with the backend server through Client code
  • Managers – Code that is in charge of app wide resource management similar to an Alamofire SessionManager or a file manager.
  • Translators – Convert incoming or outgoing yummy information to/from the backend servers
  • Server Error side dishes – Often in the form of enumerations, these are translated from raw server errors into something tasty for the rest of the code to chew on.
  • Model objects – Although not a horrible hodgepodge salad, we’ll lump together both Domain Objects and data Model structures into the word Model.
  • “Client” code – Code at the system boundary of the architecture used to talk with the backend server. It’s the last bit of non-library code before data leaves or enters the iPhone.

So the goodies are View Controllers, Presenters, Services, Managers, Translators, Server Errors, Models, and Client code. Since the word Client is overused, let’s expand on that.

Client

The article refers to the Client as:

Sends requests to backend servers and receives responses

Tastily specified in a simple yet sweet way. If outlined, the quoted goals of the Client from the “Bridging the Gap Between the Application and Backend Server” section are:

  • intermediary between the application and the backend server
  • know nothing about the data models and their structures
  • be responsible for invoking specific URLs with provided parameters
  • returning incoming JSON data parsed as JSON objects

That’s quite a mouthful! If we digest that a bit, we see their overall point. Near the oven door, the Client code is at the system boundary of the architecture. It doesn’t reference the data models, so it’s free from those code dependencies. Lip smacking good!

Visual Noise In The Client Example Code

Although I understand why the author is not using an example which depends on a library like Alamofire, the visual noise is rough when looking at the WebClient code. Specifically, the load method is the issue. With four parameters and one of them being a closure, it leaves a bad taste in my mouth:

I had to reread it a few times to visually parse it apart. I prefer the CRUD & Authorization approach used by Alamofire where method and path are captured as part of the request. Uncle Bob Martin had a good rule of thumb guide about the number of parameters. All that said, I understand why he coded it that way.

With three parameters, our code looks similar to the following:

ResponseClosure would actually be a type alias such as DictionaryResponseClosure, ArrayResponseClosure, or (Whatever)ResponseClosure depending on what the server responds with. It should be noted that our success and failure parameters are two of the three parameters. In his load method, the success and failure are both together in a completion block. To compare apples to apples, we have request and success/failure. Compare that to the load method’s request-path, method, params, and success/failure.

Applying the Client Approach To My World

This article makes me think. I love it like chocolate. One thing I saw which initially got me excited was that the ServiceError is created at the Client level as opposed to a higher level. In his WebClient.load method example, he covers the ServiceError cases of noInternetConnection, a custom error, and an other kind of error. That was exciting because I thought we could potentially simplify our own code by eliminating a level of code. That idea burst into flames like stinky burnt popcorn.

At first, I thought the code I’m working on handled the errors at a higher level than the Client level. However, his WebClient code is what our code conceptually started with. Over time, we realized we had to create a layer higher to handle translating HTTP status codes into the appropriate kind of enumeration Error case specific to the server domain we’re hitting as well as handle other things. We call that layer of objects the Gateway level.

I say server domain because we used to hit multiple servers, but now we don’t. Since the idea of the Gateway has changed from server to server domains, it has gotten confusing. Keeping track of what server domain behind the API is handling a particular request is not straightforward. With a minor exception, the server functionality is hidden behind one API now. Yet, we have to keep the Gateway objects as part of the architecture unless we find homes for all of the following:

  • Translation of HTTP status codes into error enumeration cases
  • Transforming our JSON response dictionaries into Model objects
  • Response caching

Finding Homes For Gateway Object Code

I think it’s possible to find homes for the code that is in the Gateway layer now. It would be great to get rid of that layer if possible. There’s a ton of pass through code in it. Following some approaches of the article, the following changes to our architecture approach would happen at a minimum:

  • HTTP status code transformation logic could happen in Error extensions similar to what he did in the article with ServiceError and its extension that contains the init(json: JSON).
  • Transformation of JSON dictionaries could happen in the constructor (init) methods of the Model objects.
  • Caching could potentially be handled a level higher than the gateway. Further investigation is needed for that.

Beyond the Networking Module

Since we touched on parts of the world I am working on, let’s go over what my world looks like in general. Although there is a food fight of architecture styles in the app I am currently having fun with, the general architecture design involves the following:

  • View Controllers – We consider View Controllers to be part of the View in the Model View ViewModel sense.
  • Presenters – There’s debate as to whether we’re set up more for a Model-view-presenter (MVP) architecture, versus Model View ViewModel (MVVM) architecture, versus a Presentation Model architecture. This kind of discussion has gone on for over a decade. Presenters are where our display logic lives.
  • Service layer – Interacts with the rest of the system such as with Managers, Gateways, and account information objects. The Service layer uses the Gateways layer.
  • Managers – as described above in the “iOS Architecture Menu” section
  • Gateways – as described above in the “Applying the Client Approach To My World” section
  • Translators translating inbound data – Used by the Gateway layer, the Translators are coded as an extension to Dictionary. They create the Model objects.
  • Translators translating outbound objects – This responsibility falls on the Request objects and are used by the Gateway
  • Request objects – Define which HTTP method to use, translate objects or raw data into parameters, and do other request specific set up. As mentioned, they are modeled after the Alamofire CRUD & Authorization example.
  • Model objects – as described above in the “iOS Architecture Menu” section. Sometimes Model objects have toDictionary methods to help out the Request objects.

On top of that, there is Protocol-Oriented Programming related code in there as well. Not all pieces are involved for every feature. OK. Now we’re cooking!

Network Request Maker

In addition to the Gateway code, we also have a lower level object called the Network Request Maker (NRM). The NRM contains the code that is common across Gateway code items. The NRM interacts with the SessionManager, ensures authentication information is added, and casts the Alamofire results into things like [String: Any]. Borrowing a term from Aspect-Oriented programming, the NRM also handles other cross-cutting concerns like logging.

A Great Start

So, do I like the article How to Isolate Client-Server Interaction Logic in iOS Applications? Yes, I do. Looking at the article is a great start. I am grateful to the awesome person who shared this with me where I work at CARFAX. However, it’s not the entire meal. As one would expect, you’ll have to take the ideas presented and adapt them to your own world. What’re gonna do? What’re gonna do?

Pretty candle balls sparkling in the snow

How Big Is The Ball of Wax For Quality Assurance?

At CARFAX, we have awesome Quality Assurance (QA) support for the CARFAX iOS app and other apps. In order to do an awesome job, QA needs adequate time to do adequate regression testing before the next release of an app. The question is how much time is needed?

I like to think about this in terms of balls of wax. Amongst other things, the total effort of a project includes both an amount of coding plus a certain amount of testing. Therefore, the coding-ball-of-wax plus the testing-ball-of-wax equals the whole ball-of-wax.

In order to give QA a feel for how long it will take to regression test at the end of a project, they need some data. One idea that I’ve heard a few times is to use Story Points. What Are Story Points?

As I put it once, Story Points are how much brain-power plus actual coding is required to make the change. Do Story Points help give QA a feel for how much functionality change is being introduced? Not always.

A Story Point size is reflective of how much effort a developer has to put into making the change. It’s not reflective of how much testing is needed for that specifically changed feature.

How can we give QA the data they need in order for them to size the amount of QA work? If one feeds into the process, a “Feature Change Amount“ that will give them the information they need. The Feature Change Amount could be sized the same way as Story Points are sized. As opposed to Story Points however, a Feature Change Amount directly reflects something useful by QA so they know how much QAing is needed.

How does your QA get a feel for how long it takes to QA a project before it’s released for your customers to use? Feel free to share it with me on Twitter!

Pretty candle balls sparkling in the snow

CARROT iPhone App Video Marketing Done Right

The maker of the CARROT TODO app does user experience and marketing right in so many ways. To keep this post short, let’s just focus on one bit of video marketing titled CARROT Launch Trailer. The video is fun enough that I even saved it to watch again!

The video starts with some exciting music and a strong message similar to this: “the todo list with a personality.” It’s a good clear message which shows how it distinguishes itself and what the app does.

With the blue circle pulsing, the video moves on to the playful and humorous statements of “Let’s play a game. We’ll call it ‘Don’t Suck at Life.'” It shows some functionality of the app and then moves on to the rules of the game. Get stuff done or CARROT will get mad!

As someone who has used the app for years, I guarantee she (CARROT) will get mad. CARROT is often referred to as the user’s “new mistress.” Here’s the video:

One thing the video doesn’t show you is the funny and sometimes creepy (in a humorous way) push notifications that the app receives. I highly encourage you to turn the notifications on. Although the humor is often dark, it is fantastically funny. Check out the app for yourself!

Communities in Nonsocial Mobile Apps

Nonsocial apps with a social community inside of them. It seems to make sense since you already have a captive audience. However, does it always make sense? Let’s first dive into mobile apps that come with communities.

Besides the obvious social mobile apps, there are multiple apps with communities aka groups inside of them. These apps are not primarily social apps, but they attempt to support a community. Since humans are social beings, it can be a nice feature which can make the app more appealing and thus used more often.

Some Community Supporting Apps:

  • Argus – It’s a highly social activity tracker app that has a groups section. There’s more on Argus further down in this article. iPhone and Android apps exist.
  • Coursera has a “Discussions” area in the iPhone app. There’s an Android app too. Discussions are not available until a course starts. So, one cannot peek inside right away and explore it.
  • Lose It! – Weight Loss Program and Calorie Counter – Has a “Social” section with many groups in it. The group activity in it is kind of strong. Strongest are the groups which are promoted as “Featured Groups”. iPhone app and Android app exist.
  • Not quite a discussions area, but Udemy has a Q & A section which has questions directed at the instructor in a particular instructor. This provides longterm value with credibility in the information since the answers come from the instructor. However, there is no sense of community. They offer an iPhone app and an Android app.
  • It’s worth noting that I have seen apps drop support for groups. That can sometimes be the smartest move.

More on Argus

The target audience of the app focuses on people who like to be social while tracking health, fitness, meditation, sleep and other trackable activities. Even though the semi-new groups section is pretty dead, there’s a novel tie in from a group into the tracked activities of group members which I find interesting.

It’s interesting in how it provides a kind of newsfeed style of tracked activities. You can tap on a button in the group and see a combined newsfeed from the group members. Compared to the messages button, this has much more activity. Unfortunately, most of the activities have nothing to do with the particular group you are in since they are not filtered. At a glance, it’s also a slightly confusing user experience.

Also, the downside of the groups section is that it’s slow to load and has an annoying bug which makes the iPhone app crash when you click on a link. As a side note, I happen to run a meditation group in Argus that has 25 members.

All that said, it’s worth a look. The groups has potential if a few things are tweaked, promoted, and fixed.

Externally Support the Community?

Should one just tie into an existing social app such as Facebook to support a community? It’s a fair question. Facebook has a groups feature which can be successful. The most successful Facebook group I have been a part of is the Octalysis Explorers. It has over 2000 members. Like any group, a lot of effort goes into keeping the community engaged. Fortunately for the Octalysis Explorers, Yu-kai Chou is a master at the art and science of engagement. He has a handful of awesome and loyal people who support the effort.

The people supporting the Octalysis Explorers group are continuously driving people to the Facebook group through multiple channels. Even in its own “Join the Movement” section, the group is highlighted in Yu-kai Chou’s great book titled Actionable Gamification – Beyond Points, Badges, and Leaderboards. There are also multiple administrators for the group. Also, there is always at least one person actively interacting with every post. A successful community relies on that kind of wonderful support from its administrators and its members.

Summary

Even though the implementation and user experience is pretty rough, it seems like the app with the most group activity without overt support is “Lose It!” However, that’s not saying much. The app with the most potential from a technology point of view may be Argus. However, Argus is already a highly social app so its group feature doesn’t add much.

If one can dedicate people to supporting a community on Facebook, that might be your best bet. If you go that route, you will need to continuously drive people to the group through your existing email marketing, verbal announcements in events or podcasts, and every other chance you get. Communities require care and feeding.

No matter how you support a community experience, it can provide an advantage over other solutions who don’t provide such. If one cannot dedicate the resources to the community experience or if the community experience doesn’t ever pay off, my advice is to drop it and refocus on other things.

iOS Color War! Appearance Proxy vs UIButton Title Text Color

Now here’s a weird issue that appeared in iOS 10, I found myself saying one day. I was seeing an iOS button’s title change color as a I navigated from one scene to the next. It got worse. When I navigated back, the button’s title color stayed the wrong color. It was the same color as all the labels in the iOS app. What’s going on?

We dug in deeper and discovered something. There was a nasty appearance proxy color issue where the appearance proxy would change the color of the UIButton‘s title text color even though the code was explicitly setting the color. In fact, the appearance proxy would set it to be the same color as it has configured for all UILabel objects. The configuration code for labels was like this:

UILabel.appearance().textColor = labelTextColor()

Since a UIButton contains a UILabel, that might make sense. How does one fix this situation?

The answer to fixing it was discovered after thinking about what was read in this Stack Overflow post titled Appearance Proxy overridden when resetting text:

iOS applies appearance changes when a view enters a window, it doesn’t change the appearance of a view that’s already in a window.

It turns out the code to explicitly set the color on the UIButton was getting called before the view appeared on the screen aka viewWillAppear. Once the view appeared on the screen, the appearance proxy would set the button’s label’s text color aka button title color.

As a side note, this only seemed to happen when using setAttributedTitle on the button as opposed to calling setTitleColor and setTitle separately. Unfortunately, we wanted different parts of the text to be different sizes. So, using setAttributedTitle still seemed like the thing we should do.

Given all that knowledge, we called setNeedsLayout on the UIButton in viewWillAppear and viewWillDisappear That fixed everything up! The button no longer appeared to change color when switching between screens. Hurrah!

 

 

Adding Swift Initializers Through Extensions

While watching the Stack Views with Enums by objc.io, I learned an interesting little tidbit. You can add a convenience constructor aka convenience initializer by extending a class. The benefit of doing such is that you don’t have to subclass the class. In hindsight, the availability of that feature seems obvious. The Swift reference even has a section called “Initializers”. However, this ability may be news to others as well.

Similar to what they show at 7:45 in the video, one can write the following:

Next, you can call this new init like any other initializer:

I highly recommend you watch the entire Stack Views with Enums video. Enjoy!

RxSwift and Squishing the Pyramid of Doom

Say, you just learned RxSwift and all about subscribeNext. You have a series of Harry Potter related service calls to make and they affect each other. Let’s also say they all return the same type of Observable<Person>. So, you start to call these services one after another using subscribeNext.

The indention of calls is known as the Pyramid of Doom. This is a case of not using all of the power that RxSwift offers. If we inject flatMapLatest into the mix instead of all subscribeNext calls, we get the following:

Not only is the flatMapLatest approach easier to read, but one can see easily where the addDisposableTo(disposeBag) should be added at the end to manage the memory.

This concept is also covered in the RxSwift Tips document. Enjoy all that RxSwift has to offer!

Swizzle Swift Class Methods

While writing some test Swift code, I ran into a situation where I wanted the implementation of a class method swapped out with some fake code. Basically, I wanted to swap out SomeClass‘s saveSomeThings class method with a fake one.

Inspired by NSHipster’s Swift & the Objective-C Runtime article, I came up with some concise code that does what I want. Focusing on only what shows the concept, we have the following example SomeClass:

The saveSomeThings method implementation is what we want to swap out with some fake code for testing purposes. How, you might ask? First, ensure the method has the dynamic keyword as discussed in this Stack Overflow post. Next, we grab a handle to the class method like so:

Now we need something to swap it with. What if we had a method inside a SomeTestClass class like the following just for testing purposes?:

We can grab a handle to the method like so:

The actual call to swap the implementation between the saveSomeThings and forTesting_SaveSomeThings is done using method_exchangeImplementations like this:

The SomeTestClass prefixes the originalMethod and swizzledMethod because I am storing them as static members of the SomeTestClass.

Those are all the magical pieces of this puzzle! Besides being on github, here is a complete Swift playground example to see it all in context:

At CARFAX, I have been fortunate enough to work on such interesting challenges like the one above in Swift, Objective-C and Java based languages. Since CARFAX is a growing company with opportunities that open up from time to time, you might want to checkout the CARFAX Career link.

Regardless, I hope you found the above information interesting and useful. Enjoy!

Hit Your Own Server On Your Mac With Your iPhone

To truly evaluate the user experience, there is no substitute for having the app on your iPhone and in your hands. Yet, it’s rare that a person can make an iPhone app that doesn’t depend on a server of some kind.

If you don’t have a server deployed somewhere yet, what can one do? Well, you can hardcode values in the app. However, that can be more trouble than it’s worth. Also, you’re not testing the whole app end to end. So, what one can do is create a mock server that returns hardcoded values using Node.js or some other framework. Great!

Just one thing, the mock server your iPhone depends on is not ready to be deployed. It’s on your machine only. What to do?

You can run the server locally on your mac and have your iPhone hit it over wireless. Sounds easy enough. Right?

After figuring out your IP address of your machine for the phone to hit, changing the iPhone app to hit whatever URL it needs, and probably temporarily turning off App Transport Security so you can hit it with an http as opposed to an https URL, you may find that your iPhone app still cannot hit the machine your mock server is running on.

Stack Overflow to the rescue! After digging into the google world, I found the key sentence I was looking for in a Stack Overflow post:

I turned off my WI-FI on my Mac and then turned it on again, which solved the problem.

Although I’m not positive, it seems like I had to flip the wireless from on to off and back to on more than once. To test that the phone can reach the machine, I used safari on the iPhone to check that I can hit anything at all on that machine.

Once I figured all this out, I was so happy that I decided to write this for others to use. If it wasn’t for Stack Overflow and our community of knowledge sharers, where would we be today?

Fire! Wife, Apple Watch, and Meditation are Heroes

Ring! Ring! There goes my Apple Watch. The iPhone is in another room and my hands are dirty-dishes-gross. “That’s weird and annoying”, I think reflexively to myself. My wife is calling but she just left.

With the final ring sounding, I quickly do the right thing regardless of my thoughts. I rinse off a pinky finger and answer the call on my Apple Watch with it. I hear her Apple Watch’ed voice say loudly “Get a trashcan full of water and …. <something about a bin>.” What’d she say? After quickly rinsing off my hands, I grab a trashcan, yank the trash out, and mutter about needing caffeine while filling it up with water.

Next, I’m outside. Hmm! Lots of smoke is coming from the trash bin area. Uh oh… A quick emotion check reveals slight concern and the thought that I better get out there soon and see what’s up. I’m slightly surprised about how matter-of-fact my mind feels.

Keeping a clear mind is a credit to the meditation training I’ve done over my life and most recently received from my two favorite meditation apps. At some point, I run toward the smoke and check out the situation. Oh! This fire could spread quickly!

My wife had emptied the wastebasket full of water and tells me to get the fire extinguisher. “Good thinking”, I reflect while running back. I dug it out from the back of the closet, run it out to her, and she puts the fire out while I am running back for more water. Success!

A photo of the inside trash bin area that caught fire.

Where the Fire Started Inside the Bin Area

Just then, the maintenance person shows up that my wife had called. Good. He can do any needed follow up with whomever.

Fire scorched trash bin area fence

Fire Scorched Fence Outside of the Bin Area

Let’s recap. The trash bin area is completely surrounded by a dry timber fence. If I hadn’t been wearing my Apple Watch, had my meditation training, and/or my hero wife hadn’t seen the fire, it might have gone completely up in flames. As my wise wife said, who knows if a floating fire ember would have started another fire on a nearby home?

As far as who the hero is that kept people and property safe, the credit goes to my wife.

This is also a moment of gratefulness. I am grateful for my wife’s levelheadedness. I’m also thankful to Apple for their innovation and specifically my Apple Watch. Finally, I feel lucky to have experienced first hand the fruit of all of my meditation training which kept the whole event in perspective and kept me from overreacting.

May you also be as blessed as I in such times!