Bartłomiej Semańczyk
Bartłomiej Semańczyk

Reputation: 61774

Assign list of variadic parameters to variable

This is the case:

public enum NodeFeature: UInt16 {
    case relay      = 0x01
    case proxy      = 0x02
    case friend     = 0x04
    case lpn        = 0x08
}

public struct NodeFeatures {
    public let rawValue: UInt16

    public init(rawValue: UInt16) {
        self.rawValue = rawValue
    }

    public init(features: NodeFeature...) {
        var rawValue = UInt16(0)
        for feature in features {
            rawValue |= feature.rawValue
        }
        self.rawValue = rawValue
    }

    public func hasFeature(_ feature: NodeFeature) -> Bool {
        return rawValue & feature.rawValue > 0
    }
}

And this is a response from server:

"feature": {
  "relay": true,
  "proxy": false,
  "friend": false,
  "lowPower": false
}

Now I need to create an instance of NodeFeatures with only true values:

var features = [NodeFeature]() // how to declare and prepare the list of args?

if true {
    features.append(.first)
} else if true {
    features.append(.third)
}

let wrapper = NodeFeatures(features: features) //... to pass it as a variable to the initializer.

But the error is following:

Cannot convert value of type '[NodeFeature]' to expected argument type 'NodeFeatures'

Upvotes: 1

Views: 345

Answers (1)

Martin R
Martin R

Reputation: 539685

You cannot pass an array to a function taking a variadic argument, or dynamically "build" a variadic argument list, compare e.g. Passing an array to a function with variable number of args in Swift.

But fortunately, the type has another initializer

public init(rawValue: UInt16)

which you can use in different ways.

Option 1: Use an integer bit mask to assemble the features instead of an array:

var rawFeatures = UInt16(0)
if condition {
    rawFeatures |= NodeFeature.relay.rawValue
} else if condition {
    rawFeatures |= NodeFeature.proxy.rawValue
}
let wrapper = NodeFeatures(rawValue: rawFeatures)

Option 2: Keep your array, but compute the combined raw value to create the NodeFeatures value:

var features = [NodeFeature]()
if condition {
    features.append(.relay)
} else if condition {
    features.append(.proxy)
}

let rawFeatures = features.reduce(0, { $0 | $1.rawValue })
let wrapper = NodeFeatures(rawValue: rawFeatures)

Option 3: Define another initializer taking an array argument in an extension:

extension NodeFeatures {

    public init(features: [NodeFeature]) {
        let rawValue = features.reduce(0, { $0 | $1.rawValue })
        self.init(rawValue: rawValue)
    }
}

Now you can pass your array directly:

var features = [NodeFeature]()
if condition {
    features.append(.relay)
} else if condition {
    features.append(.proxy)
}
let wrapper = NodeFeatures(features: features)

Upvotes: 2

Related Questions