A blog exploring functional programming and Swift.

Cyber Monday Sale: 30% Off Point-Free

Monday Dec 2, 2019

Point-Free is getting into the Cyber Monday spirit by offering 30% off the first year to first-time subscribers!

Click here to redeem the coupon code now. The offer will only remain valid through December 2, 2019.


A Crash Course in Combine

Wednesday Nov 20, 2019

At this year’s WWDC, Apple introduced the Combine Framework, a composable library for handling asynchronous events over time, providing another alternative to open source libraries like ReactiveSwift and RxSwift.

The Combine framework is seriously powerful, and is responsible for handling a lot of SwiftUI’s high-level state management solutions under the hood! It’s a great library in its own right, and a worthy addition to your own libraries and application code.

The past couple weeks we have released two completely free videos dedicated to studying the Combine framework from first principles and showing how you can incorporate it into a library or application of your own:

  • The Combine Framework and Effects: Part 1: In this video, we explore Combine’s core components, including the Publisher and Subscriber protocols. We also cover some convenience functions and operators, which are layered over these more basic units.

  • The Combine Framework and Effects: Part 2: In this video we show how the Combine framework can be used to describe side effects in a reducer-based architecture, like Redux. We take an existing library we’ve been experimenting with and use Combine to help solve a critical problem.

If you’re curious about what SwiftUI has to say about architecture (and what it doesn’t have to say), we also have 3 free videos that explore just that:

We’re pretty excited about what Apple introduced this year and we can’t wait to see what it enables folks to build and how it evolves in the future!


Higher-Order Snapshot Testing

Thursday Nov 7, 2019


We love higher-order constructions here on Point-Free. For the uninitiated, if you have some construction in Swift, say a generic type F<A>, then a higher-order version of it is simply a function (F<A>) -> F<B>. That is, a function that takes your construction as input and returns it as output. Considering such higher-order constructions instantly allows you to enrich your code with very little work.

We’ve considered higher-order constructions quite a bit on Point-Free:

  • A higher-order function is a function that takes a function as input and returns a function as output.
  • A higher-order random number generator is a function that takes an RNG as input and returns an RNG as output. This, for example, allows you to construct randomly sized arrays of random elements given more basic generators.
  • A higher-order parser is a function that takes a parser as input and returns a parser as output. This, for example, allows you to parse any number of values from a string given a way to parse a single value.
  • A higher-order reducer is a function that takes a reducer as input and returns a reducer as output. This, for example, allows you to add logging abilities to any reducer.

We’d like to describe yet another application of higher-order ideas: enhancing snapshot testing strategies!

Snapshot Testing

Snapshot testing is a form of testing that saves a snapshot of a value you want to assert against, so that when you perform the assertion you compare the current value against a value saved to disk. The most popular form of snapshot testing is screenshot testing, in which you snapshot some kind of view into an image so that a single pixel difference can be caught if needed.

We first discussed snapshot testing in order to explore alternatives to protocol-oriented programming. We started by building the entire library in the protocol-oriented style (part 1, part 2), and although it worked just fine, there were definitely some drawbacks. It wasn’t capable of snapshotting types in multiple ways, and it was quite inert and rigid.

So, we scrapped the protocols and tried using simple, concrete data types to express the abstraction of snapshotting, and amazing things happened! Not only could we define multiple snapshot strategies for a single type, but snapshot strategies became a transformable thing. In particular, we defined a pullback operation that allows one to pullback snapshot strategies on “smaller” types to strategies on “larger” types. For example, we can pullback the image snapshotting strategy on UIView back to an image snapshotting strategy on UIViewController via the function { $0.view }.

These types of transformations were completely hidden from us when dealing with protocols. If you are interested in seeing how to use our library in a real world code base, you may be interested in our 🆓 tour of snapshot testing.

Waiting for Strategies

But what we didn’t discuss too much in our snapshot testing episodes is the concept of “higher-order snapshot strategies”, that is, functions that transform existing strategies into new strategies. Of course, the pullback operation is an example of such an operation, but there is so much more to explore.

A higher-order snapshot strategy allows you to enhance an existing strategy with behavior that it doesn’t need to know anything about. As a concrete example, many times when snapshotting a value we need to wait a little to give it time to prepare itself. Views may be animating, controllers may be pushing/popping, and alerts may be appearing. Unfortunately we do not have easy hooks into those lifecycle events, and so we really have no choice but to wait for a little bit of time.

The standard way to allow for this behavior in XCTestCase is using expectations:

func testController() {
  let vc = // create your view controller

  // Wait a little bit of time using expectations
  let expectation = self.expectation(description: "wait")
  DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
    expectation.fulfill()
  }
  self.wait(for: [expectation], timeout: 2)

  // Assert the snapshot after waiting...
  assertSnapshot(matching: vc, as: .image)
}

It’s not terrible, a little bit of boilerplate, and maybe we could even hide that in a helper on XCTestCase. Perhaps better would be to bake it directly into the assertSnapshot helper so that we could allow anyone snapshotting to easily wait for some time:

func testController() {
  let vc = // create your view controller

  // Assert the snapshot after waiting...
  assertSnapshot(matching: vc, as: .image, wait: 1)
}

That’s quite a bit nicer. However, the assertSnapshot function is quite complicated (here’s the helper that powers it). In fact, it’s already a bit too long for comfort, and adding this additional waiting logic comes at a serious cost.

Luckily for us, we can allow any snapshot strategy to be enriched with this functionality without needing special helpers on XCTestCase or ballooning the assertSnapshot API. And the tool we will use is none other than higher-order snapshot strategies!

We want to transform an existing strategy such that when we invoke its snapshot function we will automatically bake in the waiting logic. We can start by getting the signature of such a transformation in place:

extension Snapshotting {
  static func waiting(
    for duration: TimeInterval,
    on strategy: Snapshotting
  ) -> Snapshotting {
    fatalError("Unimplemented")
  }
}

We chose to define this as a static function so that at the call site in a test it would look like this:

func testController() {
  let vc = // create your view controller

  // Assert the snapshot after waiting...
  assertSnapshot(matching: vc, as: .waiting(for: 1, on: .image))
}

The first thing we need to do in this unimplemented method is return a new Snapshotting instance. We could call its initializer, which requires a pathExtension, a diffing strategy, and an snapshot function.

extension Snapshotting {
  static func waiting(
    for duration: TimeInterval,
    on strategy: Snapshotting
  ) -> Snapshotting {
    return Snapshotting(
      pathExtension: strategy.pathExtension,
      diffing: strategy.diffing,
      snapshot: { value in
        fatalError("Unimplemented")
    })
  }
}

But because these arguments are just passthroughs, and we are purely concerned with transforming how we snapshot the value, we can leverage pullback instead:

extension Snapshotting {
  static func waiting(
    for duration: TimeInterval,
    on strategy: Snapshotting
  ) -> Snapshotting {
    return self.pullback { value in
      fatalError("Unimplemented")
    }
  }
}

Inside the pullback we can finally do our expectation work. It will look almost exactly like the expectation work we did previously, except this time since we are operating outside an XCTestCase, so we need to use XCTestExpectation and XCTWaiter directly:

extension Snapshotting {
  static func waiting(
    for duration: TimeInterval,
    on strategy: Snapshotting
  ) -> Snapshotting {
    return strategy.pullback { value in
      let expectation = XCTestExpectation(description: "Wait")
      DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
        expectation.fulfill()
      }
      _ = XCTWaiter.wait(for: [expectation], timeout: duration + 1)
      return value
    }
  }
}

And just like that we have the ability to transform any snapshot strategy into one that can wait for a bit of time before it performs its snapshot work!

Conclusion

We have now shown that higher-order snapshot strategies allow us to add the functionality of waiting before taking snapshots without making any changes to the core library. All of the code we wrote could live outside the library, and that is the power of having a transformable and composable API. It allows you to enrich the functionality of a construction without needing to bake that functionality directly into the library.

Incidentally, we have also added this higher-order snapshot strategy to our open source library 😀. Check out the PR that adds the wait functionality here!


Older blog posts

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 randomess 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?

Wednesday Dec 19, 2018

Save 30% on Point-Free

To end 2018 on a high note we're offering a 30% discount to new subscribers for their first year. Just click through to read the full post below! We hope you'll join us for all of the great material we have planned for 2019.

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!