1

Please have a look at the following playground code:

import Foundation

public extension Data {

    public func to<T>(type: T.Type) -> T {
        return self.withUnsafeBytes { $0.pointee }
    }
}

let data = Data(bytes: [1, 0, 0, 0])

do {
    let type = Int32.self
    print("\(type)'s type is \(type(of: type))") // prints "Int32's type is Int32.Type"
    let value = data.to(type: type)
    print(value) // prints "1"
}

do {
    func getType() -> Any.Type { return Int32.self }

    let type = getType()
    print("\(type)'s type is \(type(of: type))") // prints "Int32's type is Int32.Type"
    let value = data.to(type: type) // error: cannot invoke 'to' with an argument list of type '(type: Any.Type)'
    print(value)
}

The first do block executes as expected. The second do block illustrates my issue. The basic idea of the second block is: I do not know the type of the data; something else is going to tell me. Is what I want to do possible? Perhaps the signatures of the getType function and/or the Data extension's to method need to be different?

Hamish
  • 69,859
  • 16
  • 167
  • 245
Verticon
  • 2,129
  • 11
  • 24
  • 1
    How would the compiler know what type you're getting at run time? How would it write code for that? – Alexander May 15 '17 at 19:36
  • Well, the compiler cannot know. And, I know that swift wants to be type safe. I also know that there is a lot of "under the hood stuff" that a savvy programmer can employ to get the job done while making the compiler happy. The Data extension's to method is a point in case (At least, I think it is pretty spiffy). – Verticon May 15 '17 at 21:55
  • 1
    That's my point, the compiler can't know. You're basically trying to implement dynamically typing within Swift. To what end? – Alexander May 15 '17 at 22:42
  • @Alexander, It seems that you are saying: "No way". Well, if that is how it is then I accept it. As for the "To what end"; my question gave it: "The basic idea ..." My actual use case would take a lot more explaining but the question captures its essence. – Verticon May 16 '17 at 17:33
  • @Hamish, I notice that you edited my question. I'd love for you to chime in. – Verticon May 16 '17 at 17:36
  • Hmmm ... http://stackoverflow.com/questions/29924477/is-swift-a-dynamic-or-static-language might be pointing the way. – Verticon May 16 '17 at 17:40
  • 1
    @Verticon I think Alexander has perfectly summed up why you can't do what you want to do – `withUnsafeBytes(_:)` uses a generic placeholder, therefore the type used to satisfy that placeholder simply must be known at compile time. In addition, for a given generic placeholder `T`, a parameter of type `T.Type` where `T` is a protocol `P`, is in fact `P.Protocol` (i.e the metatype that describes the protocol itself, not "any given metatype for something that conforms"), hence why you cannot give it an `Any.Type` – it expects `Any.Protocol`. May I ask exactly what you're trying to achieve here? – Hamish May 16 '17 at 18:27
  • @Hamish. I am creating an app that explores the local bluetooth neighborhood The result of reading a BLE characteristic's data is a Data object. The app cannot know the data's true type unless the characteristic is is from one of the SIG's predefined services. Via the UI, the user can inform the app of the data's actual type; string, double, int32, etc. The app can then present the data as something other than a series of bytes. Currently I switch on the user provided type and execute code for each case. I was looking for a more concise way of coding it. – Verticon May 16 '17 at 21:54
  • @Verticon Won't the data be encoded in some way? (It must be for `String` – you cannot just send the raw stack-bytes of a string, it has indirect storage). If that's the case, then you won't be able to directly use `withUnsafeBytes(_:)` anyway. You *probably* just want a protocol that describes a given type that can be decoded from your data received (i.e with an initialiser requirement that takes raw data) – then talk in terms of `MyProtocol.Type`, simply calling the initialiser requirement on the user-selected metatype value with the given data and getting back a `MyProtocol`-typed instance. – Hamish May 16 '17 at 22:17
  • @Hamish Thx. Yeah, for strings the app also allows the user to select the encoding. And, for ints/uints (1, 2, 4, 0r 8 byte), the endianness (BLE might specify the "on the wire" endianness, I'm confused at the moment). As for the protocol - if I understand then you mean http://stackoverflow.com/questions/38023838/round-trip-swift-number-types-to-from-data – Verticon May 17 '17 at 11:40
  • @Verticon Yes, exactly re: the protocol – so for example have a metatype of type `DataConvertible.Type` in order to represent the type the user has picked, and then call the initialiser on that metatype. Although if you also want to allow the user to select the encoding, you may either want to handle the string case separately via type-casting, or perhaps even better, use an enumeration to represent the different user-selection cases (with the string case for example having an associated value for the encoding). – Hamish May 18 '17 at 14:09
  • and you could probably use the numeric type's `init(bitPattern:)` initialisers in order to consolidate the decoding logic for numeric types (as well as handling the endianness of the bit pattern with the suitable integer big/little endian initialisers). – Hamish May 18 '17 at 14:09

0 Answers0