WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

Key points

There are two things I’d like to discuss briefly, both extending what you already learned to help push your skills even further.

First, the #targetEnvironment(simulator) compiler directive. Swift has several of these, and I want to demonstrate two just briefly: #line and #if swift. #line is easy enough: when your code gets built it automatically gets replaced with the current line number. You can also use #filename and #function, and the combination of these are very useful in debugging strings.

The #if swift directive allows you to conditionally compile code depending on the Swift compiler version being used. So, you could write Swift 4.2 code and Swift 5.0 code in the same file, and have Xcode choose the right version automatically.

Now why, do you think, might you want such functionality? Well, there are two situations that you’re likely to encounter:

  1. You create a library that you distribute as Swift source code. Supporting more than one version of Swift helps reduce complexity for your users without breaking their code.
  2. You want to experiment with a future version of Swift without breaking your existing code. Having both in the same file means you can toggle between them easily enough.

Here’s some example code to get you started:

#if swift(>=5.0)
print("Running Swift 5.0 or later")
#else
print("Running Swift 4.2")
#endif

The second thing I’d like to touch on briefly is image rendering. Hacking with Swift is written specifically for the latest and greatest APIs from Apple, because if you’re learning from scratch it’s usually not worth bothering learning older technologies.

However, the case of image rendering is unique because the old technology – i.e., everything before iOS 10.0 – takes only a minute to learn. So, I want to show you just quickly how to render images before iOS 10, because it’s likely you’ll come across it in the wider world.

Here’s the iOS 10 and later code we would use to render a circle:

let renderer = UIGraphicsImageRenderer(size: CGSize(width: 512, height: 512))
let img = renderer.image { ctx in
    ctx.cgContext.setFillColor(UIColor.red.cgColor)
    ctx.cgContext.setStrokeColor(UIColor.green.cgColor)
    ctx.cgContext.setLineWidth(10)

    let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512)
    ctx.cgContext.addEllipse(in: rectangle)
    ctx.cgContext.drawPath(using: .fillStroke)
}

To convert that to pre-iOS 10 rendering, you need to learn four new functions:

  1. UIGraphicsBeginImageContextWithOptions(). This starts a new Core Graphics rendering pass. Pass it your size, then a rendering scale, and whether the image should be opaque. If you want to use the current device’s scale, use 0 for the scale parameter.
  2. Just starting a rendering pass doesn’t give you a context. To do that, you need to use UIGraphicsGetCurrentContext(), which returns a CGContext?. It’s optional because of course Swift doesn’t know we just started a rendering pass.
  3. Call UIGraphicsGetImageFromCurrentImageContext() when you want to extract a UIImage from your rendering. Again, this returns an optional (in this case UIImage?) because Swift doesn’t know a rendering pass is active.
  4. Call UIGraphicsEndImageContext() when you’ve finished, to free up the memory from your rendering.

As you can see, the older calls are a little more flaky: having extra optionality is never welcome, and you need to remember to both start and end your rendering blocks. Still, if you want to give it a try, here’s the same circle rendering code rewritten for iOS 9.3 and earlier:

UIGraphicsBeginImageContextWithOptions(CGSize(width: 512, height: 512), false, 0)

if let ctx = UIGraphicsGetCurrentContext() {
    ctx.setFillColor(UIColor.red.cgColor)
    ctx.setStrokeColor(UIColor.green.cgColor)
    ctx.setLineWidth(10)

    let rectangle = CGRect(x: 5, y: 5, width: 502, height: 502)
    ctx.addEllipse(in: rectangle)
    ctx.drawPath(using: .fillStroke)
}

if let img = UIGraphicsGetImageFromCurrentImageContext() {
    // "img" is now a valid UIImage – use it here!
}

UIGraphicsEndImageContext()
Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Average rating: 5.0/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.