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.

2 Comments

  • Although nice when you want to treat bits as “options”, it still bugs me that the SetAlgebra interface doesn’t let you do a lot of common bit masking operations. You mention invertibility, but there’s also no way to perform bijective permutations (such as circular shifts), which would be nice for working with bit masks in cryptography, random number generation, graphics, and other low-level contexts. Not that SetAlgebra is the right protocol for those, but it’d be nice if there was something analogous to OptionSet that provided this stuff.

  • […] Erica Sadun: […]