Bookish Development Diary, episode 5.
It’s been quiet around here over Christmas, and I cut myself a bit of slack and didn’t really do a lot last week, but I’m back to it now.
Pretty much the last decision I made before the holidays was that I needed to develop some basic view classes for use with Datastore, and that they should live in a companion module (called DatastoreKit), as another published product of the Datastore package along with the Datastore module itself.
I also decided that this should go hand-in-hand with a test application, which is a separate project and a client of both Datastore and DatastoreKit.
This serves the dual purpose of allowing me to work through the basic user interface problems in a relatively uncomplicated environment, and also giving me a tool that can open any arbitrary store file and display its contents.
Having such a tool, which gives a raw view of the data, with minimal interpretation, is really useful for diagnosing problems in higher level application logic. In extreme cases it potentially also allows manual rescue and/or rebuild of user’s data; something that hopefully won’t be needed often, but a useful tool to have in the toolbox.
I suspect that for Bookish I’ll still need to customise the view classes that DatastoreKit provides, but hopefully they’ll provide a solid foundation.
This is one of those decisions that could be described as over-complication. I can hear the YAGNI brigade screaming that I should just make some views in Bookish, and be done with it. Perhaps I’m just destined to repeat the same mistakes forever, but I don’t buy that argument (in this case, at least).
I think it would be valid if Bookish was the only client, but past experience has shown me the value of a database viewer tool, and having two clients gives me a reason to move the views into a separate module.
It also gives me a fighting chance of keeping the view classes genuinely decoupled from either client. Or not… we’ll see.
The choice of where to put a UI library, and indeed how to implement it, has become a bit more complicated in world where Xcode and Swift Package Manager have collided.
I was never a Cocoapods or Carthage fan, but I really like the SPM model, and I adopted it as soon as I could. It was a pain to integrate with Xcode in the beginning, but since XCode 11 it’s been pretty easy for code-only packages.
The problem is that SPM is platform neutral, and right now doesn’t support any of the non-code tasks that are required to build a fully fledged macOS/iOS application or a framework, such as compiling xibs, packaging resources and making bundles.
Ultimately this is a problem that needs solving, but the SPM team have been cautious about how they go about it, and frankly they probably have other priorities. A while back I messed around a bit with extending SPM to solve this problem myself, and that’s something I may go back to at some point.
Given the progress that has already been made with Xcode integration, I’m hopeful that the Xcode and/or SPM teams may get there before me.
In the meantime therefore, my general approach is to make anything UI-related as a normal Xcode app or framework, and just use SPM for code-only things.
This presents a bit of a problem for DatastoreKit, since I really wanted it to be part of the Datastore SPM module.
Historically, I’ve been very sceptical of anyone choosing to eschew Interface Builder (or ResEdit/Resourcerer/PowerPlant/MacApp equivalents!) and make their UI in code.
The motivation people had for doing this often seemed quiet dubious to me (more about a resistance to learning new things than a principalled stand).
From Hypercard onwards, I was an early convert to the value of being able to build a UI with direct manipulation, and being able to (in effect) data-drive the UI construction by describing it in something other than code (resource files, xib files, etc).
I’d like to think that I was also honest about the down sides - particularly the difficult of tracing all the connections in a complex UI at runtime - but on balance I always felt that the advantages outweighed the disadvantages.
It’s really easy for a reasoned argument like this to slide into dogma though, and I think that’s what I may have done. A lot of water has passed under the bridge since I first thought about it.
The sort of things that Xcode can do with playgrounds and live-previewing SwiftUI - in particular, the ability to round-trip between a textual and a visual representation, and edit either - are game changers.
They were always theoretically possible, but rarely (if ever) very well done. Having an implementation that works well offers a glimpse of a best-of-both-worlds solution, which moves the goalposts a bit.
Having worked in teams on large projects, I can also attest to the value of being able to diff changes, and the difficulties of doing so with an IB-based interface.
So although I’m not actually using SwiftUI in this case, I’ve decided to be pragmatic and see how far I can get with building the UI components of DatastoreKit in code.
Doing so allows me to publish the module with SPM, and it should be an interesting experiment.
We’re not talking about a vast number of components here either, so it’s a relatively contained problem.
The test view app, on the other hand, remains a fully-fledged Xcode project, with an interface built in IB.
It (and Bookish) are both clients of the Datastore SPM package, and are free to import both the code and the UI components from it.