Make array type from type - swift

I am getting a lot of data from my server.
To serialize it I use ModelMapper. I have lots of Mappable objects, so I need a function that would be able to map any kind of mappable data. Something like this:
func serializeData(of type: Mappable.Type) -> [Mappable]? {
return try? response?.map(to: [type].self)
}
My issue is that map(:) method requires [Mappable].Type as input. [type].self however is [Mappable.Type]. I am getting lost here. Please help

Passing the type as foo.Type is a very objective-c-ish pattern.
In Swift I'd prefer a generic solution, something like
func serializeData<T : Mappable>() -> [T]? {
return try? response?.map(to: [T].self)
}
or still swiftier
func serializeData<T : Mappable>() throws -> [T] {
return try response?.map(to: [T].self) ?? []
}

Related

Can I use a function with generic type inside a protocol extension of that same type?

I have a protocol :
protocol Repository {
associatedtype Element
func getAll() -> Single<[Element]>
}
And a function using this protocol in a generic way :
class Client {
static func fetchCollection<T: Repository>(of params: T) -> Single<[T.Element]> {
[...]
return sendRequest(of: params)
.flatMap({ (response: HTTPURLResponse, jsonData: Data) -> Single<[T.Element]> in
[...]
})
}
}
It works just fine and is really convenient. The getAll() method I need looks like this in 2 examples :
class TypeARepository: Repository {
typealias Element = TypeA
func getAll() -> Single<[Element]> {
return Client.fetchCollection(of: self)
}
}
class TypeBRepository: Repository {
typealias Element = TypeB
func getAll() -> Single<[Element]> {
return Client.fetchCollection(of: self)
}
}
It works, but it's repetitive.
I would like to put the getAll() function inside a protocol extension because I have many objects implementing Repository and it would be cleaner to write it once.
But it does not work and I cannot find a way to fix it :
extension Repository {
func getAll() -> Single<[Element]> {
return Client.fetchCollection(of: self)
}
}
Type of expression is ambiguous without more context
Here is the error (with my names, I simplified them above) :
Any ideas why ? And can it be fixed ?!
but it's repetitive
I don't think you can do anything about that. They are actually completely different methods because the generic placeholder is resolved differently in each case. Thus you cannot "inject" any common implementation.
Thanks for the help
Working through gists helped me find a solution, it's a good way to work I'm happy to have learned that.
https://gist.github.com/Cocatrix/8660a34b28ccac77a9073b204b2ce933
Actually I simplified my code because the Element property itself had an associated property.
The only missing thing to compile was because I wrote Single<Element> instead of Single<Element.Parsable> in the original question.
The error displayed was not clear and I thought it was something similar to what matt discussed https://stackoverflow.com/a/68578593/7490668
It's resolved.

How to make a function that returns a decodable type in Swift?

So I have this enum that I use for the few url requests I use in my app :
enum Netwrok {
case popular
case topRated
case latest
// ...
static let baseUrl = "http://..."
func path() -> String {
switch self {
case .popular: return "/popular"
// ...
}
}
}
And I would like to add a function that returns the Decodable Type of model the network stack should decode the data with.
So I thought something like that would do the job :
func returnType<T>() -> T.Type where T : Decodable {
switch self {
case .popular:
return Popular.self
// ...
}
}
But I can't make it work, it says :
Cannot convert return expression of type 'Popular.Type' to return type 'T.Type'
Asking me to force cast in T.Type.
How can I make a function that returns the decodable so that type can be handled but the JSONDecoder's decode function ?
Thanks.
What you're asking is straightforward, but it probably isn't what you want. What you're asking to do is to return a type. There's nothing generic about that.
func returnType<T>() -> T.Type where T : Decodable {
This syntax defines a type parameter, T, that is passed by the caller. It's not defined by your function. That means the caller may pass any type that is Decodable and your function will return it. For example, the caller can set T to be Int (since that's Decodable), and you will return Int.Type. That's easy to implement (return T.self), but not what you mean.
What you mean is that the function returns some type that is Decodable that the function knows, but the caller doesn't:
func returnType() -> Decodable.Type { ... }
This will work fine, and do exactly what you are asking for, but it suggests you're probably building this network stack incorrectly and will have headaches later.
The reason this approach is likely to be a problem is that you probably want to write a line of code like this:
let result = JSONDecoder().decode(networkType.returnType(), from: data)
That's going to break, because Decodable.Type is not itself a Decodable type. (You you decode Int, but you can't decode the type of Int.) Say it did work. What type would result be? What could you do with it? The only thing you'd know about it is that it's Decodable (and you've already decoded it).
You likely want something more like Vasu Chand's implementation, or the similar approach discussed in my blog series.
You can use escaping closure for your returning result of an API Call.
Assuming you are hitting a get request . A simple working example for passing Codable model for get request api.
class func GETRequest<ResponseType :Decodable>(url : URL,responseType : ResponseType.Type ,completion: #escaping (ResponseType? ,Error? ) -> Void){
var request = URLRequest(url: url)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else{
completion(nil,error)
return
}
let decoder = JSONDecoder()
do{
let responseData = try decoder.decode(ResponseType.self, from: data)
completion(responseData, nil)
}
catch let error{
completion(nil, error)
}
}
task.resume()
}
How to call this network function.
Network.GETRequest(url: url, responseType: Model.self) { (model, error) in
completion(model,error)
}
Model class contains
struct Model : Codable{
}
You can pass any response model for any get request to network class .
Similarly you can build api network for post request where request body is simply Codable model .
For sorry you can't as according to your need the supply for the first parameter here
JSONDecoder().decode(AdecodableType.self,from:data)
need to be inferred right when you write the code so it can't be Any 1 from a collection of types that conform to Decodable

enums with Associated Values + generics + protocol with associatedtype

I'm trying to make my API Service as generic as possible:
API Service Class
class ApiService {
func send<T>(request: RestRequest) -> T {
return request.parse()
}
}
So that the compiler can infer the response type from the request categories .auth and .data:
let apiService = ApiService()
// String
let stringResponse = apiService.send(request: .auth(.signupWithFacebook(token: "9999999999999")))
// Int
let intResponse = apiService.send(request: .data(.content(id: "123")))
I tried to come up with a solution using generics and a protocol with associated type to handle the parsing in a clean way. However I'm having trouble associating the request cases with the different response types in a way that it's simple and type-safe:
protocol Parseable {
associatedtype ResponseType
func parse() -> ResponseType
}
Endpoints
enum RestRequest {
case auth(_ request: AuthRequest)
case data(_ request: DataRequest)
// COMPILER ERROR HERE: Generic parameter 'T' is not used in function signature
func parse<T: Parseable>() -> T.ResponseType {
switch self {
case .auth(let request): return (request as T).parse()
case .data(let request): return (request as T).parse()
}
}
enum AuthRequest: Parseable {
case login(email: String, password: String)
case signupWithFacebook(token: String)
typealias ResponseType = String
func parse() -> ResponseType {
return "String!!!"
}
}
enum DataRequest: Parseable {
case content(id: String?)
case package(id: String?)
typealias ResponseType = Int
func parse() -> ResponseType {
return 16
}
}
}
How is T not used in function signature even though I'm using T.ResponseType as function return?
Is there a better still clean way to achieve this?
I'm trying to make my API Service as generic as possible:
First, and most importantly, this should never be a goal. Instead, you should start with use cases, and make sure that your API Service meets them. "As generic as possible" doesn't mean anything, and only will get you into type nightmares as you add "generic features" to things, which is not the same thing as being generally useful to many use cases. What callers require this flexibility? Start with the callers, and the protocols will follow.
func send<T>(request: RestRequest) -> T
Next, this is a very bad signature. You don't want type inference on return types. It's a nightmare to manage. Instead, the standard way to do this in Swift is:
func send<ResultType>(request: RestRequest, returning: ResultType.type) -> ResultType
By passing the expected result type as a parameter, you get rid of the type inference headaches. The headache looks like this:
let stringResponse = apiService.send(request: .auth(.signupWithFacebook(token: "9999999999999")))
How is the compiler to know that stringResponse is supposed to be a String? Nothing here says "String." So instead you have to do this:
let stringResponse: String = ...
And that's very ugly Swift. Instead you probably want (but not really):
let stringResponse = apiService.send(request: .auth(.signupWithFacebook(token: "9999999999999")),
returning: String.self)
"But not really" because there's no way to implement this well. How can send know how to translate "whatever response I get" into "an unknown type that happens to be called String?" What would that do?
protocol Parseable {
associatedtype ResponseType
func parse() -> ResponseType
}
This PAT (protocol w/ associated type) doesn't really make sense. It says something is parseable if an instance of it can return a ResponseType. But that would be a parser not "something that can be parsed."
For something that can be parsed, you want an init that can take some input and create itself. The best for that is Codable usually, but you could make your own, such as:
protocol Parseable {
init(parsing data: Data) throws
}
But I'd lean towards Codable, or just passing the parsing function (see below).
enum RestRequest {}
This is probably a bad use of enum, especially if what you're looking for is general usability. Every new RestRequest will require updating parse, which is the wrong place for this kind of code. Enums make it easy to add new "things that all instances implement" but hard to add "new kinds of instances." Structs (+ protocols) are the opposite. They make it easy to add new kinds of the protocol, but hard to add new protocol requirements. Requests, especially in a generic system, are the latter kind. You want to add new requests all the time. Enums make that hard.
Is there a better still clean way to achieve this?
It depends on what "this" is. What does your calling code look like? Where does your current system create code duplication that you want to eliminate? What are your use cases? There is no such thing as "as generic as possible." There are just systems that can adapt to use cases along axes they were prepared to handle. Different configuration axes lead to different kinds of polymorphism, and have different trade-offs.
What do you want your calling code to look like?
Just to provide an example of what this might look like, though, it'd be something like this.
final class ApiService {
let urlSession: URLSession
init(urlSession: URLSession = .shared) {
self.urlSession = urlSession
}
func send<Response: Decodable>(request: URLRequest,
returning: Response.Type,
completion: #escaping (Response?) -> Void) {
urlSession.dataTask(with: request) { (data, response, error) in
if let error = error {
// Log your error
completion(nil)
return
}
if let data = data {
let result = try? JSONDecoder().decode(Response.self, from: data)
// Probably check for nil here and log an error
completion(result)
return
}
// Probably log an error
completion(nil)
}
}
}
This is very generic, and can apply to numerous kinds of use cases (though this particular form is very primitive). You may find it doesn't apply to all your use cases, so you'd begin to expand on it. For example, maybe you don't like using Decodable here. You want a more generic parser. That's fine, make the parser configurable:
func send<Response>(request: URLRequest,
returning: Response.Type,
parsedBy: #escaping (Data) -> Response?,
completion: #escaping (Response?) -> Void) {
urlSession.dataTask(with: request) { (data, response, error) in
if let error = error {
// Log your error
completion(nil)
return
}
if let data = data {
let result = parsedBy(data)
// Probably check for nil here and log an error
completion(result)
return
}
// Probably log an error
completion(nil)
}
}
Maybe you want both approaches. That's fine, build one on top of the other:
func send<Response: Decodable>(request: URLRequest,
returning: Response.Type,
completion: #escaping (Response?) -> Void) {
send(request: request,
returning: returning,
parsedBy: { try? JSONDecoder().decode(Response.self, from: $0) },
completion: completion)
}
If you're looking for even more on this topic, you may be interested in "Beyond Crusty" which includes a worked-out example of tying together parsers of the kind you're discussing. It's a bit dated, and Swift protocols are more powerful now, but the basic message is unchanged and the foundation of things like parsedBy in this example.

In Swift 4, how can you get the string-representation of a data type stored in a variable of type 'Any'?

What is the easiest way to get the string-representation of a value's data type if that value is stored in an 'Any' variable?
For instance, I'm debugging code that has this...
extension SomeClass : Mappable{
static func map(value:Any) -> SomeClass{
return Parse(value)
}
}
I'm trying to figure out what data types are being passed through the function, but if I use type(of:) I keep getting 'Any' and not the value held in it.
extension SomeClass : Mappable{
static func map(value:Any) -> SomeClass{
let nameOfType = ??? <-- This is what I'm trying to figure out
log(nameOfType)
return Parse(value)
}
}
I simply want to print the data type to the debug window, not do testing with is or as, etc. It's strictly for logging/debugging reasons.
Ok, I figured it out. It's a two-step process.
You have to:
Use type(of:) to get the type of the variable (as others have described)
Use String(describing:) to get the name of that type (that was the missing piece)
Here's an example...
let typeName = String(describing: type(of:value))
That's what I was after. Thanks for the other answers. Hope this helps!
static func map(value:AnyObject) -> AnyClass{
return value.classForCoder
}
Or
static func map(value:Any) -> AnyClass{
return (value as AnyObject).classForCoder
}
In Swift 4 you can achieve that like this:
static func map(value: Any) -> Any.Type {
return type(of: value)
}

Swift: Protocol Based Type Construction

I'm trying to create a protocol in Swift I can use for object construction. The problem I'm running into is that I need to store the type information so the type can be constructed later and returned in a callback. I can't seem to find a way to store it without either crashing the compiler or creating build errors. Here's the basics (a contrived, but working example):
protocol Model {
init(values: [String])
func printValues()
}
struct Request<T:Model> {
let returnType:T.Type
let callback:T -> ()
}
We have a simple protocol that declares a init (for construction) and another func printValues() (for testing). We also define a struct we can use to store the type information and a callback to return the new type when its constructed.
Next we create a constructor:
class Constructor {
var callbacks: [Request<Model>] = []
func construct<T:Model>(type:T.Type, callback: T -> ()) {
callback(type(values: ["value1", "value2"]))
}
func queueRequest<T:Model>(request: Request<T>) {
callbacks.append(request)
}
func next() {
if let request = callbacks.first {
let model = request.returnType(values: ["value1", "value2"])
request.callback(model)
}
}
}
A couple things to note: This causes a compiler crash. It can't figure this out for some reason. The problem appears to be var callbacks: [Request<Model>] = []. If I comment out everything else, the compiler still crashes. Commenting out the var callbacks and the compiler stops crashing.
Also, the func construct works fine. But it doesn't store the type information so it's not so useful to me. I put in there for demonstration.
I found I could prevent the compiler from crashing if I remove the protocol requirement from the Request struct: struct Request<T>. In this case everything works and compiles but I still need to comment out let model = request.returnType(values: ["value1", "value2"]) in func next(). That is also causing a compiler crash.
Here's a usage example:
func construct() {
let constructor = Constructor()
let request = Request(returnType: TypeA.self) { req in req.printValues() }
//This works fine
constructor.construct(TypeA.self) { a in
a.printValues()
}
//This is what I want
constructor.queueRequest(request)
constructor.next() //The callback in the request object should be called and the values should print
}
Does anyone know how I can store type information restricted to a specific protocol to the type can later be constructed dynamically and returned in a callback?
If you want the exact same behavior of next I would suggest to do this:
class Constructor {
// store closures
var callbacks: [[String] -> ()] = []
func construct<T:Model>(type:T.Type, callback: T -> ()) {
callback(type(values: ["value1", "value2"]))
}
func queueRequest<T:Model>(request: Request<T>) {
// some code from the next function so you don't need to store the generic type itself
// **EDIT** changed closure to type [String] -> () in order to call it with different values
callbacks.append({ values in
let model = request.returnType(values: values)
request.callback(model)
})
}
func next(values: [String]) {
callbacks.first?(values)
}
}
Now you can call next with your values. Hopefully this works for you.
EDIT: Made some changes to the closure type and the next function
Unfortunately there is no way to save specific generic types in an array and dynamically call their methods because Swift is a static typed language (and Array has to have unambiguous types).
But hopefully we can express something like this in the future like so:
var callbacks: [Request<T: Model>] = []
Where T could be anything but has to conform to Model for example.
Your queueRequest method shouldn't have to know the generic type the Request it's being passed. Since callbacks is an array of Request<Model> types, the method just needs to know that the request being queued is of the type Request<Model>. It doesn't matter what the generic type is.
This code builds for me in a Playground:
class Constructor {
var callbacks: [Request<Model>] = []
func construct<T:Model>(type:T.Type, callback: T -> ()) {
callback(type(values: ["value1", "value2"]))
}
func queueRequest(request: Request<Model>) {
callbacks.append(request)
}
func next() {
if let request = callbacks.first {
let model = request.returnType(values: ["value1", "value2"])
request.callback(model)
}
}
}
So I found an answer that seems to do exactly what I want. I haven't confirmed this works yet in live code, but it does compile without any errors. Turns out, I needed to add one more level of redirection:
I create another protocol explicitly for object construction:
protocol ModelConstructor {
func constructWith(values:[String])
}
In my Request struct, I conform to this protocol:
struct Request<T:Model> : ModelConstructor {
let returnType:T.Type
let callback:T -> ()
func constructWith(values:[String]) {
let model = returnType(values: values)
callback(model)
}
}
Notice the actual construction is moved into the Request struct. Technically, the Constructor is no longer constructing, but for now I leave its name alone. I can now store the Request struct as ModelConstructor and correctly queue Requests:
class Constructor {
var callbacks: [ModelConstructor] = []
func queueRequest(request: Request<Model>) {
queueRequest(request)
}
func queueRequest(request: ModelConstructor) {
callbacks.append(request)
}
func next() {
if let request = callbacks.first {
request.constructWith(["value1", "value2"])
callbacks.removeAtIndex(0)
}
}
}
Note something special here: I can now successfully "queue" (or store in an array) Request<Model>, but I must do so indirectly by calling queueRequest(request: ModelConstructor). In this case, I'm overloading but that's not necessary. What matters here is that if I try to call callbacks.append(request) in the queueRequest(request: Request<Model>) function, the Swift compiler crashes. Apparently we need to hold the compiler's hand here a little so it can understand what exactly we want.
What I've found is that you cannot separate Type information from Type Construction. It needs to be all in the same place (in this case it's the Request struct). But so long as you keep construction coupled with the Type information, you're free to delay/store the construction until you have the information you need to actually construct the object.