AnyObject doesn't recognize proper functions when called - ios

I have a protocol StateMachineDelegate, a class DataSource that conforms to it, and a class StateMachine that has a delegate with such protocol
Both classes implement a function found on the protocol so that if the class has a delegate, let them handle the functions; otherwise the class handles it itself.
StateMachine contains a function like this:
func target() -> AnyObject {
return delegate ?? self
}
My full code goes like this:
import Foundation
#objc protocol StateMachineDelegate {
optional func stateWillChange()
optional func stateDidChange()
optional func missingTransitionFromState(fromState: String?, toState: String?) -> String
}
class StateMachine {
var delegate: StateMachineDelegate?
func target() -> AnyObject {
return delegate ?? self
}
func missingTransitionFromState(fromState: String, toState: String) -> String {
return "Hello"
}
}
class DataSource: StateMachineDelegate {
func missingTransitionFromState(fromState: String?, toState: String?) -> String {
return "Hi"
}
}
When I was running some tests in playground and the StateMachine instance did not possess a delegate the target function returned the same instance as AnyObject. But once I called missingTransitionFromState from target it crashed so I change it to missingTransitionFromState?() with returned nil
Last function line should have returned "Hello"
Once delegate was given the target returned the delegateObject and proceeded to run the function as normal
The playground test are these:

All of your calls to missingTransitionFromState have a ? at the end except for the last one that won't execute. Replacing the ! with a ? fixes the problem. I didn't really understand what the code is doing but the question mark fixes it.

Making the following changes fixes the problem:
Annotating StateMachine's missingTransitionFromState method with dynamic
Changing the parameter types in StateMachine's missingTransitionFromState method from String to String?, to match the signature of the other missingTransitionFromState method.
I believe that the method needs dynamic or #objc in order to be able to be called dynamically using AnyObject. However, after adding that, the compiler will complain that calls to missingTransitionFromState on an AnyObject is ambiguous, because there are two signatures, so you have to fix the signature.

Related

Passing a generic type to a function with generic constraint

I have a protocol with an associated Type
protocol ProtocolA {
associatedType someType
}
Now i have two generic functions
func funcA<someType>(_ data:someType) {
funcB(data) // cannot call
}
func funcB<someType:ProtocolA>(_ data:someType) {
}
I have been trying to call funcB from funcA, but it is not working I am getting the error
Instance method 'funcB' requires that 'someType' conform to 'ProtocolA'
Now i know for a fact that the Generic Type in funcA is conforming to ProtocolA. Is there anyway to also make sure that the compilers know it too ?
I cannot change the funcA method declaration to put a generic constraint as it is the requirement of another protocol.
I have tried using the some keyword in funcA by doing
var obj : some ProtocolA = data
However i am getting the error
Property declares an opaque return type, but cannot infer the underlying type from its initializer expression
Basically in short is there anyway i can call funcB from funcA without changing funcA signature, however funcB signature can be changed to whatever is required
****EDIT*****Added More Information
funcA is called by the protocl
protocol CommonService {
func funcA<ModelType>(_ data:ModelType)
}
class CommonServiceImpl : CommonService {
func funcA<someType>(_ data:someType) {
funcB(data) // cannot call
}
func funcB<someType:ProtocolA>(_ data:someType) {
//SomeCode here required that someType must implement ProtocolA
}
}
ProtocolA is contained in a third party pod that cannot be changed.
*******Edit***********How i solved the problem
So thanks to #Mojtaba Hosseini in answers i got a really good idea on how to solve my problem.
I simply wrote an overloaded function in my CommonServiceProtocol
protocol CommonService {
func funcA<ModelType>(_ data:ModelType)
func funcA<ModelType>(_ data:ModelType) where ModelType:ProtocolA
}
class CommonServiceImpl : CommonService {
func funcA<someType>(_ data:someType) {
funcB(data) // cannot call
}
func funcA<someType>(_ data:someType) where ModelType:ProtocolA {
funcB(data) // can be called
}
func funcB<someType:ProtocolA>(_ data:someType) {
//SomeCode here required that someType must implement ProtocolA
}
}
I mean it is not a perfect solution but given the hard dependency on an associatedType using ProtocolA in a third party pod i would say it works alright and that is one of the reasons to avoid third party dependencies as much as possible.
Is there any way to also make sure that the compilers know it too?
You have to implement an overload for the funcA and constraint it:
func funcA<someType>(_ data: someType) {
/* funcB(data) */ cannot call
print("Not detected")
}
func funcA<someType>(_ data: someType) where someType: ProtocolA {
funcB(data) // can call ✅
print("Detected")
}
so calling funcA("") will result Not detected
but conforming to the protocol and calling the same function will result in Detected
// extension String: ProtocolA { typealias someType = String } // uncomment to see
funcA("")
As per your requirement, I think you can match the signature of your funcB with funcA. Refer to the code below:
func funcA<someType>(_ data:someType) {
funcB(data)
}
func funcB<someType>(_ data:someType) {
}
As shown in the above code, you can remove the type constraint for someType in funcB.
In functionB just add a parameter that will tell the compiler the type you expect, if you really want to be sure the type is your protocol add a check :
func transform<T>( data : T ){
guard let data = data as? Monster else {
print("data is not a monster type")
return
}
intoMonster(Monster.self, data: data)
}
func intoMonster<T> (_ type : T.Type , data : T){
}

Not able to extend ObjC class using Protocol in swift

I am trying to extend an ObjC class for dependency injection using a protocol in swift , this class isn't owned by me so I cant make changes. For one of the method its working but for other Xcode always says that the class isn't confirming to the protocol and suggests me to add the method to the extension
Below is the declaration in ObjC header
- (BOOL) subscribeToTopic:(NSString *)topic
QoS:(AWSIoTMQTTQoS)qos
extendedCallback:(AWSIoTMQTTExtendedNewMessageBlock)callback;
Here is the protocol and its extension
protocol PopAWSIoTDataManagerProtocol {
func publishString(_ data:String, onTopic:String, QoS:AWSIoTMQTTQoS) -> Bool
func subscribeToTopic(_ topic: String, qoS: AWSIoTMQTTQoS, extendedCallback: (NSObject, String, Data) -> Void) -> Bool
}
extension AWSIoTDataManager : PopAWSIoTDataManagerProtocol {
}
Notice the error below it suggests me exactly same func to be added to extension which I already have added to main protocol
Not sure whats wrong , as I was able to add another method just fine.
Article used as a reference is this https://medium.com/flawless-app-stories/the-complete-guide-to-network-unit-testing-in-swift-db8b3ee2c327
You must confirm protocol PopAWSIoTDataManagerProtocol, cuz it extension of class AWSIoTDataManager
Or you can try this
extension PopAWSIoTDataManagerProtocol where Self: AWSIoTDataManager {
...
}
The protocol that you have defined has all function's to implemented as mandatory.
You need to define them as optional. Either you define them as optional or implement all of them which was the error image that you have attached is asking for.
#objc protocol PopAWSIoTDataManagerProtocol {
#objc optional func publishString(_ data:String, onTopic:String, QoS:AWSIoTMQTTQoS) -> Bool
#objc optional func subscribeToTopic(_ topic: String, qoS: AWSIoTMQTTQoS, extendedCallback: (NSObject, String, Data) -> Void) -> Bool
}
Default behavior of Protocol, when implemented is that you have to override all the methods. These are categorized as Protocol Requirements.
There are Optional Protocol Requirements , which do not have to be implemented by types that conform to the protocol.
To achieve Optional Protcol Requirements, the protocol and the requirement i.e the optional function must be marked as #objc.

Return an array of type Self

I'm having trouble figuring out a way to return an array of instances of a specific dynamic class type, at runtime, in Swift.
I successfully compiled and tested this version which returns a single instance of a class:
class Generic {
class func all() -> Self {
return self.init()
}
required init() {
}
}
class A: Generic {
}
let a = A.all() // is of type A
The challenge here is to get compilation to allow the all function to be prototyped as follows: class func all() -> [Self] (i.e return an array of instances, working with subclasses, without any cast).
class Generic {
class func all() -> [Self] {
return [self.init()]
}
required init() {
}
}
class A: Generic {
}
let a = A.all() // won't compile
I could return an array of Generic instances with class func all() -> [Generic] but this requires an additional cast with as! to get the correct type A. I'd like to take advantage of begin in the context of class A and using the Self keyword, to let the compiler infer the 'real' type. Do you guys think it's possible?
It seems to be only possible to return single instances, not arrays.
EDIT: Got this to work using AnyObject. Better, but not optimal as it requires a cast to the correct type.
class Generic {
class func all() -> [AnyObject] {
return [self.init()]
}
required init() {
}
}
class A: Generic {
}
let a = A.all() as! [A]
Thanks!
PS: Any other way to do this using generics or protocols/protocol extensions is also an option. If you have a more "Swifty" version in mind, please be my guest. Can't help myself thinking there's maybe a better way to do this, but can't figure out how.
The only option I can see of doing something like that is using protocols instead of a base class, like this:
protocol Generic {
func all() -> [Self]
init()
}
extension Generic {
func all() -> [Self] {
return [self.dynamicType.init()]
}
}
final class A : Generic {
}
A().all()
You have two limitations doing it like this. First, all classes that conform to your protocol have to be final. Second, all classes must obviously implement the init defined in the protocol, otherwise we wouldn't be able to have the all method defined.
Edit: you don't actually need to define the init as long as you don't define any other initializers
Edit 2: I didn't notice you used class functions, you can modify my example to use class functions instead of instance methods by replacing func all() -> [Self] with static func all() -> [Self] and
func all() -> [Self] {
return [self.dynamicType.init()]
}
with
static func all() -> [Self] {
return [self.init()]
}
Unfortunately, there doesn't seem to be a way to do this using Self. Self cannot be used in expressions, so [Self] or Array<Self> are not allowed.
However, I think that your use case is completely valid and you should repot it as a bug.

Using Swift Protocol Inheritance

I am trying to create a delegate that uses traditional polymorphism to compensate for a device being bluetooth LE, bluetooth, etc and can't seem to get the syntax right for casting.
Here is my parent protocol and class:
#objc protocol DeviceDelegate
{
func didConnectToDevice(name:String)
func didFailToConnectToDevice(name:String)
func didDisconnectFromDevice(name:String)
func didWriteData(data:NSData)
func didReceiveData(data:NSData)
}
class Device: NSObject
{
var delegate: DeviceDelegate?
}
Now here is the child class and protocol simplified down:
protocol BluetoothDelegate : DeviceDelegate
{
func didFindService(name:String)
func didFindCharacteristic(name:String)
}
class BLE: Device
{
func someFunc()
{
let bluetoothDelegate = (delegate as? BluetoothDelegate)
bluetoothDelegate.didFindService(UUIDString)
}
}
It throws the following error on the first line of that function:
Cannot downcast from 'DeviceDelegate?' to non-#objc protocol type 'BluetoothDelegate'
This doesn't make sense to me since it should allow casting to a child like a usual object does.
If I put #objc in front of BluetoothDelegate I get the following error:
#objc protocol 'BluetoothDelegate' cannot refine non-#objc protocol 'DeviceDelegate'
Anybody have any ideas on this?
When I copy your code and paste it directly into a playground and add #objc in front of your BluetoothDelegate definition, I get a message on this line:
bluetoothDelegate.didFindService("asdf")
'BluetoothDelegate?' does not have a member named 'didFindService'
Because you have used as?, there is a chance that bluetoothDelegate is nil. You should be using optional chaining here. Replacing with the following line reports no errors in the playground, indicating that you may have done something else in your code that you're not showing us.
bluetoothDelegate?.didFindService("asdf")
Alternatively, you could use this:
if let bluetoothDelegate = delegate as? BluetoothDelegate {
bluetoothDelegate.didFindService(UUIDString)
}
The message you're seeing about DeviceDelegate not being an objc protocol indicates to me that you have written these in two different files and maybe forward-declared DeviceDelegate incorrectly.

Implement delegate using generic protocol in Swift

I'm trying to create a delegate protocol that implements a function which passes an array of a generic type. I've tried several combinations but none of them seem to do the trick.
This is the most approximate thing i've reached to. This is the protocol:
protocol APIControllerProtocol {
typealias T
func didReceiveAPIResults(results: [T])
}
And this is the the delegator object:
class APIController<U:APIControllerProtocol> {
typealias ElementType = U
var delegate: ElementType?
init(delegate: ElementType){
self.delegate = delegate
}
func getAPIResults(){
// Perform some action before delegation
// "results" is an Array of dictionaries got from NSJSONSerialization
self.delegate?.didReceiveAPIResults(results.map{dict in Album(json:dict)})
}
}
However, the last line get this error: "Album is not convertible to U.T"
"Album" is the model object used to return the results.
What am i doing wrong?
EDIT:
Following Mike S advice, i've made the protocol method didReceiveAPIResults a generic function, and specified what T is in the delegate. However, when receiving and assigning the argument of type T to a property in the delegate, i get the error: "T is not identical to T"
class TestDelegate: APIControllerProtocol {
typealias T = Album
var albums:[T] = [T]()
func didReceiveAPIResults<T>(results: [T]) {
// ...
self.albums = results //ERROR: "T is not identical to T"
}
}
Your didReceiveAPIResults declaration in APIControllerProtocol needs to be a generic function so that the generic type T is passed along to it correctly.
protocol APIControllerProtocol {
typealias T
func didReceiveAPIResults<T>(results: [T])
}
Note: This means your delegate definition will need to define what T is:
class TestDelegate: APIControllerProtocol {
typealias T = Album
func didReceiveAPIResults<T>(results: [T]) {
// ...
}
}
Update: While the code above does get rid of the original error, it turns out that it acts more like a workaround and doesn't really address the root of the problem.
The real issue seems to be that the compiler is having trouble reconciling what U.T is with no ambiguity. That's actually easy enough to fix though, we just need to give it a more precise definition (note the where clause in the APIController definition):
protocol APIControllerProtocol {
typealias T
func didReceiveAPIResults(results: [T])
}
class APIController<U:APIControllerProtocol where U.T == Album> {
typealias ElementType = U
// ...
}
Note: I removed the <T> that I added to the function in the protocol previously; that's not needed anymore and will end up causing problems later.
With that, the TestDelegate class works as expected (you don't even need the typealias anymore):
class TestDelegate: APIControllerProtocol {
var albums: [Album]? = nil
func didReceiveAPIResults(results: [Album]) {
albums = results
}
}

Resources