Swift static factory design pattern
In this article I'll teach you about the static factory design pattern and show some use cases using the Swift programming language.
Real world static factory pattern examples
Named constructors
The first good thing about the static factory pattern is that every static factory method can have a name. Apple uses this pattern in their UIColor
class implementation to create named colors like .red
, .yellow
, etc. Please note that the implementation in Swift is not really a method, but a static property, which returns the actual instance.
public extension TimeInterval {
public static var second: TimeInterval { return 1 }
public static var minute: TimeInterval { return 60 }
public static var hour: TimeInterval { return 3_600 }
public static var day: TimeInterval { return 86_400 }
public static var week: TimeInterval { return 604_800 }
}
If it’s so hard to memorize how many seconds is a day or a week why don’t we create a named initializer for it. See? TimeInterval.week
is much better than 604_800
. 😅
Cached objects
The next good thing about the static factory pattern is that it can support caching for the sake of better memory usage. This way you can limit the number of objects created if you are initializing it through the static constructor (aka. static factory method). 🏭
class Service {
var name: String
// MARK: - cache
private static var cache: [String:Service] = [:]
private static func cached(name: String) -> Service {
if Service.cache[name] == nil {
Service.cache[name] = Service(named: name)
}
return Service.cache[name]!
}
// MARK: - static factory
static var local: Service {
return Service.cached(name: "local")
}
static var remote: Service {
return Service.cached(name: "remote")
}
// MARK: - init
init(named name: String) {
self.name = name
}
}
Local initialization scope
Another good thing about static factory methods that you can limit the initialization of a class to a private scope level. In other words object creation will only be available through the static factory method. You just have to make the init
method private.
public final class Service {
var name: String
private init(name: String) {
self.name = name
}
public static var local: Service {
return Service(name: "local")
}
public static var remote: Service {
return Service(name: "remote")
}
}
Note that you can restrict subclassing with the final & static keyword. If you want to allow subclassing you should remove final
and use the class
keyword instead of the static
for the properties, this way subclasses can override factory methods. 🤔
Statically return anything
Static factory can also return subtypes of a given object, but why don’t we take this even one step further? You can also return any kind of type from a static method, I know this seems like a cheat, because I’m not creating an instance of UIColor
here, but I believe that it’s worth to mention this method here, because it’s closely related to static factories. This technique can be really useful sometimes. 😛
extension UIColor {
private static func image(with color: UIColor) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()!
context.setFillColor(color.cgColor)
context.fill(rect)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img!
}
static var redImage: UIImage {
return UIColor.image(with: .red)
}
}
Related posts
Event-driven generic hooks for Swift
In this article I am going to show you how to implement a basic event processing system for your modular Swift application.
Iterator design pattern in Swift
Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.
Lazy initialization in Swift
Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.
Lenses and prisms in Swift
Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.