Can Swift enums be inferred and restricted generically? - swift

I can't find the syntax, but I want to do something like this:
class MyClass {
let stringValue: String // filled in later
let integerValue: Int // filled in later
init(stringValue: String) {
self.stringValue = stringValue
self.integerValue = stringValue.hashValue
}
init(integerValue: Int) {
self.integerValue = integerValue
self.stringValue = String(integerValue)
}
}
extension MyClass {
// This is invalid syntax, but I think you can understand
// vvvvvvvvv I'm trying to give back an enum whose type is inferred
var enumValue<T: enum>: T? {
get {
// This is also invalid; I want to check the type of the enum's raw value
if T is String {
return T(rawValue: self.stringValue)
} else if T is Int {
return T(rawValue: self.integerValue)
} else {
return nil
}
}
}
}
The usage would be like:
enum MyEnum: String {
case foo
case bar
}
func baz(_ some: MyClass) {
if let myEnum: MyEnum = some.enumValue {
print(myEnum)
}
}
let some = MyClass(stringValue: "foo")
baz(some) // prints "foo"
Is this possible in Swift? That is, to have a generically-typed field or function whose type is constricted to enums and inferred based on usage, then use that to instantiate an enum value?

A possible solution would be a generic overloaded function:
extension MyClass {
func enumValue<T: RawRepresentable>() -> T? where T.RawValue == String {
return T(rawValue: stringValue)
}
func enumValue<T: RawRepresentable>() -> T? where T.RawValue == Int {
return T(rawValue: integerValue)
}
}
which is then called as
func baz(_ some: MyClass) {
if let myEnum: MyEnum = some.enumValue() {
print(myEnum)
}
}
Alternatively, pass the enum type as an argument:
extension MyClass {
func enumValue<T: RawRepresentable>(_ type: T.Type) -> T? where T.RawValue == String {
return T(rawValue: stringValue)
}
func enumValue<T: RawRepresentable>(_ type: T.Type) -> T? where T.RawValue == Int {
return T(rawValue: integerValue)
}
}
and call it like
func baz(_ some: MyClass) {
if let myEnum = some.enumValue(MyEnum.self) {
print(myEnum)
}
}

Related

Swift abstraction for enums with associated value

I have an enum with associated value ConfigurationValue that gets stored in a map. I have type specific getters for each possible enum value.
How do I minimize the code duplication between these getters
enum ConfigurationValue {
case bool(Bool)
case int(Int)
case string(String)
}
var map: [T:ConfigurationValue] = [:]
public mutating func set(key: T, _ value: Bool) {
map[key] = .bool(value)
}
public mutating func set(key: T, _ value: Int) {
map[key] = .int(value)
}
public mutating func set(key: T, _ value: String) {
map[key] = .string(value)
}
private func getValue(key: T) -> ConfigurationValue? {
return map[key]
}
public func get(key: T) -> Bool? {
guard let value = getValue(key: key),
case .bool(let innerValue) = value else {
return nil
}
return innerValue
}
public func get(key: T) -> Int? {
guard let value = getValue(key: key),
case .int(let innerValue) = value else {
return nil
}
return innerValue
}
public func get(key: T) -> String? {
guard let value = getValue(key: key),
case .string(let innerValue) = value else {
return nil
}
return innerValue
}
I would not use protocols but instead I would add two methods in ConfigurationValue the first to create a configuration value and the other to get the inner value of a case.
enum ConfigurationValue {
case bool(Bool)
case int(Int)
case string(String)
static func with<Value>(value: Value?) -> ConfigurationValue? {
if let value = value as? Bool {
return .bool(value)
}
if let value = value as? Int {
return .int(value)
}
if let value = value as? String {
return .string(value)
}
return nil
}
func get<Value>(ofType type: Value.Type = Value.self) -> Value? {
switch self {
case .bool(let bool):
return bool as? Value
case .int(let int):
return int as? Value
case .string(let string):
return string as? Value
}
}
}
let boolValue: Bool? = ConfigurationValue.bool(false).get() // Optional(false)
let intValue: Int? = ConfigurationValue.bool(false).get() // nil
let stringValue: String? = ConfigurationValue.bool(false).get() // nil
let boolConfiguration: ConfigurationValue? = .with(value: true) // bool(true)
let intConfiguration: ConfigurationValue? = .with(value: 42) // int(42)
let stringConfiguration: ConfigurationValue? = .with(value: "42") // string("42")
let doubleConfiguration: ConfigurationValue? = .with(value: 42.0) // nil
Now you can avoid boilerplate, let's say you have a struct Foo:
struct Foo<T: Hashable> {
var map: [T:ConfigurationValue] = [:]
mutating func set<Value>(_ value: Value?, forKey key: T) {
map[key] = .with(value: value)
}
func get<Value>(key: T) -> Value? {
guard let value = map[key] else {
return nil
}
return value.get()
}
}
var map: [String:ConfigurationValue] = [
"bool": .bool(false),
"int": .int(0),
"string": .string("string")
]
var bar = Foo(map: map)
var bool: Bool? = bar.get(key: "bool") // Optional(false)
var int: Int? = bar.get(key: "int") // 0
var string: String? = bar.get(key: "string") // string
bar.set(true, forKey: "bool")
bool = bar.get(key: "bool") // Optional(true)
bar.set(42, forKey: "int")
int = bar.get(key: "int") // 42
bar.set("Hello World!", forKey: "string")
string = bar.get(key: "string") // Hello World!
It doesn't seem like an enum is the right tool for the job here since you can get the same usefulness out of just casting the values (though admittedly you wouldn't get the benefit of exhaustive switch errors). If you're wanting to utilize generics to cut down on some of this duplication, I'd go with a wrapper around the map like so:
protocol Configurable {}
extension Int: Configurable {}
extension Bool: Configurable {}
extension String: Configurable {}
class ConfigurationMap<Key: Hashable> {
private var map = [Key: Configurable]()
func getValue(at key: Key) -> Configurable? {
return map[key]
}
func setValue(_ value: Configurable, at key: Key) {
map[key] = value
}
}
Here Key would replace your T generic. To incorporate more data types, just extend the type that you want to use to be Configurable. The ConfigurationMap can also be a struct if you don't mind the mutating methods.
In Swift, this is not called a "map", but instead, a "dictionary". Type restriction can be achieved with a protocol, and explicit typing for retrieval.
public protocol ConfigurationValue { }
extension Int: ConfigurationValue { }
extension Bool: ConfigurationValue { }
extension String: ConfigurationValue { }
public struct ConfigurationDictionary<Key: Hashable> {
private var dictionary: [Key: ConfigurationValue] = [:]
}
public extension ConfigurationDictionary {
subscript<Value: ConfigurationValue>(key: Key) -> Value? {
get { dictionary[key] as? Value }
set { dictionary[key] = newValue }
}
}
var configurationDictionary = ConfigurationDictionary<String>()
configurationDictionary["i"] = 1
configurationDictionary["i"] as Int? // 1
configurationDictionary["i"] as Bool? // nil
configurationDictionary["b"] = true
let b: Bool? = configurationDictionary["b"] // true
let i: Int? = configurationDictionary["b"] // nil

Init a FloatingPoint from a string

I am trying to convert a 2d array of Strings into my custom generic type Matrix:
func convert(_ arr: [[String]]) -> Matrix<Element> {
var m: Matrix<Element> = Matrix()
for row in arr {
var v: [Element] = []
for e in row {
let convE: Element = Element(string: e) // right here I'd like to implement something like that: Element(string: e)
v.append(convE)
}
m.vectors.append(Vector(v))
}
return m
}
The Matrix.Element does conform to the FloatingPoint protocol. Please tell me if you wish to see the Matrix struct but I think I haven't implemented anything that's important for this question other than that the generic Element type of Matrix does conform to the FloatingPoint protocol.
My problem is I want Element to be something like Float, Double... (any of the FloatingPoint types) but how can I initialize a FloatingPoint from a string? I tried:
extension FloatingPoint {
init(string: String) {
self.init(Int(string)!)
}
}
which obviously only works for strings like "1", "2"... and not "1.2", "3.541" and so on which I want.
Edit:(#Leo Dabus)
protocol DArray: Sequence {
associatedtype Component: FloatingPoint
}
extension DArray {
static func * <T: DArray>(lhs: Self, rhs: T) -> Vector<Component> {
let v = lhs as? Vector<Component> ?? rhs as! Vector<Component>
let m = lhs as? Matrix<Component> ?? rhs as! Matrix<Component>
return Vector(m.map { zip(v, $0).map(*).reduce(0, +) })
}
static func / <T: DArray>(lhs: Self, rhs: T) -> Vector<Component> {
let v = lhs as? Vector<Component> ?? lhs as! Vector<Component>
let m = lhs as? Matrix<Component> ?? lhs as! Matrix<Component>
return Vector(m.map { zip(v, $0).map(/).reduce(0, +) })
}
}
struct Vector<Component: FloatingPoint>: DArray {
var components: [Component]
var count: Int {
return components.count
}
init(_ Components: [Component] = []) {
self.components = Components
}
subscript(i: Int) -> Component {
get {
return components[i]
} set {
components[i] = newValue
}
}
static func + (lhs: Self, rhs: Self) -> Self {
return Vector(zip(lhs, rhs).map(+))
}
static func - (lhs: Self, rhs: Self) -> Self {
return Vector(zip(lhs, rhs).map(-))
}
static func * (lhs: Self, rhs: Self) -> Self {
return Vector(zip(lhs, rhs).map(*))
}
static func / (lhs: Self, rhs: Self) -> Self {
return Vector(zip(lhs, rhs).map(/))
}
func empty(of length: Int) -> Self {
return Vector(Array(repeating: 0, count: length))
}
}
struct Matrix<Component: FloatingPoint>: DArray {
var vectors: [Vector<Component>]
var nRows: Int {
return vectors.count
}
var nColumns: Int {
guard !vectors.isEmpty else { return 0 }
return vectors[0].count
}
var count: Int {
return vectors.count
}
init(_ vectors: [Vector<Component>] = []) {
self.vectors = vectors
}
subscript(r: Int) -> Vector<Component> {
get {
return vectors[r]
}
set {
vectors[r] = newValue
}
}
subscript(r: Int, c: Int) -> Component {
get {
return vectors[r][c]
}
set {
vectors[r][c] = newValue
}
}
}
Additionally I have my two structs conform to the Sequence protocol.
(Note: I am the OP)
What I came up with now is:
extension FloatingPoint {
public init?(string: String) {
if Self.self == Double.self {
self = Double(string) as! Self
} else if Self.self == Float.self {
self = Float(string) as! Self
} else if Self.self == Float80.self {
self = Float80(string) as! Self
} else {
return nil
}
}
}
It works for my use case but I was wondering whether it is a good way of achieving what I am looking for. So I'd be happy for someone to evaluate my solution. (#Leo Dabus)
You can extend FloatingPoint protocol and constrain the generic type to LosslessStringConvertible:
extension StringProtocol {
func floatingPoint<T: FloatingPoint>() -> T? where T: LosslessStringConvertible {
T(String(self))
}
}
Note that CGFloat does NOT conform to LosslessStringConvertible so you would need to implement a custom String initializer:
extension CGFloat: LosslessStringConvertible {
private static let formatter = NumberFormatter()
public init?(_ description: String) {
guard let number = CGFloat.formatter.number(from: description) as? CGFloat else { return nil }
self = number
}
}
let double: Double? = "2.7".floatingPoint() // 2.7
let float: Float? = "2.7".floatingPoint() // 2.7
let float80: Float80? = "2.7".floatingPoint() // 2.7
let cgfloat: CGFloat? = "2.7".floatingPoint() // 2.7
There is already an initializer for FloatingPoint types but to make your code work you need to conform your Matrix Component to LosslessStringConvertible.
Can you try something like this:
extension FloatingPoint where Self == Double {
init(string: String) {
self.init(Double(string)!)
}
}
extension FloatingPoint where Self == Float {
init(string: String) {
self.init(Float(string)!)
}
}
this should work as well
extension FloatingPoint {
init(string: String) {
self.init(Self(string)!)
}
}

How to overcome the error of "Generic parameter 'T' is not used in function signature"?

I'm trying to convert the following to be generic.
extension RLMOrganization: DataProvider {
func getLastSyncToken() -> String {
let lastUpdated: RLMOrganization? = self.findAll(sortedBy: "syncToken").last
if let syncToken = lastUpdated?.syncToken {
return syncToken
} else {
return "00000000000000000000000000000000"
}
}
}
And have tried this:
protocol DataProvider: DatabaseLayer {
associatedtype T: Object
func findAll<T: Object>(sortedBy key: String) -> [T]
}
extension DataProvider {
func findAll<T: Object>(sortedBy key: String) -> [T] {
let database = self.getDatabase()
if let allObjects = database?.objects(T.self) {
let results = allObjects.sorted(byKeyPath: key, ascending: true)
return Array(results)
}
return []
}
func getLastSyncToken<T: Object>() -> String {
let lastUpdated = self.findAll(sortedBy: "syncToken").last as? T
if let value = lastUpdated?.value(forKey: "syncToken") { // get value from object by string name
let syncToken = value as! String
return syncToken
} else {
return "00000000000000000000000000000000"
}
}
...
But can't seem to overcome the error of:
Generic parameter 'T' is not used in function signature
I would think the compiler has everything it needs to determine type usage.
Below works for me, I don't know how findAll is defined but the problem is the reference to self as I see it so you need to define T there using associatedtype.
protocol DataProvider: DatabaseLayer {
associatedtype T: Object
func findAll(sortedBy: String) -> T?
}

Swift generic completion handler

I'm trying to figure out how to create a generic completion handler. Below is an example illustrating an example "internal" generic completion handler and the same generic completion handler as I would want to be able to create it if I could do that in "external" form. The problem is that I don't know how to write the equivalent of the internalCompletion<T: MyEnum>... in a completion handler. I've written in the externalCompletion function what I'd imagine it could look like: something along the lines of func externalCompletion(_ completer<T: MyEnum>: ((T) -> Void) where T: Hashable)), but this is obviously not correct. Is what I'm trying to do possible? My hunch is that swift won't let the completion handler remain generic, always requiring a type casting at the function level, which would defeat the purpose per my example (i.e. func externalCompletetion<T: MyEnum>(_ completer: ((T) -> Void)) where T: Hashable, the problem with this being I would have to choose between EnumA, EnumB, and EnumC, not being able to run the completer on all three.)
typealias MyEnumKeyedData<T: MyEnum> = [T: String] where T: Hashable
// MARK : - MyEnum Protocol
protocol MyEnum {
static func all<T: MyEnum>(_:T.Type) -> [T] where T: Hashable
static var all: [MyEnum] { get }
}
extension MyEnum {
static func all<T: MyEnum>(_:T.Type) -> [T] where T: Hashable { return Self.all as! [T] }
}
// MARK : - My enums
enum EnumA: MyEnum {
case first
static var all: [MyEnum] { return [EnumA.first]}
}
enum EnumB: MyEnum {
case first
static var all: [MyEnum] { return [EnumB.first]}
}
enum EnumC: MyEnum {
case first
static var all: [MyEnum] { return [EnumC.first]}
}
// MARK : - MyEnum Data Iterator
class MyDataEnumIterator {
var dataA: MyEnumKeyedData<EnumA> = [:]
var dataB: MyEnumKeyedData<EnumB> = [:]
var dataC: MyEnumKeyedData<EnumC> = [:]
func updateData<T: MyEnum>(_ key: T, _ value: String) where T: Hashable {
switch T.self {
case is EnumA.Type: dataA[key as! EnumA] = value
case is EnumB.Type: dataB[key as! EnumB] = value
case is EnumC.Type: dataC[key as! EnumC] = value
default: fatalError("Enum does not exist")
}
}
// Internal (This works)
func internalEnumIterator() {
for key in EnumA.all(EnumA.self) { internalCompletion(key) }
for key in EnumB.all(EnumB.self) { internalCompletion(key) }
for key in EnumC.all(EnumC.self) { internalCompletion(key) }
}
func internalCompletion<T: MyEnum>(_ key: T) where T: Hashable {
let value = "\(key)"
updateData(key, value)
}
// External (This obviously doesn't, just sketching the idea)
func externalEnumIterator(_ completer<T: MyEnum>: ((T) -> Void) where T: Hashable) {
for key in EnumA.all(EnumA.self) { completer(key) }
for key in EnumB.all(EnumB.self) { completer(key) }
for key in EnumC.all(EnumC.self) { completer(key) }
}
}
// MARK : - Test cases (internal works, external does not, just sketching example)
let iterator = MyDataEnumIterator()
iterator.externalEnumIterator({ <T: MyEnum> (T) where T: Hashable in
let value = "\(key)"
iterator.updateData(key, value)
})
iterator.internalEnumIterator()
Here is a working version of the code with the minimal changes necessary to get it going
typealias MyEnumKeyedData<T: MyEnum> = [T: String] where T: Hashable
// MARK : - MyEnum Protocol
protocol MyEnum {
static func all<T: MyEnum>(_:T.Type) -> [T] where T: Hashable
static var all: [MyEnum] { get }
}
extension MyEnum {
static func all<T: MyEnum>(_:T.Type) -> [T] where T: Hashable { return Self.all as! [T] }
}
// MARK : - My enums
enum EnumA: MyEnum {
case first
static var all: [MyEnum] { return [EnumA.first]}
}
enum EnumB: MyEnum {
case first
static var all: [MyEnum] { return [EnumB.first]}
}
enum EnumC: MyEnum {
case first
static var all: [MyEnum] { return [EnumC.first]}
}
// MARK : - MyEnum Data Iterator
class MyDataEnumIterator {
var dataA: MyEnumKeyedData<EnumA> = [:]
var dataB: MyEnumKeyedData<EnumB> = [:]
var dataC: MyEnumKeyedData<EnumC> = [:]
func updateData(_ key: MyEnum, _ value: String) {
switch key {
case let key as EnumA: dataA[key] = value
case let key as EnumB: dataB[key] = value
case let key as EnumC: dataC[key] = value
default: fatalError("Enum does not exist")
}
}
// Internal (This works)
func internalEnumIterator() {
for key in EnumA.all(EnumA.self) { internalCompletion(key) }
for key in EnumB.all(EnumB.self) { internalCompletion(key) }
for key in EnumC.all(EnumC.self) { internalCompletion(key) }
}
func internalCompletion<T: MyEnum>(_ key: T) where T: Hashable {
let value = "\(key)"
updateData(key, value)
}
func EnumIterator(_ compeltitionHandler: (MyEnum) -> Void) {
for key in EnumA.all(EnumA.self) { compeltitionHandler(key as MyEnum) }
for key in EnumB.all(EnumB.self) { compeltitionHandler(key as MyEnum) }
for key in EnumC.all(EnumC.self) { compeltitionHandler(key as MyEnum) }
}
}
let iterator = MyDataEnumIterator()
iterator.EnumIterator{ key in
let value = "\(key)"
iterator.updateData(key, value)
}
iterator.internalEnumIterator()
Here is a sane version of the code that removes all the nonsense, and adds subscript syntax:
// MARK : - MyEnum Protocol
protocol MyEnum {
static func all() -> [MyEnum]
}
// MARK : - My enums
enum EnumA: MyEnum {
case first
static func all() -> [MyEnum] { return [EnumA.first] }
}
enum EnumB: MyEnum {
case first
static func all() -> [MyEnum] { return [EnumB.first] }
}
enum EnumC: MyEnum {
case first
static func all() -> [MyEnum] { return [EnumC.first] }
}
// MARK : - MyEnum Data Iterator
class MyDataEnumIterator {
var dataA = [EnumA: String]()
var dataB = [EnumB: String]()
var dataC = [EnumC: String]()
subscript(key: MyEnum) -> String? {
get {
switch key {
case let key as EnumA: return dataA[key]
case let key as EnumB: return dataB[key]
case let key as EnumC: return dataC[key]
default: fatalError("Enum does not exist")
}
}
set {
switch key {
case let key as EnumA: dataA[key] = newValue
case let key as EnumB: dataB[key] = newValue
case let key as EnumC: dataC[key] = newValue
default: fatalError("Enum does not exist")
}
}
}
func EnumIterator(_ body: (MyEnum) -> Void) {
EnumA.all().forEach(body);
EnumB.all().forEach(body);
EnumC.all().forEach(body);
}
}
let iterator = MyDataEnumIterator()
iterator.EnumIterator{
iterator[$0] = "\($0)"
}

Swift: check return type of generic function

I know how to check type of named variable - if var is T. But can't find how to check supposed return type for generic function.
Live example, dealing with SwiftyJSON, ugly solution:
func getValue<T>(key: String) -> T? {
let result: T // so ugly approach...
if result is Bool {
return json[key].bool as? T
}
if result is Int {
return json[key].int as? T
}
if result is String {
return json[key].string as? T
}
fatalError("unsupported type \(result.dynamicType)")
}
Looking for more elegant approach.
This would work:
func getValue<T>(key: String) -> T? {
if T.self is Bool.Type {
return json[key].bool as? T
}
if T.self is Int.Type {
return json[key].int as? T
}
if T.self is String.Type {
return json[key].string as? T
}
fatalError("unsupported type \(T.self)")
}
But I'm not sure it's any more elegant than yours.
Overloading is something worth trying:
func getValue(key: String) -> Bool? {
return json[key].bool
}
func getValue(key: String) -> Int? {
return json[key].int
}
func getValue(key: String) -> String? {
return json[key].string
}
With this, you can find errors in compile time, rather than getting fatal errors in runtime.