A helper class for creating icons from a PNG image and colour in Swift I have made a helper struct in Swift that creates circular icons from a PNG image (a white icon on transparent background) and a background UIColor. These variables are user-definable in my app, and the created icons are used in notifications and Spotlight, for example.

The PNG images are named iconA.png, iconB.png etc. and are all a standard 100-points square. The default image is a fully transparent one called iconEmpty.png. I wanted to provide a CGSize argument so that I could use this method for other purposes as well, should the need arise.

It works as expected, but I am wondering how it might be improved or made more efficient, as I am not very experienced with drawing contexts.

import UIKit

struct IconMaker {

    func makeIcon(with color: UIColor, size: CGSize, icon: UIImage = UIImage(named: "iconEmpty.png")!) -&gt; UIImage {

        // Make solid color background
        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
        color.setFill()
        UIRectFill(rect)
        let backgroundImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        // Add other image (which has transparency)
        UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
        backgroundImage.draw(in: rect, blendMode: .normal, alpha: 1)

        icon.draw(in: rect, blendMode: .normal, alpha: 1)

        let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        // Circular mask
        let cornerRadius = newImage.size.height/2
        UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
        let bounds = CGRect(origin: CGPoint.zero, size: size)
        UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).addClip()
        newImage.draw(in: bounds)
        let finalImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return finalImage!
    }
}
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)


can be simplified to

let rect = CGRect(origin: .zero, size: size)


The explicit type annotation in

let backgroundImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!


is not needed. In

UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)


one can pass 0.0 as the last argument, that is also interpreted
as the scale factor of the device’s main screen.

let cornerRadius = newImage.size.height/2
UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).addClip()


can be simplified to

UIBezierPath(ovalIn: bounds).addClip()


(and that would work even for a non-quadratic frame).

Instead of creating and drawing into a  bitmap-based graphics context
three times, with two intermediate images, it suffices to use a single graphics context, and


Add a round clipping path,
fill with the given background color,
draw the given image,


all with the same context.

Instead of the fully transparent default icon "iconEmpty.png" you
could make that parameter optional, and draw the icon only if an icon
was provided by the caller.

Finally, the method does not access any properties of the IconMaker
type. You can make it static, so that it can be called without
creating an IconMaker instance:

let newIcon = IconMaker.makeIcon(...)


Putting it all together, the method could look like this:

static func makeIcon(with color: UIColor, size: CGSize, icon: UIImage? = nil) -&gt; UIImage {

    // Create graphics context:
    let rect = CGRect(origin: .zero, size: size)
    UIGraphicsBeginImageContextWithOptions(size, false, 0.0)

    // Circular mask
    UIBezierPath(ovalIn: rect).addClip()

    // Make solid color background:
    color.setFill()
    UIRectFill(rect)

    if let icon = icon {
        // Add other image (which has transparency)
        icon.draw(in: rect, blendMode: .normal, alpha: 1.0)
    }

    // Create new image and release context:
    let newImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    return newImage
}


Another option is to use the defer statement for releasing 
the graphics context:

static func makeIcon(with color: UIColor, size: CGSize, icon: UIImage? = nil) -&gt; UIImage {

    // Create graphics context:
    let rect = CGRect(origin: .zero, size: size)
    UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
    defer { UIGraphicsEndImageContext() }

    // ...

    return UIGraphicsGetImageFromCurrentImageContext()!
}


The block passed to the defer statement is executed just before
the function returns. Here it allows to get rid of the newImage
variable. Generally it is a good approach to ensure that all
acquired resources are released properly, no matter how program
control leaves the function.