"initialize" class method for classes in Swift?
IosSwiftIos Problem Overview
I'm looking for behavior similar to Objective-C's +(void)initialize
class method, in that the method is called once when the class is initialized, and never again thereafter.
A simple class init () {}
in a class
closure would be really sleek! And obviously when we get to use "class var
s" instead of "static var
s in a struct closure", this will all match really well!
Ios Solutions
Solution 1 - Ios
If you have an Objective-C class, it's easiest to just override +initialize
. However, make sure subclasses of your class also override +initialize
or else your class's +initialize
may get called more than once! If you want, you can use dispatch_once()
(mentioned below) to safeguard against multiple calls.
class MyView : UIView {
override class func initialize () {
// Do stuff
}
}
If you have a Swift class, the best you can get is dispatch_once()
inside the init()
statement.
private var once = dispatch_once_t()
class MyObject {
init () {
dispatch_once(&once) {
// Do stuff
}
}
}
This solution differs from +initialize
(which is called the first time an Objective-C class is messaged) and thus isn't a true answer to the question. But it works good enough, IMO.
Solution 2 - Ios
There is no type initializer in Swift.
>“Unlike stored instance properties, you must always give stored type properties a default value. This is because the type itself does not have an initializer that can assign a value to a stored type property at initialization time.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
You could use a type property which default value is a closure. So the code in the closure would be executed when the type property (or class variable) is set.
class FirstClass {
class var someProperty = {
// you can init the class member with anything you like or perform any code
return SomeType
}()
}
But class stored properties not yet supported
(tested in Xcode 8).
One answer is to use static
, it is the same as class final
.
Good link for that is >Setting a Default Property Value with a Closure or Function
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
Code example:
class FirstClass {
static let someProperty = {
() -> [Bool] in
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
print("setting default property value with a closure")
return temporaryBoard
}()
}
print("start")
FirstClass.someProperty
Prints >start > >setting default property value with a closure
So it is lazy evaluated.
Solution 3 - Ios
For @objc
classes, class func initialize()
definitely works, since +initialize
is implemented by the Objective-C runtime. But for "native" Swift classes, you'll have to see the other answers.
Solution 4 - Ios
You can use stored type properties instead of initialize
method.
class SomeClass: {
private static let initializer: Void = {
//some initialization
}()
}
But since stored types properties are actually lazily initialized on their first access, you will need refer them somewhere. You can do this with ordinary stored property:
class SomeClass: {
private static let initializer: Void = {
//some initialization
}()
private let initializer: Void = SomeClass.initializer
}
Solution 5 - Ios
@aleclarson nailed it, but as of recent Swift 4 you cannot directly override initialize
. You still can achieve it with Objective-C and categories for classes inheriting from NSObject
with a class / static swiftyInitialize
method, which gets invoked from Objective-C in MyClass.m
, which you include in compile sources alongside MyClass.swift
:
# MyView.swift
import Foundation
public class MyView: UIView
{
@objc public static func swiftyInitialize() {
Swift.print("Rock 'n' roll!")
}
}
# MyView.m
#import "MyProject-Swift.h"
@implementation MyView (private)
+ (void)initialize { [self swiftyInitialize]; }
@end
If your class cannot inherit from NSObject
and using +load
instead of +initialize
is a suitable fit, you can do something like this:
# MyClass.swift
import Foundation
public class MyClass
{
public static func load() {
Swift.print("Rock 'n' roll!")
}
}
public class MyClassObjC: NSObject
{
@objc public static func swiftyLoad() {
MyClass.load()
}
}
# MyClass.m
#import "MyProject-Swift.h"
@implementation MyClassObjC (private)
+ (void)load { [self swiftyLoad]; }
@end
There are couple of gotchas, especially when using this approach in static libraries, check out the complete post on Medium for details! ✌️
Solution 6 - Ios
I can't find any valid use case to have something like +[initialize]
in Swift. Maybe this explains way it does not exist
+[initialize]
in ObjC?
Why do we need To initialize some global variable
static NSArray *array;
+ (void)initialize {
array = @[1,2,3];
}
which in Swift
struct Foo {
static let array = [1,2,3]
}
To do some hack
+ (void)initialize {
swizzle_methodImplementation()
}
which is not supported by Swift (I can't figure out how to do it for pure Swift class/struct/enum)