CatFancy
Introduction
CatFancy is an iOS app that demonstrates iOS-development techniques. Users can browse breeds of cats with images from various sources and breed info from Wikipedia. I created CatFancy to accompany a blog post about iOS-developer coding challenges.
Build Tools & Versions Used
I developed CatFancy using Xcode 14.1 Beta 3, iOS 16.1, SwiftLint 0.30.1, and this song.
Discussion
Aside from meeting requirements that are typical of this sort of coding challenge, my emphases in CatFancy are using a technique, dependency injection, and an architectural pattern, the coordinator, that facilitate unit testing. I have come to value unit testing because, as Jon Reid observed, a "robust suite of unit tests acts as a safety harness, giving you courage to make bold changes." In nuts-and-bolts terms, dependency injection makes objects testable by isolating dependencies and side effects. I have written elsewhere on dependency injection. The coordinator pattern makes UIViewController
s more testable by removing from them the work of navigating to other UIViewController
s, whether in the app itself or in external apps. CatFancy's unit-test coverage is 88.5%.
CatFancy demonstrates two new Swift features, if let
shorthand and async
/await
concurrency.
Although the breed-list screen, implemented primarily in BrowseBreedsVC
and BrowseBreedsView
, is the only screen whose implementation a take-home programming assignment typically requires, I chose to implement two additional screens. I wanted to give the user, the reviewer, the ability to choose different JSON files for display and different sort orders for the breeds. The settings screen, implemented in SettingsVC
and SettingView
, resulted. I wanted to give the user the ability to see information that cannot be displayed in the breed list. The details screen, implemented in BreedDetailVC
and BreedDetailView
, resulted. This screen shows walls of text about breeds and allows the user to view the breeds' Wikipedia pages and photo licenses in Safari. The settings and details screens exercise the coordinator pattern to an extent that the browsing screen, acting alone, could not.
CatFancy supports iPhone, iPad, landscape orientation, portrait orientation, Light Mode, and Dark Mode. I developed CatFancy primarily for iPhone. iPad would benefit from, for example, higher information density in breed rows. Landscape orientation on iPhone would be more idiomatic if it used UISplitViewController
like Mail does. Due to time constraints that are characteristic of coding challenges, CatFancy may imperfectly support VoiceOver, but accessibility is important to me.
In both professional and side-project apps, I use color palettes from designers or the website Coolors. In CatFancy, I relied on system-provided colors due to time constraints.
The breeds fit perfectly the dainty iPhone SE form factor. If some breed info ended up not fitting in a BreedCell
, tweaks would be in order. MarqueeLabel
, a UILabel
replacement that has performed well in RaceRunner, would obviate the need for either String
truncation or widespread UI changes. Dynamic Type, which I did not test due to time constraints, might also necessitate tweaks.
Coding challenges typically do not state a requirement for internationalization or localization so, in the interest of time, I did not wrap user-facing String
s in NSLocalizedString()
. I am, however, comfortable with that API.
Warning
If you run CatFancy in the simulator, as I did during development, you may see the following warning:
[plugin] AddInstanceForFactory: No factory registered for id <CFUUID 0x6000001ad4a0> F8BB1C28-BAE8-11D6-9C31-00039315CD46
My research indicates that this warning is harmless. I expect Apple to fix it in a future version of Xcode and iOS.
Screenshots
Browsing | Details |
---|---|
Settings | Splash |
---|---|
Credits
Christy Presler created the app icon and released it under the CC BY-SA 3.0 license.
Sound Jay created the chime and sad-trombone sounds and "allow[s] ... incorporat[ion of these sounds] into ... projects, be it for commercial or non-commercial use." Sound Jay "and its licensors retain all ownership rights to the sound files".
The humorous badges at the top of this readme are from my curated list of iOS-development podcasts.
Settings.swift
and the GetterSetter
files reflect an approach to storing and retrieving settings that I developed for Immigration and also use in Racerunner (GitHub), Conjugar (GitHub), and Conjuguer (GitHub).
RealSoundPlayer.swift
reflects an approach to playing sounds that I developed for Immigration and also use in RaceRunner, Conjugar, and Conjuguer, though I added dependency injection to CatFancy's implementation because playing a sound is a side effect, I realized, that is undesirable in unit tests.
UIViewControllerExtensionTests.swift
contains code I developed for Conjugar.
Doug Suriano shared the boilerplate-reduction technique in NSLayoutConstraintExtension.swift
.
Antoine van der Lee shared the boilerplate-reduction technique in UsesAutoLayout.swift
.
Point-Free developed and evangelized CatFancy's approach to dependency injection, The World.
Paul Hudson shared the approach to dependency injection for URLSession
used in URLProtocolStub.swift
and URLSessionExtension.swift
.
Soroush Khanlou developed the coordinator pattern that CatFancy uses for navigation. Two posts by Paul Hudson also informed CatFancy's implementation.
Geoff Hackworth shared the technique of bespoke app/scene delegates for unit tests exemplified by TestingRootViewController.swift
, TestingAppDelegate.swift
, TestingSceneDelegate.swift
, and main.swift
, building on a technique shared by Jon Reid.