A blog exploring functional programming and Swift.

Unobtrusive runtime warnings for libraries

Monday Jan 3, 2022

Runtime warnings in libraries are a great way to notify your users that something unexpected has happened, or that an API is being used in an incorrect manner. There are a number of ways of handling this, from in-your-face assertion failures to easy-to-miss console logging. In this post we give an overview of some of those techniques that can be employed easily today, as well as discuss a technique for surfacing runtime warnings that is both very visible and unobtrusive.

Current state of runtime warnings

Since the very early days of the Composable Architecture, we have performed certain checks inside the library to make sure that its APIs are being used properly. This first started with raising an assertion failure when we detected an action was sent to an optional reducer while state was nil. We did this because sending such an action is considered an application logic error and could hide subtle bugs in your application since the actions are being silently ignored. The assertion message provides a detailed explanation of why it was triggered, as well as how one might potentially fix it:

Although it is nice to be notified of these problems early and in a visible manner, it’s also quite disruptive. Because assertion failures crash the application you have no choice but to restart, which means you lose your current working context. Further, there are a lot more of these types of application logic errors we’d like to catch in the library, but that means we will be creating a minefield of assertion failures that our users can trip over. That creates an unhappy experience when using the library.

So, we looked for less obtrusive ways to surface these messages. One option is to simply print the messages to the console, but it is very easy for that to get lost amongst everything else being printed to the console. Perhaps a happy medium between terminating the application with an assertion failure and printing to the console would be to temporarily stop the application with a breakpoint.

Interestingly Apple’s Combine framework even ships with such a tool. The .breakpoint operator allows you to tell Combine to breakpoint in the middle of a publisher chain when a condition is met. This makes it easy to debug long, complex publisher chains, which are notoriously difficult to get insight into their internals.

Stopping a process with the debugger activated is as simple as raising a SIGTRAP signal, which in Swift can be accomplished like this:

raise(SIGTRAP)

However, raising SIGTRAP like this when the debugger is not attached will crash the process. So, a little extra work has to be done to first detect if the debugger is attached, and only if it is do we raise the signal:

/// Raises a debug breakpoint if a debugger is attached.
@inline(__always) func breakpoint(_ message: @autoclosure () -> String = "") {
  #if DEBUG
    // https://github.com/bitstadium/HockeySDK-iOS/blob/c6e8d1e940299bec0c0585b1f7b86baf3b17fc82/Classes/BITHockeyHelper.m#L346-L370
    var name = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
    var info = kinfo_proc()
    var info_size = MemoryLayout<kinfo_proc>.size

    let isDebuggerAttached = sysctl(&name, 4, &info, &info_size, nil, 0) != -1
      && info.kp_proc.p_flag & P_TRACED != 0

    if isDebuggerAttached {
      fputs(
        """
        \(message())

        Caught debug breakpoint. Type "continue" ("c") to resume execution.

        """,
        stderr
      )
      raise(SIGTRAP)
    }
  #endif
}

This style of runtime warnings is much better than assertion failures. It only temporarily stops the application, allowing the user to see why we are warning them and then they simply click the continue button (⌃+⌘+Y) or type “c” into the prompt to resume execution.

In fact, this new experience for runtime warnings was so much better we started sprinkle in more of them to catch even more application logic errors. This includes when actions are sent to .forEach reducers for ids that no longer exist in the collection (see here), as well as when SwitchStores are used in a non-exhaustive manner (see here), and most recently in order to perform certain thread checks in the Store and ViewStore (see here).

A better way

While using breakpoints provided a much better user experience than assertion failures, we still felt there was room for improvement. The fact that breakpoints were being triggered was not expected by our users and caused confusion. Many thought that their application was crashing, and that the only way to resume was to restart the application. Even worse, the stack trace at the moment of breakpoint doesn’t point exactly to where the SIGTRAP is raised, but rather there are a few un-symbolicated frames in front of your frame, which can be very confusing. And on top of all of that, getting caught on a breakpoint can still be quite disruptive to your workflow.

Xcode actually provides some really great, unobtrusive runtime warnings for certain things, such as when you mutate an ObservedObject being used in a SwiftUI view on a background thread:

Screenshot of purple runtime warning in Xcode that says: Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.

It even helpfully lets you know how one usually fixes the problem.

Wouldn’t it be great if we could show these types of warnings instead of stopping the application on a breakpoint? It allows the user to test their application without us interrupting them, and we get a very visible way to raise warnings to their attention.

Unfortunately, Apple does not publicly expose APIs for creating these runtime warnings (we filed a feedback, and encourage you to do the same!). But, that doesn’t mean we can’t do some dynamic Swift runtime hacking to invoke private LLDB functions behind the scenes!

We came across an old Stack Overflow post where Saagar Jha (website) demonstrates a crude way to piggyback on the main thread checker to show custom purple warnings in Xcode. This was exciting to see, but sadly the warnings were denoted as being related to main thread checking, which would be strange to see for warnings that have nothing to do with threading.

So, we reached out to Saagar to see if there were other options. It turns out that the warnings can be generated by writing to a specific OSLog in a very specific manner. There is an overload of os_log that allows you to specify the type of log (e.g. default, info, debug, error, fault), an OSLog and then the string you want to log:

os_log(
  <#os.OSLogType#>,
  <#log: os.OSLog#>,
  <#StaticString#>,
  <#CVarArg#>
)

Some of these arguments are easier to fill in than others. For example, the type of log can be .fault to denote a system-level error, and the StaticString is just whatever we want to log:

os_log(
  .fault,
  log: <#os.OSLog#>,
  "We encountered a runtime warning"
)

For the log argument we can construct an OSLog from scratch that targets the subsystem “com.apple.runtime-issues”, and the category can be anything we want:

os_log(
  .fault,
  log: OSLog(
    subsystem: "com.apple.runtime-issues",
    category: "ComposableArchitecture"
  ),
  "We encountered a runtime warning"
)

This subsystem seems to be what Xcode watches in order to know when to show the runtime purple warnings in the editor. However, if we simply run an application with this logging performed immediately we will see that no purple warnings pop up.

It turns out that it is not enough to simply log to the subsystem. Historically it seems that these purple warnings could only be initiated from private, magic functions in LLDB, such as the thread sanitizer, undefined behavior sanitizer, and a few others. However, when SwiftUI launched, Apple privileged that framework with the ability to also create these warnings, and so if we can make it seem as if the os_log is happening from within SwiftUI we may trick Xcode into showing the purple warning.

To do this one must provide a dso argument to os_log, which sadly has very little documentation:

os_log(
  .fault,
  dso: <#UnsafeRawPointer#>,
  log: OSLog(
    subsystem: "com.apple.runtime-issues",
    category: "ComposableArchitecture"
  ),
  "We encountered a runtime warning"
)

The dso argument is just a nebulous UnsafeRawPointer. There is very little documentation on what exactly dso is or how to construct them. It’s an acronym for “dynamic shared object”, and there’s a Twitter thread from 2016 where Joe Groff alludes to it being used to identify a dynamic library, and so we need to somehow construct one that points to one of Apple’s libraries, in particular SwiftUI.

The way one opens dynamic libraries is via the dlopen function, which we’ve actually used before in our xctest-dynamic-overlay library for accessing XCTest symbols dynamically, which allows us to conditionally use XCTFail in code that was not built for testing.

Xcode does not provide any info on how to use dlopen, but it’s just a plain C function so we can access its man pages from terminal:

NAME
dlopen -- load and link a dynamic library or bundle

SYNOPSIS
#include <dlfcn.h>

void*
dlopen(const char* path, int mode);

The path is where we want to search for the dynamic library, which we can use nil if we want to search all paths, and the mode allows us to pass along various options for searching. The man pages suggest using RTLD_LAZY as a default, so that is what we will do:

dlopen(nil, RTLD_LAZY)

This returns an UnsafeMutableRawPointer?, which can be used to look up the address of a particular symbol using another C function, dlsym. Again we can refer to the man pages of this function to get some more information:

NAME
dlsym -- get address of a symbol

SYNOPSIS
#include <dlfcn.h>

void*
dlsym(void* handle, const char* symbol);

It takes a handle as the first argument, which is the thing returned to us from dlopen, and the symbol we want to search for as the second argument. Searching for a symbol in xctest-dynamic-overlay was quite easy because we were looking for a plain C function, and so could refer it by such directly. However there are no C functions in the SwiftUI framework. Instead we must find the name of a symbol in SwiftUI.framework, which can be difficult because Swift symbols are mangled to encode a lot of information about the symbol in a small, textual description.

Luckily Xcode ships with a tool for exploring of the symbol names in a framework, and it’s called nm. If you run the following command in terminal you will see over 52,000 symbols printed to the console:

$ nm -g /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/SwiftUI.framework/SwiftUI.tbd

00000000 S _$s10Foundation15AttributeScopesO7SwiftUIE05swiftE0AcDE0D12UIAttributesVmvg
00000000 S _$s10Foundation15AttributeScopesO7SwiftUIE05swiftE0AcDE0D12UIAttributesVmvpMV
00000000 S _$s10Foundation15AttributeScopesO7SwiftUIE0D12UIAttributesV014BaselineOffsetB0O4nameSSvgZ
00000000 S _$s10Foundation15AttributeScopesO7SwiftUIE0D12UIAttributesV014BaselineOffsetB0OAA19AttributedStringKeyADMc
00000000 S _$s10Foundation15AttributeScopesO7SwiftUIE0D12UIAttributesV014BaselineOffsetB0OAA28DecodableAttributedStringKeyADMc
...
52,000 more lines...


----------
nm -gU /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/SwiftUI.framework/SwiftUI.tbd | perl -e 'print sort { length($a) <=> length($b) } <>'
----------

If you are curious what one of these mangled names represents you can use the swift demangle tool to get access to the Swift signature that you are more familiar with:

$ swift demangle s10Foundation15AttributeScopesO7SwiftUIE05swiftE0AcDE0D12UIAttributesVmvg

$s10Foundation15AttributeScopesO7SwiftUIE05swiftE0AcDE0D12UIAttributesVmvg ---> (extension in SwiftUI):Foundation.AttributeScopes.swiftUI.getter : (extension in SwiftUI):Foundation.AttributeScopes.SwiftUIAttributes.Type

We can chose one of the mangled symbol names and plug it into dlsym:

dlsym(
  dlopen(nil, RTLD_LAZY),
  "$s10Foundation15AttributeScopesO7SwiftUIE05swiftE0AcDE0D12UIAttributesVmvg"
)

And this returns a UnsafeMutableRawPointer!. This is a handle to the address of the symbol we hard coded above, which we can finally use to find the dynamic library holding the symbol, i.e. SwiftUI. We can do this via the dladdr C function, which we can find some information from its man page:

NAME
dladdr -- find the image containing a given address

SYNOPSIS
#include <dlfcn.h>

int
dladdr(const void* addr, Dl_info* info);

This function takes an addr, which is the thing that dlsym returned, and a mutable info, which is a structuring containing a few fields. To invoke dladdr we need to construct a mutable Dl_info that we can pass as an inout:

var info = Dl_info()
dladdr(
  dlsym(
    dlopen(nil, RTLD_LAZY),
    "$s10Foundation15AttributeScopesO7SwiftUIE05swiftE0AcDE0D12UIAttributesVmvg"
  ),
  &info
)

And once that is done info.dli_fbase holds the address of the SwiftUI dynamic library. This is the value we must pass for the dso argument:

os_log(
  .fault,
  dso: info.dli_fbase,
  log: OSLog(
    subsystem: "com.apple.runtime-issues",
    category: "ComposableArchitecture"
  ),
  "We encountered a runtime warning"
)

And if we execute this code in an application running in the simulator we will see something amazing:

Screenshot of Xcode showing a purple runtime warning with stack trace.

Xcode has displayed a purple warning directly on the line where we performed the os_log, and the warning has displayed in the issue navigator (⌘+5) with the full stack trace captured at the moment the warning was logged!

A word of warning

If it wasn’t clear from our multiple uses of dynamic loading C functions that have no documentation whatsoever in Xcode let us make it clear: none of the above is officially endorsed by Apple. All of that code should be wrapped in #if DEBUG so that it is never shipped to the App Store, which may cause it to be rejected, and the code could easily break in future iOS releases.

However, we hope we have convinced you that having access to these kinds of warnings would be hugely beneficial to library maintainers, and so ideally Apple would provide first class support for this in Xcode. We encourage all of our readers and viewers to submit a duplicate our feedback asking for Apple to give developers access to these runtime warnings.

Try it out today

We have just released version 0.32.0 of the Composable Architecture that replaces all breakpoint warnings with new, unobtrusive warnings. We’ve been using it for the past few weeks while developing new features for isowords, and it’s a game changer in terms of developer productivity. We now have instant insight into moments we accidentally break invariants that the library expects us to uphold without disrupting what we are currently working on.

Update your projects to the new Composable Architecture release to give it a spin today, and let us know what you think!


2021 Year-in-review

Wednesday Dec 22, 2021

It’s the end of the year again, and we’re feeling nostalgic 😊. We’re really proud of everything we produced for 2021, so join us for a quick review of some of our favorite highlights.

We are also offering 25% off the first year for first-time subscribers. If you’ve been on the fence on whether or not to subscribe, now is the time!

Highlights

2021 was our biggest year yet:

  • 42 episodes released for a total of 29 hours of video.
  • 72k unique vistors to the site.
  • Over 124k video views, 4 years and 100 days watching time, and over 42 terabytes of video streamed.
  • 5 new projects open sourced.

But these high-level stats don’t scratch the surface of what we covered in 2021:

SwiftUI Navigation

By far, the most ambitious series of episodes we tackled in 2021 was SwiftUI Navigation. Over the course of 9 episodes we gave a precise definition of what navigation means in an application, explored SwiftUI’s navigation tools (including tabs, alerts, modal sheets, and links), and then showed how to build new navigation tools that allow us to model our domains more concisely and correctly.

After completing that series we open sourced a library with all the tools discussed in the series. This makes it easy to model navigation in your application using optionals and enums, and makes it straightforward to drive deep-linking with your domain’s state.

We also used the application built in the series to explore two additional topics at the end of the year. First, we rebuilt the application in UIKit (part 1, part 2), all without making a single change to the view model layer. This shows just how powerful it is to drive navigation off of state. Second, we explored modularity (part 1, part 2) by breaking down the application into many modules. Along the way to explored different types of modularity, how to structure a modern Xcode project with SPM, and how to build preview apps that allow you to run small portions of your code base without building the entire application.

Open Source

Since launching Point-Free in 2018 we have open sourced over 20 projects, and this year alone we released 5 new projects (3 of which were extracted from our Composable Architecture library):

isowords

In May of this year we released a word game for iOS called isowords. Alongside the release we also open sourced the entire code base. Both the client and server code are written in Swift, and the client code shows how to build a large, modularized application in SwiftUI and the Composable Architecture. We also released a 4-part series of episodes showing off some of the cooler aspects of the code base.

xctest-dynamic-overlay

It is very common to write test support code for libraries and applications, but due to how Xcode works one cannot do this easily. If you import XCTest in a file, then that file cannot be compiled to run on a simulator or device. This forces you to extract test helper code into its own target/module, even though ideally the code should live right next to your library code.

The xctest-dynamic-overlay library makes it possible to use the XCTFail assertion function from the XCTest framework in library and application code. It will dynamically find the XCTFail implementation in tests, and act as a no-op outside of tests.

swift-identified-collections

When modeling a collection of elements in your application’s state, it is easy to reach for a standard Array. However, as your application becomes more complex, this approach can break down in many ways, including accidentally making mutations to the wrong elements or even crashing. 😬

Identified collections are designed to solve all of these problems by providing data structures for working with collections of identifiable elements in an ergonomic, performant way.

swift-custom-dump

Swift comes with a wonderful tool for debug-printing the contents of any value to a string, and it’s called dump. It prints all the fields and sub-fields of a value into a tree-like description. However, the output is less than ideal: dictionaries are printed in non-deterministic order, values are printed with superfluous extra type information, and some types don’t print any useful information at all.

The swift-custom-dump library ships with a function that emulates the behavior of dump, but provides a more refined output of nested structures, optimizing for readability. Further, it uses the more refined output to provide two additional tools. One for outputting a nicely formatted diff between two values of the same type, and another that acts as a drop-in replacement for XCTAssertEqual with a much better error message when a test fails.

swiftui-navigation

A collection of tools for making SwiftUI navigation simpler, more ergonomic and more precise. The library allows you to model your application’s navigation as optionals and enums, and then provides the tools for driving alerts, modal sheets, and navigation links from state.

🎉 2022 🎉

We’re thankful to all of our subscribers for supporting us and helping us create this content and these libraries. We could not do it without you.

Next year we have even more planned, including a deep dive into Swift’s new concurrency tools, improvements to the Composable Architecture to play better with concurrency and SwiftUI navigation, as well as all new parsing episodes (including result builders, reversible parsing, routing) and more!

To celebrate the end of the year we are also offering 25% off the first year for first-time subscribers. If you’ve been on the fence on whether or not to subscribe, now is the time!

See you in 2022!


Open Sourcing SwiftUI Navigation

Tuesday Nov 16, 2021

Over the past 9 weeks we have built up the concepts of SwiftUI navigation from the ground up. When we started the series we didn’t think it would take us 6 hours of video to accomplish this, but along the way we discovered many tools for making working with SwiftUI navigation simpler, more ergonomic and more precise.

We believe the tools we uncovered are highly applicable to everyone working with SwiftUI, and so today we are excited to release them in a new open source library.

Motivation

SwiftUI comes with many forms of navigation (tabs, alerts, dialogs, modal sheets, popovers, navigation links, and more), and each comes with many ways to construct them. The ways of constructing roughly fall in two categories:

  • “Fire-and-forget”: Some initializers and methods do not take any bindings, which means SwiftUI fully manages navigation by itself. This means it is easy to get something on the screen quickly, but you also have no programmatic control over the navigation. Examples of this are the initializers on TabView and NavigationLink that do not take a binding.
  • “State-driven”: Most other initializers and methods do take a binding, which means you can mutate state in your domain to tell SwiftUI when it should activate or de-activate navigation. Using these APIs is more complicated than the “fire-and-forget” style, but doing so instantly gives you the ability to deep-link into any state of your application by just constructing a piece of data, handing it to a SwiftUI view, and letting SwiftUI handle the rest.

Navigation that is “state-driven” is the more powerful form of navigation, albeit slightly more complicated, but unfortunately SwiftUI does not ship with all the tools necessary to model our domains as concisely as possible and use SwiftUI’s navigation APIs.

For example, to show a modal sheet in SwiftUI you can provide a binding of some optional state so that when the state flips to non-nil the modal is presented. However, the content of that model must be determined by a non-binding value:

struct ContentView: View {
  @State var draft: Post?

  var body: some View {
    Button("Edit") {
      self.draft = Post()
    }
    .sheet(item: self.$draft) { (draft: Post) in
      EditPostView(post: draft)
    }
  }
}

This means that whatever actions EditPostView performs will be disconnected from the source of truth, draft. Ideally we could derive a Binding<Post> for the draft so that any mutations EditPostView makes will be instantly visible in ContentView.

Another problem arises when trying to model multiple navigation destinations as multiple optional values. For example, suppose there are 3 different sheets that can be shown in a screen:

.sheet(item: self.$draft) { (draft: Post) in
  EditPostView(post: draft)
}
.sheet(item: self.$settings) { (settings: Settings) in
  SettingsView(settings: settings)
}
.sheet(item: self.$userProfile) { (userProfile: Profile) in
  UserProfile(profile: userProfile)
}

This forces us to hold 3 optional values in state, which has 2^3=8 different states, 4 of which are invalid. The only valid states is for all values to be nil or exactly one be non-nil. It makes no sense if two or more values are non-nil, for that would representing wanting to show two modal sheets at the same time.

Ideally we’d like to represent these navigation destinations as 3 mutually exclusive states so that we could guarantee at compile time that only one can be active at a time. Luckily for us Swift’s enums are perfect for this:

enum Route {
  case draft(Post)
  case settings(Settings)
  case userProfile(Profile)
}

And then we could hold an optional Route in state to represent that we are either navigating to a specific destination or we are not navigating anywhere:

@State var route: Route?

This would be the most optimal way to model our navigation domain, but unfortunately SwiftUI’s tools do not make easy for us to drive navigation off of enums.

This library comes with a number of Binding transformations and navigation API overloads that allow you to model your domain as concisely as possible, using enums, while still allowing you to use SwiftUI’s navigation tools.

For example, powering multiple modal sheets off a single Route enum looks like this with the tools in this library:

.sheet(unwrapping: self.$route, case: /Route.draft) { $draft in
  EditPostView(post: $draft)
}
.sheet(unwrapping: self.$route, case: /Route.settings) { $settings in
  SettingsView(settings: $settings)
}
.sheet(unwrapping: self.$route, case: /Route.userProfile) { $userProfile in
  UserProfile(profile: $userProfile)
}

Tools

This library comes with many tools that allow you to model your domain as concisely as possible, using enums, while still allowing you to use SwiftUI’s navigation APIs.

Navigation overloads

This library provides additional overloads for all of SwiftUI’s “state-driven” navigation APIs that allow you to activate navigation based on a particular case of an enum. Further, all overloads unify presentation in a single, consistent API:

  • NavigationLink.init(unwrapping:case:)
  • View.alert(unwrapping:case:)
  • View.confirmationDialog(unwrapping:case:)
  • View.fullScreenCover(unwrapping:case:)
  • View.popover(unwrapping:case:)
  • View.sheet(unwrapping:case:)

For example, here is how a navigation link, a modal sheet and an alert can all be driven off a single enum with 3 cases:

enum Route {
  case add(Post)
  case alert(Alert)
  case edit(Post)
}
struct ContentView {
  @State var posts: [Post]
  @State var route: Route?

  var body: some View {
    ForEach(self.posts) { post in
      NavigationLink(
        unwrapping: self.$route,
        case: /Route.edit,
        onNavigate: { isActive in self.route = isActive ? .edit(post) : nil },
        destination: EditPostView.init(post:)
      ) {
        Text(post.title)
      }
    }
    .sheet(unwrapping: self.$route, case: /Route.add) { $post in
      EditPostView(post: $post)
    }
    .alert(
      title: { Text("Delete \($0.title)?") },
      unwrapping: self.$route,
      case: /Route.alert
      actions: { post in
        Button("Delete") {
          self.posts.remove(post)
        }
      },
      message: { Text($0.summary) }
    )
  }
}

struct EditPostView: View {
  @Binding var post: Post

  var body: some View {
    ...
  }
}

Navigation views

This library comes with additional SwiftUI views that transform and destructure bindings, allowing you to better handle optional and enum state:

  • IfLet
  • IfCaseLet
  • Switch

For example, suppose you were working on an inventory application that modeled in-stock and out-of-stock as an enum:

enum ItemStatus {
  case inStock(quantity: Int)
  case outOfStock(isOnBackorder: Bool)
}

If you want to conditionally show a stepper view for the quantity when in-stock and a toggle for the backorder when out-of-stock, you’re out of luck when it comes to using SwiftUI’s standard tools. However, the Switch view that comes with this library allows you to destructure a Binding<ItemStatus> in bindings of each case so that you can present different views:

struct InventoryItemView {
  @State var status: ItemStatus?

  var body: some View {
    Switch(self.$status) {
      CaseLet(/ItemStatus.inStock) { $quantity in
        HStack {
          Text("Quantity: \(quantity)")
          Stepper("Quantity", value: $quantity)
        }
        Button("Out of stock") { self.status = .outOfStock(isOnBackorder: false) }
      }

      CaseLet(/ItemStatus.outOfStock) { $isOnBackorder in
        Toggle("Is on back order?", isOn: $isOnBackorder)
        Button("In stock") { self.status = .inStock(quantity: 1) }
      }
    }
  }
}

Binding transformations

This library comes with tools that transform and destructure bindings of optional and enum state, which allows you to build your own navigation views similar to the ones that ship in this library.

  • Binding.init(unwrapping:)
  • Binding.case(_:)
  • Binding.isPresent() and Binding.isPresent(_:)

For example, suppose you have built a BottomSheet view for presenting a modal-like view that only takes up the bottom half of the screen. You can build the entire view using the most simplistic domain modeling where navigation is driven off a single boolean binding:

struct BottomSheet<Content>: View where Content: View {
  @Binding var isActive: Bool
  let content: () -> Content

  var body: some View {
    ...
  }
}

Then, additional convenience initializers can be introduced that allow the bottom sheet to be created with a more concisely modeled domain.

For example, an initializer that allows the bottom sheet to be presented and dismissed with optional state, and further the content closure is provided a binding of the non-optional state. We can accomplish this using the isPresent() method and Binding(unwrapping:) initializer:

extension BottomSheet {
  init<Value, WrappedContent>(
    unwrapping value: Binding<Value?>,
    @ViewBuilder content: @escaping (Binding<Value>) -> WrappedContent
  )
  where Content == WrappedContent?
  {
    self.init(
      isActive: value.isPresent(),
      content: { Binding(unwrapping: value).map(content) }
    )
  }
}

An even more robust initializer can be provided by providing a binding to an optional enum and a case path to specify which case of the enum triggers navigation. This can be accomplished using the case(_:) method on binding:

extension BottomSheet {
  init<Enum, Case, WrappedContent>(
    unwrapping enum: Binding<Enum?>,
    case casePath: CasePath<Enum, Case>,
    @ViewBuilder content: @escaping (Binding<Case>) -> WrappedContent
  )
  where Content == WrappedContent?
  {
    self.init(
      unwrapping: `enum`.case(casePath),
      content: content
    )
  }
}

Both of these more powerful initializers are just conveniences. If the user of BottomSheet does not want to worry about concise domain modeling they are free to continue using the isActive boolean binding. But the day they need the more powerful APIs they will be available.

Try it today

We’ve already started to get a lot of use out of SwiftUI Navigation, but we think there is so much more than can be done. Give it a spin today and start modeling your domains more precisely with enums while still making use of everything SwiftUI has to offer.


Older blog posts

Tuesday Oct 26, 2021

Give the Gift of Point-Free

Today we are excited to announce that you can now gift a Point-Free subscription to your friends, colleagues and loved ones.

Wednesday Sep 15, 2021

Point Freebies: Swift Concurrency and More

We're celebrating the release of Xcode 13 by making all of our WWDC 2021 videos free! Explore SwiftUI's new refreshable and FocusState APIs, both in the context of vanilla SwiftUI and the Composable Architecture, and learn how to build a map-powered application from scratch using the new searchable API.

Monday Sep 6, 2021

The Composable Architecture ❤️ SwiftUI Bindings

Today we are improving the Composable Architecture's first-party support for SwiftUI bindings with a safer, even conciser syntax.

Monday Aug 23, 2021

Open Sourcing: Custom Dump

Today we are open sourcing Custom Dump, a collection of tools for debugging, diffing, and testing your application's data structures.

Wednesday Jul 14, 2021

Better Performance Bonanza

The past 3 weeks we've shipped updates to 3 of our libraries, focused on improving the performance of your Composable Architecture applications, and more!

Monday Jul 12, 2021

Open Sourcing Identified Collections

Today we are open sourcing Identified Collections, a library of data structures for working with collections of identifiable elements in a performant way.

Monday Jun 14, 2021

Announcing SwitchStore for the Composable Architecture

We are adding new tools for handling enum state in the Composable Architecture, with a focus on safety and performance.

Wednesday May 12, 2021

A Tour of isowords

This past month we released four completely free videos dedicated to diving into the real-world Swift code base of an iOS game we recently launched and open sourced: isowords.

Monday Mar 22, 2021

Better Testing Bonanza

We're open sourcing a library that makes it easier to be more exhaustive in writing tests.

Wednesday Mar 17, 2021

Open Sourcing isowords

We're open sourcing the entire code base to our newly released iOS word game, isowords!

Wednesday Mar 17, 2021

Announcing: isowords

We are excited to release isowords to the App Store, a new word search game for your phone. Download today!

Monday Mar 8, 2021

Composable Architecture Test Store Improvements

Composable Architecture 0.16.0 comes with significant improvements to its testing capabilities for tracking down effect-related failures.

Monday Feb 1, 2021

Composable Forms: Say "Bye" to Boilerplate!

Today we are releasing first-party support for concisely handling form data in the Composable Architecture.

Wednesday Dec 23, 2020

2020 Year-in-review

The Composable Architecture, dependency management, parsers, Combine schedulers and more! Join us for a review of everything we accomplished in 2020!

Monday Dec 21, 2020

Open Sourcing Parsing

Today we are open sourcing Parsing, a library for turning nebulous data into well-structured data, with a focus on composition, performance, and generality.

Tuesday Jun 30, 2020

The Composable Architecture and SwiftUI Alerts

Today we are releasing a new version of the Composable Architecture with helpers that make working with SwiftUI alerts and action sheets a breeze.

Monday Jun 22, 2020

Core Motion support in the Composable Architecture

We are releasing our second mini-library for the Composable Architecture, which makes it easy to use Core Motion.

Monday Jun 15, 2020

Open Sourcing CombineSchedulers

Today we are open-sourcing CombineSchedulers, a library that introduces a few schedulers that makes working with Combine more testable and more versatile.

Wednesday May 27, 2020

Instrumenting features built in the Composable Architecture

Today we are releasing first-party support for instrumenting features built in the Composable Architecture.

Wednesday May 20, 2020

Core Location support in the Composable Architecture

We are releasing a mini-library that makes it easy to use Core Location inside the Composable Architecture.

Tuesday May 12, 2020

Regional Discounts

Regional discounts takes 50% off a monthly or yearly personal subscription to anyone whose credit card is issued from a particular list of countries.

Monday May 4, 2020

Composable Architecture, the library

Today we are releasing the Composable Architecture as an open-source library. It is a way to build applications in a consistent and understandable way, with composition, testing and ergonomics in mind.

Wednesday Mar 11, 2020

Announcing Episode Collections

After over two years and nearly 100 episodes we are finally launching episode collections on Point-Free!

Friday Feb 21, 2020

Share Point-Free with friends and save!

Today we're excited to announce support for referral bonuses! When friends and colleagues of yours subscribe to Point-Free with your referral link, both of you will get one month free.

Tuesday Feb 4, 2020

Open Sourcing Case Paths

Today we are open sourcing CasePaths, a library that introduces the power and ergonomics of key paths to enums!

Monday Dec 30, 2019

2019 Year-in-review

Random number generators, parsers, SwiftUI, composable architecture and more! Join us for a review of everything we accomplished in 2019!

Monday Dec 23, 2019

Snapshot Testing SwiftUI

Snapshot testing gives us broad test coverage on our SwiftUI views with very little up front work.

Wednesday Dec 18, 2019

Free Video: Testing SwiftUI

A free video exploring how to test SwiftUI.

Wednesday Nov 20, 2019

A Crash Course in Combine

Two free videos exploring Apple's new Combine framework, its core components, and how to integrate it in your code.

Thursday Nov 7, 2019

Higher-Order Snapshot Testing

How to enrich snapshot testing strategies with additional behavior using higher-order constructions.

Tuesday Jul 30, 2019

SwiftUI and State Management Corrections

Xcode 11 beta 5 has brought lots of changes to SwiftUI, and we'd like to take a moment to provide corrections to our episodes based on these changes.

Thursday May 9, 2019

Enterprise Subscriptions

Point-Free now supports enterprise subscriptions, making it easier than ever to manage a team subscription for larger organizations! For a fixed yearly rate, everyone with an email from your company's domain will get instant access to everything Point-Free has to offer. Contact us for more info!

Monday Apr 29, 2019

Open Sourcing Enum Properties

We wanted to make Swift enum data access as ergonomic as struct data access, so today we are open sourcing a code generation tool to do just that!

Monday Mar 18, 2019

Open Sourcing Gen

Today we are open sourcing Gen: a lightweight wrapper around Swift's randomness API's that makes randomness more composable, transformable and controllable!

Tuesday Jan 8, 2019

Announcing swift-html 0.2.0

Announcing swift-html 0.2.0: support for CocoaPods, Carthage, SnapshotTesting, and more!

Wednesday Dec 19, 2018

2018 Year-in-Review

41 episodes, 19 hours of video, 25 blog posts, 8 open source libraries, 3.8K stars, 36K visitors, and we’re just getting started?

Monday Dec 3, 2018

SnapshotTesting 1.0: Delightful Swift snapshot testing

Today we are open sourcing SnapshotTesting 1.0: a modern, composable snapshot testing library built entirely in Swift!

Monday Oct 29, 2018

Some news about contramap

We've seen that contramap is a powerful operation, but the name isn't fantastic. We propose a much more intuitive name for this operation, and in doing so make our code much easier to read.

Tuesday Oct 9, 2018

How to Control the World

APIs that interact with the outside world are unpredictable and make it difficult to test and simulate code paths in our apps. Existing solutions to this problem are verbose and complicated, so let's explore a simpler solution by embracing singletons and global mutation, and rejecting protocol-oriented programming and dependency injection.

Monday Oct 8, 2018

Watch episodes in your favorite podcast app!

Follow along with the newest Point-Free episodes using your favorite podcast app. We now support podcast-friendly RSS feeds for viewing all of our videos.

Thursday Sep 20, 2018

Random Zalgo Generator

Let's create a random Zalgo text generator using the simple Gen type we defined in this week's episode!

Thursday Sep 13, 2018

Type-safe HTML with Kitura

Today we're releasing a Kitura plug-in for rendering type-safe HTML. It provides a Swift compile-time API to HTML that prevents many of the runtime errors and vulnerabilities of traditional templated HTML rendering.

Thursday Sep 13, 2018

Type-safe HTML with Vapor

Today we're releasing a Vapor plug-in for rendering type-safe HTML. It provides a Swift compile-time API to HTML that prevents many of the runtime errors and vulnerabilities of traditional templated HTML rendering.

Wednesday Sep 12, 2018

Open sourcing swift-html: A Type-Safe Alternative to Templating Languages in Swift

Today we are open sourcing a new library for building HTML documents in Swift. It's extensible, transformable, type-safe, and provides many benefits over templating languages.

Friday Aug 17, 2018

Overture 0.3.0: Now with Zip

Today we are releasing Overture 0.3.0 with a bunch of useful zip functions.

Friday Aug 17, 2018

Open Sourcing Validated

Today we are open sourcing Validated, a tiny functional Swift library for handling multiple errors: functionality that you don't get from throwing functions and the Result type.

Thursday Aug 16, 2018

Solutions to Exercises: Zip Part 3

Today we solve the exercises to the third and final part of our introductory series on zip.

Wednesday Aug 15, 2018

Solutions to Exercises: Zip Part 2

Today we solve the exercises to the second part of our introductory series on zip.

Tuesday Aug 14, 2018

Solutions to Exercises: Zip Part 1

Today we solve the exercises to the first part of our introductory series on zip.

Monday Aug 6, 2018

Announcing Student Discounts

Get 50% off your Point-Free subscription with proof of enrollment at a university or coding school.

Monday Jul 30, 2018

Celebrating 6 Months

This week marks 6 months since our launch, and we’re making one of our most popular episodes free to the public!

Monday Jul 2, 2018

Conditional Coding

What happens when we combine Swift's conditional conformance with codability?

Monday Jun 25, 2018

Open Sourcing NonEmpty

Today we are open sourcing NonEmpty, a Swift library for modeling non-empty collection types. This small library can help make your code safer and more expressive with very little work.

Monday Jun 18, 2018

Tagged Seconds and Milliseconds

Let's create a type-safe interface for dealing with seconds and milliseconds in our programs. We'll use the `Tagged` type, which allows us to construct all new types in a lightweight way.

Wednesday May 30, 2018

Styling with Functions: Free for Everyone!

We are making one of our early episodes, “UIKit Styling with Functions”, free to everyone today! It’s a seminal episode that sets the foundation for some later work in the Point-Free series.

Tuesday May 15, 2018

Overture: Now with Functional Setters

Announcing Overture 0.2.0! This release is all about setters: functions that allow us to build complex transformations out of smaller units.

Monday May 7, 2018

Solutions to Exercises: Contravariance

This week we solve the exercises from our episode on contravariance, because there were _a lot_ of them!

Monday Apr 23, 2018

Case Study: Algebraic Data Types

Let’s look at a real world use for algebraic data types. We will refactor a data type that is used in the code on this very site so that the invalid states are unrepresentable by the compiler.

Monday Apr 23, 2018

Announcing Point-Free Pointers!

Today we are excited to announcement launch of Point-Free Pointers, a blog to supplement our video series for all the content we couldn’t fit in. Expect to find regularly postings here that dive even deeper into functional programming, showing real world use cases and more!