End of Source Breaking Changes for Swift 3

Ted Kremenek writes: “Today is July 27 — and the last planned day to take source-breaking changes for Swift 3. It has been an incredible ride to this point, so let’s take stock of where we are.”

Accepted but not implemented reviews:

Ted continues:

“These are all changes the community has approved for Swift but did not make today’s cutoff. Some of these proposals have implementations actively underway. For those proposals already in active development — and near completion — I am okay with extending the deadline for those changes to Friday, July 29. Such changes need to be approved by the release manager (myself) and should be merged into master via a pull request. When creating the pull request, please assign it to me (tkremenek), and mention the pull request on the swift-dev mailing list as well with the SE number in the email title.

The rest of the unimplemented proposals do not make Swift 3. This leaves us with the question of what to do with them. These proposals represent the known and reviewed changes we want to make to Swift, but inevitably there will also be changes that we don’t even know about today that we will want to take into Swift that can impact core source stability. That said, we also have a very strong desire to maintain source compatibility with Swift 3 and Swift 4 as much as possible to provide some stability for which Swift users to build upon. The challenge of course is reconciling these diametrically opposing goals: maintaining source stability while having the ability to incorporate more core (and important) language changes that are possibly source-breaking.

The Swift team at Apple has reflected on this and decided what it “means” for Swift 3 to be source compatible with Swift 4 and later releases going forward. Our goal is to allow app developers to combine a mix of Swift modules (e.g., SwiftPM packages), where each module is known to compile with a specific version of the language (module A works with Swift 3, module B works with Swift 3.1, etc.), then combine those modules into a single binary. The key feature is that a module can be migrated from Swift 3 to 3.1 to 4 (and beyond) independently of its dependencies.

While the exact details of how we will accomplish this feat are still being discussed, here is a sketch of how this will likely work in the Swift 4 timeframe. The key enabler is a new compiler flag that indicates the language version to compile for (e.g., similar to the clang -std=c99 flag). The compiler flag will be provided by the build system you are using (e.g., Xcode, SwiftPM, etc.) on a per-module basis:

For language syntax/semantics, the compiler can use the language mode to properly implement the language version being used by a module.

For the Standard Library, additive and subtractive changes are easily handled (the former by just adding them, the later by using deprecation techniques). For semantics changes, things are much more complicated, and will need further study.

The great thing about this approach is that a single Swift 4 compiler is building all of the sources in an application. This allows us to roll out this approach before achieving full ABI stability — something that will be a goal for Swift 4, but is impractical to achieve for a Swift 3.x release. It also provides us a general framework in the future for handling source compatibility as Swift evolves.

To make this more concrete, suppose an application is written to use Swift 4, but uses packages via SwiftPM that are written using Swift 3. A single compiler would build both the app and the packages — thus ensuring that all the compiled sources are binary compatible. It would not be the case that a framework built with the Swift 3 compiler could be used by an app built using the Swift 4 compiler. That kind of library binary stability (ABI) will be a key goal of the Swift 4 release.

These constraints mentioned above will serve as scaffolding for Swift 4 development. Discussion about Swift 4 commences on Monday. Ahead of that, Chris Lattner plans to send out thoughts from the Core team on some of the known key goals (and non-goals) for the release. In the meantime, the focus over the next couple days should be taking stock of what has landed for Swift 3 and to see if any of the proposals mentioned above are close to being completed or are truly out of scope.

Thank you again to everyone for making Swift 3 such as fantastic release!”

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-0129: Package Manager Test Naming Conventions Accepted. Team writes:

There was relatively little feedback on the proposal but it had unanimous support, along with additional support for the future directions that are unblocked by the proposal with regard to allowing additional target types under the `Tests` convention directory.

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 is Deferred from Swift 3. “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.” Team writes:

This is a very large proposal very late in the Swift 3 schedule. It has some clearly good pieces to it, but also many parts that are controversial with the community, and requires more design and iteration than time permits. It is best to defer this and make a change when we have something truly great, giving the benefit of proper time to design and evaluate the proposal.

Thank you to Brent Royal-Gordon and Dave Abrahams for driving this discussion. When the goals and parameters of Swift 4 are established, we should pick up this thread and see what makes sense to do here.

SE-0133: Rename flatten() to joined() in review to 7/26 Accepted. “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:).” Team writes:

This proposal had significant positive community feedback for aligning common operation names, but raised questions about whether ‘flatten’ was term of art, and what it would mean for related operations like ‘flatMap’. The core team discussed both sides of this debate, and decided it is best to rename ‘flatten’ to ‘joined’, but keep ‘flatMap’ as it is, to preserve its term of art. The core team prefers that it remain a distinct overload of joined(separator:) to preserve performance. It also requests that the returned collection types (FlattenCollection and friends) be left as-is for now, since they are not names commonly directly referenced, and we’d like to keep the change minimal.

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

This proposal has two parts. The core team approves the first part: “Rename nulTerminatedUTF8CString to utf8CString”. This property is intended for use when interoperating with APIs that take C-style “char *” parameters.

For the second part, the proposal suggests “Rename nulTerminatedUTF8 to nullTerminatedUTF8.” The core team discussed this, looked at various uses of the API (among code bases publicly available on github) and came to the conclusion that it is best to just remove this property outright. The clients we found would be better served using the .utf8CString property.

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: