Swift subscript setter that accepts a different type than the getter's return value - swift

I have a custom collection that can receive values of any type and converts them to strings. For example:
collection["key"] = 10
let value = collection["key"] // value is a String
Is there a way to do this? I tried implementing two subscript methods but Swift doesn't support write-only subscripts.
subscript(key: String) -> String {
get { ... }
}
// Doesn't compile
subscript(key: String) -> AnyObject {
set { ... }
}

You can use two different subscript implementations and disable the getter for one of them:
subscript(key: String) -> String {
get { return "howdy" } // put real implementation here
}
subscript(key: String) -> AnyObject {
get { fatalError("Oooops") }
set { }
}
However, this still leaves open the question of how to distinguish between these two subscript calls in context. It would be better to give them different signatures through their external parameter names:
subscript(key: String) -> String {
get { return "howdy" } // put real implementation here
}
subscript(# any: String) -> AnyObject {
get { fatalError("Oooops") }
set { }
}
And here's how to use it:
let m = MyClass()
m[any:"thing"] = 1
println(m["thing"]) // "1", presumably

Define subscript to return AnyObject (or Any as needed) and at the point you use the getter cast the result to String. You may already need to deal with subscript returning an optional so the coercion is just all part of extracting your desired value.
if let value = collection["key"] as String { ... }
else {...}

You could also define your own type and make it conform to the IntegerLiteralConvertible and the StringLiteralConvertible protocols.
Technically you could also write an extension for String to make it conform to IntegerLiteralConvertible but that might get confusing, since it will be available in your entire project.

I was facing a similar problem here and I solved it using a generic type for my variable and returning the type I want on its getter. You can try doing something like this:
class StorageClass {
private var _value: String?
public var value: Any? {
set {
if let num = newValue as? Int {
self._value = String(format: "%d",num)
}
}
get {
return self._value
}
}
}
By doing this, it is possible to do something like:
var storage = StorageClass()
storage.value = 10 /* setting value as an Integer */
let aString = storage.value as! String /* receiving a String value back */

Related

Extend a Generic Type With Another Generic

Is it possible to extend a generic type with a where clause requirement containing another generic?
Basically something like this:
class WeakStore<Element: AnyObject> {
weak var value: Element?
init(_ value: Element) {
self.value = value
}
}
extension Dictionary where Value == WeakStore<Element> {
public subscript(weak key: Key) -> Element? {
get {
self[key].value
}
set {
if let exists = self[key] {
exists.value = newValue
} else {
self[key] = WeakStore(newValue)
}
}
}
}
To clarify, this does not build and provides numerous errors. Additionally, while what prompts this specific question IS trying to get a weak dictionary working, please stay on topic about how to get a generic type extended with another generic (if even possible) as it is something that applies well beyond this specific use case.
I don't believe it can be done quite this way. (I'm not actually certain why; it feels like it's just syntax and compiler features, but it's possible that there's something deeper I haven't discovered.)
I can, however, be done by adding a protocol:
public protocol WeakStoreType: AnyObject {
associatedtype Element: AnyObject
var value: Element? { get set }
init(_ value: Element)
}
extension WeakStore: WeakStoreType {}
This just wraps WeakStore into a protocol, and then conforms WeakStore to that protocol, replacing a generic parameter with an associatedtype.
There are a few mistakes in your Dictionary extension, but they can be fixed as follows:
public extension Dictionary where Value: WeakStoreType {
subscript(weak key: Key) -> Value.Element? {
get {
self[key]?.value
}
set {
if let exists = self[key] {
exists.value = newValue
} else if let newValue = newValue {
self[key] = Value(newValue)
} else {
self[key] = nil
}
}
}
}

Accessing Typed Value of Generic Argument in Swift

I am trying to make a dispatch function which can take Payload as an argument. The Payload can be Int, String or any other type. I tried the following approaches but inside the dispatch function the payload.value is always T and not Int or String. Casting is an option but I thought that was the whole point of generics.
struct Payload<T> {
let value: T
}
func dispatch<T>(payload: Payload<T>) {
print(payload.value) // get the value as Int, String or some other type
}
let payload = Payload<Int>(value: 100)
let payload2 = Payload<String>(value: "FOO")
dispatch(payload: payload)
dispatch(payload: payload2)
As you already know T is unconstrained so it can be any type. Your only option is to cast the value to the types you are expecting. You can simply switch the value:
switch payload.value {
case let value as Int:
print("Int:", value)
case let value as String:
print("String:", value)
default:
print("some other type", payload.value)
}
Depends on what you want to get inside your dispatch, you can create a protocol and requires T to be conformed to that protocol.
protocol Printable {
func printValue() -> String
}
struct Payload<T> {
let value: T
}
func dispatch<T: Printable>(payload: Payload<T>) {
print(payload.value.printValue()) // get the value as Int, String or some other type
}
extension String: Printable {
func printValue() -> String {
return self
}
}
extension Int: Printable {
func printValue() -> String {
return "\(self)"
}
}

Returning the associatedtype of an opaque return type

I have a simple protocol with an associated type, and a protocol extension that returns an array of this type.
protocol Foo {
associatedtype Unit
}
extension Foo {
var allTheFoos: [Unit] {
return []
}
}
I then have a struct which returns some Foo in a computed property, and another computed property that returns the allTheFoos array.
struct FakeFoo: Foo {
typealias Unit = Int
}
struct FooFactory {
var myFoo: some Foo {
return FakeFoo()
}
/* WHICH RETURN TYPE WILL
PLEASE THE SWIFT GODS?!
*/
var allTheFoos: [Foo.Unit] {
return myFoo.allTheFoos
}
}
The return type of allTheFoos matches Xcode's autocomplete type suggestion for the myFoo.allTheFoos call, but understandably, this yields a:
// var allTheFoos: [Foo.Unit] {}
ERROR: Associated type 'Unit' can only be used with a concrete type or generic parameter base
My question is: What return type will make Xcode happy?
Below are my attempts, and their corresponding errors
// var allTheFoos: [some Foo.Unit] {}
ERROR: 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions
// func allTheFoos() -> some [Foo.Unit]
ERROR: Associated type 'Unit' can only be used with a concrete type or generic parameter base
// func allTheFoos<U: Foo.Unit>() -> [U]
ERROR: Associated type 'Unit' can only be used with a concrete type or generic parameter base
ERROR: Cannot convert return expression of type '[(some Foo).Unit]' to return type '[U]'
// func allTheFoos<U>() -> [U] where U: (some Foo).Unit
ERROR: 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions
FYI: The reason I'm doing this in a computed property in the first place is to keep things clean in some SwiftUI code.
Thanks for any help you can give!
=========== UPDATE ===========
I missed some important stuff in my sample code, so to give some context: the code is used in a unit conversion app, so something that can turn Celsius into Kelvin, Kg into lbs, and anything else into anything else.
protocol Unit: Equatable {
var suffix: String { get }
}
struct Value<UnitType: Unit> {
let amount: Double
let unit: UnitType
var description: String {
let formatted = String(format: "%.2f", amount)
return "\(formatted)\(unit.suffix)"
}
}
Value is constrained to a unit type, so that it's not possible to convert Celsius into Litres.
Therefore, we have a Conversion protocol that stores all similar units together:
protocol Conversion {
associatedtype UnitType: Unit
var allUnits: [UnitType] { get }
func convert(value: Value<UnitType>, to unit: UnitType) -> Value<UnitType>
}
extension Conversion {
func allConversions(for value: Value<UnitType>) -> [Value<UnitType>] {
let units = self.allUnits.filter { $0 != value.unit }
return units.map { convert(value: value, to: $0) }
}
}
So an example of a conversion for Temperature would be:
struct Temperature: Conversion {
enum Units: String, Unit, CaseIterable {
case celsius, farenheit, kelvin
var suffix: String {
switch self {
case .celsius: return "˚C"
case .farenheit: return "˚F"
case .kelvin: return "K"
}
}
}
var allUnits: [Units] { return Units.allCases }
func convert(value: Value<Units>, to unit: Units) -> Value<Units> {
/* ... */
}
}
Finally, the actual app code where the problem occurs is here:
struct MyApp {
var current: some Conversion {
return Temperature()
}
// ERROR: Associated type 'UnitType' can only be used with a concrete type or generic parameter base
var allConversions: [Value<Conversion.UnitType>] {
// This value is grabbed from the UI
let amount = 100.0
let unit = current.allUnits.first!
let value = Value(amount: amount, unit: unit)
return current.allConversions(for: value)
}
}
Looking at how you've implemented AnyValue, I think what you want here is just:
var allConversions: [String] {
let units = self.allUnits.filter { $0 != value.unit }
return units.map { convert(value: value, to: $0) }.description
}
Or something like that. All the algorithms that match what you're describing are just "conversion -> string." If that's the case, all you really want is CustomStringConvertible.
Managed to solve this issue using some Type Erasure:
struct AnyValue {
let description: String
init<U: Unit>(_ value: Value<U>) {
self.description = value.description
}
}
allowing for:
var allConversions: [AnyValue] {
// This value is grabbed from the UI
let amount = 100.0
let unit = current.allUnits.first!
let value = Value(amount: amount, unit: unit)
return current.allConversions(for: value).map(AnyValue.init)
}
However, this feels like a clunky solution (and one that opaque return types was introduced to avoid). Is there a better way?

Get all key paths from a struct in Swift 4

Let's say I have that struct:
struct MyStruct {
let x: Bool
let y: Bool
}
In Swift 4 we can now access it's properties with the myStruct[keyPath: \MyStruct.x] interface.
What I need is a way to access all it's key paths, something like:
extension MyStruct {
static func getAllKeyPaths() -> [WritableKeyPath<MyStruct, Bool>] {
return [
\MyStruct.x,
\MyStruct.y
]
}
}
But, obviously, without me having to manually declare every property in an array.
How can I achieve that?
DISCLAIMER:
Please note that the following code is for educational purpose only and it should not be used in a real application, and might contains a lot of bugs/strange behaviors if KeyPath are used this way.
Answer:
I don't know if your question is still relevant today, but the challenge was fun :)
This is actually possible using the mirroring API.
The KeyPath API currently doesn't allow us to initialize a new KeyPath from a string, but it does support dictionary "parsing".
The idea here is to build a dictionary that will describe the struct using the mirroring API, then iterate over the key to build the KeyPath array.
Swift 4.2 playground:
protocol KeyPathListable {
// require empty init as the implementation use the mirroring API, which require
// to be used on an instance. So we need to be able to create a new instance of the
// type.
init()
var _keyPathReadableFormat: [String: Any] { get }
static var allKeyPaths: [KeyPath<Foo, Any?>] { get }
}
extension KeyPathListable {
var _keyPathReadableFormat: [String: Any] {
let mirror = Mirror(reflecting: self)
var description: [String: Any] = [:]
for case let (label?, value) in mirror.children {
description[label] = value
}
return description
}
static var allKeyPaths: [KeyPath<Self, Any?>] {
var keyPaths: [KeyPath<Self, Any?>] = []
let instance = Self()
for (key, _) in instance._keyPathReadableFormat {
keyPaths.append(\Self._keyPathReadableFormat[key])
}
return keyPaths
}
}
struct Foo: KeyPathListable {
var x: Int
var y: Int
}
extension Foo {
// Custom init inside an extension to keep auto generated `init(x:, y:)`
init() {
x = 0
y = 0
}
}
let xKey = Foo.allKeyPaths[0]
let yKey = Foo.allKeyPaths[1]
var foo = Foo(x: 10, y: 20)
let x = foo[keyPath: xKey]!
let y = foo[keyPath: yKey]!
print(x)
print(y)
Note that the printed output is not always in the same order (probably because of the mirroring API, but not so sure about that).
After modifying rraphael's answer I asked about this on the Swift forums.
It is possible, discussion here:
Getting KeyPaths to members automatically using Mirror
Also, the Swift for TensorFlow team has this already built in to Swift for TensorFlow, which may make its way to pure swift:
Dynamic property iteration using key paths
I propose my solution. It has the advantage of dealing correctly with #Published values when using the Combine framework.
For the sake of clarity, it is a simplified version of what I have really. In the full version, I pass some options to the Mirror.allKeyPaths() function to change behaviour ( To enumerate structs and/or classes properties in sub-dictionaries for example ).
The first Mirror extension propose some functions to simplify properties enumeration.
The second extension implements the keyPaths dictionaries creation, replacing
#Published properties by correct name and value
The last part is the KeyPathIterable protocol, that add enumeration
capability to associated object
swift
// MARK: - Convenience extensions
extension String {
/// Returns string without first character
var byRemovingFirstCharacter: String {
guard count > 1 else { return "" }
return String(suffix(count-1))
}
}
// MARK: - Mirror convenience extension
extension Mirror {
/// Iterates through all children
static func forEachProperty(of object: Any, doClosure: (String, Any)->Void) {
for (property, value) in Mirror(reflecting: object).children where property != nil {
doClosure(property!, value)
}
}
/// Executes closure if property named 'property' is found
///
/// Returns true if property was found
#discardableResult static func withProperty(_ property: String, of object: Any, doClosure: (String, Any)->Void) -> Bool {
for (property, value) in Mirror(reflecting: object).children where property == property {
doClosure(property!, value)
return true
}
return false
}
/// Utility function to determine if a value is marked #Published
static func isValuePublished(_ value: Any) -> Bool {
let valueTypeAsString = String(describing: type(of: value))
let prefix = valueTypeAsString.prefix { $0 != "<" }
return prefix == "Published"
}
}
// MARK: - Mirror extension to return any object properties as [Property, Value] dictionary
extension Mirror {
/// Returns objects properties as a dictionary [property: value]
static func allKeyPaths(for object: Any) -> [String: Any] {
var out = [String: Any]()
Mirror.forEachProperty(of: object) { property, value in
// If value is of type Published<Some>, we transform to 'regular' property label and value
if Self.isValuePublished(value) {
Mirror.withProperty("value", of: value) { _, subValue in
out[property.byRemovingFirstCharacter] = subValue
}
} else {
out[property] = value
}
}
return out
}
}
// MARK: - KeyPathIterable protocol
protocol KeyPathIterable {
}
extension KeyPathIterable {
/// Returns all object properties
var allKeyPaths: [String: Any] {
return Mirror.allKeyPaths(for: self)
}
}

Swift Generic Protocol Function Parameters

This seems like it should work to me. All I am trying to do is make the Rule protocol able to performRule on whatever struct adopts that Rule protocol and then return a boolean. However, with the way my code is currently I cannot access any properties on the performRule(:value) value parameter. I feel like I am missing an important concept or something is buggy. You should be able to copy the code below into a playground to see the issue for yourself.
import Foundation
protocol NumberCalculation {
var number : NSNumber { get set }
}
protocol Rule {
var invalidMessage : String { get set }
func performRule<T>(value: T) -> Bool
}
struct GreaterThanRule : Rule, NumberCalculation {
var invalidMessage: String
var number : NSNumber
init(valueMustBeGreaterThan value: NSNumber, withInvalidMessage message : String = "") {
number = value
invalidMessage = message
}
func performRule<NSNumber>(value: NSNumber) -> Bool {
number.decimalValue // works
value.decimalValue // doesn't work
return true
}
}
Saying <NSNumber> defines a new generic placeholder type in your performRule(value:) method, which, as you've named it NSNumber, will shadow Foundation's NSNumber class – meaning that the value: parameter is of type your generic placeholder, not Foundation's NSNumber.
If you want it so that types conforming to Rule can choose their own type for the parameter of the performRule(value:) method – then you want an associated type, not a generic placeholder.
protocol NumberCalculation {
var number : NSNumber { get set }
}
protocol Rule {
// define associated type that conforming types must satisfy
// by providing a type to replace it with
associatedtype Value
var invalidMessage : String { get set }
func performRule(value: Value) -> Bool
}
struct GreaterThanRule : Rule, NumberCalculation {
var invalidMessage: String
var number : NSNumber
init(valueMustBeGreaterThan value: NSNumber, withInvalidMessage message : String = "") {
number = value
invalidMessage = message
}
// satisfy associated type implicitly by annotating the type of the parameter
// as NSNumber – the compiler will infer that Value == NSNumber.
func performRule(value: NSNumber) -> Bool {
number.decimalValue // works
value.decimalValue // also works!
return true
}
}