Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialising member to class function causes 'self' used in method call error

I have a class attribute that points to one of the class functions. However when I try to initialize this variable with one of the functions, I get the following error:

'self' used in method call before all stored properties are initialized.

I'm able to initialize any other variable to those functions, but the error makes it sound like I'm calling the function even though I'm not.

import UIKit
import AudioToolbox

class BeatMaker {

    // iPhone 7 and up use beat function, iPhone 6s use beatFallback
    let hapticFunction: () -> ()
    let impactGenerator = UIImpactFeedbackGenerator.init(style: .heavy)

    init(supportsImpactGenerator: Bool) {
        // error 1: 'self' used in method call 'beat' before all stored properties are initialized
        // error 2: 'self' used in method call 'beatFallback' before all stored properties are initialized
        self.hapticFunction = (supportsImpactGenerator) ? beat : beatFallback
    }

    private func beat() {
        impactGenerator.impactOccurred()
    }

    private func beatFallback() {
        AudioServicesPlaySystemSound(1520)
    }

    func makeABeat() {
        hapticFunction()
    }
}

In this specific case I want to make use of the Taptic Engine and simulate a click through the UIImpactFeedbackGenerator. The iPhone 6s doesn't support this engine, so I want to call a fallback function that produces a similar effect.

I also tried initializing the variable on the spot:

// this works
var hapticFunction: (BeatMaker) -> () -> () = beat

init(supportsImpactGenerator: Bool) {
    if !supportsImpactGenerator {
        // error: Cannot assign value of type '() -> ()' to type '(BeatMaker) -> () -> ()'
        self.hapticFunction = beatFallback

        // produces same error
        self.hapticFunction = beatFallback.self
    }
}

I know that I could make everything static or put everything out of the class, but this feels like it should work yet it doesn't. Am I missing something?

EDIT

Setting the type of type of hapticFunction to an optional seems to work, but this doesn't make any sense to me. What's the difference?

// this works
var hapticFunction: (() -> ())?

init(supportsImpactGenerator: Bool) {
    self.hapticFunction = (supportsImpactGenerator) ? beat : beatFallback
}
like image 843
Felix Jassler Avatar asked Apr 03 '19 06:04

Felix Jassler


1 Answers

It might be better to not use a Bool, but rather a nested Enum, which is also more extendible if you wanna add some other modes of haptic feedback later on.

I have a generalized solution for a generalized problem of your question. So either you do:


public class FunctionOwner {

    private let mode: Mode

    public init(`do` mode: Mode = .default) {
        self.mode = mode
    }
}


public extension FunctionOwner {

    enum Mode {
        case foo, bar
    }

    func fooOrBar() {
        switch mode {
        case .foo: foo()
        case .bar: bar()
        }
    }
}

private extension FunctionOwner {
    func foo() {
        print("doing foo")
    }

    func bar() {
        print("doing bar")
    }
}

public extension FunctionOwner.Mode {
    static var `default`: FunctionOwner.Mode {
        return .foo
    }
}

// USAGE
FunctionOwner(do: .bar).fooOrBar() // prints "doing foo"
FunctionOwner(do: .foo).fooOrBar() // prints "doing bar"

Or if you for some reason do want to keep the stored Mode, you can do this (might be relevant for your actual question on how you do a workaround of referencing self in the init.):

public class FunctionOwner {

    private let _function: (FunctionOwner) -> Void

    public init(`do` mode: Mode = .default) {
        _function = { functionOwner in
            switch mode {
            case .foo: functionOwner.foo()
            case .bar: functionOwner.bar()
            }
        }
    }
}

public extension FunctionOwner {

    enum Mode {
        case foo, bar
    }

    func fooOrBar() {
        _function(self)
    }
}

// The rest of the code is the same as the example above
like image 199
Sajjon Avatar answered Oct 29 '22 02:10

Sajjon