The Joys of iOS 10 UIKit Drawing

I just spent a few days enjoying all the iOS 10 UIGraphics renderer utilities for images and PDFs and wide colors and so forth. It’s lovely. I thought I’d share a post comparing the old world and the new.

Old

Remember this?

// Create a color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
if (colorSpace == NULL) {
    NSLog(@"Error allocating color space");
    return nil; 
}

// Create the bitmap context.
CGContextRef context = CGBitmapContextCreate(
    NULL, width, height,
    BITS_PER_COMPONENT, // bits = 8 per component 
    width * ARGB_COUNT, // 4 bytes for ARGB 
    colorSpace,
    (CGBitmapInfo) kCGImageAlphaPremultipliedFirst); 

if (context == NULL) {
    NSLog(@"Error: Context not created!"); 
    CGColorSpaceRelease(colorSpace ); 
    return nil;
}

// Push the context.
UIGraphicsPushContext(context);

// Perform drawing here

UIGraphicsPopContext();

// Convert to image
CGImageRef imageRef = CGBitmapContextCreateImage(context); 
UIImage *image = [UIImage imageWithCGImage:imageRef];

// Clean up 
CGColorSpaceRelease(colorSpace ); 
CGContextRelease(context); 
CFRelease(imageRef);

New

let image = renderer.image { context in
    let bounds = context.format.bounds
    for amount in stride(from: 1.0 as CGFloat, to: 0.0, by: -0.1) {
        let color = UIColor(hue: amount, saturation: 1.0, 
            brightness: 1.0, alpha: 1.0)
        let rects = bounds.divided(
            atDistance: amount * bounds.size.width, from: .maxXEdge)
        color.set(); UIRectFill(rects.0)
    }
}

and

public func imageExample(size: CGSize) -> UIImage? {
    let bounds = CGRect(origin: .zero, size: size)
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let (width, height) = (Int(size.width), Int(size.height))
    
    // Build Core Graphics ARGB context
    guard let context = CGContext(data: nil, width: width, 
        height: height, bitsPerComponent: 8, 
        bytesPerRow: width * 4, space: colorSpace, 
        bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue) 
        else { return nil }
    
    // Prepare CG Context for UIKit
    UIGraphicsPushContext(context); defer { UIGraphicsPopContext() }
    
    // Draw to context using UIKit calls
    UIColor.blue.set(); UIRectFill(bounds)
    let oval = UIBezierPath(ovalIn: bounds)
    UIColor.red.set(); oval.fill()
    
    // Fetch the image from the context
    guard let imageRef = context.makeImage() else { return nil }
    return UIImage(cgImage: imageRef)
}

and

extension UIImage {
    public func grayscaled() -> UIImage? {
        guard let cgImage = cgImage else { return nil }
        let colorSpace = CGColorSpaceCreateDeviceGray()
        let (width, height) = (Int(size.width), Int(size.height))
        
        // Build context: one byte per pixel, no alpha
        guard let context = CGContext(data: nil, width: width, 
            height: height, bitsPerComponent: 8, 
            bytesPerRow: width, space: colorSpace, 
            bitmapInfo: CGImageAlphaInfo.none.rawValue) 
            else { return nil }
        
        // Draw to context
        let destination = CGRect(origin: .zero, size: size)
        context.draw(cgImage, in: destination)
        
        // Return the grayscale image
        guard let imageRef = context.makeImage() 
            else { return nil }
        return UIImage(cgImage: imageRef)
    }
}

Okay, I admit the bitmapInfo is still a little ugly, but isn’t the rest of it grand?

  • No more hacky UIGraphicsBeginImageContext()/UIGraphicsEndImageContext() stuff, let alone getting image from context. Why wasn’t it like this years ago?
  • I do love my Swift constructors. Creating the CGRect from the size is much cleaner now.
  • If you want Core Graphics, Swift gives you Core Graphics: there’s still good reasons to create custom contexts (for example, device gray color spaces) or otherwise work at a low level without having to fire up Accelerate, Core Image, or other power frameworks.
  • You can pair the graphic stack context push with its pop if you do need custom context work. I love defer pairs that prepare for cleanup at the same time you do set-up. (We need to extend Swift reference types to allow paired deinit tasks too!)
  • Swift handles all the memory management. All of it!
  • Swift optionals and errors let you fail so much more gracefully.
  • As you’d probably expect, PDF drawing is just as easy as working with images.
  • The “hoisted” CG utilities (like CGRect’s divided(atDistance:, from:) and the context’s draw and makeImage) are lovely too.

I’m seeing the light at the end of the tunnel for Swift Style wrapping up. Anyone interested in me revisiting “iOS Drawing” for Swift? Or are there other topics you’d rather me follow on?

How do I: Cast an optional string to NSString

McFly says: I keep getting the following error and can’t seem to find solution on stackoverflow: “Cannot convert value of type ‘String?’ to type ‘NSString’ in coercion”

First the answer: you have to unwrap an optional before casting it to a different type.  “String?” means a value is stored within an Optional enumeration, which can contain a value or be nil. That’s why you’re getting the error.

You don’t want to force unwrap so you’ll want to conditionally bind that string and cast it to NSString. But before going there, let me step back a moment and discuss why you have an optional string to begin with. You may have used an optional initializer, or called a function that returns an optional string:

let test = "abcdef"
let optionalString = String(test)

Or you more typically may have looked up a key in a dictionary, and your variable is actually typed as Any?, not String?.

let optionalValue = dict["key"]

The thing that kept nagging at me is that it’s pretty rare to pick up optional strings and then want to convert them to NSString. At first I thought McFly was pulling values from a JSON dictionary or the like (which is [AnyHashable: Any]) but the error message for calling a function that expects an NSString value with an Any optional is “error: cannot convert value of type ‘Any?’ to expected argument type ‘NSString'” so no, he really was working with String? and NSString.

I asked McFly why he had a String?, and it turns out he was using this API:

My Post class has the following declared:

private var _imageURL: String?

In any case (leaving apart the sin of naming a string as “URL”, argh), given this scenario, you have two goals:

  1. Safely unwrap the value to get at the string
  2. Convert that string to NSString so you can pass it to the function

Don’t do this:

functionCall(string! as NSString)

Forced unwrapping kills Tinkerbell quicker than kids who refuse to applaud. Instead, conditionally unwrap and *then* cast:

if let string = optionalString {
    myFunction(string as NSString)
}

(And yes, you usually shadow with the same name as the optional string so you don’t accidentally collide with a different existing symbol. I wanted to make clear the roles of the newly bound unwrapped variable and the existing optional string)

To do everything at once, so you have a bound local variable that stores an NSString instance, you can cast your optional string to Optional<NSString> and then conditionally bind:

if let string = (optionalString as Optional<NSString>) {
    myFunction(string)
}

This solution is easy for new devs because you can explain: “What you’ve got is an Optional<String> and what you want is an Optional<NSString>, so cast with as, which can be checked at compile time and is guaranteed to succeed.

Experienced developers might prefer optional mapping:

if let string = optionalString.map({ $0 as NSString }) {
    myFunction(string)
}

This is not an especially obvious solution for new learners. Assuming you’ve already gotten past the basic concept of optionals that can store either a value or nil via the Optional enumeration, picking up the notion of  “mapping” optionals let alone collections isn’t intuitively clear especially without an existing background in  functional languages.

The nice advantage to this approach though is that it easily expands to type-erased casting. Substitute flatMap for map so you don’t conditionally bind an Optional(Optional).

if let string = string.flatMap({ $0 as? NSString }) {
    myFunction(string)
}

In any case, I told McFly, I’d write this up. And, as it turned out, I cut out about half or more of this post to save for another day because there are some particularly interesting ways that Swift handles casting “Any?” lookups from dictionaries.

As always, I’m sure I probably messed up somewhere in this post, so I invite you as always to offer feedback and corrections. Thanks!

 

Converting optionals to thrown errors

Soroush Khanlou writes: “Lots of times i just wish there were no optional type, and it was all just `Result`”

He gives this example:

struct NilError: Error { }

func throwable<T>( _ block: @autoclosure() -> T?) throws -> T {
    guard let result: T = block() else {
        throw NilError()
    }
    return result
}

let image = try throwable(UIImage(data: imageData))

I decided to fancy it up a little:

// Inspired by Soroush Khanlou

public enum Throws {
    public struct NilError: Error, CustomStringConvertible {
        public var description: String { return _description }
        public init(file: String, line: Int) {
            _description = "Nil returned at "
                + (file as NSString).lastPathComponent + ":\(line)"
        }
        private let _description: String
    }
    
    public static func this<T>(
        file: String = #file, line: Int = #line,
        block: () -> T?) throws -> T
    {
        guard let result = block() 
            else { throw NilError(file: file, line: line) }
        return result
    }
}

do {
    let imageData = Data()
    let image = try Throws.this { NSImage(data: imageData) }
} catch { print(error) }

The obvious difference is that my errors look like “Nil return at playground13.swift:24” but I also added a few things according to some style choices I’m testing out:

  • No autoclosure. Reserve autoclosure for lazy evaluation. Apple writes, “The context and function name should make it clear that evaluation is being deferred.”
  • No global freestanding function. Embed globals into types as static members.
  • Nested error declaration. It’s a subordinate and specific to the type.
  • Custom description. The error tells you more about itself than just its name.
  • Allman after a multi-line complex initializer declaration.
  • Late private property declaration. I’m kicking the wheels on this, which I picked up from the Standard Library folk.

Thoughts?

Update: Loic has a really nice alternative that inspired me to tweak:

public struct NilError: Error, CustomStringConvertible {
    public var description: String { return _description }
    public init(file: String, line: Int) {
        _description = "Nil returned at "
            + (file as NSString).lastPathComponent + ":\(line)"
    }
    private let _description: String
}

extension Optional {
    public func unwrap(file: String = #file, line: Int = #line) throws -> Wrapped {
        guard let unwrapped = self else { throw NilError(file: file, line: line) }
        return unwrapped
    }
}

do {
    let imageData = Data()
    let image = try NSImage(data: imageData).unwrap()
} catch { print(error) }

Swift Style Update

coverscaled250

I finished the first draft today. It’s about 160 pages long, which is a bit longer than I expected. I’ll be revising and tweaking coverage next. If there are any specific style issues you want me to research and/or address please let me know now.

Also, if you’ve purchased any of my self-published titles, please make sure you have the latest versions. The current revision is always listed in the first section with the legal stuff and “Distributed worldwide by Erica Sadun”. The current versions are:

  • Swift Two to Three: 1.0.2
  • Swift Documentation Markup: 2.0
  • Playground Secrets and Power Tips: 3.5

If you do not have the right version, check for available downloads in iTunes or download the latest version from Leanpub.

Here’s the current Table of Contents for the book. If you noticed, I’m using a brand new toolchain for writing so I’ve got a much better ToC than I have had in the previous “Swift. Slowly.” titles:

 

Fixed floating point strides

In case you haven’t noticed, Xiaodi Wu fixed floating point strides in the latest version of Swift.

You can read about the background of the problem here but to tl;dr summarize it, the traditional C for-loop had a big problem with floating point progressions. Errors accumulated the longer the loop ran. That problem then passed into Swift strides.

This issue is now fixed and the error at the end of stride(from:0.0, through: 1.0, by: 0.05) is now no larger than it was at the start. Yay!

Responding to bad errors with bug reporting

Soroush K writes,  Here’s one annoying thing about Swift 3. The line

let location = geofenceNotifier.locationDataStore
    .cached?.first(where: { $0.id = locationID })

should be using `==` instead of `=` but the error you get is `Cannot call value of non-function type ‘Location?’`. It should give me an error saying “Hey, `id` is a `let` so you can’t assign to it” or “You’re not returning a Boolean.” and not “we’re trying to call `Location?` as a function.

While I can see where the team was going with this error I agree that it probably reflects the wrong most common error case. When you encounter bad Swift errors, file a bug report at bugs. swift.org and explain why you think it’s a bad error and what you think should have been a better approach.

Quick Style survey

I put up a poll asking whether it was good style to move attributes and access modifiers to their own line. Results are here.

My take on things after the poll and after talking directly with a number of developers is this: Placing attributes like @objc, @testable, @available, @discardableResult on their own lines before a member declaration has become a conventional Swift style.

@objc(objectAtIndex:)
internal func objectAt(_ index: Int) -> AnyObject {

@available(*, unavailable, renamed: "Iterator")
public typealias Generator = Iterator

@discardableResult
func _copyContents(initializing ptr:
UnsafeMutablePointer<Iterator.Element>)
-> UnsafeMutablePointer<Iterator.Element>

This approach limits declaration length. It allows a member to float below its attribute and supports flush-left access modifiers, so `internal`, `public`, etc appear in the leftmost column. Many developers mix-and-match styles for short Swift attributes like `@objc`. It is common to see `@objc(countByEnumeratingWithState:objects:count:)` on its own line while `@objc` moves down to the primary declaration, particularly for single-line declarations:

@objc internal var count: Int

@objc internal final class _SwiftDeferredNSArray
: _SwiftNativeNSArrayWithContiguousStorage

Others prefer to break attributes out universally, regardless of length. This approach is consistent, and it promotes consistent leftmost access modifiers:

@objc
internal var count: Int

@objc
internal final class _SwiftDeferredNSArray
: _SwiftNativeNSArrayWithContiguousStorage

Announcing tmdiff

For all I know this already exists and I just was unable to google it up. Assuming it doesn’t, tmdiff allows you to perform a command line diff on a text file against a time machine version.

Repo: https://github.com/erica/tmdiff

Usage:

Usage: tmdiff (--list)
       tmdiff [offset: 1] path

The list option just lists the dates for the backups in reverse chronological order. Supply a path to diff, e.g.

tmdiff Style600-Control\ Flow.md

It defaults to using the “but last” backup offset of 1. If you want to use the most recent backup, use 0 instead, or any number moving further back in time as the value increases:

tmdiff 0 Style600-Control\ Flow.md
tmdiff 3 Style600-Control\ Flow.md

I hope this is handy for someone out there on the opposite side of the Intertube, especially since version control is baked into stuff like TextEdit. Do let me know if you use it.

Update: See also github.com/erica/tmls and github.com/erica/tmcp. The former runs ls, complete with arguments. The latter performs a nondestructive copy with the Time Machine date appended.