Search Results for ‘playgrounds’

Good Things: SwiftUI on Mojave in iOS Playgrounds

Yes, you can upgrade to the Catalina Beta. Or, you can keep getting work done on Mojave and still enjoy the glory of SwiftUI exploration.

  1. Install Xcode 11 beta along side your 10.2.x Xcode
  2. Using the beta, create an iOS playground. (This won’t work with macOS, which attempts to use the native frameworks, which don’t  yet support SwiftUI)
  3. Import both SwiftUI and PlaygroundSupport.
  4. Set the live view to a UIHostingController instance whose rootView conforms to View.

Here’s an outline of the basic “Hello World” setup:

From there, you can create pages for each of your SwiftUI experiments, allowing you to build short, targeted applets to expand your exploration and understanding of the new technology.

Auto Layout, Playgrounds, and Xcode

Today, someone asked what the easiest way was to center a view (specifically a UIImageView) inside a parent view with minimum offsets from the top and sides.

Because you’re working with image views, it’s important that you first set the content mode. For insets, it’s almost always best to go with a “fitting” aspect-scale, which preserves the entire picture even if it has to pillarbox or letterbox. (Pillarboxing adds extra spacing to each side for tall images; letterboxing adds the top and bottom for wide ones.)

// set content mode
imageView.contentMode = .scaleAspectFit

Make sure your view can squish its content by lowering its compression resistance:

[UILayoutConstraintAxis.horizontal, .vertical].forEach {
    imageView
        .setContentCompressionResistancePriority(
            UILayoutPriority(rawValue: 100), for: $0)
}

You must preserve your image’s aspect ratio. Calculate your target ratio by dividing its width by its height:

let size = imageView.image?.size ?? CGSize()
let aspectRatio = size.width / size.height

Add strong constraints that preserve the aspect, and make the view smaller than its parent using inset values you supply:

let insets = CGSize(width: 20.0, height: 32.0)

let constraints = ([
    // Preserve image aspect
    imageView.widthAnchor
        .constraint(equalTo: imageView.heightAnchor, multiplier: aspectRatio),
 
    // Make view smaller than parent
    imageView.widthAnchor
        .constraint(lessThanOrEqualTo: parentView.widthAnchor,
                    constant: -insets.width * 2),
    imageView.heightAnchor
        .constraint(lessThanOrEqualTo: parentView.heightAnchor,
                    constant: -insets.height * 2),

    // Center in parent
    imageView.centerXAnchor
        .constraint(equalTo: parentView.centerXAnchor),
    imageView.centerYAnchor
        .constraint(equalTo: parentView.centerYAnchor),
])

If you want to be super cautious, keep the aspect and two center constraints at 1000 and bring the width and height ones down to 999. You can install the constraints as you create them but I prefer to break that part out so I can tweak the priorities for each constraint group:

constraints.forEach {
    $0.priority = UILayoutPriority(rawValue: 1000)
    $0.isActive = true
}

I always mess up with the signs (positive or negative) of the constants. It helps to test these out in a playground rather than going by memory because the signs aren’t always intuitive. Even better, write routines that automates your Auto Layout tasks because if you debug once (and add proper tests), you never have to think about it again.

Mac playgrounds are inherently superior to iOS ones as they don’t run a simulator and are faster and more responsive. That is to say, you don’t have to quit and relaunch Xcode quite so often. If you are debugging iOS layouts though, and your playground hangs when starting the simulator or running your code, learn to quit, kill the core simulator service, and relaunch Xcode. It will save you a lot of time.

I have a one liner in my utilities to deal with the service:

% cat ~/bin/simfix
killall -9 -v com.apple.CoreSimulator.CoreSimulatorService

Most of the time a single quit, simfix, and launch will get things moving again with recalcitrant iOS playgrounds. Be aware that malformed format strings and other auto layout issues won’t produce especially helpful feedback, especially compared to the runtime output of compiled projects. If you know what you’re doing, you can set up a simple layout testbed in playgrounds with less overhead and time than, for example, a one view project but at a cost.

Stick to projects, and avoid playgrounds, when testing even mildly complex code-based layouts. You cannot, at this time (at least as far as I know), control which simulator will be used in the playground so it’s not great for checks on a multitude of simulator geometries. The tl;dr is that playgrounds work for general layout routines. Prefer compiled projects for specific tasks.

Carrying user-sourced code forward in Swift Playgrounds for iOS

Had a really neat challenge today, as a Slack-buddy attempted to work with Apple’s exquisitely insufficient Playground Book documentation. His goal was simple: he wanted to be able to incrementally grow and test code from page to page, copying the user’s work as they moved on.

In theory, Swift Playgrounds for iOS enables you to build books where your reader/student incrementally builds code. Each page introduces a new concept, a new tweak, or a new approach. It’s a great way to layer each lesson on a previous take, or to take one lesson and branch it out to multiple endpoints.

You can either build, build, build to one big story or take one core concept and show many different ways to apply it. Either way, you want to be able to bring code — whether from the most recently edited page or from a shared core page — forward, so the reader/student can further engage with it, edit it, and make it fit with each page’s challenge.

Implementing “code forwarding” (I just made up that term) proved trickier than expected. Playground book workflow is often “understood” (that is, you have a deep understanding of what’s required because you’ve worked with it a lot or you’ve poked around at Apple’s examples or reversed engineered to see how things work) rather than explained step by step in the official docs. Because of this, he ran into several roadblocks along the way.

  • First among these, is that Apple does not provide a Playgrounds Book Author tool for Mac. You have to build your books by hand, going through the specs and hoping that each iteration works. Most of the time it does. Sometimes, maddeningly, it does not.
  • Second, you have to transfer the book to the iPad for each test (I use AirDrop™), and guess at what went wrong if it doesn’t work. When testing a series of book-based exercises, you have to either hard-code each “success” sequence (and there’s no way to set a “I am debugging/developing this book” flag) or actually do the coding, which can take a lot of time, especially if you’re debugging page 7 and you have to work through the exercises on page 1 – 6 for each test of page 7.
  • Finally, if your book is even a little out of spec with what Swift Playgrounds for iOS expects, it’s going to die without much feedback or explanation, leaving you scratching your head, cursing Dev Tools (but we really love you guys, we do), or otherwise venting frustration.

I wasted a bunch of hours because I wanted to make this work. And finally, I managed to get things working to my satisfaction. I thought I’d take the time to write things up to save you some bother. Here are a few things I learned while deep diving into today’s experiments.

The Zen of User Code

Code-forwarding allows you to propagate user-sourced/user-edited code from an earlier page in a playground book to a later page in a playground book. When you code-forward from a “source” to a “destination”, Swift Playgrounds for iOS makes a copy of the earlier code and places onto the current page.

That code is copied once and you aren’t given the option to re-copy unless you reset the page. Every page in Swift Playgrounds offers a reset option in the ellipsis menu, but its discoverability is low. Apple expects each reader/student to work through exercises linearly, progressing only when each previous problem is solved. This means that you don’t get “live” updates by popping back and making new changes to the source page. The destination copies once.

That also means you cannot apply code-forwarding until your page is set as complete. By “complete”, I mean that the book’s source code and Swift Playgrounds accept that the reader/student has done sufficient work to move forward and progress to the next page/exercise.

This usually happens by executing a page epilogue. The epilogue tests the state of the page’s data, determines if the problem was solved (for example, whether the robot reached the end square and the code progressed to a hidden portion containing this test), and then updates a user assessment. Unless a page’s assessment status is “passed” (that is, done), the reader/student is not offered code copying on the following page.

This is built into Swift Playgrounds for iOS, and is an underlying assumption on how progressive learning plans operate. It’s a critical pathway for building page-by-page progress and enabling code-forwarding. This is why the following snippet includes its bit of hidden code. This code allows a user to pass, that is receive a passing grade/assessment for the page, without doing any more work than running the current page:

//#-copy-source(id1)
//#-editable-code 
func foo() {
    // ... starter code here
}
//#-end-editable-code
//#-end-copy-source

//#-hidden-code
import PlaygroundSupport
PlaygroundPage.current.assessmentStatus = 
    .pass(message: "Great job!")
//#-end-hidden-code

Building Code-Forwarding

The preceding code incorporates two essential parts of using a copy-source markup area.

  • First, is the actual tagged copy-source code. This delimits the code that gets copied forward to one or more other pages. Make sure to mark it editable when you want to present a challenge requiring end-user-reader modification. You can omit editable tags when you want the next step or branches to start with code you source yourself. It’s an unusual approach but it’s not illegal to do so.
  • Second, is the hidden assessment update. Normally you’d use more sophisticated logic to determine whether a reader/student has met those challenges laid out in the current page before allowing them to .pass or .fail. When you just want to demonstrate core functionality, make it clear in your marked-up write-up that the user must run the code before continuing. Use the approach in this code to “pass on first run” for demonstration. You’ll probably want to update the message to something along the lines of “Great! You’ve seen this work, move to the next page to start making changes.”

Building The Destination

Crafting a destination page is trickier than laying out acopy-source area: You must update your page’s manifest as well as its content source. The manifest will expect properly internationalized source strings. That means at a minimum, a code receiving page will need a Contents.swift file, a Manifest.plist file, and a PrivateResources folder with at least one localized lproj folder (in my case, en.lproj), which in turn holds the ManifestPlist.strings file.

Here’s what a simple manifest looks like for a copying destination. Keep in mind that each value entry for the CodeCopySetup keys is actually a placeholder for localization.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CodeCopySetup</key>
	<dict>
		<key>CopyCommandButtonTitle</key>
		<string>CopyCommandButtonTitle</string>
		<key>DefaultCommandButtonTitle</key>
		<string>DefaultCommandButtonTitle</string>
		<key>NavigateCommandButtonTitle</key>
		<string>NavigateCommandButtonTitle</string>
		<key>NotReadyToCopyInstructions</key>
		<string>NotReadyToCopyInstructions</string>
		<key>ReadyToCopyInstructions</key>
		<string>ReadyToCopyInstructions</string>
	</dict>
	<key>Description</key>
	<string>Description</string>
	<key>LiveViewEdgeToEdge</key>
	<true/>
	<key>LiveViewMode</key>
	<string>VisibleByDefault</string>
	<key>MaximumSupportedExecutionSpeed</key>
	<string>Fastest</string>
	<key>Name</key>
	<string>Name</string>
	<key>PlaygroundLoggingMode</key>
	<string>Off</string>
	<key>Version</key>
	<string>1.0</string>
</dict>
</plist>

I followed Apple’s example in my ManifestPlist.strings file, so the English expressions aren’t terribly exciting. The Name field used in the manifest is spelled out in addition to the button text:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CopyCommandButtonTitle</key>
	<string>Copy My Code From the Last Page</string>
	<key>DefaultCommandButtonTitle</key>
	<string>Start Coding on This Page</string>
	<key>Name</key>
	<string>Copying Text from the Previous Page</string>
	<key>NavigateCommandButtonTitle</key>
	<string>Return to Previous Page</string>
	<key>NotReadyToCopyInstructions</key>
	<string>Be sure to complete the previous page before you move on to solving the next step.</string>
	<key>ReadyToCopyInstructions</key>
	<string>You can bring over your algorithm from the previous page to continue improving it.</string>
</dict>
</plist>

Here, each possible assessment state and action is given a human-readable form. I was unable to make the system “default” to the items mentioned in the Playground Page Manifest documentation (such as “Copy my code” and “Start with provided code”). I’m sure if I tried hard enough, I could have gotten this working per the docs but I didn’t have the time to push.

In building the code-destination contents, link each identifier you used in the source (it’s id1 in this example but it can be any key you want to use) and the page to copy from (Page1). This page names comes from the name of the playgroundpage file hosting the user-edited or user-sourced content.

You must mention the page because you may keep enhancing the same progression of code from page to page, while using a single identifier. If you start on page 1, update on page 2, when you get to page 3, you want to copy from the updated source on page 2, not page 1. Mentioning which source you want to copy helps keep you and the user on track.

//#-editable-code
//#-copy-destination("Page1", id1)

//#-end-copy-destination
//#-end-editable-code

If you’re using a branching storyline (for example, you might explore variations on a sort or showcase different blending modes for merging images), you can place this destination code on each branch page.

More often, you’ll want to progressively modify code through a series of exercises. To carry the code further, add copy-source tags around the destination as in the following code, using the same id1 identifier, and refer to #-copy-destination("Page2", id1) for the next copy on Page 3 and so forth. Read this directive as this is the copy destination for the code tagged with id1 sourced from page 2.

Here’s what an edit-and-carry approach looks like for a second page, referring back to ("Page1", id1). In my imagination, this is the first time code has been copied and this markup sets up a user-editable progression that can be carried to the third page and beyond.

//#-copy-source(id1)
//#-editable-code
//#-copy-destination("Page1", id1)

//#-end-copy-destination
//#-end-editable-code
//#-end-copy-source

That’s pretty much all you need: proper tags, proper localized strings, and proper id/page references. If you’d like to try out a copy of my playground, you can grab a copy from here or email me for a copy if that doesn’t work.

A few interesting things about iOS Swift Playgrounds

Misc rambles:

Interesting: You no longer “print” in iOS Swift Playgrounds. Anything that can be presented using a playground QuickLook or a custom string representation is done so with the tap-to-view item at the right. There’s no “printing” per se, no console, while I suppose you can import Foundation and NSLog stuff, I’m not sure how reasonable a solution that is, especially on the go.

Because: This morning I ended up importing AVFoundation to let AVSpeechSynthesizer “speak” feedback to me. Not ideal, but short and easy to implement, and got the job done for some stuff that wasn’t rendering nicely the way I needed it to, in a complicated playground book that was using SceneKit not UIKit.

Rad discovery of the day: Command-R runs the playground without having to tap the screen. \o/ Press and hold Command key (on a physical Bluetooth keyboard, this doesn’t work on, for example, type2phone) to discover other shortcuts:

screen-shot-2016-11-11-at-3-13-29-pm

Sad discovery of the day: You cannot air drop playground books anymore, and Swift Playgrounds is being very very careful about allowing you access to any material you didn’t create or import yourself. There’s no Documents support for iTunes. At the suggestion of Jonathan Penn, I gave it a try after rebooting and now it works. You wouldn’t believe how much time I wasted trying to extract information to look up implementation details.

Thanks again to Jonathan P for pointing me to the command-key discovery feature!

iOS Playgrounds: Where is my print output?

Nate writes: “I can’t figure out how to show console output/print in the Swift Playgrounds app for iOS.”

All print output appears in the “sidebar” to the right of the code as little pop-ups:

Screen Shot 2016-06-22 at 1.11.56 PM

Embed the viewer by tapping “Add viewer” so you don’t have to keep popping up the quick view.

The hint box shown (for example “123”, or “?”, or “abc”, etc) depends on the type of item produced. The tiny cube represents an class instance.

Screen Shot 2016-06-27 at 11.40.51 AM

Like desktop playgrounds, you can view a single value at a time or tap the embedded display to select other views like List view:

Screen Shot 2016-06-24 at 12.15.27 PM

Compilation errors appear in-line for main playground code:

https://i2.wp.com/ericasadun.com/wp-content/uploads/2016/06/Screen-Shot-2016-06-21-at-11.51.25-AM.png?resize=859%2C690&ssl=1

Sources-based errors (which  you cannot yet fix in the app) are shown as popups. When these issues occur, debug back on  your Mac before trying again on iOS.

https://i1.wp.com/ericasadun.com/wp-content/uploads/2016/06/Screen-Shot-2016-06-21-at-11.29.05-AM.png?resize=859%2C690&ssl=1

It’s important to remember that the iOS Playgrounds app almost never is running the same version of Swift as you are on the desktop and won’t be until both Swift 3 and iOS 10 go gold.

Want to learn more about iOS Playgrounds? Read Playground Secrets and Power Tips.

Dear Playgrounds Santa: Playgrounds

Dear Playgrounds Santa,

It’s awesome using #colorLiteral in Swift Playgrounds. The editor automagically picks up the literal and offers a context-sensitive picker. So might I have a new #numberLiteral to match? One that allows integer or floating point values, with max: and min:, so that the number tweaker cannot produce values outside my specifications?

#numberLiteral(0.2)
#numberLiteral(0.2, min:0.0, max:1.0)
#numberLiteral(8, min:1, max: 10)

It would sure make staging examples less error-prone, especially when working with frameworks like Core Image.

Speaking of being good this year, can I please have a standard  “Cover.png” resource file to display in the Playgrounds library? When found, it would replace the empty gray swift icon that makes one playground look so like another. Just because they’re not playground books doesn’t mean standalone playgrounds can’t be fun,  interesting, or exciting.

I’d also really like to have a new markup keyword. Can you add #-autorun, so my LiveView updates on edits (or at least when the editor has paused, say, for half a second?)

Finally (along with my pony), can I view and edit Sources when working with playgrounds (and not playground books) somehow?

Thanks!

p.s. I’ve left you milk, cookies, and Radar 27035484 next to the tree.

Playgrounds 7: In which I *finally* get the keyboard

Swift Playgrounds offers a smart, programmer-centric keyboard. Symbols, numbers, and common programming punctuation are included on the main keyboard.

Screen Shot 2016-06-24 at 11.35.29 AM

To access an alternate key, tap and then either drag down for single item keys, like qwerty’s numbers, or down-and-left / down-and-right for two-item keys, like fghj’s brackets and parentheses. Release your finger with the gray replacement key shown. Playgrounds uses smart insertion, so you may sometimes add a single bracket or you may add a bracket pair, depending on context.

Try these steps to add a line that says “let x: [Int] = []”:

  1. Tap the “let” bubble in the top keyboard accessory view. A template appears that says “let name = value”, with name highlighted.
  2. Tap x. This replaces “name”.
  3. Pull down on the “k” key until it turns into a colon and release. Playgrounds adds a colon and a space.
  4. Pull down-and-right on “h” until it turns into “]”. Release. Playgrounds adds a square-bracket pair (“[]”) and places the cursor in the middle.
  5. Tap Int on the accessory view.
  6. Tap the value placeholder in code.
  7. Repeat step 4 (Pull down-and-right on “h” until it turns into “]”. Release.) to add an empty array literal.

In addition to this sweet, super-convenient programming style, you can also connect a Bluetooth keyboard (or use your computer with a program like Houda’s Type2Phone), dictate (handy for populating comments), or switch to a standard iPhone numeric keyboard by tapping either of the “.?123” keys. You can even use Swift-supported emojis:

Screen Shot 2016-06-24 at 11.42.05 AM

After declaring the dog-cow variable, it appears in contextual completion lists like any other symbol:

Screen Shot 2016-06-24 at 11.46.52 AM

Izzy notes: The 12″ iPad doesn’t use it because the larger keyboard has an extra row of numbers/symbols. You can tap + hold or swipe up on the numbers to get the symbols without having to use shift.

Playgrounds 6: Focusing Interaction

Screen Shot 2016-06-24 at 9.37.20 AM

In this screenshot, I’ve circled a bit of directed interaction. There’s a highlight around “0.5” and a pop-up editor that allows the user to change the value. This new feature is made possible by Swift Playground markup.

Like all Swift playgrounds, this page uses live code, some of which you see and other parts of which are hidden, either by placement in Sources or by using playground markup. The code is marked up to disable edits except in this one place. Here’s what the key part of the underlying code looks like:

filter.setValue(/*#-editable-code*/1.0/*#-end-editable-code*/, forKey: "inputAmount")
imageView.image = UIImage(ciImage: filter.outputImage!)

Yes, those are in-line comments adding markup in the  setValue call. It’s kind of ugly and it relies on the old-style C /* */ comment delimiters that allow the markup to be inserted in-place.

From a coding viewpoint, it’s ugly as sin. From a user experience viewpoint, it ensures that users can explore exactly one setting to better understand the Core Image vibrancy filter.

This is no way, at least at this time, to use markup to limit values to legal parameters. For example, Core Image may not like negative numbers or numbers above 1.0. You could work around that by using a separate variable and adding clamping code, e.g.

var inputAmount = make this editable
inputAmount = clamp(inputAmount, 0.0, 1.0)
filter.setValue(inputAmount, forKey: "inputAmount")

But if you do that, you’re drawing attention away from the CI code you’re demonstrating and focusing it on user interaction instead. No one’s going to use this pattern in real code, while they may very well use the pattern in the screenshot. (Which, if you think about it is the real point of this.)

I’ve gone ahead and filed a radar (26996963) about the issue, asking for enhanced markup that allows input qualities for editable playground sections.

I probably should file another one asking for “Auto Run” markup, so these kind of examples will re-execute on each edit, just like they do on OS X.

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