Archive for the ‘Swift’ Category

Better initializers and defaulted arguments

Yesterday, I was discussing initializing UIEdgeInsets. Developer Adam Sharp cleverly added computed properties to leveraging keypaths by extending the type to adopt ExpressibleByDictionaryLiteral:

extension UIEdgeInsets: ExpressibleByDictionaryLiteral {
    public typealias Key = WritableKeyPath<UIEdgeInsets, CGFloat>
    public typealias Value = CGFloat
    
    public init(dictionaryLiteral elements: (WritableKeyPath<UIEdgeInsets, CGFloat>, CGFloat)...) {
        self = UIEdgeInsets()
        for (inset, value) in elements {
            self[keyPath: inset] = value
        }
    }
}

This approach lets you use a dictionary literal to initialize your type:

let insets: UIEdgeInsets = [\.left: 8]
print(insets) // (l: 8.0, r: 0.0, t: 0.0, b: 0.0)

Pop in a few custom properties, specifically horizontal, vertical, and all, and you have a really cute way of initializing edge insets without building a plethora of custom initializers, keeping the API boundary (Thanks Daniel J) nice and compact:

public extension UIEdgeInsets {
   public var vertical: CGFloat {
        get { return 0 } // meaningless but not fatal
        set { (top, bottom) = (newValue, newValue) }
    }
    
    public var horizontal: CGFloat {
        get { return 0 } // meaningless but not fatal
        set { (left, right) = (newValue, newValue) }
    }
    
    public var all: CGFloat {
        get { return 0 } // meaningless but not fatal
        set { (vertical, horizontal) = (newValue, newValue) }
    }
}

Unfortunately, you must supply a getter: a WriteableKeyPath is a “key path that supports reading from and writing to the resulting value.” (Emphasis mine.) That’s why I included the silly return 0 statements for each getter. I originally put in a fatal error but that only got me grief because the values were being read before writing.

Incidentally, Swift does not allow you to build a write-only type for compound abstractions like these. Just in case you were thinking of going that way with your code, here’s what you can expect:

With the dictionary-initializable approach, you may use a dictionary literal with as many or as few key paths as you need to fully customize your instance:

let insets2: UIEdgeInsets = [\.vertical: 8, \.horizontal: 20]
print(insets2) // (l: 8.0 , r: 20.0, t: 8.0, b: 20.0)

let insets3: UIEdgeInsets = [\.all: 8]
print(insets3) // (l: 8.0 , r: 8.0, t: 8.0, b: 8.0)

Stephen Celis notes, “The nice thing about key paths are they’re compiler generated code. you can write a single initializer function and get everything for free without having to define one-off enums or initializers every time.”

This approach is generally useful enough that it’s worth abstracting out a little to support dictionary literal initialization for any type with uniformly-typed property members such as CGRect or CGPoint. Nate C came up with a very clever approach to do exactly that. Here’s a modified version of his approach:

/// Allows dictionary literal initialization for any
/// conforming type that declares `typealias Value`,
/// where `Value` refers to a uniform property Type
/// that can be set through a keypath-value dictionary
///
/// - Example:
///   ```
///   extension CGPoint : UniformKeypathInitializable {
///     public typealias Value = CGFloat
///   }
///
///   let p: CGPoint = [\.x: 0, \.y: 20]
///   ```
public protocol UniformKeypathInitializable : ExpressibleByDictionaryLiteral {
    /// Allow zero-argument initializer
    init()
    
}

extension UniformKeypathInitializable {
    /// Initializes each member of a keypath-value
    /// dictionary, allowing the type to be initialized
    /// with a dictionary literal
    public init(dictionaryLiteral elements: (WritableKeyPath<Self, Value>, Value)...) {
        self.init()
        for (property, value) in elements {
            self[keyPath: property] = value
        }
    }
}

You provide a typealias for `Value`, which in this case means the type of the values supplied in the dictionary, and the magic happens for you.:

extension UIEdgeInsets: UniformKeypathInitializable {
    public typealias Value = CGFloat
}

That’s all it takes. Add the custom compound properties and you’re good to go.

Interestingly enough, during this process, I came across possibly the most inscrutable Swift error message ever (which I believe is saying something). Here’s one of my early attempts before I found Nate’s solution, and the error it produced:

Gotta love Swift.

Anyway, if there are errors in the post, fixes, improvements, or suggestions (and you know there always will be), let me know. Email, tweet, comment, whatever you like. Thanks as always!

Carrying user-sourced code forward in Swift Playgrounds for iOS

Had a really neat challenge today, as a Slack-buddy attempted to work with Apple’s exquisitely insufficient Playground Book documentation. His goal was simple: he wanted to be able to incrementally grow and test code from page to page, copying the user’s work as they moved on.

In theory, Swift Playgrounds for iOS enables you to build books where your reader/student incrementally builds code. Each page introduces a new concept, a new tweak, or a new approach. It’s a great way to layer each lesson on a previous take, or to take one lesson and branch it out to multiple endpoints.

You can either build, build, build to one big story or take one core concept and show many different ways to apply it. Either way, you want to be able to bring code — whether from the most recently edited page or from a shared core page — forward, so the reader/student can further engage with it, edit it, and make it fit with each page’s challenge.

Implementing “code forwarding” (I just made up that term) proved trickier than expected. Playground book workflow is often “understood” (that is, you have a deep understanding of what’s required because you’ve worked with it a lot or you’ve poked around at Apple’s examples or reversed engineered to see how things work) rather than explained step by step in the official docs. Because of this, he ran into several roadblocks along the way.

  • First among these, is that Apple does not provide a Playgrounds Book Author tool for Mac. You have to build your books by hand, going through the specs and hoping that each iteration works. Most of the time it does. Sometimes, maddeningly, it does not.
  • Second, you have to transfer the book to the iPad for each test (I use AirDrop™), and guess at what went wrong if it doesn’t work. When testing a series of book-based exercises, you have to either hard-code each “success” sequence (and there’s no way to set a “I am debugging/developing this book” flag) or actually do the coding, which can take a lot of time, especially if you’re debugging page 7 and you have to work through the exercises on page 1 – 6 for each test of page 7.
  • Finally, if your book is even a little out of spec with what Swift Playgrounds for iOS expects, it’s going to die without much feedback or explanation, leaving you scratching your head, cursing Dev Tools (but we really love you guys, we do), or otherwise venting frustration.

I wasted a bunch of hours because I wanted to make this work. And finally, I managed to get things working to my satisfaction. I thought I’d take the time to write things up to save you some bother. Here are a few things I learned while deep diving into today’s experiments.

The Zen of User Code

Code-forwarding allows you to propagate user-sourced/user-edited code from an earlier page in a playground book to a later page in a playground book. When you code-forward from a “source” to a “destination”, Swift Playgrounds for iOS makes a copy of the earlier code and places onto the current page.

That code is copied once and you aren’t given the option to re-copy unless you reset the page. Every page in Swift Playgrounds offers a reset option in the ellipsis menu, but its discoverability is low. Apple expects each reader/student to work through exercises linearly, progressing only when each previous problem is solved. This means that you don’t get “live” updates by popping back and making new changes to the source page. The destination copies once.

That also means you cannot apply code-forwarding until your page is set as complete. By “complete”, I mean that the book’s source code and Swift Playgrounds accept that the reader/student has done sufficient work to move forward and progress to the next page/exercise.

This usually happens by executing a page epilogue. The epilogue tests the state of the page’s data, determines if the problem was solved (for example, whether the robot reached the end square and the code progressed to a hidden portion containing this test), and then updates a user assessment. Unless a page’s assessment status is “passed” (that is, done), the reader/student is not offered code copying on the following page.

This is built into Swift Playgrounds for iOS, and is an underlying assumption on how progressive learning plans operate. It’s a critical pathway for building page-by-page progress and enabling code-forwarding. This is why the following snippet includes its bit of hidden code. This code allows a user to pass, that is receive a passing grade/assessment for the page, without doing any more work than running the current page:

//#-copy-source(id1)
//#-editable-code 
func foo() {
    // ... starter code here
}
//#-end-editable-code
//#-end-copy-source

//#-hidden-code
import PlaygroundSupport
PlaygroundPage.current.assessmentStatus = 
    .pass(message: "Great job!")
//#-end-hidden-code

Building Code-Forwarding

The preceding code incorporates two essential parts of using a copy-source markup area.

  • First, is the actual tagged copy-source code. This delimits the code that gets copied forward to one or more other pages. Make sure to mark it editable when you want to present a challenge requiring end-user-reader modification. You can omit editable tags when you want the next step or branches to start with code you source yourself. It’s an unusual approach but it’s not illegal to do so.
  • Second, is the hidden assessment update. Normally you’d use more sophisticated logic to determine whether a reader/student has met those challenges laid out in the current page before allowing them to .pass or .fail. When you just want to demonstrate core functionality, make it clear in your marked-up write-up that the user must run the code before continuing. Use the approach in this code to “pass on first run” for demonstration. You’ll probably want to update the message to something along the lines of “Great! You’ve seen this work, move to the next page to start making changes.”

Building The Destination

Crafting a destination page is trickier than laying out acopy-source area: You must update your page’s manifest as well as its content source. The manifest will expect properly internationalized source strings. That means at a minimum, a code receiving page will need a Contents.swift file, a Manifest.plist file, and a PrivateResources folder with at least one localized lproj folder (in my case, en.lproj), which in turn holds the ManifestPlist.strings file.

Here’s what a simple manifest looks like for a copying destination. Keep in mind that each value entry for the CodeCopySetup keys is actually a placeholder for localization.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CodeCopySetup</key>
	<dict>
		<key>CopyCommandButtonTitle</key>
		<string>CopyCommandButtonTitle</string>
		<key>DefaultCommandButtonTitle</key>
		<string>DefaultCommandButtonTitle</string>
		<key>NavigateCommandButtonTitle</key>
		<string>NavigateCommandButtonTitle</string>
		<key>NotReadyToCopyInstructions</key>
		<string>NotReadyToCopyInstructions</string>
		<key>ReadyToCopyInstructions</key>
		<string>ReadyToCopyInstructions</string>
	</dict>
	<key>Description</key>
	<string>Description</string>
	<key>LiveViewEdgeToEdge</key>
	<true/>
	<key>LiveViewMode</key>
	<string>VisibleByDefault</string>
	<key>MaximumSupportedExecutionSpeed</key>
	<string>Fastest</string>
	<key>Name</key>
	<string>Name</string>
	<key>PlaygroundLoggingMode</key>
	<string>Off</string>
	<key>Version</key>
	<string>1.0</string>
</dict>
</plist>

I followed Apple’s example in my ManifestPlist.strings file, so the English expressions aren’t terribly exciting. The Name field used in the manifest is spelled out in addition to the button text:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CopyCommandButtonTitle</key>
	<string>Copy My Code From the Last Page</string>
	<key>DefaultCommandButtonTitle</key>
	<string>Start Coding on This Page</string>
	<key>Name</key>
	<string>Copying Text from the Previous Page</string>
	<key>NavigateCommandButtonTitle</key>
	<string>Return to Previous Page</string>
	<key>NotReadyToCopyInstructions</key>
	<string>Be sure to complete the previous page before you move on to solving the next step.</string>
	<key>ReadyToCopyInstructions</key>
	<string>You can bring over your algorithm from the previous page to continue improving it.</string>
</dict>
</plist>

Here, each possible assessment state and action is given a human-readable form. I was unable to make the system “default” to the items mentioned in the Playground Page Manifest documentation (such as “Copy my code” and “Start with provided code”). I’m sure if I tried hard enough, I could have gotten this working per the docs but I didn’t have the time to push.

In building the code-destination contents, link each identifier you used in the source (it’s id1 in this example but it can be any key you want to use) and the page to copy from (Page1). This page names comes from the name of the playgroundpage file hosting the user-edited or user-sourced content.

You must mention the page because you may keep enhancing the same progression of code from page to page, while using a single identifier. If you start on page 1, update on page 2, when you get to page 3, you want to copy from the updated source on page 2, not page 1. Mentioning which source you want to copy helps keep you and the user on track.

//#-editable-code
//#-copy-destination("Page1", id1)

//#-end-copy-destination
//#-end-editable-code

If you’re using a branching storyline (for example, you might explore variations on a sort or showcase different blending modes for merging images), you can place this destination code on each branch page.

More often, you’ll want to progressively modify code through a series of exercises. To carry the code further, add copy-source tags around the destination as in the following code, using the same id1 identifier, and refer to #-copy-destination("Page2", id1) for the next copy on Page 3 and so forth. Read this directive as this is the copy destination for the code tagged with id1 sourced from page 2.

Here’s what an edit-and-carry approach looks like for a second page, referring back to ("Page1", id1). In my imagination, this is the first time code has been copied and this markup sets up a user-editable progression that can be carried to the third page and beyond.

//#-copy-source(id1)
//#-editable-code
//#-copy-destination("Page1", id1)

//#-end-copy-destination
//#-end-editable-code
//#-end-copy-source

That’s pretty much all you need: proper tags, proper localized strings, and proper id/page references. If you’d like to try out a copy of my playground, you can grab a copy from here or email me for a copy if that doesn’t work.

Pattern match style filtering

I’ve written about this before, but a question came up recently that I thought was worth posting, as it’s a much simpler case than the one I wrote about last year.

Byrre_b asks:

Is there any way to write “pattern matching style filtering” in a better way then using a complete `if case` statement?

Such as:

let values: [NonEquatableEnum] = [...]
let filtered = values.filter { val in
    if case .thatOneInterestingValue = val {
        return true
    }
    return false
}

Note: Several people have pointed out if the enumeration is equatable, just use == rather than pattern matching. You can match case, even with associated values with, e.g. if case .foo = value.

You can filter using the pattern match operator, as shown here, or for equatable enumerations with ==.

enum NonEquatableEnum { case nah, blah, thatOneInterestingValue }

let values: [NonEquatableEnum] = [.nah, .blah, .nah, .thatOneInterestingValue, .nah, .thatOneInterestingValue, .blah]
let filtered = values.filter({ $0 ~= .thatOneInterestingValue })

Although this stores all values matching your subject case into filtered, the results aren’t very meaningful unless you want to count how many instances of .thatOneInterestingValue appear. That’s because filtering by enumeration case is usually limited to two situations:

  • You’re working with a structure and using the enumeration as a tag for filtering
  • You’re working with associated values and want to collect the enumeration cases and then extract the values.

The first of these is made simple with Swift 4 key paths. For example, consider the following structure:

struct Foo {
    var (x, y, z) = (0, 0, 0)
    let numnum: NonEquatableEnum
    init(_ n: NonEquatableEnum) { self.numnum = n }
}

let values2: [Foo] = [Foo(.nah), Foo(.blah), Foo(.nah), Foo(.thatOneInterestingValue), Foo(.nah), Foo(.thatOneInterestingValue), Foo(.blah)]

Assuming each instance has some more interesting data than the default (0, 0, 0) triple, pull out tagged instances using the same filter approach:

let kp = \Foo.numnum
let filtered2 = values2.filter({ $0[keyPath: kp] ~= .thatOneInterestingValue })

The key path lets you “dive” into each struct to test the enumeration member, while preserving the data stored in the other structure members. Instead of just counting how many instances of a simple enumeration there are, it acts as a meaningful filtering operation.

The second challenge, retrieving associated values, is more complex, as explained in my original write-up. Hand-crafting a result with if case gets you the values you need.

enum MoreComplicated {
    case one(Int)
    case two(Int)
    case three(String, String)
}

let values3: [MoreComplicated] = [.one(3), .two(5), .two(2), .three("hi", "there")]

Here’s an example that pulls out the case three enumerations:

let results2 = values3.filter({
    if case .three = $0 { return true } else { return false }
})

If you want to filter and extract at the same time,  add let declarations into your if case statement and switch the filter operation to a flatMap :

let results3 = values3.flatMap({
    (value: MoreComplicated) -> (String, String)? in 
        guard case .three(let x, let y) = value
            else { return nil }
        return (x, y)
    })

This returns an array of tuples, containing the associated values for each matching enumeration case.

Thoughts? Improvements? Fixes? Drop a note, tweet, or email to let me know!

Advent day 3, part 1

Are you playing along with Advent of Code? I got started a little late this year, so I’m doing a couple of days each day until I catch up.

The basis of Advent of Code isn’t so much the beauty of your code as it is the correctness. Because of this, a lot of my code is hideously ugly, with design choices best described as “questionable”. For example, at one point, I used flatMap.count on returned optionals instead of returning zeroes and ones and reducing them with +. I’m using Swift because that’s the language that’s currently dominant in my brain, although a lot of the memory manipulation would have been easier with straight C.

I wanted to share my solution for the first half of day 3. The challenge stipulates a squared spiral pattern of numbers in a grid and then asks you to calculate the distance from each number to the center. The approach I came up with for part I proved completely useless for the second half of day 3, where I had to start over from scratch. (Normally, you just modify the first code with an extra function and you go from part I to part II pretty easily.)

I took a road with part I that really didn’t fit the mindset of the challenge givers. For me, I was thinking geometrically, noticing that this was a degenerate case of a series of concentric circles. However instead of calculating the distance with a sin-cos solution, the concentric squares created a stepped triangle wave instead. Because of this, I built my solution to traverse the triangle wave and deduce the distance as the radius + the phase of the wave for any given number.

I thought I’d share my code for this because it’s pretty short and I think it’s pretty unusual for the problem domain.

for n in [1024, 12, 23, 312051] {
    for i in sequence(first: 1, next: { $0 + 2 }) {
        // Find least bounding square that contains number
        let boundingSize = i * i
        if boundingSize >= n {
            // Start of the outer edge sequence
            let firstValue = (i - 2) * (i - 2) + 1
            
            // Calculate distance to center
            let radius = (i - 1) / 2
            
            // The first number is always positioned at the
            // bottom right, one up from the bottom
            var current = radius - 1
            
            // The `direction` differential is the basis of
            // the triangle wave generator. It flips at the
            // minimum (-radius) and maximum (radius) offsets
            var direction = -1
            
            // One trip through the outer edge will be
            // sufficient to find the value
            for value in firstValue ... boundingSize {
                if value == n {
                    print("Result for \(n) is \(radius + abs(current))")
                    break
                }
                
                // Generate the next value in the triangle
                // wave pattern to determine the distance
                // from the center
                current += direction
                if abs(current) == radius { direction *= -1 }
            }
            break
        }
    }
}

Needless to say, my solution for part II had nothing to do with this wave approach. Instead, I actually constructed the 2D array, populated it with numbers and then used those as an addressing scheme to collect sums. Outside the addressing, it was basically applying a 3×3 sum filter in an address-driven convolution. Much longer, not pretty, but reasonably effective.

In the end, if I had done my “fill then look up locations” approach I used for part II for the first puzzle, it would have provided a much quicker solution although I don’t think it would have been as short or, in my opinion, nifty.

What approach did you end up using for day 3? And what made you go with that design?

Building automatic `OptionSet` entries

Last night Zev Eisenberg was asking about option sets. “Do you still have to specify 1 << _n_ manually for OptionSet conformance? There’s no magic?” So I decided to build him some magic. There’s really no reason you should have to manually put in numbers like this:

public struct Traits: OptionSet {
    public typealias RawValue = Int
    public var rawValue = 0
    public init(rawValue: Traits.RawValue) {
        self.rawValue = rawValue
    }
    
    public static let bolded = 1 << 0
    public static let italicized = 1 << 1
    public static let monospaced = 1 << 2
    public static let underlined = 1 << 3
    public static let outlined = 1 << 4
}

This approach requires unnecessary bookkeeping. You have to keep track of the bits you’ve used, especially if you add or insert new options, or reorder the options  you have. It gives unnecessary prominence to the implementation detail values. There should be a more magic way.

So I decided to write him a solution that automatically generated the bit flags and hid those details from the implementation. The result looks like this:

 public static let bolded = generateOption()
 public static let italicized = generateOption()
 public static let monospaced = generateOption()
 public static let underlined = generateOption()
 public static let outlined = generateOption()

You can move things around, add new items, delete old items. It really doesn’t make a difference from a code maintenance point of view (assuming you’re doing this all during development, not after deployment, where you’d want to use availability and deprecations).

To get here, I needed to create a function that would add type-specific options to any type that conforms to OptionSet. I created a global dictionary to store option counts:

private var _optionSetDict: [AnyHashable: Int] = [:]

To be honest, I hate unparented globals like this. However, Swift does not allow adding static stored values in extensions. I couldn’t think of another better way to handle this. I also built a second global to ensure this dictionary would prevent concurrent access, so my counts would be secure:

private var _optionSetAccessQueue = DispatchQueue(
    label: "sadun.OptionSetGeneration", attributes: [.concurrent])

I needed to box my type references since Swift doesn’t allow types to conform to Hashable. They won’t work out of the box with dictionaries. This solution let me use types as keys:

/// Wraps a type to enable it for use as a dictionary key
public struct TypeWrapper<Wrapped>: Hashable {
    private let type: Wrapped.Type
    public init(_ type: Wrapped.Type) {
        self.type = type
    }
    
    /// Conforms to Equatable
    public static func ==(lhs: TypeWrapper, rhs: TypeWrapper) -> Bool {
        return lhs.type == rhs.type
    }
    
    /// Conforms to Hashable
    public var hashValue: Int {
        return ObjectIdentifier(type).hashValue
    }
}

To create a hashable type entry, I just instantiate TypeWrapper with the type.

Sven Weidauer points out I can use ObjectIdentifier directly
Here’s the OptionSet extension that implements the generateOption() magic:

public extension OptionSet where RawValue == Int {
    public static func generateOption() -> Self { 
        let key = ObjectIdentifier(Self.self)
        return _optionSetAccessQueue.sync(flags: [.barrier]) {
            // This should be locked so there's a guarantee that
            // counts are unique for each generated option
            let count = _optionSetDict[key, default: 0]
            _optionSetDict[key] = count + 1
            return .init(rawValue: 1 << count)
        }
    }
}

I’m not sure that I’d ever actually use this approach in code but it was a fun exercise in problem solving. Sven W. adds “Another thing to keep in mind is that statics are initialised the first time they are used. So in different runs of the program the values can differ. Better not persist OptionSets created by this technique.”

You can see the full implementation over at Github. And if you’re curious, you can go back through the change history to see some earlier takes on the problem.

Like it? Hate it? Got suggestions and improvements? (I always mess something up, so there’s a pretty much 100% chance there’s room for improvement.) Drop a note, a tweet, an email, or a comment.

Thanks to Ian Keen, who suggested extending OptionSet directly.

Device-only code: A polite request for help

You may not follow Swift Evolution. A lot of people don’t. It takes a lot of time and attention, and the signal-to-noise while good for lists of its type can be low for people with deadlines, managers, contracts, and real life.

So let me get to the point: do you have code that doesn’t or shouldn’t run in simulators? Maybe you’re building AVFoundation camera code or Metal or for the keychain or whatever? Instead of using tests like:

// Test for a simulator destination
#if (arch(i386) || arch(x86_64)) && (!os(macOS))
    // code suitable for simulator
#else
    // code suitable for device
#endif

wouldn’t it be a lot easier, more robust, and better for cross-platform development to have something simple like this?

// Test for a simulator destination 
#if targetEnvironment(simulator)
    // code suitable for simulator 
#else
    // code suitable for device 
#endif

If you think so, please send a note to kremenek@apple.com and mention your support of SE-0190. You can read the whole proposal at that link. Big thanks go to Graydon Hoare for his implementation.

Time is running short. Review ends on the 24th. This is very much a developer-driven proposal as opposed to a language-design proposal. I’d like to see Swift users have more presence in the SE community.

Thank you.

Styling an optional `Bool`

So  Russell Finn asked me today if I had any thoughts on styling an Optional<Bool>. His use case is something like this: what if you have an optional variable like Optional<NSWindow>, with a property or method on that type, and want to test against the one case where the value is not-nil and the property or method on that value is meaningful?

For example, what if you have a possible window that’s possibly zoomed? How would you style that in the most readable fashion? There are, of course, a lot of ways to express this:

if let zoomed = window?.isZoomed, zoomed { ... }
guard let window = window, window.isZoomed else { ... }
if let window = window {
    if window.isZoomed { ... }
}
guard let window = window else { ... }; 
    if window.isZoomed { ... }

and so forth.

I suppose the thing you should be asking yourself is how long the window is of interest. If the only goal is to dezoom it if it’s zoomed, the if approach makes sense. However, if you plan to perform multiple steps, then you’ll want to unwrap the window and then work directly with the unwrapped version.

There’s nothing particularly terrible about stacking conditions into a compound guard or if statement so long as the conditions are related and tell a single step-by-step story. But if they don’t, I’d recommend breaking them down, as in the second two examples.

If it’s a one and done (thanks Dave and Greg), you can compare directly with a truth value:

if window?.isZoomed == true { ... }

I’m afraid, I’d have to know a bit more about the exact circumstances of use to have a stronger opinion. I hope this helps.

How to `split` without consuming values

Swift’s built-in split function returns an array of subsequences, split from a sequence or collection’s elements. For example, you may have a string and want to split it into words. Here’s a simple approach that consumes whitespace, newline, and punctuation characters:

import Foundation

let string = """
    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    Praesent in purus a ante semper congue posuere at lacus.
    Nullam faucibus sem vel sem vestibulum, a ullamcorper nunc
    auctor.  
    """

extension Character {
    var firstScalar: UnicodeScalar {
        return self.unicodeScalars.first ??  UnicodeScalar(0)
    }
}

// NOTE: This code demonstrates a simple way to split a collection.
// It's not a great way to actually tokenize a string, lacking both
// localization and in-word punctuation handling such as
// hyphens and single quotes.
let targetSet = CharacterSet.whitespacesAndNewlines
    .union(CharacterSet.punctuationCharacters)
let isWordSeparator: (Character) -> Bool = { c in targetSet.contains(c.firstScalar) }
let words = string.split(whereSeparator: isWordSeparator)

But what happens when you want to split a sequence or collection without consuming values? There’s no built-in Swift solution to turn to.

A few months ago, Soroush Khanlou and I were playing around with this problem, trying to return subsequences around predicate boundaries. When the predicate failed, a new subsequence would begin. We wanted to break sequences  along logical lines, for example, where a value changed or a slope trend updated from increasing to decreasing:

let x = [1, 2, 2, 3, 3, 3, 1]
 .sliced(where: { $0 != $1 })
// [ArraySlice([1]), ArraySlice([2, 2]), ArraySlice([3, 3, 3]), ArraySlice([1])]
let z = [1, 2, 2, 1, 3, 3, 1].sliced(where: >)
// [ArraySlice([1, 2, 2]), ArraySlice([1, 3, 3]), ArraySlice([1])]

We decided to work with arrays and use ArraySlice instances. Each ArraySlice provides a view onto a larger array:

Instead of copying over the elements of a slice to new storage, an `ArraySlice` instance presents a view onto the storage of a larger array. And because `ArraySlice` presents the same interface as `Array`, you can generally perform the same operations on a slice as you could on the original array.

Slices don’t allocate new storage so the splits are efficient. Be careful not to hold onto slices long term. Each slice holds a reference to the array it describes, so you can run into memory management issues if the slice outlives the array it points to.

For safety, reference the start and end of each slice with startIndex and endIndex. These values are often not zero or the array count, the way you’d expect with actual array instances:

let array = ["a", "b", "c", "d", "e"]
let slice = array[1...3]
print(slice.startIndex) // 1
print(slice.endIndex) // 4

Here’s the code I eventually settled on. It extends ArraySlice with a recursive slicing function that ends when it runs out of data or exceeds the maximum partition count. For each slice, it walks the elements until it finds a member element that fails the predicate test, at which point it recurses with a new slice. The ArraySlice code is hidden behind an Array entry point, which wraps the slice functionality away from public view.

Here are some examples of how the code could be used to break apart arrays. They show creating subsequences and run length encoding. You could easily adapt this to more complex data. For example, you might use keypaths to chunk data structures based on time stamps or look for inflection points for rising and falling values.

// Same value
let x = [1, 2, 2, 3, 3, 3, 1]
    .sliced(where: { $0 != $1 })
// [ArraySlice([1]), ArraySlice([2, 2]), ArraySlice([3, 3, 3]), ArraySlice([1])]

// Run-length encoding
let xx = [1, 2, 2, 3, 3, 3, 3, 3, 1, 1, 2, 1, 1]
  .sliced(where: !=)
  .map { ($0.count, $0.first!) }
// [(1, 1), (2, 2), (5, 3), (2, 1), (1, 2), (2, 1)]

// Strings
let y = Array("aaaabbbbcccdef")
  .sliced(where: !=)
  .map({ String($0) })
// ["aaaa", "bbbb", "ccc", "d", "e", "f"]

// Increasing subsequences
let z = [1, 2, 2, 1, 3, 3, 1].sliced(where: >)
// [ArraySlice([1, 2, 2]), ArraySlice([1, 3, 3]), ArraySlice([1])]

// Decreasing subsequences
let w = [1, 2, 2, 1, 3, 3, 1].sliced(where: <)
// [ArraySlice([1]), ArraySlice([2, 2, 1]), ArraySlice([3, 3, 1])]

Got suggestions for improvements? Drop a note in the comments. Thanks!

Matching Regular Expressions

Last week, I wrote about Olivier Halligon’s elegant Set matching solution. Several people asked if this could be extended to pattern matching for other types, which it can. Today, I put together a little proof of concept for performing regular expression matching with strings.

I started off with the same struct plus stand-alone global pseudo-constructor approach I had used with sets. Doing so, let me have a RegexPatternMatcher type and implement a matching function to hide a constructor. After a bit of playing around,  I dropped the global function to use a normal initializer: RegexPatternMatcher("pattern") vs matching("pattern"). The call sites were longer but less garbage floating around.

Renaming the type to Regex provided a more succinct approach : Regex("pattern"). I don’t normally like using overly short type names to balance call sites but I felt that Regex said about all that needed to be spoken here:

I’m not a huge fan of NSRegularExpression. I can’t wait for a native Swift solution. The class is expensive and it relies on what feels like archaic string models that use NSRange. Olivier suggested I balance the expense with NSCache so I created a variation where converted my struct to a class, and used NSString patterns as the cache keys.

This allowed me to build each NSRegularExpression instance once and decouple the matching options from the pattern. I made the class match function ignore defaulted option arguments. Instead, I allow the cache to carry forward any matching policies that previously existed or overwrite those policies if you specify them explicitly.

There’s something wrong with my code because I could not get a cached regex to change its matching behavior even after I updated options. Look at lines 103 through 107 in this version. If you figure out what’s wrong, let me know. It returns true for all six tests, instead of four true then two false.

Anyway, it was an interesting exercise and a good way to start getting back to work as I recuperate.

A Beautifully Elegant way to Set-Match

Challenge: By default, Swift’s switch statement uses equality matching when testing Set instances (for example). So how can you switch that up to use containment instead of equality? For example, say you want to test a set of insets presented as Set<Inset>? Here’s the standard Swift solution for containment:

switch insets {
case let sides where sides.isSuperset(of: [.top, .bottom]) : ...
case let sides where sides.isSuperset(of: .top) : ...
case let sides where sides.isSuperset(of: .bottom) : ...
}

It’s not awful, but couldn’t there be a more beautiful way to allow pattern matching against a set using a more Swifty statement that didn’t abuse its where clause?

Solution: Olivier Halligon came up with the following approach. He built a custom Set containment struct whose pattern matching operator (~=) is customized to apply superset detection:

public struct SetContainmentMatcher<T: Hashable> {
    public let set: Set<T>
    
    public static func ~=(
        lhs: SetContainmentMatcher<T>, 
        rhs: Set<T>) -> Bool {
        return rhs.isSuperset(of: lhs.set)
    }
}

public func containing<T>(_ set: Set<T>) -> SetContainmentMatcher<T> {
    return SetContainmentMatcher(set: set)
}

To use, build your switch using the now-global containing function. The function enables you to test your set against other sets using standard switch pattern matching.

public enum Inset { case top, bottom, left, right }

let sides: Set<Inset> = [.top, .right]
switch sides {
case containing([.top, .bottom]):
    print("contains: top, bottom")
case containing([.top]):
    print("contains: top") // matches here
case containing([.bottom]):
    print("contains: bottom")
default:
    print("default case")
}

Nice, isn’t it? Make sure to test for the largest sets first. If you invert the logic here, testing [.top] before [.top, .bottom], you may end up with unexpected behavior.

More good reading here on Olivier’s blog.