Very last second Swift 3 reviews: big changes, little time

It’s crunch time. Several reviews are running concurrently for just a couple of days. All breaking changes must wrap up in the next week and Swift 4 design starts on 8/1.

Last Second Reviews

SE-0131: Add AnyHashable to the standard library in review to 7/25. ACCEPTED  “Currently the Objective-C type NSDictionary * is imported as [NSObject : AnyObject]. We used NSObject as the key type because it is the closest type (in spirit) to AnyObject that also conforms to Hashable. The aim of SE-0116 is to eliminate AnyObject from imported APIs, replacing it with Any. To import unannotated NSDictionaries we need an Any-like type that conforms to Hashable. Thus, unannotated NSDictionaries will be imported as [AnyHashable : Any]"

Team writes:

The feedback on this proposal was quite positive.  A few questions were raised, but were answered on-thread.  Thank you to Dmitri Gribenko for writing this proposal and driving this discussion forward.

SE-0132: Rationalizing Sequence end-operation names in review to 7/26. “Sequence and Collection offer many special operations which access or manipulate its first or last elements, but they are plagued by inconsistent naming which can make it difficult to find inverses or remember what the standard library offers. We propose that we standardize these names so they follow consistent, predictable patterns.”

SE-0133: Rename flatten() to joined() in review to 7/26. “This proposal renames flatten() to joined() (with no separator argument). It also adds a default separator of "" to the String-specific version of joined(separator:).”

SE-0134: Rename two UTF8-related properties on String in review to 7/26. “This proposal renames nulTerminatedUTF8 and nulTerminatedUTF8CString to enhance clarity and reduce mismatch between user expectations and the Swift programming language.”

Last Second Acceptances and Rejections

SE-0122: Use colons for subscript declarations is rejected. Team writes:

The feedback on this proposal from the community was overall divided with a slight bias towards reject.  Reasonable arguments were made on both sides: subscripts are definitely property-like in some ways, but they are also function-like in other ways.  Further, if we were aiming to align declaration syntax with use, arguably the parameter list should be enclosed in square brackets (but to be clear, we’re not going to do that). Overall, the core team agrees with the pervasive sentiment that this is not important enough to make a change for.

SE-0125: Remove NonObjectiveCBase and isUniquelyReferenced is accepted. Team writes:

This proposal had significant community feedback to help refine and improve its design, and Arnold has incorporated that into his v2 of the proposal.  The core team agrees the new revision is a good design.

SE-0127: Cleaning up stdlib Pointer and Buffer Routines is accepted with a modification.  Team writes:

This proposal got a significant amount of helpful feedback, which Charlie has already incorporated into the proposal.  The core team requests one additional minor change, which is to drop the addition of the ObjectIdentifier.unsafeAddress field.

SE-0128: Change failable UnicodeScalar initializers to failable is accepted. Team writes:

Feedback was universally positive, and the proposal is straight forward.

SE-0130: Replace repeating Character and UnicodeScalar forms of String.init is accepted. Team writes:

There was very little public discussion about this proposal, but the core team believes this eliminates a real ambiguity, while leaving doors open for future improved designs.

 

Wrapping up Swift 3

Of these four reviews, SE-0131 and SE-0134 are “gimmes” — that is, minor fixes that need relatively little discussion, offer obvious improvements, and can be evaluated on the spur of the moment. SE-0133 requires a little thought (“A concatenation of the elements of self“), but is also an easy up-or-down vote.

In contrast, SE-0132 is huge. It really deserves more time, attention, and consideration than the Apple-imposed deadline will allow. Brent‘s done a lot of work on perfecting this. If time permitted, it would have benefited from a few more weeks of discussion and revision.

Swift 3 Timeline

Swift 3’s  timeline concerns me. It will be ready for GM release along with Xcode 8 and iOS 10. I, personally, would prefer Swift 3 to be delayed six months or more to get it right the first time. As the planned major “breaking” release, the idea is twofold:

  • Offer incremental adoption by supporting Swift 2.3, which allows developers to build for the App Store using modern APIs in Swift 2.
  • Incorporate breaking code into Swift 3, so Swift 4 is ” as source compatible with Swift 3 as we can reasonably accomplish”. Further: “[S]ource breaking changes will be much more difficult to justify after Swift 3 is out the door, we’re prefer to roll them into Swift 3 where ever possible.”

With just a few days left before it becomes really really hard to fix fundamental design (although, I anticipate that dot-version changes to Swift 3 will be slightly more forgiving than Swift 3-to-Swift 4 changes), there’s been a big rush to find obvious flaws. For example, Open Issues Affecting Standard Library API Stability.

UpdateChris Lattner writes, “I strongly suspect there will be at least one 3.x release.”

On the other hand, August 1 is when the rush of additive proposals that have been back-burnered can come out and play. Swift 3 was all about getting the core language design fixed. Swift 4 is when new features can be considered. I’m hoping for things like method cascades, lenses, a better suite of functional programming tools, macros, strings-on-steroids, namespacing (without enums), and more.

See also: Announcement: Swift 3 Endgame and Swift 4 Kickoff

New Swift Evolution Reviews

Since so many new items have entered review after Wednesday, I’m adding an extra summary post. You can find the weekly full post here.

SE-0130: Replace repeating Character and UnicodeScalar forms of String.init in review to 7/24. “This proposal suggest replacing String initializers taking Character or UnicodeScalar as a repeating value by a more general initializer that takes a String as a repeating value. This is done to avoid the ambiguities in the current String API, which can be only resolved by explicit casting. It is also proposed to remove one of the String.append APIs to match these changes.”

SE-0129: Package Manager Test Naming Conventions in review to 7/26.  “Make the naming of tests be more predictable and more under the package author’s control. This is achieved in part by simplifying the naming conventions, and in part by reducing the number of differences between the conventions for the the Tests and the Sources top-level directories.”

SE-0128: Change failable UnicodeScalar initializers to failable in review to 7/24 “This proposal aims to change some (ones that are failable) UnicodeScalar initializers from non-failable to failable. i.e. in case a UnicodeScalar can not be constructed, nil is returned. Currently, when one passes an invalid value to the failable UnicodeScalar UInt32 initializer the swift stdlib crashes the program by calling _precondition. This is undesirable if one construct a unicode scalar from unknown input.”

SE-0127: Cleaning up stdlib Pointer and Buffer Routines in review to 7/24. “Update the API to match new API guidelines and remove redundant identifiers.”

SE-0126: Refactor Metatypes, repurpose T.self and Mirror in review to 7/24. “This proposal wants to revise metatypes T.Type, repurpose public T.self notation to return a new Type<T> type instance rather than a metatype, merge SE-0101 into Type<T>, rename the global function from SE-0096 to match the changes of this proposal and finally rename current Mirror type to introduce a new (lazy) Mirror type.”

SE-0117: Allow distinguishing between public access and public overridability revision 3 in review to 7/25 ” A public member will only be usable by other modules, but not overridable. An open member will be both usable and overridable. Similarly, a public class will only be usable by other modules, but not subclassable. An open class will be both usable and subclassable. This spirit of this proposal is to allow one to distinguish these cases while keeping them at the same level of support: it does not adversely affect code that is open, nor does it dissuade one from using open in their APIs. In fact, with this proposal, open APIs are syntactically lighter-weight than public ones.”

There’s a good roundup of (unhappy) opinion over at mjtsai.com.

Devolving your projects

So you moved to 3 and now are suffering from regretsies. Or you’re using Xcode 8 and you want to build a new 2.3 project. Fear not. Here are some tips for all your legacification needs.

Downgrading a Swift 3.0 Project

If you’re feeling especially regretful about moving a project to 3.0, you can downgrade it back to 2.3, but expect work unless you can regress to an appropriate commit. Adjust your build settings to use Legacy Swift (aka Taylor Lautner):

Screen Shot 2016-07-22 at 10.24.46 AM

Building a New 2.3 Project in Xcode 8

Since Xcode 8 doesn’t let you build 2.3 projects, you need to create a 3.0 project. Use build settings to regress to Legacy Swift.

After, make sure to convert all the code back to 2.3. Xcode offers most of the fixits you need for skeleton projects. Be aware that using “Fix all in scope” tends to crash Xcode 8. (27486260).

Screen Shot 2016-07-22 at 10.26.50 AM

Once converted, throw in a for-loop. This ensures that you’re actually running and compiling Swift 2.3 and not Swift 3:

Screen Shot 2016-07-21 at 9.31.42 PM

Importing 2.2 to Xcode 8 as Swift 2.3

If you’re working with an existing 2.2 codebase and are not ready to migrate to a language that has not, as yet, finished its design, you can choose the Swift 2.3 compiler.

First convert the project.

Screen Shot 2016-07-21 at 9.24.41 PM

Next, choose 2.3. The dialog defaults to Use Swift 3, so don’t hit Next until you mentally confirm your choice.

Screen Shot 2016-07-21 at 9.24.47 PM

For many 2.2 projects, you’re ready to rumble.

Screen Shot 2016-07-21 at 9.25.00 PM

Projects that use deprecated APIs and target iOS 10, may need upgrading:

Screen Shot 2016-07-22 at 10.36.19 AM

Explorations into the Xcode Source Editor Extensions underbelly: Part 1

Xcode source extensions are wildly exciting, surprisingly limited, and infuriatingly frustrating to work with. So I thought I’d share some experiences so you don’t have to suffer through some of my issues.

There are bunches of posts around the web on how to create extensions and I really don’t want to dupe their effort. In a nutshell, create a new Cocoa app. Then create a new macOS > Application Extension > Xcode Source Editor Extension target in that app. You’re ready to code.

If you watched the WWDC video, you’ll have seen the whole process of “run the target in Xcode and a new version of Xcode pops up with a darkened label and then you can test your extension”. I’m here to tell you that this approach is a huge steaming pile of crap doesn’t work as well as I’d hoped. What you really want to do is this:

In AppDelegate.swift of your extension/app project, add the following method. It lets you launch the app, install the latest extension and get the hell out of Dodge without using the “Xcode with the darkened label”.

func applicationDidFinishLaunching(_ aNotification: Notification) {
    let alert = NSAlert(); alert.messageText = "Extension installed! Click OK to quit."
    alert.beginSheetModal(for: window) { _ in
        exit(0)
    }
}

In your main extension Swift file (the class defined for XCSourceEditorExtensionPrincipalClass), add this. It lets you watch the OS X console and ensure that your extensions loaded the way you expected them to. Notice that I use NSLog and not print. Always use NSLog, so your output and debug info goes to the system console.

func extensionDidFinishLaunching() {
    NSLog("NAMEOFMYTARGET extensions did finish launching")
}

Set up your Info.plist in a separate editor pane or even a separate window. If you intend to create a multi-part extension (and most people will), you’ll want to build that extension incrementally and slowly add items to your XCSourceEditorCommandDefinitions dictionary. A typical entry looks like this:

Screen Shot 2016-07-21 at 10.20.38 AM

Also, don’t do what I did here. I used “sadun.ExtensionTestbed” because I know this is a throwaway. Assume you may write multiple extension groups and reverse namespace them properly, e.g. “org.sadun.ExtensionName.CommandName”.

Each time you want to test do this:

  1. Build your target.
  2. Open Products, right-click the .app file and show in Finder.
  3. Quit Xcode and run the app.
  4. Launch Xcode.
  5. Open any project that isn’t a playground because Xcode hates me and doesn’t want to properly load extensions for playgrounds. Always start with a non-playground project and *then* open a playground if you want to work with playgrounds.
  6. If the extension is greyed out (and if you follow these instructions they usually aren’t because this is the sequence for El Capitan that appears to be super reliable), check the OS X console. Look for assertion failures, XPC connection failures, and invalidated connections. If you see these, quit Xcode and relaunch. What you want to see there is the “did finish launching” log line.

I know this workflow is a super pain, especially given Apple’s promotion about in-Xcode testing without quitting and relaunching (let alone running the standalone app), but it’s transformed my source editor extension duty cycle from impossible to annoying. A huge win.

Notes:

  • Don’t “clean” your Xcode extension project while running the extension. Ooops.
  • Once the extension crashes, you need to re-run the installer app.
  • I can’t seem to grab a single cursor position, there has to be an actual selection, so my extensions for adding and removing “/// ” for doc comment markup mean you have to select a line, not just move the cursor there. Or maybe there’s a bug that’s screwing this up. I haven’t quite figured this out yet.
  • A lot of my code is super redundant: operate on all lines, operate on all selection lines, operate on selected text. I really need to re-architect a common subclassable system because the actual logic for most plugins is only a few lines or a call-out to another class.
  • Any extension that works on all lines rather than individual selections seems to be more stable in my experience.
  • My experiments trying to create interactive UI elements with extensions hasn’t been going very well, they run in the original application and not in Xcode, the window is behind xcode, they can’t be interacted with while Xcode is running, and you inevitably have to force quit something.

Screen Shot 2016-07-21 at 10.39.39 AM

Here are some things I’ve been messing with. Let me know how your extensions are going for you.

Proterozoic-Oriented Programming: Swift Evolution

It’s Wednesday, so I’ll be filling these in as they pop up:

Accepted

SE-0124: Int.init(ObjectIdentifier) and UInt.init(ObjectIdentifier) should have a bitPattern: label is accepted. Team writes:

There was very sparse commentary, and all of it was positive.  The core team believes this is an “obvious” proposal.

SE-0121: Remove Optional Comparison Operators is Accepted. Team writes:

Feedback has been universally positive from both the community and the core team, because it eliminates a surprising part of the Swift model at very little utility cost.

SE-0120: Revise partition Method Signature is Accepted. (Yay Nate!) Team writes:

There was very sparse commentary, and all of it was positive.  The one requested change from the discussion is to remove @discardableResult, which has already been made to the proposal.

SE-0116: Import Objective-C id as Swift Any type is Accepted. Team writes:

This proposal was very positively received by the community, and garnered a lot of feedback (which has already been incorporated into the proposal).  This proposal has significant implementation complexity, but the work to build it is nearing completion.  When this lands, there is some risk that we’ll need to revise the design in small ways due to unanticipated effects, but the core team believes it is the right overall design to aim for.

SE-0101: Reconfiguring sizeof and related functions into a unified MemoryLayout struct is Accepted. Team writes:

The proposal has been *accepted*, with a revision to change “MemoryLayout” into an enum with no cases.  The overall feedback was very positive.  The core team did discuss a proposal to move the generic type to each member, but felt that that would increase the verbosity of using the API (since T.self would be required) and would lead to possible confusion in the API (because it wouldn’t be clear whether you were asking for the size of a type, or the size of its metatype).

Rejected

SE-0123: Disallow coercion to optionals in operator arguments is Rejected. Team writes:

The majority of the feedback on this proposal was opposed to it.  While the goal of the proposal is laudable, SE-0121 eliminated the most surprising problem that this proposal aimed to address, and the rest of it (notably the ?? operator) can be addressed in other ways in the Swift 3 timeframe.  The core team recommends that the ?? case be handled by having the compiler produce a warning when the left side of the ?? operator is implicitly promoted to optional.  In the post-Swift 3 timeframe, we can investigate other broader options for solving this problem more generally (e.g. a new parameter attribute).

SE-0119: Remove access modifiers from extensions is Rejected. Team writes:

The majority of the feedback on this proposal was opposed to it, because it eliminated the useful ability to apply access control to a batch of methods and properties.

Withdrawn For Revision

SE-0126: Refactor Metatypes, repurpose T.self and Mirror withdrawn for revision. Team writes:

The authors of SE-0126 have requested that we withdraw the proposal. The idea will be revised, return back to the “pitch” phase, and when that converges, SE-0126 will be revised.

Returned for Revision

SE-0117: Default classes to be non-subclassable publicly returned for a second revision. Team writes:

As with the first round of discussion, the community generated a large number of emails, exploring the various aspects of the proposal.  While many community members agree with the thrust of the proposal, a number of people are concerned with the boilerplate being introduced by the proposal, among other issues.  The core team spent over two and a half hours discussing this topic from first principles, and has come up with a similar-but-different approach that should reduce the boilerplate, while still accomplishing the primary aims of the proposal.  John McCall will be revising the proposal today and we’ll restart a short discussion period about it tomorrow.

In Review

I apologize for the formatting. Swift-Ev no longer lets me cut and paste from schedule.md to add the summaries. I tried cutting and pasting the new format and it didn’t work very well.

SE-0130: Replace repeating Character and UnicodeScalar forms of String.init in review to 7/24. “This proposal suggest replacing String initializers taking Character or UnicodeScalar as a repeating value by a more general initializer that takes a String as a repeating value. This is done to avoid the ambiguities in the current String API, which can be only resolved by explicit casting. It is also proposed to remove one of the String.append APIs to match these changes.”

SE-0128: Change failable UnicodeScalar initializers to failable in review to 7/24 “This proposal aims to change some (ones that are failable) UnicodeScalar initializers from non-failable to failable. i.e. in case a UnicodeScalar can not be constructed, nil is returned. Currently, when one passes an invalid value to the failable UnicodeScalar UInt32 initializer the swift stdlib crashes the program by calling _precondition. This is undesirable if one construct a unicode scalar from unknown input.”

SE-0127: Cleaning up stdlib Pointer and Buffer Routines in review to 7/24. “Update the API to match new API guidelines and remove redundant identifiers.”

SE-0126: Refactor Metatypes, repurpose T.self and Mirror in review to 7/24. “This proposal wants to revise metatypes T.Type, repurpose public T.self notation to return a new Type<T> type instance rather than a metatype, merge SE-0101 into Type<T>, rename the global function from SE-0096 to match the changes of this proposal and finally rename current Mirror type to introduce a new (lazy) Mirror type.”

SE-0117: Allow distinguishing between public access and public overridability revision 3 in review to 7/25 ” A public member will only be usable by other modules, but not overridable. An open member will be both usable and overridable. Similarly, a public class will only be usable by other modules, but not subclassable. An open class will be both usable and subclassable. This spirit of this proposal is to allow one to distinguish these cases while keeping them at the same level of support: it does not adversely affect code that is open, nor does it dissuade one from using open in their APIs. In fact, with this proposal, open APIs are syntactically lighter-weight than public ones.”

SE-0125

to 7/22

Remove NonObjectiveCBase and isUniquelyReferenced

This replacement is as performant as the call to isUniquelyReferenced in cases where the compiler has static knowledge that the type of object is a native Swift class and dyamically has the same semantics for native swift classes. This change will remove surface API.

SE-0124

to 7/20

Int.init(ObjectIdentifier) and UInt.init(ObjectIdentifier) should have a bitPattern: label

This makes it clear at the use site that we interpret the value as a bit pattern

SE-0123

to 7/19

Disallow coercion to optionals in operator arguments

The convenience of coercing values to optionals is very nice in the context of normal function calls, but in the context of operators, it can lead to some strange and unexpected behavior.

SE-0122

to 7/24

Use colons for subscript declarations

Matches accessor declarations elsewhere in the language

SE-0121

to 7/19

Remove Optional Comparison Operators

Doesn’t work in current versions of Swift, because generics don’t support conditional conformances like extension Optional: Comparable where Wrapped: Comparable, so Optional is not actually Comparable.

SE-0120

to 7/19

Revise partition Method Signature

A more general partition algorithm using a unary (single-argument) predicate would be more flexible and generally useful.

SE-0119

to 7/19

Remove access modifiers from extensions

This proposal aims to fix access modifier inconsistency on extensions compared to other scope declarations types.

SE-0117

to 7/22

Default classes to be non-subclassable publicly

Not all classes make sense to subclass, and it takes real thought and design work to make a class subclassable well.

SE-0116

to 7/11

Import Objective-C id as Swift Any type

Objective-C’s id type is currently imported into Swift as AnyObject. This is the intuitive thing to do, but creates a growing tension between idiomatic Objective-C and Swift code.

SE-0101

to 7/19

Reconfiguring sizeof and related functions into a unified MemoryLayout struct

Discards the value-style standalone functions and combines the remaining items into a unified type.

Removing Bit Flags in Option Sets: or Fun with AVFoundation

AVFoundation’s unofficial nickname might as well be “that module with all the options”. So how do you port in an equivalent to this Objective-C number?

@(AVAssetReferenceRestrictionForbidAll & ~AVAssetReferenceRestrictionForbidLocalReferenceToLocal)

First, remember that pretty much all bit manipulation is now replaced by option sets. Second, remember the core rule of Swift renamification: start typing from the middle and let fuzzy completion find the new option set name for you.

Screen Shot 2016-07-20 at 10.03.03 AM

Then confirm you got the right item using Quick Help:

Screen Shot 2016-07-20 at 10.03.58 AM

You may be tempted to start using bit math like this. Don’t.

let forbidAll = AVAssetReferenceRestrictions.forbidAll.rawValue
let local = AVAssetReferenceRestrictions.forbidLocalReferenceToLocal.rawValue
let restrictions = AVAssetReferenceRestrictions(rawValue: forbidAll & ~local)

This is Swift. So modernize your “bits” thinking to “sets”. This code creates the complete .forbidAll set and then removes the local restriction.

var restrictions: AVAssetReferenceRestrictions = [ .forbidAll ]
restrictions .remove(.forbidLocalReferenceToLocal)

Interestingly,  you can also pass .forbidAll without brackets in the current version of Swift and it will compile.  (Update: I’m told that this option set syntax works because each element of an option set is itself an option set: [.forbidAll] is the same type and equal to .forbidAll. The array literal form of [.a, .b, .c] is syntactic niceness for creating an empty option set and then inserting (i.e. bitwise OR) each element)

What you cannot do is chain the two lines although you’d think you should be able to, wouldn’t you? Again, I think this is a bug, but you’ll get an error along the lines of:

Update: Another d’erp moment: Use the non-mutating subtracting method! (Thanks for the feedback!)

let restrictions: AVAssetReferenceRestrictions = .forbidAll.subtracting(.forbidLocalReferenceToLocal)
// Playground execution failed: error: AVTest.playground:7:50: error: cannot use mutating member on immutable value of type '[_]'
let restrictions: AVAssetReferenceRestrictions = [.forbidAll].remove(.forbidLocalReferenceToLocal)

Speaking of alternatives, while you could reduce this to one line by positively mentioning the three remaining restrictions, don’t. If Apple added a new restriction (improbable I admit), your code would break, failing to represent your intended logic.

public static var forbidRemoteReferenceToLocal: AVAssetReferenceRestrictions { get }
public static var forbidLocalReferenceToRemote: AVAssetReferenceRestrictions { get }
public static var forbidCrossSiteReference: AVAssetReferenceRestrictions { get }
public static var forbidLocalReferenceToLocal: AVAssetReferenceRestrictions { get }
public static var forbidAll: AVAssetReferenceRestrictions { get }

It’s kind of a pity that option sets (not being a separate type — and one that according to a conversation I had with Joe Groff will never be a separate type) won’t let you invert membership, even though all members must be known at compile time.

Anyway, lessons:

  • Eschew bits in Swift 3. Option masks are automagically upgraded to option sets. Use them.
  • Imported APIs with parameters whose type names contain the word Options are given a default value of [], the empty option set. If you don’t need options, you can omit mentioning the parameter.
  • When you mean to use “all but” don’t list individual cases. Look for all-encompassing “masks”, and then remove the cases you want excluded.
  • Like enumerations, option set members are lowercased in Swift 3. Keep this in mind when designing your own.

I may update this list as I think of more.

Stateful loops and sequences

An intriguing request came up on Swift-Ev a day or so ago:

I’ve come across a situation a number of times where I write code that has to try something one or more times using a `repeat…while` loop and the condition relies upon variables that should be declared within the scope of the loop.

repeat {
    let success = doSomething()
} while !success

What caught my eye about this request is the need for state that’s visible both within scope and in the control structure without being declared in the outer scope.

This mechanism already exists in Swift. Meet Swift’s new sequence functions. Swift offers two varieties. Both offer ways to establish state confined to the loop scope.

public func sequence<T>(first: T, next: (T) -> T?) -> UnfoldSequence<T, (T?, Bool)>

public func sequence<T, State>(state: State, next: (inout State) -> T?) -> UnfoldSequence<T, State>

The difference between the two is this: The simpler first function produces a sequence of the same type as its state. The second variation (which actually used to implement the first one) differentiates the state type from the output type, so you could generate integers and operate on strings if you wanted to.

If you think about it, a repeat-while loop really is just a sequence written in a different form. Here’s a simple example that counts by 5.

var i = 0
repeat {
    print(i) // some loop body
    i = i + 5
} while i <= 100

You can rewrite this to incorporate the i-variable state into the control structure like so

for i in sequence(first: 0, next: { $0 + 5 }) {
    print (i) // some loop body
    if i >= 100 { break } 
}

Or you can be slightly more daring and move all behavior and state into the next closure:

for _ in sequence(first: 0, next: {
    print($0) // some loop body
    let value = $0 + 5
    return value <= 100 ? value : nil
}) {}

There are three big things to note about this modification:

  1. The for-loop doesn’t need a variable. It’s only being used to run the sequence.
  2. The loop body is empty. It’s just there to complete the syntax for for in. You could just as easily perform Array(sequence) but that would require memory allocation, which is wasteful.
  3. The sequence must terminate by returning nil. This means the closure’s return type is T?, where T is the type of the first argument. This example returns value,  but it could just as easily return any number (like 0 or 1 or 42 or Int()) because that value will never be used in any meaningful way here. It’s just checking for false/nil.

If you’re going to treat Boolean conditions as optionals, it helps to have a quick way to convert a Boolean to an optional equivalent. This is overkill but it gets the job done.

extension Bool { var opt: Bool? { return self ? self : nil } }

Alternatively, you can build a function that operates on Boolean tests so you don’t need to convert Booleans to optionals when building weird for-loop sequences.

The perform function that follows creates a stateful repeat-while loop, which is more or less what I believe the writer was aiming for. It uses a Boolean test, hides the use of sequence(state:next:), and allows a trailing closure for the body of the loop.

func perform<T>(
    with state: T,
    while test: (T) -> Bool,
    repeat body: (inout T) -> Void) -> T {
    var updatedState: T = state
    let boolSequence = sequence(state: state, next: {
        (state: inout T) -> Bool? in
        body(&state)
        updatedState = state
        return test(updatedState) ? true : nil
    })
    for _ in boolSequence {}
    return updatedState
}

// The following example joins the words into a single space-delimited string.
let joinedWords = perform(
    with: ["Lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit"],
    while: { $0.count > 1 })
{
    (state: inout [String]) in
    guard state.count >= 2 else { return }
    let (last, butLast) = (state.removeLast(), state.removeLast())
    let joinedLast = butLast + " " + last
    state.append(joinedLast)
}.first!

debugPrint(joinedWords)

The key bit to notice is that the initial word array isn’t stored anywhere external to the loop but can be operated upon within the loop body. I believe this is exactly what the original writer asked for when he wrote, “code that has to try something one or more times using a `repeat…while` loop and the condition relies upon variables that should be declared within the scope of the loop”

Dear Erica: No-case Enums?

Dear Erica, Why would you create an enum with no cases?

This is such a great question! No-case enumerations represent a fairly obscure Swift “power tool”, and one that most developers are unaware of.

The answer, in a nutshell,  is that they enable programmers to establish a mini-namespace with little overhead and an assurance that you can’t accidentally create type instances. Under their umbrella, you can group static members into namespaces, build singletons, create cohesive collections of functionality with a minimum surface eliminating freestanding functions, and in one outlier case  provide services built around generic types.

Namespacing

For example, the standard library uses no-case enums to represent command line arguments for the current process. The Process enum has no cases. It provides a native Swift interface to the command line argc (count)/argv (strings) arguments passed to the current process.

/// Command-line arguments for the current process.
public enum Process {

    /// Access to the raw argc value from C.
    public static var argc: CInt { get }

    /// Access to the raw argv value from C. Accessing the argument vector
    /// through this pointer is unsafe.
    public static var unsafeArgv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?> { get }

    /// Access to the swift arguments, also use lazy initialization of static
    /// properties to safely initialize the swift arguments.
    ///
    /// NOTE: we can not use static lazy let initializer as they can be moved
    /// around by the optimizer which will break the data dependence on argc
    /// and argv.
    public static var arguments: [String] { get }
}

Singletons

If you’re thinking the preceding enumeration feels very much like a singleton, you would not be wrong. Consumers are prevented from creating instances and there’s a single entry point for all the functionality. Should you need  supporting state, you can always include an embedded type within a no-case enumeration.

Here’s a real world example I built that offers synchronous text-to-speech generation :

/// Synchronous Text-to-Speech support
public enum Speaker {
    /// Internal synthesizer
    private struct Synthesizer {
        static let shared = SynchronousSpeech()
    }
    
    /// Say the utterance passed as the argument
    public static func say(_ utterance: String) {
        Synthesizer.shared.say(utterance)
    }
}

The synthesizer is hidden from view outside the module, as you see in the public declaration.

import AVFoundation
import Foundation

/// Synchronous Text-to-Speech support
public enum Speaker {
    /// Say the utterance passed as the argument
    public static func say(_ utterance: String)
}

Users cannot create instances and the single entry point (Speaker.say()) limits access to the singleton’s restricted functionality.

Wrappers

I created a PlaygroundState enumeration singleton to centralize and simplify playground code I access over and over. Instead of typing PlaygroundSupport.PlaygroundPage.current.needsIndefiniteExecution, I use my PlaygroundState singleton to runForever().

The following example, which is cut down massively from its actual implementation, wraps several technologies including PlaygroundSupport and ProcessInfo to generalize access to simulator details and execution control:

/// Controls and informs the playground's state
public enum PlaygroundState {
    /// Establishes that the playground page needs to execute indefinitely
    static func runForever() {
        page.needsIndefiniteExecution = true
    }
    
    /// Instructs Xcode that the playground page has finished execution.
    static func stop() {
        page.current.finishExecution()
    }
    
    /// The playground's environmental variables
    public static var processEnvironment: [String: String] {
        return processInfo.environment
    }

    #if !os(OSX)
    /// Simulator's device family
    public static var deviceFamily: String {
    return processEnvironment["IPHONE_SIMULATOR_DEVICE"] ?? "Unknown Device Family"
    }
    
    /// Simulator's device name
    public static var deviceName: String {
    return processEnvironment["SIMULATOR_DEVICE_NAME"] ?? "Unknown Device Name"
    }
    
    /// Simulator's firmware version
    public static var runtimeVersion: String {
    return processEnvironment["SIMULATOR_RUNTIME_VERSION"] ?? "Unknown Runtime Version"
    }
    #endif
}

Consolidating Type Information through Generics

When reviewing SE-0101, Brent Royal-Gordon asked why the proposed MemoryLayout type needed to be a struct:

I think grouping these into a type is a sensible approach, but I don’t like that it allows the creation of meaningless MemoryLayout instances. The simplest fix would be to make `MemoryLayout` an empty enum instead of an empty struct. This would convey that no MemoryLayout instances do or can exist.

This is a pretty outlier use of caseless enumerations but it’s a valuable one. Rewriting my original struct into an enum, produces the following example. It uses generics to extract information about types:

/// Accesses the memory layout of `T` through its
/// `size`, `stride`, and `alignment` properties
public enum MemoryLayout<T> {
    /// Returns the contiguous memory footprint of `T`.
    ///
    /// Does not include any dynamically-allocated or "remote"
    /// storage. In particular, `MemoryLayout.size`, when
    /// `T` is a class type, is the same regardless of how many
    /// stored properties `T` has.
    public static var size: Int { return sizeof(T.self) }
    
    /// For instances of `T` in an `Array`, returns the number of
    /// bytes from the start of one instance to the start of the
    /// next. This is the same as the number of bytes moved when an
    /// `UnsafePointer` is incremented. `T` may have a lower minimal
    /// alignment that trades runtime performance for space
    /// efficiency. The result is always positive.
    public static var stride: Int { return strideof(T.self) }
    
    /// Returns the default memory alignment of `T`.
    public static var alignment: Int { return alignof(T.self) }
}

By using a caseless enumeration, users can query the types without ever creating instances, for example: MemoryLayout<Double>.size, MemoryLayout<NSObject>.stride, etc.

Like my posts? Consider buying a book. Content Update #1 is live. I also have books on Playgrounds (updated with the iOS 10 Swift Playgrounds App)  and Structured Documentation for sale at iBooks and LeanPub.

Update: 
See also: Natasha’s post on no-case enums and:

Announcement: Swift 3 Endgame and Swift 4 Kickoff

Via Ted Kremenek

Hi everyone,

Swift 3 has shaped up to be a remarkable release — a product of the inspiration, ideas, and hard labor many people from across the Swift open source community. It is now time, however, to talk about the endgame for the release.

Here are the key points:

  • The last day to take planned source-breaking changes for Swift 3 is July 27.
  • On that day, there will likely be a set of approved-but-not-implemented proposals for Swift 3 — including proposals for source-breaking changes. Will have an open discussion on that day on the fate of those unimplemented proposals in the context of Swift 3 and future Swift releases.
  • Starting on August 1 we will open up discussion about Swift 4. Part of this discussion will likely be guided by important work that was deferred from Swift 3, as well as the a goal of achieving binary stability in Swift 4. Until then, however, discussion should remain focused on Swift 3.
  • Note that there is an intentional gap of a few days between the last planned day to take source-breaking changes for Swift 3 and when we start talking about Swift 4. The idea is to provide some time for the community to take stock of where things have ended up for Swift 3.
  • The final branching plan for Swift 3 development is to be determined, but the final convergence branch is likely to be cut from master around that date or shortly after. Part of it comes down to the discussion on July 27 on how to handle the remaining unimplemented proposals for Swift 3.
  • The final release date for Swift 3 is TBD, but essentially after July 27 the intent is that Swift 3 is in full convergence and not in active development.

With these dates in mind, I want to call attention to some approved-but-not-yet-implemented proposals that currently I have nobody on Apple’s Swift team able to tackle in the next couple weeks:

Some proposals — like SE-0075 — are things we can add at any time, but many of the others tie into the goal of achieving some degree of source-stability for Swift in Swift 3. I’m letting the community know that these proposals currently have no implementation traction in case there is interest in helping make them happen in time for Swift 3.

Related, I’d like to call out a special thanks to the community for getting implementation traction on SE-0095:

Currently there is a JIRA issue and pull request tracking work on implementing this proposal.

In addition to these language proposals, there is also an assortment of outstanding work for the Standard Library that would be great to do for Swift 3. There is a gist summarizing those tasks:

These tasks are broken down in relative order of difficulty, with a JIRA issue associated with each one of them. If a JIRA isssue is currently not assigned to anyone, please consider them fair game to tackle if you are interested. API changes that have currently not gone through the evolution process will still need an evolution proposal, even if they are listed in the gist. If you take on a specific task, please assign the JIRA issue to yourself so others know it is being tackled.

Thank you to everyone — and I mean everyone — who has contributed to making Swift 3 happen.

Ted

Like my posts? Consider buying a book. Content Update #1 is live.