Unable to call a function with a Codable Generic Parameter - swift

I have a class that takes a generic parameter T class CacheRepository<T> {}that contains a func transform(). In which I need to call another function with the following signature func transformCodable<U: Codable>(ofType: U.Type)-- if T conforms to Codable protocol. But I failed to do so. Below are my trials with their compilation errors.
Trial 1:
func transform() {
if T.self is Codable.Type {
// Error: In argument type 'CacheRepository<T>.T.Type' (aka 'T.Type'), 'T' does not conform to expected type 'Decodable'
sharedStorage!.transformCodable(ofType: T.self)
}
}
Trial 2:
func transform() {
if T.self is Codable.Type {
//Cannot invoke 'transformCodable' with an argument list of type '(ofType: (Codable))'
//Expected an argument list of type '(ofType: U.Type)'
sharedStorage!.transformCodable(ofType: (T as! Codable).self)
}
}
Trial 3: I tried to extend CacheRepository class if T is Codable, but the extension transform function never gets called, when called in the init() function. And if I called it after the instance is created, it doesn't get called in all cases.
class CacheRepository<T> {
func transform() {
print("non-codable transform")
}
}
extension CacheRepository where T: Codable {
func transform() {
sharedStorage!.transformCodable(ofType: T.self)
}
}
example: This works
let transform = CacheRepository<Int>().transform()
But if I added a shared instance inside the CacheRepository class, and tried to call transform after instantiation, it doesn't get called. And the really odd this is, if I called the tranform on the new instance in console the right transform is called.

How is this?
extension CacheRepository where T: Codable {
func transform() {
print("Codable")
sharedStorage!.transformCodable(ofType: T.self)
}
}
extension CacheRepository {
func transform() {
print("Non-codable")
}
}
This worked as expected in a very simplified CacheRepository. But not sure if it works in your actual CacheRepository, please try.

Related

Is it possible use KVO with generic types in swift4

My method receive a generic type in parameters which associated with protocol. Inside this method I use additional NSObject class to wrap/hide generic type because KVO only works with ObjC classes.
An example is given below:
protocol OutputData: NSObjectProtocol {
associatedtype Output
var dataContainer: DataContainer<Output>? { get set }
}
class DataContainer<O>: NSObject {
var outputData: O?
}
func injectResult<O: OutputData>(from: O) {
let dataCont = from.dataContainer
observer = dataCont?.observe(\.outputData) { (object, change) in
let data = object.outputData as! Data
self.inputData = data
}
}
In this situation I don't see any compile time errors but if I try to run this code I get the next exception:
Could not extract a String from KeyPath
Swift.ReferenceWritableKeyPath<Concurrency.DataContainer<Foundation.Data>,
Swift.Optional<Foundation.Data>>:
file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-900.0.65.2/src/swift/stdlib/public/SDK/Foundation/NSObject.swift,
line 85
Any help will be appreciated.

How to call a static method on a base class from a generic class<T>?

I'm trying to call a static method in a base class from a generic subclass. See simplified playground code below.
Calling the non-static 'dump' function works.
The similar static call fails. Various attempts to typecast the array also fail.
In the full production code, "ClassA" is in a 3rd-party module and can't be changed. It can be further subclassed and extended.
Does Swift offer some magical typecast to have ClassT call ClassA.dump() directly?
class ClassT<T> {
var dict=[String:T]()
func add(key:String, obj:T) {
dict[key]=obj
let arr=Array(dict.values)
dump(arr) // works -> but not as expected, see comment below !!!
ClassA.dump(arr) // error: cannot convert value of type 'Array<T>' to expected argument type '[ClassA]'
ClassA.dump(arr as! [ClassA]) // error: cannot convert value of type 'Array<T>' to type '[ClassA]' in coercion
ClassA.dump(arr as! [AnyObject]) // error: 'AnyObject' is not a subtype of 'T'
ClassA.dump(arr as! [Any]) // error: 'Any' is not a subtype of 'T'
}
}
class ClassA {
func dump(arr:[ClassA]) {
ClassA.dump(arr)
}
static func dump(arr:[ClassA]) {
print(arr)
}
}
class ClassB:ClassA {
static let o=ClassT<ClassA>()
func test() {
ClassB.o.add("Elem1", obj:self)
}
}
You have to add a constraint to specify that T derives from ClassA.
class ClassT<T: ClassA> {
var dict = [String : T]()
func add(key: String, obj: T) {
dict[key] = obj
let arr = Array(dict.values) //probably unecessary
dump(arr) // works
ClassA.dump(arr)
}
//...
Without it, the compiler has no way to enforce that all conforming types T will be castable to ClassA.

swift inconsistent generic protocol restriction

I seem to be running into what appears to be a compiler inconsistency when passing arguments to a generic function that has a protocol restriction. I can pass a concrete argument, but not an argument as a protocol type
protocol Selectable {
func select()
}
protocol Log : Selectable {
func write()
}
class DefaultLog : Log {
func select() {
print("selecting")
}
func write() {
print("writing")
}
}
let concrete = DefaultLog()
let proto: Log = DefaultLog()
func myfunc<T: Selectable>(arg: T) {
arg.select()
}
myfunc(concrete) // <-- This works
myfunc(proto) // <-- This causes a compiler error
proto.write() // <-- This works fine
The compiler reports:
error: cannot invoke 'myfunc' with an argument list of type '(Log)'
myfunc(proto)
^
note: expected an argument list of type '(T)'
myfunc(proto)
^
If I restrict the function to the Selectable or Log protocol it still fails.
Is this a compiler bug? Any thoughts?
If you are using a protocol, it doesn't need to be generic:
func myfunc(arg: Selectable) {
arg.select()
}
I believe that T needs to be a concrete type for generics.

Swift: Generic Protocols

I have some swift structs for which protocol compliance is generated with individual extensions with equal methods names which just differ in their return types which are struct dependent. On top of That I want to use them in a generic function which Calls a protocol conforming function for a generic type).
I tried to accomplish this like that:
//: Playground - noun: a place where people can play
import UIKit
protocol FooProt {
typealias T;
static func createMe<T>()->T;
}
struct FooStruct{
}
extension FooStruct: FooProt{
typealias T = FooStruct;
static func createMe () -> FooStruct{
return FooStruct();
}
}
class Creator{
fun createOne<T where T:FooProt>(type:T.Type){
let instance = T.createMe();
}
}
Unfortunately I get the following error :
/var/folders/sn/78_zvfd15d74dzn01mdv258h0000gq/T/./lldb/3741/playground6.swift:7 :17: note: protocol requires function 'createMe()' with type ' () -> T' (aka '<τ_1_0> () -> τ_1_0')
static func createMe()->T;
What exactly doesn't comply here and is there a workaround ?
There are several problems with your code. On the one hand you have defined a protocol with an associated type. However, you define your createMe() method as a generic which uses some other type. I don't think that was your intent. I think your intent was to have a createMe() method that returns the same type as the protocol's associated type. In this case you need to remove the from the createMe() method. Also, the name createMe() implies that you aren't just returning any type, but the type of the object on which this method is being called. In this case, you don't even need an associated type protocol. You just need a protocol with a Self constraint which allows your code to be a bit simpler. In your Creator's createOne method, your type constraint is more complex than needed.
I think you want the following code:
protocol FooProt {
static func createMe()->Self;
}
struct FooStruct{
}
extension FooStruct: FooProt {
static func createMe() -> FooStruct {
return FooStruct();
}
}
class Creator{
func createOne<T:FooProt>(type: T.Type) -> T {
return T.createMe()
}
}
let foo = Creator().createOne(FooStruct.self)
Here is an alternate solution using an initializer in the protocol instead of a static method.
protocol FooProt {
init()
}
struct FooStruct{
}
extension FooStruct: FooProt {
}
class Creator{
func createOne<T:FooProt>(type: T.Type) -> T {
return T.init()
}
}
let foo = Creator().createOne(FooStruct.self)

Call Class Methods From Protocol As Parameter

I want to be able to pass a class (not an initialized object) of a certain protocol type to a method, then call the class functions of that class in the method. Code below.
I am using Swift and have an protocol defined like this
//Protocol for any object to be used with an FAUAPIConnection
protocol FAUAPIModel{
//Used to parse the object from a given dictionary to an object
class func parseFromJSON(JSON:AnyObject) -> Self
//Required default init
init()
}
What I would like to do is have a method like this
func getSomeParsingDone<T:FAUAPIModel>(model:T.Type? = nil, getPath:path, callingObj:CallingClass) -> Void
{
//GetIt is inconsequential, just logic to get an object from a certain path
var returnObj:AnyObject = GetIt.get(path)
if(model != nil){
returnObj = model!.parseFromJSON() <<<<<< Type 'T' does not conform to protocol 'AnyObject'
}
callingObj.done(returnObj)
}
Object that implements the protocol
import Foundation
class MyObj: FAUAPIModel{
var neededVal:String
var nonneededVal:String
required convenience init(){
self.init(neededVal:"VALUE")
}
init(neededVal:String, nonneededVal:String = ""){
self.neededVal = neededVal
self.nonneededVal = nonneededVal
}
class func parseFromJSON(JSON:AnyObject) -> WGMPart
{
return WGMPart() <<<<<<<< Method 'parseFromJSON' in non-final class 'WGMPart' must return 'Self' to conform to protocol 'FAUAPIModel'
}
}
However, I keep getting two errors. I have indicated these above with '<<<<<<<<<<<<'
compile error.
Lots of little things to consider here, but let's get to the heart of your question. The signature you want looks like this:
func getSomeParsingDone<T:FAUAPIModel>(model:T.Type, path:String) -> T?
I'm making the return optional beause there are a lot of things that could fail here, and you really shouldn't turn all of those into crashes.
I'd recommend your protocol look like this:
protocol FAUAPIModel {
class func parseFromJSON(JSON:AnyObject) -> Self
}
That way, you're promising that your return your own class, not just anything that is parseable. That does tend to mean that you need to make your classes final. If you don't want them to be final, you'll need to promise some init method in order to construct it. See Protocol func returning Self for more details on how to deal with that if you need it.
So putting it together, it might look something like this in practice:
protocol FAUAPIModel {
class func parseFromJSON(JSON:AnyObject) -> Self
}
func createObjectOfClass<T: FAUAPIModel>(model: T.Type, path: String) -> T? {
if let json: AnyObject = GetJSON(path) {
return model.parseFromJSON(json)
}
return nil
}
// Bogus JSON reader
func GetJSON(path: String) -> AnyObject? {
let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(path.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!, options: NSJSONReadingOptions(0), error: nil)
return json
}
// Bogus model class that returns trivial version of itself
final class Something: FAUAPIModel {
class func parseFromJSON(JSON:AnyObject) -> Something {
return Something()
}
}
// Using it
let something = createObjectOfClass(Something.self, "/path/to/file")
I just want to note that the answer to your exact question would be to declare your function like this:
func getSomeParsingDone(model:FAUAPIModel.Type? = nil, getPath:path) -> FAUAPIModel