Archive for the ‘Development’ Category

Xcode Symbology Challenge! Find examples, identify icons, gain fame and glory

You know those little icons in the Xcode developer docs? Like the 4 squares for frameworks or the M for method? How many can you find and give examples for? Also how many can you identify off the top of your head? Do you know what each of these are and what they represent?

What follows is an exhaustive list sourced from an internal Xcode framework. For each of the following, your challenge is to describe what the icon looks like and offer an example that uses the icon. For example “T” is a type alias. You can find one by following UIKit > Drawing > UIGraphicsPDFRenderer.DrawingActions or searching for DrawingActions.

  • article
  • category
  • class
  • class extension
  • constant
  • endpoint
  • enum
  • extension
  • field
  • framework
  • function
  • group
  • local_variable
  • macro
  • method
  • object
  • operator
  • property
  • protocol
  • sample code
  • struct
  • subscript
  • typedef
  • union
  • variable

Have fun and drop a comment with a link to your finds. Please don’t put answers here directly as that may spoil the challenge for others.

Prototyping CoreGraphics in the Playground

No matter how flaky, I love using playgrounds to prototype Core Graphics, SpriteKit, and many other see-as-you-go technologies. They’re fantastic for building out specific custom content with a bare minimum of coding investment. You get a lot of win for very little time.

I was helping someone out the other day, explaining the strokeEnd keypath (versus the path keypath) and building a playground showed it off to perfection.

Admittedly, it helps to have quick helper code on-hand for quick starts. I have playground-specific setup code, handing me a view controller (called vc) and a centered view, ready to start demo-ing in this one.

I also have a couple of pages of code (like the layer(path:) constructor, the animateStrokemethods, and the schedule() utility off page, in the support module. They’re all highly reusable. It’s a pity in-playground debugging is so dreadful. It would be an ideal module-building tool if not for that: build and explore (and ideally build tests) in a single place, without having to be in a fixed workspace lacking the exploration feature. Adding “convert this exploration into a test” would be icing on top.

I’m disappointed that playground-specific visualizations built for teaching and demos don’t transfer to the debugger for real-world production support. I don’t see any reason why a CGPoint instance should get a pretty graphic representation but a CGAffineTransform, for which I have quite a full presentation, does not.

I can use custom mirroring to produce valuable output for dump, and therefore for printing objects in the debugger but not for debug quicklooks. Plus as far as I can tell the custom NSObject-only quicklooks haven’t been updated in years and there’s no hint of extending this to structs and enums.

By the way, what’s the deal with all the API audits? How long are these going to go on? If you thought updating the app delegate was a minor nuisance, you haven’t seen what’s happened to all the constants and Core Graphics APIs. This update is huge and disruptive…

Return to Sender: My first dive into watchOS

Just wrote my first two watchOS apps. The first, intended to provide voice coaching for physical therapy exercises, was a failure. Timing just wasn’t reliable and the errors built up more and more over a session of repeated counts, holds, and “almost there”/”two more”/”last one” prompts.

The second was a simple D&D dice set and much more successful. (Barring, of course, my miserable sense of interface design.)

It consists of an iOS app paired with a watchOS extension, which runs as its own watchOS app, allowing anyone with an arm and a need for d20 to roll at will.

The WatchOS APIs were disappointing at first glance. For one thing watch buttons use target-action but provide no sense of sender. A single iOS call must be split into 7 calls on the watch:

// iOS. I used tags. Sue me.
@IBAction func roll(_ button: UIButton) {
    let value = Int.random(in: 1 ... button.tag)
    let percent = button.tag == 100 ? "%" : ""
    label.text = "\(value)\(percent)"
}

// watchOS
@IBAction func d4()  { label.setText("\(Int.random(in: 1...4))")  }
@IBAction func d6()  { label.setText("\(Int.random(in: 1...6))")  }
@IBAction func d8()  { label.setText("\(Int.random(in: 1...8))")  }
@IBAction func d10() { label.setText("\(Int.random(in: 1...10))") }
@IBAction func d12() { label.setText("\(Int.random(in: 1...12))") }
@IBAction func d20() { label.setText("\(Int.random(in: 1...20))") }
@IBAction func dPercent() { label.setText("\(Int.random(in: 1...100))%") }

There’s no auto-layout so far as I can tell, so you either “fill” things or you give them fixed sizes. I couldn’t figure out a way to split my buttons into thirds of the available screen space.

Setting a label’s text is a matter of a function call not a property assignment. That was surprising to me. I’m not sure what advantage was gained here beyond violating the principle of least astonishment.

I had to design my app’s interface in the WatchKitApp target but implement it in the extension target. This may be perfectly normal and in-line with other extensions on iOS but I haven’t spent much time there so it raised my eyebrows a bit. You add your app assets in the app, your complications (which I did not build) in the extension.

Right now on my priority list, I’d like to build something similar to the “breathe” app. I don’t entirely know where to start. I need to use a notification system to launch the app at set intervals, n times a day, preferably during the work day, and then use some kind of coached interaction with haptic feedback. My immediate goal is one that has me regularly exercise my sitting back muscles to build up strength.

I’m a bit sad about the timing issues I encountered with my first jab at the coaching app. I may approach that again with a new design as having my wrist count and prompt would be a lot better than having to do all the work myself (I always lose my place) or having to keep my phone nearby.

My need for reps, holds, and rest periods are simple. And while I don’t particularly like the scrolling wheel that seems to take the place of segmented controls, I can live with them.

If you’d like to see my first project (either to be inspired or to offer critiques), I’ve left a copy at github.

Unwinding a modal segue on a single view controller

For whatever reason, my unwind segues for modal presentations weren’t working when my presenting source was my navigation’s root controller. This appears to be a common issue if you hunt around for “modal unwind doesn’t work”. A lot of the advice simply went: “You just can’t do that.”

After spending far too much time dealing with, I discovered that the autocomplete version, namely:

@IBAction func unwind(for unwindSegue: UIStoryboardSegue, towards subsequentVC: UIViewController)

doesn’t work. Instead, what you need is:

@IBAction func unwindFor(_ unwindSegue: UIStoryboardSegue, towards subsequentVC: UIViewController)

If you notice, the difference is unwind(for vs unwindFor(. I figured this out by implementing

func canPerformUnwindSegueAction(_ action: Selector, from fromViewController: UIViewController, withSender sender: Any) -> Bool

and looking at the selector it expected to perform (specifically unwindFor:towards:). Moving the For outside fixed the problem and granted me a working unwind, allowing me to fetch state data from the modal controller and pass it back to the source.

I assume this issue arose because of the automatic translation of Cocoa APIs into Swift and I wonder if there are other issues that might arise from the same. Have you encountered such things? What are your thoughts?

 

Tuple Initialization

I’ve been wanting  tuple-initializable in Swift for Core Graphics, where it’s a drag to always use long and cumbersome initializers when I’m not building production code:

let point: CGPoint = (100, 50)

Yeah, it is better form to use labeled initializers but I’m anyone using CGPoint understands the correspondence between (x, y) coordinates. And I don’t want to just build CGPoint.init(_ x:, _ y:) extensions. I like the tuple form.

Right now, the closest you can get is the silly:

let point = (CGPoint.init as (CGFloat, CGFloat) -> CGPoint)(100, 50)

And that’s (pardon the pun) pointless.

It would be pretty cool to allow memberwise tuples with or without labels in place of  initializers when the tuple field types could be unambiguously determined:

let point: CGPoint = (x: 100, y: 50) 
// instead of: let point = CGPoint(x: 100, y: 50)
let person: Person = ("Mary", nil, "Santos")
// instead of: let person = Person(first: "Mary", middle: nil, last: "Santos")

It may be ugly but it would be hella useful in Playgrounds.

Converting projects by hand to Swift 4.2

If Xcode is acting up for you the way it is for me, you may want to step back from automated migration and just do it by hand. I spent far too much time this morning trying to get Xcode to finish migrating for what were extremely trivial changes.

If you’re going by hand, make sure to change your “didFinishLaunching” launch options (in your App delegate) from [UIApplicationLaunchOptionsKey: Any] to use UIApplication.LaunchOptionsKey. (Notice the period in the name.)

To set the compiler from Swift 4 to Swift 4.2, open Target > Build Settings. Navigate to Swift Language Version and use the pop-up to select the proper compiler:

For some projects (especially small sample code) this alone may be sufficient to get you back and working.

Preparing to teach/demo

Normally I use a separate account to present talks and demos but I’ve recently taken up a more regular instruction gig and in doing so, it’s too inconvenient to move from my main account. My main account is where all my development tools and code are a few clicks and keystrokes away. The demo one is very safe for public presentation but also very distant from my workflow.

To address this, I’ve built a demo-setup utility in Swift. I thought I’d share some of the features and approaches. A lot of these were non-trivial to track down in terms of time and I thought having them in one place could be useful to some of you reading my blog.

If you have others to share, please let me know.

Desktop Wallpaper

I use Backdrop from Apps from Outer Space to cover my normal desktop with a plain and  boring background. Set up preferences in the app and then use open /Applications/Backdrop.app / killall Backdrop to toggle. It covers your wall paper without having to set it back to your favorite picture each time you enter and leave demo mode.

Do Not Disturb

Automate DND with AppleScript. I grabbed code off the web, letting me enable and disable do not disturb mode. (Make sure to enable Script Editor in Accessibility so they’ll run: Settings > Security & Privacy > Accessibility > Allow the apps below to control your computer.)

I didn’t make these pretty or write the osascript stuff into my code. They’re just two scripts in ~/bin right now. At some point, time allowing, I’ll unify these into a single script or app but as they work for now, shrug:

Desktop Icons

I keep a lot of work on my desktop that I’d prefer not to share. It’s easy to hide and show my desktop icons with a simple defaults command:

defaults write com.apple.finder CreateDesktop false; killall Finder"

Substitute true for false to re-enable.

Keyboard Maestro Macros

I not only quit email (killall Mail) but I also disable my Keyboard Maestro shortcut so I don’t accidentally re-launch it from muscle memory.

osascript -e 'tell application "Keyboard Maestro" to setMacroEnable "8E84EF4C-13F8-41AB-85EC-44AF70A52909" without enable'

Grab the unique identifiers for troublesome shortcuts from the files stored in ~/Library/Application\ Support/Keyboard\ Maestro. Use with and without enable to automate the macros off and then back on.

Safari History

No one wants their personal browsing history to pop up during demos in Safari as part of auto-completion suggestions. (Imagine, if you will, typing “s”, and having all your sloth sites listed as possible completions. Or, heaven forfend, things worth than sloth sites.)

If you use iCloud bookmarks, you can disable iCloud, remove your history and bookmarks files from ~/Library/Safari, and relaunch Safari (open /Applications/Safari.app). To re-enable, restart iCloud and wait for the data to sync back to your Mac.

I automated file deletion (actually, moving the files to another location) once iCloud is off and after quitting Safari (killall Safari). However, I didn’t automate enabling/disabling iCloud Safari bookmark sync from the command line. Instead, I used open 'x-apple.systempreferences:com.apple.preference.icloud' to get me to the right place for a single check mark toggle.

I hide my favorites bar:

defaults write com.apple.Safari ShowFavoritesBar-v2 false

Use true instead of false to restore.

You may want to limit Autofill and Search options (Safari > Preferences > Autofill and Search). I didn’t as they don’t really impact my presentations. I’m okay with Safari providing Search Engine-supplied suggestions, especially when searching for tech topics, as for me these are features not bugs.

System Calls

Here’s my ancient code to perform system calls to execute all the setup and teardown without using system(). Although lightly updated, I haven’t spent a lot of time improving something that works. If you have better solutions, please let me know:

extension NSString {
  /// Trim output
  var trimmed: String {
    return self.trimmingCharacters(in: .whitespacesAndNewlines)
  }
}

/// Execute a command as a shell script
@discardableResult
func perform( _ command: String) -> [String] {
  let task = Process()
  (task.launchPath, task.arguments) = ("/bin/sh", ["-c", command])

  let pipe = Pipe()
  task.standardOutput = pipe
  task.launch()
  
  let data = pipe.fileHandleForReading.readDataToEndOfFile()
  task.waitUntilExit()

  if let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)?.trimmed {
    return output.components(separatedBy: "\n")
  }
  
  // Something went wrong
  print(task.terminationStatus); return []
}

Are you red-green color blind? How do you handle git color coding?

Git version control, whether at the GitHub site or at the command line is ostentatiously color-coded. How do red-green color blind users (a common issue for Engineers) deal with this? Any suggestions for discussing how to use the alerts and coding as in the following screenshots aimed at a potentially color blind audience? Thanks in advance!

Untracked and tracked files are color coded

Additions and deletions are color coded

Using TextEdit as your git editor

For a little while, I’ve been using emacs as my git editor because I strongly feel that vi is something that should happen to other people. It’s been fine but not everyone loves emacs or likes the debris it leaves in its wake. So I thought I’d try out TextEdit instead.

After a little testing of approaches, I first tried out this:

git config --global core.editor /Applications/TextEdit.app/Contents/MacOS/TextEdit

I picked this rather than some of the other approaches because it’s straightforward in launching the app. There’s one problem though, which is that git doesn’t pick up on the fact that a file has been edited and closed when the application remains active:

% git commit --amend 
hint: Waiting for your editor to close the file... 
(...waits forever...)

The commit completes and works when I quit TextEdit but I don’t like  to quit the app each time I update a commit, especially if I have other files open. The amended commit does complete on quit:

% git commit --amend [master 59a0934] Testing TextEdit!
Author: erica <erica@ericasadun.com>
Date: Thu Jul 12 08:02:26 2018 -0600
1 file changed, 3 insertions(+), 1 deletion(-)

So I changed my approach. I turned to open instead:

git config --global core.editor "open -W -n"

TextEdit is open‘s natural environmental editor for text. You don’t have to specify more than open for git to work with the right editor.

This approach uses two tweaks. When you launch an app with open, the -n flag instructs open to launch a new instance of the application even if one is already running. You may see two “TextEdit” icons open in your dock, for example.

Combine -n with -W and you have a slightly better solution.  The -W “wait” flag tells open to keep waiting until the launched application has finished running. This allows git to to wait “for your editor to close the file”, which you do by quitting TextEdit. Sure, I’d much prefer that TextEdit run using a “single file” mode, but this isn’t a bad solution.

Sadly, I couldn’t find any undocumented launch flags to make TextEdit run in single file mode and while it’s easy enough to write a Mac app that edits a single file at a time and quits on closing, it’s not a practical solution for everyone.

Is there a way to automatically propagate “done” back to git without ending a TextEdit process? I couldn’t find one. If you have a better idea, please let me know.

Simulators gone bad

Recently, some of my simulators launched and loaded just fine. Others simply went black. It didn’t seem to matter which firmware I was simulating. Some devices were just happier than others.

Naturally, I turned to the system console, which provides device-by-device logs, but I couldn’t really find anything.

I tried restarting and rebooting the sims. I tried resetting the core simulator service. I tried a lot of things. (No actual chickens were harmed.) Finally, I stumbled across a Apple Dev Forum thread about issues with the beta system. The advice offered in-thread was this:

  • Quit the simulator. (I also killed com.apple.CoreSimulator.CoreSimulatorService because reasons.)
  • Run xcrun simctl erase all
  • Set a framebuffer renderer hint: defaults write com.apple.CoreSimulator.IndigoFramebufferServices FramebufferRendererHint 3

It worked.

Yay! \o/

From the thread, apparently hints 1 and 2 do not work but 3 does. An Apple Engineer noted that, “hint 0 is auto (currently prefering Metal and then falling back to OpenGL if unavailable). Hint 1 prefers Metal and hint 3 prefers OpenGL. Hint 2 used to mean OpenCL, but we dropped OpenCL support.”

Upshot is that I lost many hours but I seem to have a working solution for dealing with a common problem. I hope this write-up helps you if you encounter the same issues.

Update: Guru Russ Bishop recommends against using this long term. He writes

If you (as I) have deadlines, and cannot wait for this problem to be fixed, this fix will get you back to work. Russ reminds me, though, “Use it temporarily only if you have this problem, and don’t forget to delete the pref in the future”