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
What Is the Real Benefit of Using Raycast in Arkit and Realitykit
How to Use Contains Within a Swift Array Extension
Show Am/Pm in Capitals in Swift
How to Replicate Promisekit-Style Chained Async Flow Using Combine + Swift
Swift Semantics Regarding Dictionary Access
Split Now Complains About Missing "Isseparator"
Transparent Background for Texteditor in Swiftui
Property Observers Willset and Didset; Property Getters and Setters
Swift - How to Mutate a Struct Object When Iterating Over It
Accessing Appstate in Appdelegate with Swiftui's New iOS 14 Life Cycle
How to Display Data from Firebase Faster
How to Check If a Property Value Exists in Array of Objects in Swift
How to Override Trait Collection for Initial Uiviewcontroller? (With Storyboard)
Arkit: Placing an Scntext at a Particular Point in Front of the Camera
Check Whether Swift Object Is an Instance of a Given Metatype