Introducing and explaining the PreviewBackground package

While learning and experimenting with SwiftUI, I use the canvas assistant editor to preview SwiftUI views extensively. It is an amazing feature of Xcode 11 and I love it. There is a quirk that gets difficult for me though – the default behavior of the preview provider uses a gray background. I frequently use multiple previews while making SwiftUI elements, wanting to see my creation on a background supporting both light and dark modes.

The following little stanza is a lovely way to iterate through the modes and displaying them as previews:

#if DEBUG
struct ExampleView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ForEach(ColorScheme.allCases,
                    id: \.self) { scheme in

                Text("preview")
                    .environment(\.colorScheme, scheme)
                    .frame(width: 100,
                           height: 100,
                           alignment: .center)
                    .previewDisplayName("\(scheme)")
            }
        }
    }
}
#endif

Results in the following preview:

The gray background doesn’t help all that much here. It is perfect when you are viewing a fairly composed element set, as you are often working over an existing background. When you are creating an element to stand alone, or moving an element. In those cases, I really want a background for the element.

And this is exactly what PreviewBackground provides. I made PreviewBackground into a SwiftPM package. While I could have created this effect with a ViewModifier, I tried it out as a ViewBuilder instead, thinking it would be nice to wrap the elements I want to preview explicitly.

The same example, using PreviewBackground:

import PreviewBackground

#if DEBUG
struct ExampleView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ForEach(ColorScheme.allCases,
                    id: \.self) { scheme in
                PreviewBackground {
                    Text("preview")
                }
                .environment(\.colorScheme, scheme)
                .frame(width: 100,
                       height: 100,
                       alignment: .center)
                .previewDisplayName("\(scheme)")
            }
        }
    }
}
#endif

The code is available on Github, and you may include it within your own projects by adding a swift package with the URL: https://github.com/heckj/PreviewBackground

Remember to import PreviewBackground in the views where you want to use it, and work away!

Explaining the code

There are not many examples of using ViewBuilder to construct a view, and this is a simple use case. Here is how it works:

import SwiftUI

public struct PreviewBackground<Content>: View where Content: View {
    @Environment(\.colorScheme) public var colorSchemeMode

    public let content: () -> Content

    public init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }

    public var body: some View {
        ZStack {
            if colorSchemeMode == .dark {
                Color.black
            } else {
                Color.white
            }
            content()
        }
    }
}

The heart of using ViewBuilder is using it within a View initializer to return a (specific but) generic instance of View, and using the returned closure as a property that you execute when composing a view.

There is a lot of complexity in that statement. Allow me to try and explain it:

Normally when creating a SwiftUI view, you create a struct that conforms to the View protocol. This is written in code as struct SomeView: View. You may use the default initializer that swift creates for you, or you can write your own – often to set properties on your view. ViewBuilder allows you to take a function in that initializer that returns an arbitrary View. But since the kind of view is arbitrary, we need to make the struct generic – since we can’t assert exactly what type it will be until the closure is compiled. To tell the compiler it’ll need to do the work to figure out the types, we label the struct as a being generic, using the <SomeType> syntax:

struct SomeView<Content>: View where Content: View

This says there is a generic type that we’re calling Content, and that generic type is expected to conform to the View protocol. There is a more compact way to represent this that you may prefer:

struct SomeView<Content: View>: View

Within the view itself, we have a property – which we name content. The type of this content isn’t known up front – it is the arbitrary type that the compiler gets to infer from the closure that will provided in the future. This declaration is saying the content property will be a closure – taking no parameters – that returns some an arbitrary type we are calling Content.

public let content: () -> Content

Then in the initializer, we use ViewBuilder:

public init(@ViewBuilder content: @escaping () -> Content) {
    self.content = content
}

In case it wasn’t obvious, ViewBuilder is a function builder, the swift feature that is enabling this declarative structure with SwiftUI. This is what allows us to ultimately use it with in that declarative syntax form.

The final bit of code to describe is using the @Environment property wrapper.

@Environment(\.colorScheme) public var colorSchemeMode

The property wrapper is not in common use, but perfect for this need. The property wrapper uses exposes a specific part of the existing environment as a local property for this view. This is what enables PreviewBackground to choose the color for the background appropriate to the mode. By reading the environment it chooses an appropriately colored background. It then uses that property to assemble a view by invoking the property named content (which was provided by the function builder) within a ZStack.

By using ViewBuilder, we can use the PreviewBackground struct like any other composed view within SwiftUI:

var body: some View {
    PreviewBackground {
        Text("Hello there!")
    }
}

If we had created this code as a ViewModifier, then using it would look different – instead of the curly-bracket syntax, we would be chaining on a method. The default set up for something like that looks like:

var body: some View {
    Text("Hello there!")
    .modify(PreviewBackground())
}

I wanted to enable the curly-bracket syntax for this, hence the choice of using a ViewBuilder.

A side note about moving code into a Swift package

When I created this code, I did so within the context of another project. I wanted to use it across a second project, and the code was simple enough (a single file) to copy/paste – but instead I went ahead and made it a Swift package. Partially to make it easier for anyone else to use, but also just to get a bit more experience with what it takes to set up and use this kind of thing.

The mistake that I made immediately on moving the code was not explicitly making all the structs and properties public. It moved over, compiled fine, and everything was looking great as a package, but then when I went to use it – I got some really odd errors:

Cannot call value of non-function type 'module<PreviewBackground>'

In other instances (yes, I admit this wasn’t the first time I made this mistake – and it likely won’t be the last) the swift compiler would complain about the scope of a function, letting me know that it was using the default internal scope, and was not available. But SwiftUI and this lovely function builder mechanism is making the compiler work quite a bit more, and it is not nearly as good at identifying why this mistake might have happened, only that it was failing.

If you hit the error Cannot call value of non-function type when moving code into a package, you may have forgotten to make the struct (and relevant properties) explicitly public.

Four strategies to use while developing SwiftUI components

Lets start out with the (possibly) obvious: when I code, I frequently make mistakes (and fix them); but while I am going through that process function builders are frequently kicking my butt. When you are are creating SwiftUI views, you use function builders intensely – and the compiler is often at a loss to explain how I screwed up. And yeah, even with the amazing new updates into the Diagnostic Engine alongside Swift 5.2, which I am loving.

What is a function builder? It is the thing that looks like a normal “do some work” code closure in swift that you use as the declarative structure when you are creating a SwiftUI view. When you see code such as:

import SwiftUI

struct ASimpleExampleView: View {
    var body: some View {
        Text("Hello, World!")
    }
}

The bit after some View is the function builder closure, which includes the single line Text("Hello, World!").

The first mistake I make is assuming all closures are normal “workin’ on the code” closures. I immediately start trying to put every day code inside of function builders. When I do, the compiler – often immediately and somewhat understandably – freaks out. The error message that appears in Xcode:

Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type

And some times there are other errors as well. It really depending on what I stacked together and how I grouped and composed the various underlying elements in that top level view, and ultimately what I messed up deep inside all that.

I want to do some calculations in some of what I am creating, but doing them inline in the function builder closures is definitely not happening, so my first recommended strategy:

Strategy #1: Move calculations into a function on the view

Most of the reasons I’m doing a calculation is because I want to determine a value to hand in to a SwiftUI view modifier. Fiddling with the opacity, position, or perhaps line width. If you are really careful, you can do some of that work – often simple – inline. But when I do that work, I invariably screw it up – make a mistake in matching a type, dealing with an optional, or something. At those times when the code is inline in a function builder closure, the compiler is having a hell of a hard time figuring out what to tell me about how I screwed it up. By putting the relevant calculation/code into a function that returns an explicit type, the compiler gets a far more constrained place to provide feedback about what I screwed up.

As an example:

struct ASimpleExampleView: View {
    func determineOpacity() -> Double {
        1
    }

    var body: some View {
        ZStack {
            Text("Hello World").opacity(determineOpacity())
        }
    }
}

Some times you aren’t even doing calculations, and the compiler gets into a tizzy about the inferred type being returned. I have barked my shins on that particular edge repeatedly while experimenting with all the various options, seeing what I like in a visualization. The canvas assistant editor that is available in Xcode is a god-send for fast visual feedback, but I get carried away in assembling lots of blocks with ZStacks, HStacks, and VStacks to see what I can do. This directly leads to my second biggest win:

Strategy #2: Ruthlessly refactor your views into subcomponents.

I am beginning to think that seeing repeated, multiple kinds of stacks together in a single view is possibly a code smell. But more than anything else, keeping the code within a single SwiftUI view as brutally simple as possible gives the compiler a better than odds chance of being able to tell me what I screwed up, rather than throwing up it’s proverbial hands with an inference failure.

There are a number of lovely mechanisms with Binding that make it easy to compose and link to the relevant data that you want to use. When I am making a subcomponent that provides some visual information that I expect the enclosing view to be tracking, I have started using the @Binding property wrapper to pass it in, which works nicely in the enclosing view.

TIP:

When you’re using @Binding, remember that you can make a constant binding in the PreviewProvider in that same file:

YourView(someValue: .constant(5.0))

While I was writing this, John Sundell has recently published a very in-depth look at exactly this topic. His article Avoiding Massive SwiftUI Views covers another angle of how and why to ruthlessly refactor your views.

On the topic of the mechanics of that refactoring, when we lean what to do, it leads to leveraging Xcode’s canvas assistant editor withPreviewProvider – and my next strategy:

Strategy #3: use Group and multiple view instances to see common visual options quickly

This strategy is more or less obvious, and was highlighted in a number of the SwiftUI WWDC presentations that are online. The technique is immensely useful when you have a couple of variations of your view that you want to keep operational. It allows you to visually make sure they are working as desired while you continue development. In my growing example code, this looks like:

import SwiftUI

struct ASimpleExampleView: View {
    let opacity: Double
    @Binding var makeHeavy: Bool

    func determineOpacity() -> Double {
        // maybe do some calculation here
        // mixing the incoming data
        opacity
    }

    func determineFontWeight() -> Font.Weight {
        if makeHeavy {
            return .heavy
        }
        return .regular
    }

    var body: some View {
        ZStack {
            Text("Hello World")
                .fontWeight(determineFontWeight())
                .opacity(determineOpacity())
        }
    }
}

struct ASimpleExampleView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ASimpleExampleView(opacity: 0.8, 
                makeHeavy: .constant(true))

            ASimpleExampleView(opacity: 0.8, 
                makeHeavy: .constant(false))
        }
    }
}

And the resulting canvas assistant editor view:

This does not always help you experiment with what your views look like in all variations. For sets of pre-defined options, or data that influences your view, it can make a huge difference. A good variation that I recommend anyone use is setting and checking the accessibility environment settings to make sure everything renders as you expect. Another that I have heard is in relatively more frequent use: verifying localization rendering.

The whole rapid experimentation and feedback capability is what is so compelling about using SwiftUI. Which leads pretty directly to my next strategy:

Strategy #4: Consider making throw-away control views to tweak your visualization effects

I am not fortunate enough to constantly work closely with a designer. Additionally, I often do not have the foggiest idea of how some variations will feel in terms of a final design. When the time comes, seeing the results on a device (or on multiple devices) makes a huge difference.

You do not want to do this for every possible variation. That is where mocks fit into the design and development process – take the time to make them and see what you think. Once you have narrowed down your options to a few, then this strategy can really kick in and be effective.

In the cases when I have a few number of variations to try out, I encapsulate those options into values that I can control. Then I make a throw-away view that will never be shown in the final code that allows me to tweak the view within the context of a running application. Then the whole thing goes into whatever application I am working on – macOS, iOS, etc – and I see how it looks

When I am making a throw-away control view, I often also make (another throw-away) SwiftUI view that composes the control and controlled view together, as I intend to display it in the application. This is primarily to see the combined effect in a single Preview within Xcode. The live controls are not active in the Xcode canvas assistant editor, but it helps to see how having the controls influences the rest of the view structure.

A final note: Do not be too aggressive about moving code in a SwiftPM package

You may (like me) be tempted to move your code into a library, especially with the lovely SwiftPM capabilities that now exist within Xcode 11. This does work, and it functionally works quite well, from my initial experiments. But there is a significant downside, at least with the current versions (including Xcode 11.4 beta 3) – while you are still doing active development on the library:

If you open the package to edit or tweak it with Xcode, and load and build from only Package.swift without an associated Xcode project, the SwiftUI canvas assistant preview will not be functioning. If you use an Xcode project file, it works fine – so if you do go down this route, just be cautious about removing the Xcode project file for now. I have filed feedback to Apple to report the issue – both with Xcode 11.3 (FB7619098) and Xcode 11.4 beta 3 (FB7615026).

I would not recommend moving anything into a library until you used had it stable in case. There are also still some awkward quirks about developing code and a dependent library at the same time with Xcode. It can be done, but it plays merry havoc with Xcode’s automatic build mechanisms and CI.

Using Combine v1.1 is available

After getting the major edits for the existing content done, I called the result the first release. As with any creative product, I wasn’t happy with some of the corners that still had rough edges. Over the past two weeks I fleshed those in, wrote a bunch of unit tests, figured out some of the darker corners that I’d previously ignored, and generally worked to improve on the overall consistency.

The results have been flowing into the online version as I merged them. And now the updated version, available on gumroad in PDF and ePub format, is updated as well. Anyone who’s previously purchased the content gets the updates for free – just log in and they are available for you.

The rough bits that were fleshed out include several focuses of content:

  • Tests created and content written (and updated) for the multicast and share operators. The focus was primarily how they work and how to use them.
  • Worked through what the Record publisher offers (and doesn’t offer), including how to serialize & deserialize recorded streams (while this sounds cool, its not ultimately as useful as I hoped it might be).
  • Added the missing note that swift’s Result type could also be used as a publisher, courtesy of a little extension that was added in Combine.
  • Updated some of the details of throttle and debounce with the specifics of delays that are incurred in timing, after having validated the theories with some unit tests (spoiler: debounce always delays the events by a short bit, but throttle doesn’t have much of an impact). I had previously written about throttle and debounce on this blog as well.

The new version is 1.1, tagged in the underlying repository if you are so inclined to review/poke at the unit tests beyond the narrative details I shared in the book itself.

Using Combine – first edition available

I just finished my first edit pass of the content of Using Combine, and am incredibly pleased. Sufficiently pleased, in fact, that I am going to call this version the “first edition”.

It is certainly not perfect, nor even as complete as I would like, but a significant enough improvement that I wanted to put a stake in the ground and get it out there.

I’m not planning on stopping the development work, there are more examples, more details, and useful tidbits that will be developed. I have continued to receive wonderful feedback, and plan to continue to accept updates, as all the content, example code, and sample project pieces are available in the github repository swiftui-notes.

The content will continue to remain available for free in a single-page HTML format, hosted at http://heckj.github.io/swiftui-notes/. The ePub and PDF version is available through gumroad.

SwiftUI and Combine – Binding, State, and notification of changes

When I started the project that became Using Combine, it was right after WWDC; I watched streamed WWDC sessions online, captivated like so many others about SwiftUI. I picked up this idea that SwiftUI was “built using the new framework: Combine”. In my head, I thought that meant Combine managed all the data – notifications and content – for SwiftUI. And well, that ain’t so. While the original “built using Combine” is accurate, it misses a lot of detail, and the truth is a bit more complex.

After I finished my first run through drafting the content for Using Combine, I took some time to dig back into SwiftUI. I originally intended to write (and learn) more about that. In fact, SwiftUI is what started the whole segue into Combine. I hadn’t really tried to use SwiftUI seriously, or get into the details until just recently. I realized after all the work on examples for Combine and UIKit, I had completely short shifted the SwiftUI examples.

Mirroring a common web technology pattern, SwiftUI works as a declarative structure of what gets shown with the detail being completely derived from some source of truth – derived from state stored or managed somewhere. The introductory docs made is clear that @State was how this declarative mechanism could represent a bit of local state within a View, and with the benefit of Daniel and Paul’s writing (SwiftUI Kickstart and SwiftUI by Example), it was also quickly clear that @EnvironmentObject and @ObservedObject played a role there too.

The Combine link to SwiftUI, as it turns out, is really only about notifying the SwiftUI components that a model had changed, not at all what changed. The key is the protocol from Combine: ObservableObject (Apple’s docs). This protocol, along with the @Published property wrapper, does the wonderful work of generating a combine publisher – the default type of which is represented by the class ObservableObjectPublisher. In the world of Combine, it has a defined output and failure type: <Void, Never>. The heart of that Void output type is that the data that is changing doesn’t matter – only that a change was happening.

So how does SwiftUI go and get the data it needs?Binding is the SwiftUI generic structure that is used to do this linkage. The documentation at Apple asserts:

Use a binding to create a two-way connection between a view and its underlying model. For example, you can create a binding between a Toggle and a Bool property of a State. Interacting with the toggle control changes the value of the Bool, and mutating the value of the Bool causes the toggle to update its presented state.

You can get a binding from a State by accessing its binding property. You can also use the $prefix operator with any property of a State to create a binding.

https://developer.apple.com/documentation/swiftui/binding

Looking around a bit more while creating some examples, and it becomes clear that some handy form elements (such as TextField) expect a parameter of type binding when they are declared. Binding itself works by leveraging swift’s property getters and setters. You can even manually create a Binding if you’re so inclined, defining the closures for get and set to whatever you like. Property wrappers such as @State, @ObservedObject, and @EnvironmentObject either create and expose a Binding, or create a wrapper that in turn passes back a Binding.

My take away is the flow with Combine and SwiftUI has a generally expected pattern: A model to be represented by a reference object, which sends updates when the data is about to change (by conforming to the ObservableObject protocol). SwiftUI goes and gets the data that it needs based on what was declared in the View using Binding to get to the underlying data (and potentially allowing the SwiftUI view to update it in turn if that’s relevant).

Given that SwiftUI views are also designed to be composed, I am leaning towards expecting a pattern that state will need to be defined for pretty much any variation of a view – and potentially externalized. The property wrappers for representing, and externalizing, state within SwiftUI are:

  • @State
  • @ObservedObject and @Published
  • @EnvironmentObject

@State is all about local representation, and the simplest mechanism, simply providing a link to a property and the Binding.

@ObservedObject (along with @Published) adds a notification mechanism on change, as well as a way to get a typed Binding to properties on the model. SwiftUI’s mechanism expects this always to be a reference type (aka a ‘class’), which ends up being pretty easy to define in code.

@EnvironmentObject takes that a step further and exposes a reference model not just to a single view, but allows it to be used by any number of views in their own hierarchy.

  • Drive most of the visual design choices entirely by the current state

But that’s not the only mechanism that is available: SwiftUI is also set up to react to a Combine publisher – although not in a heavily predetermined fashion. An interesting aspect is that all of the SwiftUI views also support a Combine subscriber: onReceive. So you can bring the publisher, and then write code within a View (or View component) to react to what it sends.

The onReceive subscriber acts very similarly to Combine’s sink subscriber – the single-closure version of sink (implying a Combine pipeline failure type of Never). You to define a closure within your SwiftUI view element that accepts that data and does whatever needs doing. This could be using the data, transforming and storing it into local @State, or just reacting to the fact that data was sent and updating the view based on that.

From a “What is a best practice” point of view, it seems the more you represent what you want to display within a reference model, the easier it will be to use. While you can expose a publisher right into a SwiftUI view, it tightly couples the combine publisher to the view and all links all those relevant types. You could (likely just as easily) have the model object encapsulate that detail – in which case the declaration of how you handle event changes over time are separated from how you present the view. This is likely a better separation of concerns.

The project (SwiftUI-Notes) linked to Using Combine now has two examples with Combine and SwiftUI. The first is a simple form validation (the view ReactiveForm.swift and model ReactiveFormModel.swift). This uses both the pattern of encapsulating the state within the model, and exposing a publisher to the SwiftUI View to show what can be done. I’m not espousing that the publisher mechanism is a good way to solve that particular problem, but it illustrates what can be done nicely.

The second example is a view (HeadingView.swift) that uses a model and publisher I created to use the built-in CoreLocation framework. The model (LocationModelProxy.swift) exposes the authorization as a published property, as well as the location updates through a publisher. Within the built-in Cocoa framework, those are normally exposed through a delegate callback. A large number of the existing Cocoa frameworks are convertible into a publisher-based mechanism to work with Combine using this pattern. The interesting bit was linking this up to SwiftUI, which was fun – although this example only taps the barest possibility of what could done.

It will be interesting to see what Apple might provide in terms of adopting Combine as alternative interfaces to its existing frameworks. CoreLocation is such a natural choice with its streaming updates, but there are a lot of others that could be used as well. And of course I’m looking forward to seeing how they expand on SwiftUI – and if they bring in more Combine based mechanisms into it or not.

It is OK to test the framework

When I started to write the book Using Combine, I was learning the Combine framework as I went. There was a lot I was unsure about, and especially given that it was released with the beta of the operating system, the implementation was changing between beta releases as it firmed up. I chose to use a technique that I picked up years ago from Mike Clark – write unit tests against the framework to verify my understanding of it – while writing the book. (yes, I’m still working on it – it’s a very lengthy process)

While listening to a few episodes of the Under the Radar podcast, I heard a number of references to the idea of “make sure you’re not testing the framework”. It is generally good advice, in the vein of “make sure you’re testing your code first and foremost”, but as a snippet out of context and taken as a rule – I think it’s faulty. Don’t confuse what you are testing, but reliably testing underlying frameworks or libraries, especially while learning them or they evolve, can easily be worth the effort.

I have received a huge amount of value from testing frameworks – first in verifying that I understand what the library is doing and how it works. More over, it has been a very clear signal when regressions do happen, or intentional functionality changes.

If you do add tests of a framework or library into your codebase, I recommend you break them out into their own set of tests. If something does change in the library, it will be far more clear that it is a change from the library and not a cascading side effect in your code.

Most recently, this effort paid off when I stumbled across a regression in the Combine framework functionality with the GM release of Xcode 11.2. While I’ve been coming up to speed with the various operators, I’ve written unit tests that work the operators. In this case, the throttle operator – which has an option parameter latest – changed in how it operates with this release.

Throttle is very similar to the debounce operator, and in fact it operates the same if you use the option latest=true. They both take in values over time and return a single value for a specific time window. If you want the first value that’s sent within the timeframe, theoretically you should use latest=false with the throttle operator. This worked in earlier releases of Combine and Xcode – but in the latest release, it’s now disregarding that path and sending only the latest value.

You can see the tests I wrote to verify the functionality at https://github.com/heckj/swiftui-notes/blob/master/UsingCombineTests/DebounceAndRemoveDuplicatesPublisherTests.swift, and right now I’m working on a pull request to merge in the change reflecting the current release and illustrating the regression. And before you ask, yes – I have submitted this as a bug to Apple (FB7424221). If you are relying on the specific functionality of throttle with latest=false, be aware that the latest release of Xcode & Combine is likely going to mess with it.

If you are more curious about all the other tests that were created to support Using Combine, then feel free to check out the github repository heckj/swiftui-notes – the tests are in the UsingCombineTests directory, and set up as they’re own test target in the Xcode project. There are more to write, as I drive down into the various operators, so I do expect more will appear. I won’t assert that they’re all amazing, well constructed tests – but they’re getting the job done in terms of helping me understand how they work – and how they don’t work.

A Using Combine update now available!

A new version of Using Combine (v0.7) is now available! 

The free HTML site of Using Combine has been updated automatically, and the PDF and ePub versions are available on Gumroad.

This version has relatively few updates, primarily focused on some of the missing publishers and resolving the some of the egregious flaws in ePub rendering. No significant changes have come with the later Xcode and IOS betas, and with Xcode 11 now in GM release, it was a good time for another update to be made available.

For the next release, I am focusing on fleshing out a number of the not-yet-written reference sections on operators, most of which are more specialized than the more generally used ones that have already been covered.

The project board at https://github.com/heckj/swiftui-notes/projects/1 also reflects all the various updates still remaining to be written.

Using Combine (v0.6) available!

design by Michael Critz

A new version of Using Combine is available! The free/online version of Using Combine is updated automatically as I merge changes, and the PDF and ePub versions are released periodically and available on Gumroad.

https://gumroad.com/js/gumroad.js Purchase Using Combine

The book now has some amazing cover art, designed and provided by Michael Critz, and has benefited from updates provided by a number of people, now in the acknowledgements section.

The updates also include a section broken out focusing on developing with Combine, as well as a number of general improvements and corrections.

For the next release, I am going to focus on fleshing out a number of the not-yet-written reference sections:

the publishers I haven’t touched on yet

– starting into a number of the operators, most of which are more specialized

I reviewed the content prior to this release to see what was remaining to be done, and updated the project planning board with the various updates still remaining to be written.

I do expect we’ll see beta6 from Apple before too long, although exactly when is unknown. I thought it might appear last week, in which case I was planning on trying to accommodate any updates in this release. Xcode 11 beta6 hasn’t hit the streets and I wanted to get an update regardless of its inclusion.

navigating Swift Combine, tuples, and XCTest

What started out as a Github repository to poke at SwiftUI changed course a few weeks ago and became a documentation/book project on Combine, Apple’s provided framework for handling asynchronous event streams, not unlike ReactiveX. My latest writing project (available for free online at https://heckj.github.io/swiftui-notes/) has been a foil for me to really dig into and learn Combine, how it works, how to use it, etc. Apple’s beta documentation is unfortunately highly minimal.

One of the ways I’ve been working out how its all operating is writing a copious amount of unit tests, more or less “poking the beast” of the code and seeing how it’s operating. This has been quite successful, and I’ve submitted what I suspect are a couple of bugs to Apple’s janky FeedbackAssistant along with tests illustrating the results. As I’m doing the writing, I’m generating sample code and examples, and then often writing tests to help illuminate my understanding of how various Combine operators are functioning.

In the writing, I’ve worked my way through the sections to where I’m tackling some of the operators that merge streams of data. CombineLatest is where I started, and it testing it highlighted some of the more awkward (to me) pieces of testing swift code.

The heart of the issue revolves around asserting equality with XCTest, Apple’s unit testing framework, and the side effect that Combine takes advantage of tuples as lightweight types in operators like CombineLatest. In the test I created to validate how it was operating, I collected the results of the data into an ordered list. The originated streams had simple, equatable types – one String, the other Int. The resulting collection, however, was a tuple of <(String, Int)>.

To use XCTAssertEquals, the underlying types that you are validating need to conform to Equatable protocol. In the case of checking a collection type, it drops down and relies on the equatable conformance of its underlying type. And that is where it breaks down – tuples in swift aren’t allowed to conform to protocols – so they can’t declare (or implement conformance with) equatable.

I’m certainly not the first to hit this issue – there’s a StackOverflow question from 2016 that highlights the issue, Michael Tsai highlights the same on his blog (also from 2016). A slightly later, but very useful StackOverflow Q&A entitled XCTest’ing a tuple was super helpful, with nearly identical advice to Paul Hudson’s fantastic swift tips in Hacking With Swift: how to compare equality on tuples. I don’t know that my solution is a good one – it really feels like a bit of a hack, but I’m at least confident that it’s correct.

The actual collection of results that I’m testing is based on Tristan’s Combine helper library: Entwine and EntwineTest. Entwine provides a virtual time scheduler, allowing me to validate the timing of results from operators as well as the values themselves. I ended up writing a one-off function in the test itself that did two things:

  • It leveraged an idea I saw in how to test equality of Errors (which is also a pain in the tuckus) – by converting them to Strings using debugDescription or localizedDescription. This let me take the tuple and consistently dump it into a string format, which was much easier to compare.
  • Secondarily, I also wrote the function so that the resulting tests were easy to read in how they described the timing and the results that were expected for a relatively complex operator like combineLatest.

If you’re hitting something similar and want to see how I tackled it, the code is public UsingCombineTests/MergingPipelineTests.swift. No promises that this is the best way to solve the problem, but it’s getting the job done:

func testSequenceMatch(
    sequenceItem: (VirtualTime, Signal<(String, Int), Never>),
    time: VirtualTime,
    inputvalues: (String, Int)) -> Bool {

    if sequenceItem.0 != time {
        return false
    }
    if sequenceItem.1.debugDescription != Signal<(String, Int),
       Never>.input(inputvalues).debugDescription {
        return false
    }
    return true
}

XCTAssertTrue(
    testSequenceMatch(sequenceItem: outputSignals[0], 
                      time: 300, inputvalues: ("a", 1))
)

XCTAssertTrue(
    testSequenceMatch(sequenceItem: outputSignals[1], 
                      time: 400, inputvalues: ("b", 1))
)

Tristan, the author of Entwine and EntwineTest, provided me with some great insight into how this could be improved in the future. The heart of it being that while swift tuples don’t/can’t have conformance to protocols like Hashable and Equatable, structs within Swift do. It’s probably not sane to make every possible combinatorial set in your code, but it’s perfectly reasonable to make an interim struct, map the tuples into it, and then use that struct to do the testing.

Tristan also pointed out that a different struct would be needed for the arity of the tuple – for example, a tuple of <String, Int> and <String, String, Int> would need different structs. The example implementation that Tristan showed:

struct Tuple2 {
  let t0: T0
  let t1: T1
  init(_ tuple: (T0, T1)) {
    self.t0 = tuple.0
    self.t1 = tuple.1
  }
  var raw: (T0, T1) {
    (t0, t1)
  }
 }

extension Tuple2: Equatable where T0: Equatable, T1: Equatable {}
extension Tuple2: Hashable where T0: Hashable, T1: Hashable {}

A huge thank you to Tristan for taking the time to explain the tuple <-> struct game in swift!

After having spent quite a few years with dynamic languages, this feels like jumping through a lot of hoops to get the desired result, but I’m pleased that at least it also makes sense to me, so maybe I’m not entirely lost to the dynamic languages.

Commodity and fashion with SwiftUI

I’m only just starting to dig into the new declarative UI framework that Apple announced at WWDC this year: SwiftUI, but already there are a few patterns emerging that will be fascinating to watch in the Mac & IOS development community in the coming months.

The huge benefit of SwiftUI is, as a declarative framework, the process of creating and writing UI interfaces across Apple’s platforms should be far more consistent. I fully expect that best practices will get tried, shared, and good ideas swiftly copied – and culturally the community will glob around some common forms.

This is tensioned against what Apple (and its developers) has tended to celebrate in the past decade: independence and creative expression. UI elements and design have been following a fashion pattern, with the farthest reaching elements of that design being nearly unusable as those experiments pushed the boundaries beyond what was intuitively understood in user experiences. Sometimes the limits pushed so far as to not even be explorable.

Color, Layout, and graphics design are all clearly customizable with SwiftUI. I also expect that some of the crazier innovations (such as the now common “pull to refresh” gesture) will become significantly harder to enable from declarative structures. By its very nature, the declarative structure will make the common, well established UI elements easy and accessible, so much so that I wouldn’t be surprised to see a lot of early SwiftUI apps to “all look alike”. I expect the impact of “all looking alike” to drive a number of IOS dev teams a bit nuts.

The “escape hatches” to do crazy things clearly do exist – and while I haven’t reached that level of learning with SwiftUI, it does seem to follow the “make easy things simple, and hard things possible” concept of progressive disclosure.

It will be interesting to see what happens this fall when the first SwiftUI apps become available as experiments and where that takes consistency and usability on the Apple platforms.