Archive for June, 2016

iOS Playgrounds Part 5: Editing real code

While stuck at the Dr’s office, with my daughter being attended to, I decided to try editing a Swift 2.2 playground in iOS Playgrounds, to see if I could upgrade it to Swift 3.0.

The tl;dr is that the tools, while not fully baked, are just as amazing as they were in the WWDC demos. With a few (notable) exceptions, editing source code, invoking fixits, etc. are beautifully realized to the point that I was able to perform non-trivial edits while sitting in a waiting room and using an iPad:

Screen Shot 2016-06-21 at 11.19.54 AM

I was able to fully update this playground page to run and render.

The Good

Built-in fixits are glorious. As in normal Xcode, they update code by inserting missing labels or replacing code:

Screen Shot 2016-06-21 at 11.24.44 AM

Multiple errors are paged (as you can see with the two dots at the top of the complaint), and there’s amazing cross referencing with related items, such as when a protocol is not properly implemented or partially implemented. You just tap away to the error point for the candidate issue.

Number and color edits are fantastic: I didn’t realize you could use the number tweaker to just “dial” your way up and down numbers by continuing to twirl into the number pad:

Screen Shot 2016-06-21 at 11.21.21 AM    Screen Shot 2016-06-21 at 11.21.29 AM

Pages I didn’t expect to work did work! Like this one:  It’s really short. Give it a try. Screen Shot 2016-06-21 at 11.36.42 AM

The Not So Good

You can’t fixit many errors:

Screen Shot 2016-06-21 at 11.30.49 AM

But you can select things and start to type and Playgrounds picks up the context for you:

Screen Shot 2016-06-21 at 11.31.37 AM

The Ugly

Screenshots are for live views, not playground views. And for some reason, I’m having a lot of difficulty today doing screenshots the old way (hold down sleep/wake, then click home). All of these screenshots are from Xcode’s device window instead.

A lot of confusion comes from inappropriate or missing tool tips. Sometimes it leads you down the garden path:

Screen Shot 2016-06-21 at 11.51.25 AM

Screen Shot 2016-06-21 at 11.53.11 AM

What was actually needed looks more like this:

Screen Shot 2016-06-21 at 12.09.44 PM

Trying to edit “advancedBy” to “advanced” by moving the cursor to the end of By and backspacing was simply a no-go. Playgrounds will remove the entire call at once.

You get a lot of problems with 2.2 items needing to be lowercased for 3, or using old CG constructors like CGRectMake not auto-fixing to CGRect(x:y:width:height). Playgrounds just don’t quite get the “fixit” needed for these simple switches.

Screen Shot 2016-06-21 at 12.16.05 PM

Even when you’re typing things in, Playgrounds doesn’t always offer you the right choices. In this example, all this needed was to lowercase CGAffineTransform to cgAffineTransform. No fixit and no syntax match:

Screen Shot 2016-06-21 at 12.12.39 PM

The really cool “drag parenthesis pairs into place” shown in the demos don’t seem to work in the app yet. Instead all I could drag was the letter “j”. (Update: Drag down and left or down and right and then use a second finger to tap or drag the updated key. It is hard but doable. If you’re not coordinated, use two hands. Thanks Seán Labastille)

Screen Shot 2016-06-21 at 11.44.59 AM

You can’t look up module definitions in-place.

No access to Sources that I can find so I can fix errors:

Screen Shot 2016-06-21 at 11.29.05 AM

The live views are distorted:

Screen Shot 2016-06-21 at 12.18.46 PM

Screen Shot 2016-06-21 at 12.19.23 PM

But other than that, wow, what a cool tool. I’m really enjoying it a lot.

iOS Playgrounds part 4: Mailing playgrounds

Copying Playgrounds without OS X and AirDrop

Thanks to Filip Radelic who figured a mail-solution approach:

Screen Shot 2016-06-21 at 8.49.17 AM

He writes: “I found that Mail will tell you it can’t open the attachment but if you tap and hold, it will let you copy it to Playgrounds.” I tested and it worked perfectly. Thanks, Filip! He also has a more detailed write-up on his website.

Izzy suggested trying downloading a zip from Safari, and that seemed to work very well too: Update in further testing, the Safari redirection is not working properly. What I thought was opening in Playgrounds was actually opening in a PDF app. I then deleted that app. Taps on “Open in Playgrounds” no longer worked. I rebooted and retried. From there, taps started opening in yet another non-Playgrounds app.

Screen Shot 2016-06-21 at 9.37.49 AM

Boring Internals Stuff

Turns out Playgrounds can open 3 kinds of file extensions: playgroundbook, diffpack, and playground. The app is editor for the two playground types and viewer for the diffpack type.

The associated frameworks are really neat. There’s a CommonMark framework, which I wish were public, along with an assortment of other items.

Screen Shot 2016-06-21 at 8.58.01 AM

As far as available Swift modules go, Playgrounds provides a nice suite (a sweet suite?) of options. The bundled arm64 toolchain for my iPad includes AVFoundation, Core Audio, Core Bluetooth, Core Data, Core Graphics, Core Image, Core Location, Core Media, Darwin, Dispatch, Foundation, GameKit, GameplayKit, GLKit, Multipeer Connectivity, ObjC, SceneKit, simd, SpriteKit, Swift core, UIKit, and WebKit.

Screen Shot 2016-06-21 at 8.59.36 AM

Tucked away in the Challenges is one for maze running, which if memory serves is not presented yet to users:

StoreBanner@2x

There are also some interesting teases in the Documentation cache, with topics on model building, UIKit GUI development, and HTML resources. Is this documentation available yet in-app?

Screen Shot 2016-06-21 at 9.26.52 AM

Screen Shot 2016-06-21 at 9.27.53 AM 1

Screen Shot 2016-06-21 at 9.28.57 AM

Screen Shot 2016-06-21 at 9.29.25 AM

Screen Shot 2016-06-21 at 9.30.27 AM

Xcode Woes and Noes

Update: I did finally get stuff working after re-installing Xcode about a dozen times. And then it started acting up again. And then a few installs later it worked, etc. etc. Anyway, the gist at the bottom of this post works and I think it’s much more Swifty.

Screen Shot 2016-06-20 at 1.36.29 PM

So I’m watching 414. And I look at that code and I’m all “I could totally improve on that”. So I start, and once again, Xcode 8 blows up. I get the dreaded “Unable to determine compiler to use – the abstract compiler specification is missing from this Xcode installation” error, just like I do every time I try to create a new extension project or do anything particularly stressing to Xcode 8.

Screen Shot 2016-06-20 at 12.44.00 PM

Reinstalling Xcode 8 fixes everything until I attempt to open or create a project with an extension or I look at it wrong, because this issue isn’t limited to extensions.

You’d think this would be a really easy fix — edit a file somewhere or create a link or something — but I can’t figure out what to fix.  It’s particularly annoying because There’s so much in this original code that ticks me off (and no, that’s not my final code at the top of the list, that was just my first go at it, freehand).

Screen Shot 2016-06-20 at 1.40.47 PM

I can’t specify line numbers because the demonstrator didn’t enable them in Xcode (Preferences > Text Editing > Editing > Show > Line numbers). But here are a few key irritations:

  • Using an integer line index instead of enumerating and using key-value
  • Not guarding a conditional cast versus forced casting
  • Creating an array with () instead of a literal (: [Int] = [])
  • Building an Int-ish array instead of a [XCSourceTextRange] one from the start.
  • The newline update thing surely could have been done better.
  • Rule of Lily violation with “map”.
  • I hate “in” on the same line in closures.
  • The dictionary is ugly. And it’s declared in every run of the loop.

In any case, it’s unlikely I’ll get to play around with this because Xcode hates me. I thought I’d share and hope someone knows how to fix the dependency issue, and also it’s a kind of fun bad Swift snippet to fix, even if you can’t run the code and test it.

Let me know what you’d fix and if you get it running, share some code!

p.s. Update: Still no idea if this will work, but if you try it do let me know how it goes:

Playgrounds Part III

Screen Shot 2016-06-14 at 2.49.47 PM

I have to call it a day, but I’m really happy with what I’ve learned about authoring Playground Books for iOS today. (I’m less happy learning about how to completely reset Xcode — either reboot your system or hop into terminal, do a ps -ax and grep for Xcode and then kill anything with the name in it.) I thought I’d share a few lessons.

If you are going to author for iOS, start with a new playground created from scratch in Xcode 8 rather than modifying an existing one. If you cannot import PlaygroundSupport, reboot your computer. If it doesn’t show up and you’re still using XCPlayground (now deprecated), you’re doing it wrong.

Understand that the Swift snapshot on iOS 10 is older than the Swift snapshot in Xcode so don’t expect to use really recent evolution implementations:

Screen Shot 2016-06-14 at 2.37.05 PM

I ended up implementing my own sequence functions to make my code work.

The easiest way to get started is just to AirDrop a normal playground.

Screen Shot 2016-06-14 at 3.09.40 PM

It will open automagically in Playgrounds for you. When you transfer books, you have to choose “open in Playgrounds” from the AirDrop menu.

If you want to author a Playgrounds Book before tomorrow’s session, just customize a blank document (download at your own risk). My blank book is a one-pager, and you can tweak the Contents.swift file in Blank.playgroundbook/Contents/Chapters/Document1.playgroundchapter/Pages/Blank.playgroundpage. There are manifests at every point along the way, try not to mess them up otherwise the book won’t open.

When authoring pages, either in a normal playground or playground book, you can annotate them to omit details that would otherwise clutter the interaction space or should not be visible to the reader. In my Spirograph playground (download at your own risk), I use special mix of normal playground markup (I have a couple of books that cover this markup format: my Doc Markup book and my Playgrounds book) and these new keywords:

  • Use #-hidden-code and #-end-hidden-code to exclude code from the reader’s view.
  • Mark editable areas with #-editable-code and #-end-editable-code, and if you like you can add a note to the opening tag.
  • To simplify code completion, you can globally omit code completion #-code-completion(everything, hide) and then choose which selectors to present, as I do in the following sample. You see this in the above screenshot, which offers one-tap access to just three customization points for my spirograph.
/*:
 **Spirograph:** Play around.
 
 - Note: The default values are:
 * minor: -3.0
 * offset: 130.0
 * color: .black()
 
 */
//#-hidden-code
import UIKit
import PlaygroundSupport

public var color = UIColor.black()
public var minor = -2.0
public var offset = 130.0

public func setMinor(_ value: Double) { minor = value }
public func setColor(_ value: UIColor) { color = value }
public func setOffset(_ value: Double) { offset = value }
//#-end-hidden-code
//#-code-completion(everything, hide)
//#-code-completion(identifier, show, setColor(_:), setMinor(_:), setOffset(_:))
//#-editable-code Tap to enter code
setMinor(-3.0)
setColor(.black())
setOffset(130.0)
//#-end-editable-code

//#-hidden-code
let liveView = UIImageView(600, 600, backgroundColor: .white())
PlaygroundPage.current.liveView = liveView
liveView.image = spiroImage(
 size: CGSize(width: 600.0, height: 600.0),
 samples: 4096, minor: minor, offset: offset, color: color)
//#-end-hidden-code

Playgrounds Part II

Apple’s pretty highlight page on Swift Playgrounds is here.

Screen Shot 2016-06-14 at 8.09.21 AM

Jonathan Penn figured out how to send documents between my Mac and iPad: just use AirDrop. With that tip, I was easily able to create content on the iPad and move it Macwards and see the material that Apple provided itself.

Here’s how to go from iPad to Mac:

  1. Open AirDrop on your Mac
  2. Tap “My Playgrounds” (4 squares)
  3. Tap Action (top-left, square with arrow pointing up)
  4. Tap the playground you want to send
  5. Tap an AirDrop destination
  6. Wait for the transfer.

iPad Playgrounds use a new format: “Playground Book”:

Screen Shot 2016-06-14 at 8.14.51 AM

It consists of the playground’s contents and user-created edits (stored as a diffpack), which are stored separately. The contents consist of chapters, which in turn have individual playground pages, shared resources and sources. It’s basically the playground we’ve come to know and love on steroids and hallucinogens.

Want to know more about playgrounds? I have a fairly extensive book on playgrounds that’s available right now, which you may be interested in checking out.

The iPad playgrounds app consumes at least two kinds of playground pages: standard pages and cut scenes. These are included as parts of a playground book, which consists of playground chapters, in which the pages and cut scenes are stored. You can read more about the Playground Book Package Format on Apple’s website. (Thanks to Matt Gallagher for the pointer.)

I’m going to warn you in advance that Swift does not play a huge rule in cut scenes. Instead, the cut scenes were constructed using HTML and Javascript. I’ve pasted a fairly short cut scene here, along with a screen shot of some of the resources to create just this one element:

Screen Shot 2016-06-14 at 8.18.42 AM

<!DOCTYPE html>
<html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <meta http-equiv="X-UA-Compatible" content="chrome=1,IE=edge" />
 <title>simpleCommands</title>
 <style>
 html {
 height:100%;
 }
 body {
 background-color:#AFE0D9;
 margin:0;
 height:100%;
 }
 </style>
 <!-- copy these lines to your document head: -->

 <meta name="viewport" content="user-scalable=yes, width=1024" />

 <script type="text/javascript" src="simpleCommands.hyperesources/StopPoints.js"></script>
 <script type="text/javascript" src="simpleCommands.hyperesources/HypePagingSupport.js"></script>

 <!-- end copy -->
 </head>
 <body>
 <!-- copy these lines to your document: -->

 <div id="simplecommands_hype_container" style="margin:auto;position:relative;width:1024px;height:768px;overflow:hidden;" aria-live="polite">
 <script type="text/javascript" charset="utf-8" src="simpleCommands.hyperesources/simplecommands_hype_generated_script.js?65524"></script>
 </div>
 <!-- end copy -->
 <!-- text content for search engines: -->
 <div style="display:none">
 <div>Simple Commands</div>
 <div></div>
 <div>Your goal is to figure out which instructions, in which order, will result in something great.</div>
 <div>Writing code allows you to create your own set of instructions for your iPad to carry out.</div>
 <div>Notice the mashed-together words? Code is punctuated and spaced like human languages, but commands have no spaces between words.</div>
 <div>collectGem()</div>
 <div>You need to follow the instructions in the correct order, or you'll end up with something…unexpected.</div>
 <div>For example, you'll tell Byte to move forward:</div>
 <div>Commands always end with parentheses. You'll see why later!</div>
 <div>moveForward()</div>
 <div>You’ll start by writing commands to move a character named Byte around a puzzle world, performing tasks.</div>
 <div>Now it's your turn to write some code!</div>
 <div>Have you ever followed a recipe to bake something delicious?</div>
 <div>Or followed instructions to assemble something cool?</div>
 <div>Learn to Code
</div>
 <div>Or collect a gem:</div>
 </div>
 <!-- end text content: -->
 </body>
</html>

Yeah, I know.

Playground pages include separate Contents.swift and LiveView.swift files, which really surprised me.  The Live Views use a poster asset (simple JPG), and there’s a metric ton of Super Sekrit meta-comment annotation for setting up each page:

/*:
 **Goal:** Find the bugs and fix them.
 
 When you write code, it’s easy to make mistakes. A mistake that keeps your program from running correctly is called a [**bug**](glossary://bug), and finding and fixing bugs is called [**debugging**](glossary://debug).
 
 The code below contains one or more bugs. To debug it, rearrange the commands into the right order to solve the puzzle.
 
 1. steps: Run the code to see where the mistake occurs.
 2. Identify the command that's in the wrong place, then tap it to select it.
 3. Drag the command to the correct location, then run the code again to test it.
*/
//#-hidden-code
playgroundPrologue()
//#-end-hidden-code
//#-code-completion(everything, hide)
//#-code-completion(identifier, show, moveForward(), turnLeft(), collectGem(), toggleSwitch())
//#-editable-code Tap to enter code
moveForward()
turnLeft()
moveForward()
moveForward()
collectGem()
moveForward()
toggleSwitch()
//#-end-editable-code
//#-hidden-code
playgroundEpilogue()
//#-end-hidden-code
//#-hidden-code
//
// Contents.swift
//
// Copyright (c) 2016 Apple Inc. All Rights Reserved.
//
//#-end-hidden-code

As you see, you can control which elements are presented to the reader/user/coder, to allow a subset of coding to be accomplished with simple taps. It’s a pretty limited approach for end-user consumption.

The entire system is run using a new PlaygroundSupport library, which appears to be iPad only for the moment but will replace XCPlayground, presumably soon in Xcode 8 (despite this screen shot).

Screen Shot 2016-06-14 at 9.20.59 AM

According to docs, the PlaygroundSupport module enables you to access a playground page and manage execution, display and dismiss live views, and share and access persistent data, which you use for creating a consistent experience throughout the interaction with your playground book.

A quick check shows that you can refer to: PlaygroundPage (for page state control), PlaygroundValue, PlaygroundQuickLook (should act like the current version), PlaygroundLiveViewable (protocol for live-view suitable types), PlaygroundKeyValueStore (for persistent data storage), PlaygroundRemoteLiveViewProxy,  PlaygroundLiveViewMessageHandler, PlaygroundLiveViewRepresentation  (type enum for items that can act as live views), PlaygroundRemoteLiveViewProxyDelegate.

Carl Brown tracked down some of this in the “Answers” example playground, which uses custom show() and ask() functions built around these new features.

For example, the AnswersLiveViewClient class works as a proxy delegate for interactions in the live view. It’s a subclass of the Apple-supplied PlaygroundRemoteLiveViewProxyDelegate class that acts as a PlaygroundLiveViewMessageHandler for receiving and processing live view requests.

As far as I can tell, there are not yet any tools on the Mac side that build these pieces. So don’t go looking for iPlaygroundAuthor or even an iPlaygroundBookstore quite yet.

Screen Shot 2016-06-14 at 8.35.34 AM

So what is this entire app good for then? Other than some goodwill Apple outreach? Well, it’s cool having interpretable Swift on your iPad (take that original App Store restrictive rules!) and it’s clearly meant for an audience that accepts limited tap-based interaction, with a lot of beautiful highly designed iBooks-Author-esque HTML support.

Without tools to create content (yet) or a storefront to distribute and potentially monetize, it feels like a proof of concept more than an app. We’ll see how things (if you pardon the pun) develop.

iOS Playgrounds

Screen Shot 2016-06-13 at 4.59.35 PM

Really early days:

  • App is baked into iOS 10. Just install a beta to your favorite (newerish) iPad.
  • Houdah’s Type2Phone is the best for working with iOS playgrounds. Really great for copy/pasting.
  • Some (but not all) Emacs keybindings work, which makes editing much easier. However, ^A/^E don’t respect the onscreen folding, so in squish mode, they’ll jump to the start and end of each \n-delineated line, not the folded version.
  • I can’t figure out how to get things onto and off of the iPad: tried emailing playgrounds, playground pages, tried going through iTunes, looked in Xcode, even used PhoneView (Ecamm) and iBrowser (my little hack app) to try to track this stuff down. No luck so far.
  • XCPlayground seems to be PlaygroundSupport — took me forever to figure that out because it’s still XCPlayground on the desktop. And of course, all the new ObjC name import stuff isn’t working as expecting, so you do have to hunt around. Live views are PlaygroundPage.current.liveView assignments.
  • To see both code and live view (or just live view), slide from the right.

Lots still to explore!

 

WWDC Thoughts

Screen Shot 2016-06-13 at 12.42.33 PM

This year, Apple offered a particularly subdued WWDC keynote introducing refreshes along all its product lines with few surprises or innovations. While the audience was politely enthusiastic, I’m not sure the tech press will be wowed.

I love the iOS playgrounds, although I think this is a better opportunity for institutions (say, Stanford) than little tutorial writers, and I got Sherlocked. Xcode introduces source editor extensions and there’s a Siri API using extensions.

Screen Shot 2016-06-13 at 1.06.26 PM

The download and upgrade process for Xcode, OS X (I mean macOS), and iOS has been amazing — just a dream process compared to previous years. Everything is operating smoothly from updating the conference schedule to providing bandwidth for desperate developer downloads. Fingers crossed this continues as well as it has started. I’m amazed at how clean the download site is, how simple the configuration profile approach is working (“Can I temporarily remove some large apps for space and re-install them later?” “Why YES!”). Big applause to whoever made these improvements.

Looking forward to the state of the union later today.

Swift:

Tuesday

  • What’s new in Swift: Tues 11-11:40
  • Getting Started with Swift: Tues 2:40 – 3:40
  • What’s new in Foundation for Swift: Tues 5 – 5:40
  • What’s new in LLVM: Tues 6-6:40

Wednesday

  • Introducing Swift Playgrounds: Weds 12-1
  • Modern Best Practices: Weds 4-4:40
  • Swiftogether: Weds 7:15-8:45

Thursday

  • Using and Extending Xcode’s Source Editor: Thus 6-6:40

Friday

  • Server-side with Swift Open Source: Fri 10-10:40
  • Swift Performance: Fri 12-1
  • Debugging Tips & Tricks: Fri: 2:40 – 3:40
  • Concurrent GCD w/ Swift: Fri 5-5:40
  • Protocol and Value Oriented Programing (in UIKit Apps): 5-5:40

Not Swift:

  • What’s new in Cocoa: Tues 12-1
  • What’s new in Cocoa Touch: Tues 2:40-3:40
  • Typography and Fonts: Weds 10-10:40
  • Speech Recognition API: Weds 5-5 (!?)
  • Intro to SiriKit: Weds 6-7
  • Extending Apps with SiriKit: Thus 2:40-3:40
  • What’s new in GameplayKit: Thus 10 – 10:40
  • Making Apps Adaptive: Thus 12-12:40, Fri 10-10:40
  • Proactive Suggestions: Fri: 2:40-3:40
  • Measurements and Units: Fri 5-5:40

Also: Some nice internationalization and accessibility sessions, a couple of worthy “adopting Metal” sessions, CloudKit best practices

iOS 10 Compatible Devices

I think this is probably the most reliable screenshot:

Screen Shot 2016-06-13 at 1.43.05 PM

There’s also this:

Screen Shot 2016-06-13 at 12.46.07 PM

and this:

Screen Shot 2016-06-13 at 1.46.51 PM

macOS Sierra Compatible Devices

Screen Shot 2016-06-13 at 1.59.25 PM

 

Dear Erica: ex-var parameters

Dear Erica: “Bah.  Swift 2.whatever removed func _( var _ : _){}. I found it a useful convenience. How do I work around this?” — Rather Outspoken

Starting in Swift 2.2, SE-0003 removed var parameters from the language. Both var and inout created mutable parameters but only those labeled inout wrote values back to the originating instance.

This language evolution really only affects value types. Reference types already allow modification because the reference doesn’t change. It’s only the data that the reference points to. This is still legal in Swift 2.2:

func modifyMyLabel(myLabel: UILabel) {
   myLabel.text = "Hello"
}

let label = UILabel()
modifyMyLabel(label)
print(label.text) // Optional("Hello")

Imagine in pre-Swift 2.2 you had this code:

func foo(var i: Int) {
    i += 1 // the change to i would not propagate back
}

Now, if you want to modify a value passed as an argument without using inout, you must create a var instance within the function scope, the most common way to do this is shadowing, or by declaring a new variable with the same name as the one used in the function signature:

func foo(i: Int) { // no "var" allowed
    var i = i // shadowing
    i += 1 // the change to i still doesn't propagate back
}

Shadowing fixes most problems with the language update. In more complex situations, Brent Royal Gordon and I came up with a slightly different solution. We have a proposal in the Swift Evolution pull request queue that introduces a with operator to handle updates to immutable constants. To give an example of use, the standard library includes an operator for concatenating two RangeReplaceableCollections with this implementation:

var lhs = lhs
// FIXME: what if lhs is a reference type? This will mutate it.
lhs.reserveCapacity(lhs.count + numericCast(rhs.count))
lhs.append(contentsOf: rhs)
return lhs

Using our proposed with function, you can eliminate the shadowing of lhs:

// FIXME: what if lhs is a reference type?  This will mutate it.
return with(lhs) {
  $0.reserveCapacity($0.count + numericCast(rhs.count))
  $0.append(contentsOf: rhs)
}

Our proposed with implementation does not resolve the “FIXME” comment. Like the var lhs = lhs in the original code, with only copies value types, not reference types. If RangeReplaceableCollection included a Foundation-like copy()method that was guaranteed to return a copy even if it was a reference type, with would work nicely with that solution:

return with(lhs.copy()) {
  $0.reserveCapacity($0.count + numericCast(rhs.count))
  $0.append(contentsOf: rhs)
}

WWDC sale: Gourmet Cookbook, Swift Cookbook, and more!

ShowCover-1.aspx  ShowCover.aspx  ShowCover-2.aspx

Happy WWDC! My Gourmet Cookbook is InformIT’s eBook Deal of the Week this week (from 6/12 to 6/18), selling at a 53% discount. You can also snag a cool 37% discount off any of my other Pearson books by using the code IOSDEV37. Hop by my InformIT author page to grab a deal.

Update: @InformIT sent me a corrected discount code Monday morning. I apologize if you had any trouble yesterday or today with an invalid code.

http://twitter.com/InformIT/status/742344017866858497

Swift: Where oh where can my where clause be

SE-0099, which was just accepted in Swift, eliminates where clauses from the condition clauses used in guard, if, and while statements. Specifically, transforms the grammar of optional binding conditions (“if lets”) and case conditions (“if cases”) to remove where clauses and limits each clause to a single case or assignment, massively simplifying the grammar.

Now, the discussion has turned to whether the while clause grammar should be fixed in for-in loops or eliminated entirely.

The Challenge

To get started, predict the outcome of the following two loops. And no, you may not peek before running the code.

print("for in")
var theArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for x in theArray where x % 2 == 1 { print (x) }

print("while")
var anArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]
while let x = anArray.popLast() where x % 2 == 1 { print(x) }

So did you predict the two outcomes correctly?

Some difficulty lies in way a where clause behaves depending on the kind of iterator being used. In for-in loops, it acts as a filter, using syntactic sugar for continue when its condition is not met. In while loops, it’s a conjoined Boolean, and will break when its condition is not met.

Where and While

I’ve had a bit of experience with people new to the language being confused as to whether where will filter or break. I do a lot of peer support. Because I think “for in where” is cool, I’ve pushed it.

This confusion is why this week someone introduced the notion of adding while as an alternative to where in for-in loops, to expand the grammar for breaking.

There are several problems with this proposed enhancement, the biggest one of all is at the end of the post. I’m saving it for the kicker. But I’ll work up towards that.

To get started, for-in loop where is deformed. Unlike in switch statements and do loops, a for-in loop’s where-clause is separated from the pattern it modifies. Brent Royal-Gordon first pointed this out and I was really surprised that I had never noticed:

for case? pattern in expression where-clause? code-block

case-item-list → pattern where-clause? | pattern where-clause? , case-item-list

catch pattern? where-clause? code-block

This separation makes the clause harder to associate with the pattern. It can confuse users as to whether it modifies the expression or the pattern, and represents an inconsistency in Swift’s grammar. The where clause really should read like this:

for case? pattern where-clause? in expression code-block

And if regularized, you’re looking at code that reads like this:

// if `where` is fixed
for x where x % 2 == 1 in 1...9 { ... }
// and if `while` is added
for x while x % 2 == 1 in 1...9 { ... }

Still not super clear.

Using Guard

I don’t think where (or while) should appear in the for-in loop declaration at all. (That may be a style/linter choice it may be a language modification, I haven’t made up my mind.) I think they are better expressed (and read) as guard conditions.

Guard conditions can continue (mimicking the current use of where) or break (introducing the recently pitched while behavior).  This limits the current situation where people new to the language expect while behavior and expect termination rather than sequence filtering.

for x in sequence {
    guard (condition) else { continue } // current where behavior
    guard condition else { break } // proposed while behavior
}

It’s much easier to read. Removing where from for-in loops (whether style guided/linted or language enforced) benefits these new users, reduces cognitive burden for all users, and enhances readability and predictability.

The Kicker

It’s really hard to do regular expression searches on Github and my go-to-source searchcode.com was impossible to deal with for these common words, so I ended up grepping through my “Cool 3rd Party Swift repos” folder and the Swift standard library source to see exactly how often “for in where” gets used.

My results:

stdlib: for-in: just over 600 (including a few false positives), for-in-where: 3:

private/StdlibUnittest/StdlibUnittest.swift.gyb: for j in instances.indices where i != j {
public/core/Algorithm.swift: for value in rest where value < minValue {
public/core/Algorithm.swift: for value in rest where value >= maxValue {

third party repos folder: for-in: about 650 (and ditto), for-in-where: 1:

Carthage/Source/CarthageKit/Algorithms.swift: for (node, var incomingEdges) in workingGraph where incomingEdges.contains(lastSource) {

If this construct isn’t being used by Swift experts, I think I can reasonably conclude that it isn’t going to be used by the Swift new developer either. Maybe it’s time to give it the boot.

Do you use “for in where” in your code? Can you do a search on your code base and let me know how often you use that compared to base “for in”? Please let me know!