Archive for June, 2017

Lessons Learned: iOS Extensions and Keychain Accessibility

Today’s lesson is courtesy of Rizwan Sattar, who writes:

If you’re building a notification service or content extension, keep in mind that these extensions will run even after a device restart, before the first unlock. Normally Apple recommends to set your Keychain items as kSecAttrAccessibleAfterFirstUnlock for “items that need to be accessed by background applications”. When you’re accessing the keychain for things in your notification extensions, you’ll want to set it to kSecAttrAccessibleAlways or kSecAttrAccessibleAlwaysThisDeviceOnly, etc instead.

Thanks Rizwan!

Working with optional errors in completion handlers

Foundation likes to pass optional errors (versus, say, a unified Result type) to completion handlers. A typical closure uses a (Value?, Error?) -> Void signature,where Value is some sort of data result that varies by operation.

A colleague was struggling to use conditional binding along with casting in his handler. Leaving aside for the moment any rational need to cast to NSError, this is an interesting demonstration of how you perform these two operations synchronously in code.

A cast from Error to NSError is guaranteed to succeed, so you can use the as operator. However, you must phrase the cast just right. Otherwise the Swift compiler emits warnings, as you see in the following screenshot. Here is an example of how you don’t get this job done properly:

As my friend complained in frustration: “But it tells me “did I mean as” and when I switch `as?` to `as` then it complains that error is Error? and isn’t convertible to NSError.

This attempt followed the normal pattern of conditional casting. Swift automatically lifts double optionals into a single optional result when used this way, but the cast from Error to NSError will always succeed, so you can’t use as? here.

To resolve, use a non-conditional cast to NSError? and then perform conditional binding to unwrap the value:

enum Bad: Error { case luck }

let error: Bad? = Bad.luck

if let error = (error as NSError?) {
    print("Worked")
}

The parens around the non-conditional cast make all the difference. Swift removes its warnings and makes everything work as expected.

Dropbox’s very bad not okay UX on iOS

Consider this question for a second: why do people own Dropbox? So they can put stuff into it and grab stuff out. Sure, there are other features but that mission statement pretty much the bottom line for iOS use. So why is Dropbox’s iOS client so horrible?

Start with the main menu bar. After selecting an item that you want to open in another app, which of these icons do you tap? The garbage one is clearly wrong, but the ellipsis is a very muddy choice. This should obviously use the (user-facing) share/(developer-facing) actions icon instead.

Apple’s design team agrees.

Once tapped, what’s the next step for “Open in”? Move and copy make sense but you have to tap “Export”, which finally uses the actions icon to give the hint that you want to open the selected file in another application. Bad naming, although it does appear properly as the first action.

Once tapped, you next have to find “Open In”. I have a couple of blockers installed (which shouldn’t even appear in this action sheet), so you have to scroll right to find the right icon.

It’s only after I scroll right that I finally locate “Open In” and can start the progress of opening my file in an appropriate application.

To summarize:

  • The ellipsis is a bad choice. It should be the Action/Share icon and it should directly open the Action Sheet.
  • Rename and Move should be placed into a separate “File actions” menu, along with discard (the trash can). They don’t belong grouped with the Action/Share sheet, which has a specific and conventional meaning in iOS.
  • The app should use care, especially with known file extensions, to limit the options presented in the sheet. “Open In” should be about the only choice for an epub extension file, and the other actions should be constrained to the invoked context.
  • Dropbox made me sad.

iOS 11 Screenshots

The new iOS 11 screenshot / edit feature has got to be my very favorite thing of the upcoming firmware update. Saving a screenshot creates a thumbnail in the bottom corner of the screen. Tap it to enter an edit mode that lets you crop and mark up the image.

  • Scroll left and right to move between screenshots if you’ve snapped more than one.
  • Use the blue handles on the sides and corners of the screenshot to crop the shot.
  • Select an editing tool (pen, highlight, pencil, eraser, lasso, and an assortment colors) then draw with your finger.
  • You undo-and-redo using buttons at the bottom left of the editor window (not shown here, because I hadn’t made any edits yet).
  • Tap Done at the top left to leave. You can save your shot to the photos album.
  • Otherwise, tap the action button (top right), to print, air drop, email, or otherwise work with your now-edited image.  I love the air drop option, which lets me snap, edit, and then throw the image into a blog post or a slack channel.

No wires, no fuss, just great convenience.

Holy War: Why I utterly loathe the new app switcher in iOS 11

Beta 3 Update: iOS 11 Beta 3 now supports swipe-up-to-close instead of the tiny little “x” buttons.

Under iOS 10, you could switch between apps or remove an app from the “recent list” by following these simple steps:

  1. Double-click the Home button to see recently used apps.
  2. Swipe left or right to find the app that you want to use.
  3. Tap the app or swipe upwards to quit/remove it from the list.

  • Each page was  clear and easy to identify. The app name and icon appeared on top.
  • The nearly full size rolodex presentation ensured that users with poor eyesight could easily identify each app.
  • The swipe area to select or remove an app was large, supporting users with a wide range of dexterity skills and motor limitations.

It was a great system that worked well.

In iOS 11, Apple redesigned. It decided to combine this recently used apps list with the control center, so that you could put as much information on-screen at once as possible. This produced an interface with teeny tiny images, and lots of user confusion overload.

In other words, in the current beta and the presentation at WWDC, they espoused a system that is best used by nimble millenials who unlike most every fidget-spinner-ing millenial I have ever met, would not be overwhelmed by sensory overload when presented with far too much information on a single screen.

In my opinion, this new design doesn’t work for the young, the old, the millenial, the seasoned pro, the able, the dis, the hawkeyed, or the near blind. Apple basically disregarded every rule of human usability and thrown it all together into a jumbled disorganized mess:

Compare this screen, with its dock, its windows, and its nearly two dozen control affordances to the iOS 10 version.  It’s a big jumbled insane mishmash of a UI design mess.

The controls in particular are unlabeled. Consider the timer, alarm, and stopwatch icons. They’re nearly identical and randomly scattered. And I can’t for the life of me remember what the dot is to the right of the camera.

As I constantly harp on about, a key factor in enhancing usability is to prioritize recognition over recall. As much as the Apple engineers have aimed to make all the buttons recognizable, they really aren’t.

You have to remember what a lot of these items do: the man in a circle, the magnifying glass, the three different clock faces, etc. You must further remember what happens when you tap and hold these as several of these items have secondary panels with embedded controls beneath them.

This 3-in-1 design breaks George A. Miller’s basic rule of working memory. Miller’s rule argues that you should not present the user with an overwhelming number of interactive items at once. The user cannot effectively remember and strategize interactions when shown more than seven or nine items at a single time. It’s as if Apple didn’t bother passing this screen through a usability evaluation process.

Working memory is not all that’s wrong with this design. In iOS 10, you just tapped or swiped up to manage apps. Those are both relatively large motor functions that require little fine control. That makes them an excellent match for a wide range of user ages and abilities. (Plus the pictures are all big and easy to recognize!)

Compare with iOS 11. To remove an app from the recents list, here’s what you have to do:

  1. Enter the control center. (In an improvement over iOS 10, this can be done with double tap, even if you’ve disabled swipe-up for the control center.)
  2. Swipe left and right to locate the app of interest.
  3. Tap and hold any app screen until the “X” buttons appear (and, maddeningly, the icons and app names disappear). This wait is short but frustrating.
  4. Tap exactly on the “X” to dismiss any app. This usually takes me upwards of three or four tries because the “X” is so small, my coordination is bad, and apparently I may need to re-align my touches with the OS.

In the end, the new control center a big giant mess trying to do too much in a massive design mishmash. I wish Apple would go back to its original design, although I wouldn’t mind some way to access the control center from the App Switcher.

Update: Oh you have to be kidding me:

If you like my posts and you want to say thanks for helping to prevent the meltdown of global civilization (“But if I’m right, and we can swap out that screen shot… Lenny, you will have saved the lives of millions of registered voters”), consider buying a book.  Save the Turtle, save the World. Choose the form of your Constructor. Thanks, all!

More on SE-0110: Important fallout, please read (Updated)


Update: Statement from Austin

…I’d like to appreciate my heartfelt thanks to everyone who reached out to me one way or another. It’s clear to me that the Swift, Apple platform developers, and swift-evolution communities are amazing, and that the people in them are kind, wonderful, generous, passionate, and caring. The Core Team in particular has done an incredible job shepherding the community, befriending people on and off the lists, and leading an open-source project of great technical and social complexity.

After thinking about things, I plan to continue participating in swift-evolution and looking for new ways in which I can serve the Swift and Apple developer communities. I hope to listen more, speak less, be more sensitive to other peoples’ feelings, and offer fair, well-considered feedback.


Often the Swift core team will ask for community help to develop and sponsor a proposal. I’ve worked on several of these. These proposals are generally aimed towards simplifying the compiler, enhancing the language, or addressing technical issues that place stumbling blocks in the effective delivery of compilation.

The reconsideration of SE-0110 should not reflect in any negative way on Austin Zheng. He worked hard on a proposal whose intent was to serve the large Swift developer community. I congratulate Austin for shepherding through this proposal, which can be a long, frustrating process.

The usability regression was unexpected. I applaud the core team for its flexibility in responding to the community’s real concerns when its implementation showed issues.

Today, Austin tweeted:

If my posting of the SE-0110 notice last night contributed to a negative atmosphere, I apologize. I have written to Austin and I hope he will reconsider his decision and rejoin Swift Evolution.

On the SE-0110 Regression (full text)

Doug Gregor writes:

Hello Swift community,

Swift 3’s SE-0110 eliminated the equivalence between function types that accept a single type and function types that take multiple arguments. However, for various implementation reasons, the implementation of SE-0110 (as well as the elimination of tuple “splat” behavior in SE-0029) was not fully completed.

 

Swift 4 implemented more of SE-0110, which caused a fairly serious usability regression, particularly with closures. Here are a few simple examples involving closures that worked in Swift 3 but do not work in Swift 4:
// #1: Works in Swift 3, error in Swift 4
myDictionary.forEach {
  print(“\($0) -> \($1)”)
}
 
// #2: Works in Swift 3, error in Swift 4
myDictionary.forEach { key, value in
  print(“\(key) -> \(value)”)
}
 
// #3: Works in Swift 3, error in Swift 4
myDictionary.forEach { (key, value) in
  print(“\(key) -> \(value)”)
}
Similar issues occur with passing multi-argument functions where a tuple argument is expected:
// #4: Works in Swift 3, error in Swift 4
_ = zip(array1, array2).map(+)
In all of these cases, it is possible to write a closure that achieves the desired effect, but the result is more verbose and less intuitive:
// Works in both Swift 3 and Swift 4
myDictionary.forEach { element in
  let (key, value) = element
  print(“\(key) -> \(value)”)
}

 

The Swift core team feels that these usability regressions are unacceptable for Swift 4. There are a number of promising solutions that would provide a better model for closures and address the usability regression, but fully designing and implementing those are out of scope for Swift 4.  Therefore, we will “back out” the SE-0110 change regarding function arguments from Swift 4.

 

Specifically, when passing an argument value of function type (including closures) to a parameter of function type, a multi-parameter argument function can be passed to a parameter whose function type accepts a single tuple (whose tuple elements match the parameter types of the argument function). Practically speaking, all of the examples #1-#4 will be accepted in both Swift 3 and Swift 4.

 

We will revisit the design in this area post-Swift 4.

 

– Doug

 

The startling uniquing of Swift 4 dictionaries

As you’ve probably heard, Swift 4 now has multiline strings. Rejoice! And thank John Holdsworth. For now you can do stuff like this:

let xml = """
    <?xml version="1.0"?>
    <catalog>
    <book id="bk101" empty="">
        <author>\\(author)</author>
        <title>XML Developer's Guide</title>
        <genre>Computer</genre>
        <price>44.95</price>
        <publish_date>2000-10-01</publish_date>
        <description>An in-depth look at creating applications with XML.</description>
    </book>
    </catalog>
"""

It’s super handy, allowing you to incorporate newline and individual " characters without having to escape them. (You do have to escape the backslash, as in the preceding example).

One of the things you might want to do with a big hefty string is to count the number of words, and maybe find out which word occurs the most. So here’s another multi-line string, one pulled from a lorem ipsum generator:

let lipsum = """
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur vitae hendrerit orci. Suspendisse porta ante sed commodo tincidunt.

    Etiam vitae nunc est. Vestibulum et molestie tortor. Ut nec cursus ipsum, id euismod diam. Sed quis imperdiet neque.

    Mauris sit amet sem mattis, egestas ligula ac, fringilla ligula. Nam nec eros posuere, rhoncus neque ut, varius massa.
    """

This particular example occupies 5 lines and includes a lot of text and punctuation. Because you can now treat Strings as collections, you can do stuff like this:

let w = "Hello".filter({ $0 != "l" }) // "Heo"

Similarly, you can use character set membership to select only letters and spaces:

let desiredCharacters = CharacterSet.letters
    .union(CharacterSet(charactersIn: " "))
let workString = lipsum.filter({ character in
    let uniScalars = character.unicodeScalars
    return desiredCharacters
        .contains(uniScalars[uniScalars.startIndex])
})

Unfortunately, Character and CharacterSet are still struggling a bit to get along with each other, which is why I’m doing that nonsense with the unicodeScalars.  Anyway, this gives you a single line string with just letters and spaces, so you can then break the string into words.

// Split along spaces
let words = workString.split(separator: " ")

Dictionary now has a feature that allows you to recognize you’re overwriting an existing key and apply a function to a key’s value each time the key is added. It’s called uniquing, and it lets you do neat things like count the number of times a token appears in a sequence:

// Add to dictionary, with "uniquing"
let baseCounts = zip(words.map(String.init), repeatElement(1, count: .max))
let wordCounts = Dictionary(baseCounts, uniquingKeysWith: +)

This code creates an infinite sequence of the number 1, and applies addition each time a duplicate key is found. You get exactly the same results by applying + 1 closure, although this is uglier and a little wasteful:

let wordCounts = Dictionary(baseCounts, 
    uniquingKeysWith: { (old, _) in old + 1 })

You can then find the word that appears the most

// Find the word that appears most often
var (maxword, maxcount) = ("UNDEFINED", Int.min)
for (word, count) in wordCounts {
    if count > maxcount { (maxword, maxcount) = (word, count) }
}
print("\(maxword) appears \(maxcount) times")
// et appears 8 times (at least it did 
// in my much longer text)

You can use uniqueKeysWithValues to fill up a dictionary by zipping two sequences:

let letterOrders = Dictionary(uniqueKeysWithValues: zip("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1...))
print(letterOrders)
// ["H": 8, "X": 24, "D": 4, "J": 10, "I": 9, "M": 13, "Z": 26,
//  "S": 19, "A": 1, "C": 3, "N": 14, "Y": 25, "R": 18, "G": 7, 
//  "E": 5, "V": 22, "U": 21, "L": 12, "B": 2, "K": 11, "F": 6, 
//  "O": 15, "W": 23, "T": 20, "P": 16, "Q": 17]

Another thing you might do with updated dictionaries is to build a set or array out of sequence values. This next example collects values for each key:

let invitedFriends: [(String, String)] = [
    ("Rizwan", "John"), ("Rizwan", "Abe"),
    ("Soroush", "Dave"), ("Joe", "Dave"), 
    ("Soroush", "Zev"), ("Soroush", "Erica")]
let invitationLists = Dictionary(
    invitedFriends.map({ ($0.0, [$0.1]) }),
    uniquingKeysWith: { (old: [String], new: [String]) in
        return old + new }
)
print(invitationLists)
// ["Rizwan": ["John", "Abe"], "Soroush": ["Dave", "Zev", "Erica"], "Joe": ["Dave"]]

You can store a tuple of the maximum and minimum values found for each unique key. The value structure has to be established in the initial streams, which can be ugly:

// Create 100 random numbers
let hundredRandom: [(Int, Int)] = (1...100).map({ _ in let value = Int(arc4random_uniform(10000)); return (value, value) })

// Create ten sequences of 1 through 10
let tens = sequence(state: 1, next: { (value: inout Int) -> Int in
    value += 1; return (value % 10) + 1
})

// Build the two together
let values = zip(tens, hundredRandom)
let extremes = Dictionary(values, uniquingKeysWith: { (old: (Int, Int), new: (Int, Int)) in
    return (min(old.0, new.0), max(old.1, new.1))
})
print(extremes)
// [10: (504, 8342), 2: (770, 8874), 4: (164, 7871), 9: (177, 8903), 
//  5: (1707, 9627), 6: (577, 8318), 7: (174, 8818), 3: (2837, 9198),
//  8: (3573, 9432), 1: (474, 8652)]

I probably could have made this a little more elegant but I was running out of time because I had to pick up my kids. If you have improvements for the last few examples, let me know. Sorry about the rush.

p.s. Thanks for the tip about using unicodeScalars on char.

Holy War: Mutable copies

Applying mutableCopy() to an NSObject returns Any, not the version of the type you’re attempting to make mutable, for example, NSMutableArray, NSMutableParagraphStyle, NSMutableAttributedString or whatever.

Nate asks:

Is is acceptable to use as! with mutableCopy() or is there a better way to do this?

// Approach 1: Forced unwrap
let mutableStyle1 = style.mutableCopy() as! NSMutableParagraphStyle

The forced as! cast used in this approach will always succeed (even if using as! makes you want to wash your hands afterwards). But there are other approaches to consider. What do you think of these alternative takes on the question? Here are some other solutions for you to weigh in on.

// Approach 2: Forced unwrap with explanation on failure

/// Very low precedence group
precedencegroup VeryLowPrecedence { lowerThan: FunctionArrowPrecedence }

infix operator !!: VeryLowPrecedence

/// Guaranteed safe unwrap or fatal error with custom error string
public func !! <Wrapped>(value: Wrapped?, complaint: String) -> Wrapped {
    guard let value = value
        else { fatalError(complaint) }
    return value
}

let mutableStyle2 = style.mutableCopy() as? NSMutableParagraphStyle !! "Guaranteed cast to mutable paragraph style failed"

// Approach 3: Guard with explanatory fatal error
guard let mutableStyle3 = style.mutableCopy() as? NSMutableParagraphStyle
    else { fatalError("Guaranteed cast to mutable paragraph style failed") }

// Approach 4: Create then set with current attributes
let mutableStyle4 = NSMutableParagraphStyle()
mutableStyle4.setParagraphStyle(style)

// Approach 5: Protocol to expose typed mutable version
public protocol AvailableMutatingVersion: NSObjectProtocol {
    associatedtype MutableForm
    func mutableCopy() -> Any
    var mutableVersion : MutableForm { get }
}

extension AvailableMutatingVersion {
    public var mutableVersion: MutableForm {
        guard let copy = self.mutableCopy() as? MutableForm
            else { fatalError("Guaranteed mutable copy could not be constructed") }
        return copy
    }
}

extension NSParagraphStyle: AvailableMutatingVersion {
    public typealias MutableForm = NSMutableParagraphStyle
}

let mutableStyle5 = style.mutableVersion

Which approach reigns supreme? Vote now or offer some alternatives…