Reviving Shopster - in Review

I’m trying to revive Shopster, an app we launched 2013, and I’m writing througout the process. If this is the first post you see, you can start here.


After some time, I finally submitted Shopster 1.2 for App Store review. As I write this, it’s currently “In Review”, and I expect it to be available soon.

I wanted to wrap up this series with a summary of the changes and what I expect to do in the future.

Final Metrics

I took some baseline metrics before I started. Here’s the final summary:

github.com/AlDanial/cloc v 1.70 T=1.10 s (101.4 files/s, 6675.3 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Objective C 46 1176 494 3638
C/C++ Header 53 295 368 477
Swift 7 122 65 350
HTML 1 19 0 159
JSON 2 0 0 140
Markdown 1 3 0 33
XML 1 11 0 12
Bourne Shell 1 2 4 5
-------------------------------------------------------------------------------
SUM: 112 1628 931 4814
-------------------------------------------------------------------------------

Worth noting:

  • I started using Swift. All new classes were written in Swift, and one small refactor (code I moved out from the AppDelegate) was rewritten in Swift as well.
  • There are some shell scripts, JSON files, etc, that were not present before. I integrated fastlane.

Dependencies

In the process, I also removed some third party dependencies we used. The app is now much tighter and less dependent on third party code that I don’t manage.

Deprecations

I solved all the deprecation warnings except one: I’m still using ABCreateStringWithAddressDictionary. As far as I can tell, the suggested replacement (CNPostalAddressFormatter) doesn’t quite cover my use case. I’ll review it in the future, and maybe rewrite that method, as it’s not that important for the app.

UI and AutoLayout

All the screens now use Auto Layout. Where there were animations that changed the frame, I changed to update the constraints and animate that change instead.

Short Term Work

Here’s a short list of things I’d like to tackle in the near future (wihtout counting major architecture changes such as using CloudKit):

  • Start using the default system font (San Francisco) instead of the custom font we are using.
  • Adopt Dynamic Type across the app.
  • Remove some graphics I think make the app look dated, like the logo on the Navigation Bar.
  • Localise the app in Spanish1. It’s currently English only.
  • Fix the missing deprecation warning.
  • Use the new location based notification trigger. Currently, we observe significant location changes, and when they match the areas we want, we trigger a Local Notification immediately. The new UserNotifications framework, allows you to set a location as a notification trigger, which would help eliminate part of this code. Since this is core to the app, I didn’t want to take on it now, as it will require more testing.
  • Add a new onboarding screen. As soon as you start the app, you need to allow location services and notifications. We simply throw the system dialogs to the user. We can do better.
  • Add @3x assets. I didn’t have them, and wanted to ship soon. They’ll come at a later update.
  • Make the app free, and show ads only to new users (the ones who got the app for free, basically).

Long Term Work

This list is shorter in items, but way harder, so I don’t know if or when I’ll do it:

  • Take on major refactors for the app. Adopt MVVM.
  • Start using CloudKit for sync.
  • Adopt a freemium model, where a one time payment unlocks some functionality.

Closing Thoughts

  • Don’t let your apps rotten! Either pull them from the App Store gracefully, or spend the required time to keep them up to date at the very least through major OS versoins.
  • It’s hard giving up on an app you put effort on. I couldn’t, that’s why this version is on review. This is probably not the best example to follow.
  1. My native language! Shameful.

Updating the Frame of a UITableView in iOS 10

In Shopster, when you edit an element in the main UITableView, we show a “ruler” to allow for selecting the item quantity. When the ruler is shown, we make the table view narrower, so the text is still visible to the user.

In iOS 10, however, Apple slightly changed the behaviour of UITableView, and the animation started failing as follows:

In the animation closure, we are simply changing the UITableView’s frame. As you can see on the video, this makes the animation “jump”. The disclosure arrows move before the animation even start.

The solution, luckily, is simple: just wrap the frame update in a beginUpdates / endUpdates pair as follows:

self.tableView.beginUpdates
// assumes currentFrame is set previously.
self.tableView.frame = currentFrame
self.tableView.endUpdates()

If you want to play around with the example, here’s an interactive playground you can download to reproduce the issue or see the workaround.

Reviving Shopster - AutoLayout

I’m trying to revive Shopster, an app we launched 2013, and I’m writing througout the process. If this is the first post you see, you can start here.


Shopster uses Storyboards. I like them and can say I was an early adopter of them. Now with Storyboard references, I think they are even better and simpler to use.

As I wrote before, our app only supported 4 inch phones (iPhone 5 / 5s / 5c / SE), and was not written using Auto Layout. So my next step is to enable AutoLayout and fix the layout of every screen.

Luckily, the app has about 8 screens total, so that should not be too hard.

Writing Unit Tests in Swift for Objective-C Projects

As I wrote previously, I decided to write my (missing) unit tests for an old Objective-C app in Swift.

Immediately after starting, I run into an issue: I can’t import the Swift module on the test target. All the settings appear to be correct: I’m defining a module, including a bridging header. However, whenever I import my module on Swift I get an error.

The only workaround I could find, was to include a Swift file in the Objective-C project. I’m not sure what dark magic this triggers in Xcode 8, but it makes the trick.

Here’s in all its glory, Dummy.swift:

import Foundation
// This file is not used at all on the project, but triggers Xcode to properly
// create the module for testing purposes. ¯\_(ツ)_/¯

Reviving Shopster - Testing in Swift

I’m trying to revive Shopster, an app we launched 2013, and I’m writing througout the process. If this is the first post you see, you can start here.


When I mentioned the pile of technical debt in Shopster, I neglected to say that it doesn’t have any unit nor UI tests1.

Now, with lots of changes to be done to fix deprecations, it’s one of those times when you regret not having those tests that give you the confidence to move forward with the changes.

So with a better late than never attitude, I created a test target on the project with a slight twist: I’ll write all the tests in Swift. This is a low friction place to start adding Swift to the codebase, without rewriting nor having to deal with too much interop. I’m pretty sure that as soon as I add new classes to the app, those will be Swift as well, but I haven’t hit that yet.

  1. Even though I talked about it during an NSConf Argentina!

Reviving Shopster - Deprecations and iOS 10

A nice thing about reviving a project, is that I get to choose the target platform again. In this case, I don’t see a reason not to make it iOS 10 only: up until iOS 9, the app worked (even if it was not optimized for), so users of iOS 9 can remain on the old version.

This gives me the chance of remove a lot of old stuff that I had laying around, mainly to support older OS versions.

Reviving Shopster - Baseline Metrics

Before any change to the codebase (apart for making it build), I figure it’s a good idea to have a baseline of the amount of code the app has. For that, I’ll use cloc. Here’s the initial output:

127 text files.
127 unique files.
25 files ignored.
github.com/AlDanial/cloc v 1.70 T=0.74 s (137.4 files/s, 8946.6 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Objective C 47 1200 493 3688
C/C++ Header 53 296 372 476
JSON 2 0 0 117
-------------------------------------------------------------------------------
SUM: 102 1496 865 4281
-------------------------------------------------------------------------------

This was run only on the folder where our code resides, so it’s excluding all external dependencies (basically, the Pods folder).

Reviving Shopster - Make It Build

I started analyzing wether it was worth reviving Shopster or not.

So first step into the plan: clone the project’s repo, and see what breaks.

For starters, I did not follow my own CocoaPods suggestion1, so the Pods folder is not part of the repository.

Luckily, Shopster’s Podfile is pretty short:

platform :ios, '7.0'
xcodeproj 'Groceries'
pod 'PSAlertView'
pod 'TestFlightSDK', '~> 3.0'
pod 'BlockAlertsAnd-ActionSheets', '~> 1.0'
pod 'FMMoveTableView'
pod 'OHAttributedLabel', '~> 3.4'

Apart from these Pods, we are using Crashlytics and AskingPoint, which I no longer would like to have in the app.

First step to fix the Podfile then: remove old dependencies (TestFlight was acquired by Apple long ago!) and adopt the new syntax. The resulting Podfile looks like this:

platform :ios, '7.0'
project 'Groceries'
target 'Shopster' do
pod 'PSAlertView'
pod 'BlockAlertsAnd-ActionSheets', '~> 1.0'
pod 'FMMoveTableView'
pod 'OHAttributedLabel', '~> 3.4'
end

This new Podfile has this warning: [!] OHAttributedLabel has been deprecated in favor of DTCoreText. I’ll deal with that later.

I also removed AskingPoint.framework. The project still fails to build. The pch (remember those?) is including <TestFlightSDK/TestFlight.h>, and of course, I’m initializing it in the AppDelegate and using it in a reporter class. Removing these makes the project build!

Current state: Shopster runs on the iOS 10 Simulator, just like it runs on my iPhone. All the low hanging fruit has been taken care of.

  1. No wonder why: the post is from May 2014, and Shopster is from June 2013. A testament of how can I change my opinion on tools usage in a year.

Reviving Shopster - Worth It?

We launched Shopster in 2013. It was the last app we developed we published under Quadion’s name. Even though it had great launch coverage, it tanked.

For 2016 standards, the app has piled up on technical debt. It currently:

  • Doesn’t dupport screen sizes larger than the 4’ iPhone 5.
  • Is not using auto-layout.
  • Uses several deprecated APIs.
  • Has workarounds for several things.

And, of course, it’s 100% Objective-C, as Swift was not around by the time we wrote it.

So, what would be the motivation to keep investing in it?

  1. We like the app. We still keep it in our home screens and use it.
  2. It still has some users (iTunes reports about 100 active users per day).
  3. Is an app we like to showcase.
  4. We could try a new business model.

And finally, we got a ticket last week asking us to please, please, add support for iOS 10.

On Sharing

When we launched Shopster, we neglected to include a sync feature. We figured it was not that important, but our users kept asking for it.

By the time we had to make the call, the best choice was to roll our own syncing engine and incur in a monthly cost, knowing almost for certain, that we might not be able to afford it in the long run.

Fast forward a few years, and Apple shipped CloudKit and more recently CloudKit sharing, which would allow us to enable sharing of shopping lists at no monthly cost.

Will we do it?

I’m not sure. What I’ll sure do (and write about) is see the state of our app, and what’s the effort involved in bringing it back to life.