Swift: Declaration in Generic Class

Swift generics with variables

You need to declare the readings params as [T].

class GeneralUpdate<T: Reading> {
var readingType: ReadingType
var dataSource: DataSource
var readings: [T]

init(readingType: ReadingType, dataSource: DataSource, readings: [T]) {
self.readingType = readingType
self.dataSource = dataSource
self.readings = readings
}
}

And please get rid of all that ugly ! Implicitly Unwrapped Optionals.

Swift: Assign an instance of a generic type to a class variable?

Break down the create method signature and the Batch1 type

public func create<Request: JSONRPCKit.Request>(_ request: Request) -> Batch1<Request>

public struct Batch1<Request: JSONRPCKit.Request>: Batch {
public typealias Responses = Request.Response
public typealias Results = Result<Request.Response, JSONRPCError>
}

create is a generic function that takes a single parameter of any type. The constraint <Request: JSONRPCKit.Request> specifies that the parameter's type must conform to the protocol JSONRPCKit.Request.

Batch1 is a generic struct that needs to define two internal types, both of which are associated with some arbitrary type. Again, <Request: JSONRPCKit.Request> specifies that that arbitrary type must conform to the protocol JSONRPCKit.Request.

The return type Batch1<Request> ties these two generics together, saying that the type used for the returned Batch1 struct will match the type of the request parameter.

When you call the create method, you must fill in the generic type with a concrete type, as in the example

let batch = batchFactory.create(Subtract(minuend: 42, subtrahend: 23))

Now the compiler can go through all of the definitions and create concrete implementations for that type:

public func create(_ request: Subtract) -> Batch1<Subtract>

public struct Batch1<Subtract>: Batch {
public typealias Responses = Int
public typealias Results = Result<Int, JSONRPCError>
}

This uses the fact that Subtract defines typealias Response = Int. Note that nothing is generic anymore; these are all concrete types. You would have no issue with trying to store a property of type Batch1<Subtract>.


This is why you can't easily store the batch in a property: Swift has no idea what types to put in it!

One way around this is to instead store a closure, this can wrap the generic batch such that the class doesn't have to know about it

// closure property
var responseProcessor: ((Any) -> Void)?

func createBatch<R: JSONRPCKit.Request>(request: R, processor: @escaping (R.Response) -> Void) {
let batch = batchFactory.create(request)
self.responseProcessor = { responseObject in
let response = try! batch.responses(from: responseObject)
processor(response)
}
}

// somewhere else when you get the responseObject
responseProcessor?(responseObject)

This method takes a specific closure that matches the generic type and wraps it in a closure that is no longer dependent on the generic. This way every batch can share the same closure property.

How to use a generic class without the type argument in Swift?

Swift doesn’t yet support wildcard-style generics like Java does (i.e., Animal<?>). As such, a common pattern is to define a type-erased superclass, protocol (or wrapper) to enable such usage instead. For instance:

public class AnyAnimal {
/* non-generic methods */
}

and then use it as your superclass:

public class Animal<T: YummyObject>: AnyAnimal {
...
}

Finally, use AnyAnimal in your non-generic code instead:

private static var animal: AnyAnimal!

Examples in the Swift Standard Library. For a practical example, see the KeyPath, PartialKeyPath, and AnyKeyPath classes hierarchy. They follow the same pattern I outlined above. The Collections framework provides even further type-erasing examples, but using wrappers instead.

Swift 5: how to specify a generic type conforming to protocol when declaring a variable

An associated type is used when you want your protocol to work with a variety of types, think a Container protocol that might have several methods all dealing with one contained type.

But your protocol is not that, it doesn't need to know any other types to specify the necessary behavior, so get rid of the associated type.

protocol Pipe {
func await() -> Void
func yield( to: Any, with listener: Selector ) -> Void
}

class Foo {
var imageSource: Pipe & Renderable
}

Swift generic class with numeric and comparable protocols

Since your class is a "RangeValidator", I suggest making your class initializer take a ClosedRange<T>. Declaring T: Comparable allows you to use range.contains(value) to do the validation:

Note: There is no need to restrict your class to Numeric, but if you want to then you can declare it as class NumberRangeValidator<T: Comparable & Numeric> as @JoakinDanielson mentioned in the comments.

class NumberRangeValidator<T: Comparable & Numeric> {

let range: ClosedRange<T>

init(range: ClosedRange<T>) {
self.range = range
}

// allow range to be specified with minValue, maxValue
init(minValue: T, maxValue: T) {
guard minValue <= maxValue else { fatalError("Can't form Range with maxValue < minValue") }
self.range = minValue...maxValue
}

func validate(_ value: T) -> Bool {
return range.contains(value)
}
}

Examples:

let validator = NumberRangeValidator(range: 10.0 ... 30.0)

print(validator.validate(9)) // false
print(validator.validate(10)) // true
print(validator.validate(20)) // true
print(validator.validate(30)) // true
print(validator.validate(31)) // false

let floatValidator = NumberRangeValidator(minValue: Float(0), maxValue: 5)
print(floatValidator.validate(0)) // true
print(floatValidator.validate(10)) // false

Note: There is no reason this needs to be a class. A struct will do the job just as well.



Related Topics



Leave a reply



Submit