Create generic Swift instance with static variable - swift

I have a small generic Swift struct that looks like this:
public protocol FilterOption: Equatable {
var name: String { get }
}
public struct Filter<T: FilterOption> {
public init(selected: [T], available: [T]) {
self.selected = selected
self.available = available
}
public static var empty: Filter<T> {
return Filter(selected: [], available: [])
}
public let available: [T]
public let selected: [T]
}
However, I am at a loss as to how to call .empty in any other context than as a fallback or alternate result.
For instance, if myFilter is nil, this works:
let filter = myFilter ?? .empty
Another example is to have a conditional result, like this:
let filter = useFilter ? myFilter : .empty
These cases work and are reasons alone to have the empty var, but I have no idea how to just create an empty filter.
For instance, this doesn't work:
let filter: Filter<UserFilter> = .empty
and fails with the error message 'Filter' is ambiguous for type lookup in this context.
This also doesn't work:
let filter = Filter<Language>.empty
and fails with the error message Cannot specialize a non-generic definition.
How can I provide type information when creating an empty filter? Is this even possible?

Actually the question doesn't need a separate answer as the code is supposed to work provided that the generic placeholder is satisfied properly. It should be salvageable by the comment I made in the question. But as you have asked, I've converted my comment to a detailed answer.
The concrete type that you are providing to the generic placeholder must conform to the protocol (for your case it's FilterOption). Like you have defined:
let filter = Filter<Language>.empty
Make sure your Language type properly conforms to the FilterOption protocol:
struct Language: FilterOption {
// Fulfill the requirements of the FilterOption protocol
}
As soon as you do that you are good to create your empty filter.

Related

Can a protocol define subscript(keyPath:) without an explicit implementation in the adopting object?

Since Swift 4, objects have gained subscript(keyPath:) which can be used to retrieve values using AnyKeyPath and its subclasses. According to the Swift book, the subscript is available on all types. For example, an instance of a class TestClass may be subscripted with an AnyKeyPath like so:
class TestClass {
let property = true
}
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = TestClass()[keyPath: anyKeyPath]
This compiles correctly as expected. Use of any other valid subclass would also compile including PartialKeyPath<TestClass>, KeyPath<TestClass, Bool>, etc. This functionality is unavailable in a protocol extension. For example, the following is invalid:
class TestClass {
let property = true
}
protocol KeyPathSubscriptable {
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = self[keyPath: anyKeyPath] // Value of type 'Self' has no subscripts
}
}
If we want to use that keyPath subscript in the protocol, we can include it in the protocol definition. However, the compiler will not resolve it automatically:
protocol KeyPathSubscriptable {
subscript(keyPath: AnyKeyPath) -> Any? { get }
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath // This can be any valid KeyPath
_ = self[keyPath: anyKeyPath]
}
}
class TestClass: KeyPathSubscriptable { // Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'
let property = true
}
With this, we get a compile error: Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'. In order to resolve this, we must include a redundant implementation of that subscript in TestClass:
class TestClass: KeyPathSubscriptable {
let property = true
subscript(keyPath: AnyKeyPath) -> Any? {
fatalError() // This is never executed
}
}
This resolves the conformance issue and produces the goal result although it is seemingly unnecessary and illogical. I'm not sure how, but the subscript implementation is never even used. It's finding the expected implementation of subscript(keyPath:) and using that instead, but how? Where is that and is there any way to use it in a protocol? Why is this required by the compiler even though it's never used?
The context of this use case is in a logging module. The goal is that an object should be able to adopt a particular protocol which, with no additional setup on the object, would provide a human readable description of the object, instead of the default for many objects which is a memory address. The protocol would use Mirror to fetch KeyPaths of an object, read the values, and print them to the console. It is intended for debugging purposes and would not run in any production environment.
Please let me know if I can make any clarifications. I may post this to the Swift team if others think that this could potentially be a bug of sorts. All help is appreciated. Thanks in advance.
Full gist located here.

Swift: Creating array of objects with generic type parameter

I have a type called Setting that takes a generic type parameter as such:
Setting<T>
Every setting contains a value that can be an Int32, String, Bool, or a custom object type, etc. Here is some of the full implementation of Setting:
class Setting<T> {
var key:String?
var defaultValue:T?
//...
}
This all works with various type params as expected, however, now there is a requirement for a collection that contains multiple Setting objects that could have various type parameters. When I declare an array variable of type [Setting], obviously the compiler expects a type which is unknown at runtime.
I've tried using a protocol and an extension on the types that could be used for the generic type parameter such as this:
protocol SettingProtocol {
func getType() -> Self.Type
}
extension Int32:SettingProtocol {
func getType() -> Int32.Type {
return Int32.self
}
}
extension String:SettingProtocol {
func getType() -> String.Type {
return String.self
}
}
//...
and declaring my array as
var settings = [Setting<SettingProtocol>]()
but this does not work when I try to append a Setting instance to the array as follows:
var newSetting = Setting<String>()
newSetting.setDefaultValue(value: "SomeString")
settings?.append(newSetting) // compile error here
and results in the following compiler error:
Cannot convert value of type 'Setting<String>' to expected argument type 'Setting<SettingProtocol>'
Also, using the protocol/extension route might require an extension on every type that might be encountered when building these objects which seems really clunky.
I feel like there should be a way to accomplish this. Also hoping that when I pull these items out of the array that I can avoid a lot of type checking.
Can anyone offer any advice?
Change
class Setting<T>
to
class Setting<T:SettingProtocol>
and try compiling.
Actually, you can't define:
var settings = [Setting<SettingProtocol>]()
because the generic type of Setting must be one of the concrete types but not the protocol itself. For example, you could declare it as:
var settings = [Setting<String>]() // since you already implemented extension String:SettingProtocol { ...
Therefore you could append objects of type Setting<String>, however that's not what are you looking for, you need settings to be a heterogeneous container.
So what you could do is:
class Setting {
var key:String?
var defaultValue:SettingProtocol?
}
protocol SettingProtocol { }
extension Int32:SettingProtocol {}
extension String: SettingProtocol {}
At this point, you declared defaultValue to be of type SettingProtocol, without the need of dealing with a generic.
Therefore:
var newStringSetting = Setting()
newStringSetting.defaultValue = "My String"
settings.append(newStringSetting)
var newInt32Setting = Setting()
newInt32Setting.defaultValue = Int32(100)
settings.append(newInt32Setting)
for setting in settings {
print(setting.defaultValue)
// Optional("My String")
// Optional(100)
}

Generic constrained type default value

Consider the following code:
protocol JSONParserType {
associatedtype Element
}
// MARK: - Entities
struct Item {}
// MARK: - Parsers
struct OuterParser<T: JSONParserType where T.Element == Item>: JSONParserType {
typealias Element = Item
let innerParser: T
init(innerParser: T = InnerParser()) {
self.innerParser = innerParser
}
}
struct InnerParser: JSONParserType {
typealias Element = Item
}
The OuterParser has a child parser that should be constrained to a specific type. Unfortunately providing a default value in the initializer (or in the property definition itself) does lead to the compiler throwing a "Default argument value of type 'InnerParser' cannot be converted to type 'T'".
If I remove the default value assignment and just instantiate the OuterParser providing the InnerParser explicitly, everything is fine.
let outerParser = OuterParser(innerParser: InnerParser())
My question is what's the reason that the approach providing a default value that actually meets the constraints does not work.
The problem is that the actual type of T isn't defined by the class – it's defined by the code that uses the class. It will therefore be defined before you do anything in your class (at either instance or static level). You therefore can't assign InnerParser to T, as T has already been defined to be a given type by that point, which may well not be InnerParser.
For example, let's consider that you have another parser struct:
struct AnotherParser: JSONParserType {
typealias Element = Item
}
and let's assume that your current code compiles. Now consider what would happen when you do this:
let parser = OuterParser<AnotherParser>()
You've defined the generic type to be AnotherParser – but the initialiser will try to assign InnerParser to your property (now of type AnotherParser). These types don't match, therefore it cannot possibly work.
Following the same logic, this implementation also won't work:
struct OuterParser<T: JSONParserType where T.Element == Item>: JSONParserType {
typealias Element = Item
let innerParser: T
init() {
self.innerParser = InnerParser()
}
init(innerParser: T) {
self.innerParser = innerParser
}
}
As there's no guarantee that the generic type T will be the same type as InnerParser. Sure, you can force downcast to T – but that'll just make you code crash if the types aren't compatible.
Unfortunately, there's no real clean solution to this problem. I think the best your best option is probably to create two factory methods for creating your OuterParser instance.
enum Parser {
static func createParser() -> OuterParser<InnerParser> {
return OuterParser(innerParser:InnerParser())
}
static func createParser<T>(innerParser:T) -> OuterParser<T> {
return OuterParser(innerParser:innerParser)
}
}
let innerParser = Parser.createParser() // OuterParser<InnerParser>
let anotherParser = Parser.createParser(AnotherParser()) // OuterParser<AnotherParser>
We're using an caseless enum here to avoid polluting the global namespace with extra functions.
Although this isn't very Swifty, and for that reason I would also recommend maybe rethinking your logic for how you define your parsers.
type T more like a child protocol of JSONParserType you can convert it:
init(innerParser: T = InnerParser() as! T) {
self.innerParser = innerParser
}

Swift dynamicType does not work with generic function

Say I have a protocol:
protocol VehicleModel {...}
It is implemented by a number of different structs. (e.g. CarModel, TruckModel, etc.)
I have a generic method to get the vehicle's 'model identifier'.
func modelIdentifierForVehicle<V: VehicleModel>(vehicleType: V.Type) -> String {
return "\(vehicleType)"
}
If I call modelIdentifierForVehicle(CarModel.self) this returns "Car" just fine. But if I have a polymorphic collections of VehicleModel's and I try to call modelIdentifierForVehicle(model.dynamicType) on each of them, Xcode says "Cannot invoke 'modelIdentifierForVehicle' with argument list of type (VehicleModel.Type)" Why is this? And how can I work around it?
Since you're only converting vehicleType to a String in modelIdentifierForVehicle, I would argue why you need to use constrain V to VehicleModel, or even use generics at all:
func typeIdentifier(t: Any.Type) -> String {
return "\(t)"
}
let vehicles: [VehicleModel.Type] = [CarModel.self, TruckModel.self]
typeIdentifier(vehicles[0]) // CarModel
If there's a reason you need use a VehicleModel, assuming VehicleModel doesn't use Self or associated type requirements, you could do:
func modelIdentifierForVehicle(vehicleType: VehicleModel.Type) -> String {
return "\(vehicleType)"
}
If you're using Swift 2, you could instead use a protocol extension:
extension VehicleModel {
static var modelIdentifier: String {
return "\(self.dynamicType)"
}
}
// The array from earlier.
vehicles[1].modelIdentifier // TruckModel.Type

Swift: How to hold any possible instance of a generic type in a variable

The distillation of what I am trying to do is this:
public struct HolderOfWrappers
{
let anyWrappedItem: MyResource<Any>
}
public struct MyResource<A>
{
let wrappedItem : A
let convert: String -> A
}
func holdResource<A>( resource: MyResource<A> ) -> HolderOfWrappers
{
// Error on this line, A is not Any...
let wrapHolder : HolderOfWrappers = HolderOfWrappers( resource )
return wrapHolder
}
As it stands, this code produces the compiler error in the last holdResource method where I'm trying to build a HolderOfWrappers:
Cannot convert the expression's type 'MyResource<A>' to type '(anyWrappedItem: MyResource<Any>)'
Which is understandable as the code indicates HolderOfWrappers can only hold a MyResource built for Any type, not any possible type. What I'm really after with the HolderOfWrappers is something like this:
public struct HolderOfWrappers
{
let anyWrappedItem: MyResource<>
}
or even MyResource<*> - I am trying to say with this code that I'd like a variable that can hold any type of MyResource. If I try to use either syntax though, I get a compiler error that it expects a type.
I could just have anyWrappedItem by of type Any, but then you lose the type information for future use. I also do not want HolderOfWrappers to be generic (because then I'd just have the same problem later).
It's almost like I am trying to treat the generic type as a protocol for the anyWrappedItem storage variable, which will not work for other reasons...
I think you can do what you want by putting a generic parameter in your HolderOfWrappers init method. Basically, the init method just generates a new MyResource using the resource that you provide, like this:
public struct HolderOfWrappers {
let anyWrappedItem: MyResource<Any>
public init<A>(resource: MyResource<A>) {
self.anyWrappedItem = MyResource(wrappedItem: resource.wrappedItem, convert: resource.convert)
}
}
I think that will do what you want. I don't know if it will be slower since you are initializing an entirely new MyResource instead of just copying one.
At any rate, it makes it so that HolderOfWrappers itself is not generic and will fill anyWrappedItem with a MyResource<Any> that holds the same values as the resource that you pass in.
How about this
protocol Wrapper {}
public struct HolderOfWrappers {
let anyWrappedItem: MyResource<Wrapper>
}
public struct MyResource<A> {
let wrappedItem : A
let convert: String -> A
}
func holdResource( resource: MyResource<Wrapper>) -> HolderOfWrappers {
// Error on this line, A is not Any...
let wrapHolder : HolderOfWrappers = HolderOfWrappers(anyWrappedItem: resource)
return wrapHolder
}
of course the downside is you'd have to do extension A: Wrapper { } for any type A that you pass into MyResource.