Wrapping a callback type with Swift protocols - swift

Let's say I'm dealing with a library I don't control and I'm trying to wrap a class that defines a callback type to decouple my code for testing purposes. Here's the class, inside module AXSwift:
public class Application {
public typealias Callback = (element: UIElement) -> ()
public func createObserver(callback: Callback) -> Observer? {
// ...
}
}
Here's the wrapper protocol for testing:
protocol UIElementProtocol {}
extension AXSwift.UIElement: UIElementProtocol {}
protocol ApplicationProtocol {
func createObserver(callback: (element: UIElementProtocol) -> ()) -> Observer?
}
extension AXSwift.Application: ApplicationProtocol {}
I get Type 'Application' does not conform to protocol 'ApplicationProtocol'. If I change UIElementProtocol inside the ApplicationProtocol callback back to UIElement, it works. But UIElement conforms to UIElementProtocol, so why doesn't this work?
Second question: Is there a better way to design the library API to allow for this sort of thing?

The solution was really simple, just implement the protocol in the extension with a method that calls the original method:
extension AXSwift.Application: ApplicationProtocol {
func createObserver(callback: (element: UIElementProtocol) -> ()) -> Observer? {
return createObserver(callback as Callback)
}
}
Where Callback is the expected type. Apparently you have to be explicit about passing a general function type as a more specific type.

Related

Create interface with generic types

I'm trying to create public interface for my class operating on generics.
I've created interface
public protocol StorageProtocol {
associatedtype StorageObject: Codable
func store(storeObject: StorageObject)
func get() -> StorageObject?
}
and implementation
public class Storage<T: Codable>: StorageProtocol {
public func store(storeObject: T) {}
public func get() -> T?
Now, when I try to create instance it forces me any keyword
let myStorage: any StorageProtocol = Storage<Credentials>()
and I can't call storage.store(storeObject: Credentials()) as I'm getting error:
Associated type 'StorageObject' can only be used with a concrete type or generic parameter base.
What I'm missing here?
The compiler does not know what type to require in the parameter storeObject when you constrain myStorage to be any StoreageProtocol, as the generic StorageObject could be any Codable object. A workaround could be to explicitly add a parameter for the type of the generic.
public protocol StorageProtocol<StorageObject> { // add a generic parameter
associatedtype StorageObject: Codable
func store(storeObject: StorageObject)
func get() -> StorageObject?
}
and then when you define the variable
let myStorage: any StorageProtocol<Credentials> = Storage<Credentials>()
Then the compiler will know you want to store Credentials for this variable and allow you to call store as you specialized the protocol to the correct type.

How can i make protocol implementation as argument of function

How do i create Protocol as an argument of function.
In Android java, we can make an argument of interface like this.
Button.setOnClickListener(new View.OnClicklistener()
{
#Override
public void onClick(View v)
{
}
});
But how do i do same thing in Swift.
For example, i have following Protocol, how can i pass protocol while constructing it.
protocol ButtonListener
{
func onClick()
}
It seems that there is a misunderstanding for what you should use to achieve it. Probably you want to pass a closure as a parameter to the function.
Citing from the Swift programming language - Closures:
Closures are self-contained blocks of functionality that can be passed
around and used in your code. Closures in Swift are similar to blocks
in C and Objective-C and to lambdas in other programming languages.
Example:
protocol Foo {
func myFunc(onClick: (_ myView: UIView) -> Void)
}
class MyClass: Foo {
func myFunc(onClick: (UIView) -> Void) {
// ...
}
}
Here we have a Foo protocol contains myFunc which has a closure parameter of type (UIView) -> Void.
Therefore:
let object = MyClass()
object.myFunc { view in
// you can access `view` here as:
view.backgroundColor = ...
view.frame = ...
}
In Swift, these single-method protocols, as event handlers are not necessary. In Swift, you can create any kind of closure type you want.
Your onClick method can be represented by the closure type () -> Void (accepts no parameters and returns Void). Instead of setOnClickListener as a method, you can just declare onClick as a property:
var onClick: (() -> Void)?
And then you can set it just like you would with any other property:
onClick = {
print("I am clicked!")
}
To call onClick, you can unwrap it first to make sure it's not nil:
if let onClick = self.onClick {
onClick()
}
// or
onClick?()
If you are familiar Java 8, closure types are kind of like functional interfaces, except you get more freedom with closure types. Consumer<String> would correspond to (String) -> Void, Function<String, Integer> would correspond to (String) -> Int, etc.
You can give it by protocol.
protocol ButtonListener {}
extension ButtonListener where Self: UIView {
func btnClick() {
// print func
}
}

Why doesn't this behave the way I expect (hope) it would?

I have a several protocols set up in my framework to deal with resources. In one of the protocols, I have set up an extension to provide a default implementation for a decode function. It's simpler to show the code and what happens (see calls to fatalError). There's a lot more code in the actual implementation, but this illustrates the issue:
This is the "base" protocol:
public protocol Resourceful {
associatedtype AssociatedResource
typealias ResourceCompletionHandler = (AssociatedResource?, Error?) -> Void
func fetch(_ completion: #escaping ResourceCompletionHandler)
}
This is a generic, concrete implementaion of Resourceful:
open class WebResourceApiCall<Resource>: Resourceful {
public typealias AssociatedResource = Resource
public typealias FetchedResponse = (data: Data?, urlResponse: URLResponse?)
public init() {
}
public func fetch(_ completion: #escaping ResourceCompletionHandler) {
try! decode(fetched: (data: nil, urlResponse: nil))
}
public func decode(fetched: FetchedResponse) throws -> Resource {
fatalError("It ends up here, but I don't want it to!")
}
}
extension WebResourceApiCall where Resource: Decodable {
public func decode(fetched: FetchedResponse) throws -> Resource {
fatalError("This is where I want it to go...")
}
}
This is how I'm attempting to use it:
public struct Something: Decodable { }
var apiCall = WebResourceApiCall<Something>()
apiCall.fetch { _, _ in } // Implictly calls decode... but not the decode I expected it to! See fatalError() calls...
Instead of calling decode on the extension, like I hoped it would, the "default" decode method with no constraints is always called.
Why doesn't this work the way I expect it to?
Thanks in advance!
Swift is a statically dispatched language, thus the address of the decode() function to be called is computed at compile time, and because the call happens inside the base definition of the class, the compiler picks the original implementation.
Now, if you call the method from a place where the compiler has enough information to pick the implementation you need, it will work:
var apiCall = WebResourceApiCall<Something>()
try apiCall.decode(fetched: (nil, nil))
The above code will call the method from the specialized extension, as at this point the compiler is a better position to know that it has a more specialized implementation to call.
It should be possible to achieve the behaviour you need if you move the decode() method in the dynamic dispatch world - i.e. at the protocol level.

Swift 3: Inheritance from non-named type

I have the following SSCIE:
protocol Foo {
associatedtype Bar
associatedtype Baz: (Self.Bar) -> Void
var currentValue: Bar { get }
}
That I want to use like this:
func call<T: Foo>(foo: T, callback: #escaping T.Baz) {
DispatchQueue.main.async {
callback(foo.currentValue)
}
}
But it fails to compile, with the error:
Inheritance from non-named type '(`Self`.Bar)'
This also fails to compile when I use (Bar) -> Void and (Foo.Bar) -> Void.
Sadly, Googling this didn't come up with any useful results.
Does anyone have any idea what this error means, what I'm doing wrong, and how to correct it?
Associated types in Swift 3 can only be "is-a"-constrained. So your Bar is required to be an Any. Which, by the way, is not much of a constraint ;). In other words, you can remove it.
However, (Self.Bar) -> Void is a function type and you can't constrain an associated type like this.
If you want to define a callback type, you can use a typealias:
protocol Foo
{
associatedtype Bar
typealias Callback = (Self.Bar) -> Void
var currentValue: Bar { get }
func f(callback: Callback) -> Void
}
Using #escaping does not currently work in a typealias (see SR-2316 and its various duplicates). This is a bug that was supposed to have a fix soon (as of August 2016). So you will have to spell it out for now:
func call<T: Foo>(foo: T, callback: #escaping (T.Bar) -> Void) {
DispatchQueue.main.async {
callback(foo.currentValue)
}
}
Update: As Hamish suggested, I filed SR-4967. I'll update this post as soon as there is any news about it.
As already mentioned, function types can't be used as associated types.
Try this instead:
func call<T: Foo>(foo: T, callback: #escaping (T.Bar) -> Void) {
...
}
and using this design you can mix-and-match function types (for the callback arg) for each specific helper function (call in your example) you come up with.

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)