"initialize" class method for classes in Swift?

IosSwift

Ios 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 vars" instead of "static vars 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

Why do we need +[initialize] in ObjC?

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)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionaleclarsonView Question on Stackoverflow
Solution 1 - IosaleclarsonView Answer on Stackoverflow
Solution 2 - IosBinarianView Answer on Stackoverflow
Solution 3 - IosnewacctView Answer on Stackoverflow
Solution 4 - IosCy-4AHView Answer on Stackoverflow
Solution 5 - IosIan BytchekView Answer on Stackoverflow
Solution 6 - IosBryan ChenView Answer on Stackoverflow