Have a Variable with Multiple Types in Swift

Have a variable with multiple types in Swift

Here is how you can achieve it. Works exactly how you'd expect.

protocol StringOrInt { }

extension Int: StringOrInt { }
extension String: StringOrInt { }

var a: StringOrInt = "10"
a = 10 //> 10
a = "q" //> "q"
a = 0.8 //> Error

NB! I would not suggest you to use it in production code. It might be confusing for your teammates.

UPD: as @Martin R mentioned: Note that this restricts the possible types only “by convention.” Any module (or source file) can add a extension MyType: StringOrInt { } conformance.

How to define a variable with multiple specific custom types ? (in Swift)

Seems like you need to use protocols.

Something like:

protocol CustomType {
// your definitions ...
}

extension String: CustomType {}
extension Int: CustomType {}

let customType1: CustomType = "string"
print(customType1)
let customType2: CustomType = 0
print(customType2)

// Error: Value of type 'Double' does not conform to specified type 'CustomType'
// let customType3: CustomType = 0.0

In this case, values of CustomType type will only accept String and Int types (because of the protocol conformance).

Swift: Modeling a variable with multiple types

Since you want to use Swift I'd suggest using protocols. That would give what you need. Simple example:

protocol Sound {
let title: String { get }
let length: TimeInterval { get }
}

The protocol would define the interface. Then you can just create various classes which would conform to the protocol - the underlaying implementation can be completely different:

class Song: Sound {
var title: String
var length: TimeInterval

init(title: String, length: TimeInterval) {
self.title = title
self.length = length
}
}

class Artist: Sound {
var title: String {
return "Artist"
}
var length: TimeInterval {
return 11.1
}
}

Then you can simply access title on any object conforming to the Sound protocol:

let sound1: Sound = Song(title: "Song", length: 1)
let sound2: Sound = Artist()

print("\(sound1.title)") // prints: Song
print("\(sound2.title)") // prints: Artist

Allow Swift function parameter to be of multiple types

You mentioned enums, which sound like an excellent fit for this use case.
You can explicitly only require the types you expect, and nothing else.
By adding a property to the enum you can then expose a UIControl property to interact as needed, without the need for down-casting (which is generally considered to be an anti-pattern).

enum Component {
case `switch`(UISwitch)
case stepper(UIStepper)

var control: UIControl {
switch self {
case .switch(let comp):
return comp
case .stepper(let comp):
return comp
}
}
}

Then ask for a Component as a parameter to the function.

func controlValueChanged(_ myComponent: Component) {
// Now you can use them as a generic UIControl
let control = myComponent.control

// ...or different behaviours for each element
switch myComponent {
case .switch(let swit):
// use the `swit`
case .stepper(let step):
// use the `step`
}
}

Having said that, if the implementations for these types are totally different anyway, it may be more clear to define two separate functions.

Multiple types in function

A possible solution is to add a protocol

protocol Reusable {
var delegate : ReusableDelegate { get set } // change the type to the real delegate type
}

and constrain the generic type to the protocol. You should constrain the generic type to UITableViewCell anyway

func configureQuestionCell<T>(cellType: T) where T : UITableViewCell & Reusable {
let cell = self.blockContent.dequeueReusableCell(withIdentifier: ReusableCellID.ratingCell.rawValue) as! T
cell.delegate = self
}

For multiple different delegate types use an associated type

protocol Reusable {
associatedtype DelegateType
var delegate : DelegateType { get set }
}

And in the cell adopt the protocol and add

typealias DelegateType = < The actual delegate type of the cell >

multiple types in Codable

I definitely agree with @vadian. What you have is an optional rating. IMO this is a perfect scenario for using a propertyWrapper. This would allow you to use this Rated type with any model without having to manually implement a custom encoder/decoder to each model:

@propertyWrapper
struct RatedDouble: Codable {

var wrappedValue: Double?
init(wrappedValue: Double?) {
self.wrappedValue = wrappedValue
}

private struct Rated: Decodable {
let value: Double
}

public init(from decoder: Decoder) throws {
do {
wrappedValue = try decoder.singleValueContainer().decode(Rated.self).value
} catch DecodingError.typeMismatch {
let bool = try decoder.singleValueContainer().decode(Bool.self)
guard !bool else {
throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "Corrupted data"))
}
wrappedValue = nil
}
}

public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
guard let double = wrappedValue else {
try container.encode(false)
return
}
try container.encode(["value": double])
}
}

Usage:

struct AccountState: Codable {
let id: Int?
let favorite: Bool?
let watchlist: Bool?
@RatedDouble var rated: Double?
}


let json1 = #"{"id":550,"favorite":false,"rated":{"value":9.0},"watchlist":false}"#
let json2 = #"{"id":550,"favorite":false,"rated":false,"watchlist":false}"#
do {
let accountState1 = try JSONDecoder().decode(AccountState.self, from: Data(json1.utf8))
print(accountState1.rated ?? "nil") // "9.0\n"
let accountState2 = try JSONDecoder().decode(AccountState.self, from: Data(json2.utf8))
print(accountState2.rated ?? "nil") // "nil\n"
let encoded1 = try JSONEncoder().encode(accountState1)
print(String(data: encoded1, encoding: .utf8) ?? "nil")
let encoded2 = try JSONEncoder().encode(accountState2)
print(String(data: encoded2, encoding: .utf8) ?? "nil")
} catch {
print(error)
}

This would print:

9.0

nil

{"watchlist":false,"id":550,"favorite":false,"rated":{"value":9}}

{"watchlist":false,"id":550,"favorite":false,"rated":false}

Swift - check if instance is one of multiple types

You can use type(of:) and .contains:

let types = [A.self, C.self]

let test = A()

if types.contains(where: { $0 == type(of:test) }) {
print("Here")
}


Related Topics



Leave a reply



Submit