Implementing Objective-C protocol in Swift class - objective-c

I have a comprehension question. I want to use the Dropbox Objective-C framework in an iOS Swift app.
I already imported the framework successfully and set the import clause in the bridging header.
I was also able to run the authorization process so I think the framework works.
Then I try to use a component of the framework which is declared as protocol:
class ViewController: UIViewController, DBRestClientDelegate {
}
I sat the delegate property, called the loadMetadata method and implemented the corresponding event function:
let dbRestClient = DBRestClient(DBSession.shared())
dbRestClient.delegate = self
dbRestClient.loadMetadata("/")
...
func restClient(client: DBRestClient!, loadedMetadata metadata: DBMetadata!) {
}
What I'm wondering is that it seems not necessary to implement all methods of that protocol. Is this correct? In Swift implementing only a part of a protocol is enough?
I ask because the compiler displays no errors but the delegation method is never called.

Generally, in Swift you have to implement ALL methods of a protocol. (See this question about optional protocol methods: How to define optional methods in Swift protocol?)
But as you said, the framework is written in Objective-C. Objective-C supports optional methods in protocols.
#protocol ProtocolName
#required
// list of required methods
#optional
// list of optional methods
#end
Source
That's why you don't necessarily need to implement all methods declared in a protocol. Usually, only the most important methods are marked with #required, because when calling an optional method, you should always check, if the delegate implemented it.

Related

Implement an Objective-C method with light weight generics in Swift

I have to implement this method in a DataSource protocol of an Objective-C library
(nullable id<SomeClass>)someMethod;
I am trying to implement it in my class in Swift, specifically, the AppDelegate, with what I believe keeps equal the signature
extension AppDelegate: LIBDataSource {
#objc func someMethod<T: SomeClass>() -> T? {
return nil // temporary
}
}
The problem is that
As it is, I have a warning and an error. The error says Method cannot be marked #objc because it has generic parameters (the warning below is also shown)
If I remove #objc, the warning says Non-#objc method 'someMethod()' cannot satisfy optional requirement of #objc protocol LIBDataSource
Is there a way to implement a generic Obj-C method of a Obj-C protocol in Swift? Or do I have to do a separate Objective-C class to accomplish this?
The syntax
id<SomeClass>
is nothing to do with lightweight generics, it means "any Objective-C class as long as it conforms the protocol SomeClass". Your method doesn't need to be generic but it does need to return an object that conforms to the SomeClass protocol. It's signature should probably be something like
func someMethod() -> SomeClass?

Objective-C protocol not being recognized in Swift

We are in the process of migrating existing classes from Objective-C to Swift. In a view controller class I am implementing a protocol and attempting to add the view controller as a delegate of an object contained within it. When I try to add 'self' as the object's delegate in Swift I get the following compile error:
Cannot call value of non-function type '((ListenerProtocol) -> Void)?
Here is the existing implementation in Obj-C
#interface SomeViewController : UIViewController <ListenerProtocol> ...
And adding the class as the listener (delegate)
[manager addListener:self];
This works without any problems. But the Swift version fails in what seems like the same code. Here is the Swift version of the same call:
#objc class SomeSwiftViewController: UIViewController, ListenerProtocol ...
And the call to 'addListener'
manager?.addListener(self)
I have verified successfully that 'self' is a ListenerProtocol object at runtime by checking:
if self.conformsToProtocol(ListenerProtocol){
// ...
}
In the object containing the delegate property the addListener method is defined in Objective-C as follows:
- (void)addListener:(id<ListenerProtocol>)listener {
// ...
}
The Swift class completely implements all of the methods defined in ListenerProtocol. I can't understand why this doesn't work in Swift. Can anyone make a suggestion? Thanks!
The problem is not with ListenerProtocol, it's with the protocol that manager implements. Judging by the type, addListener appears to be provided as an optional method. Notice the question mark at the end of the function type:
((ListenerProtocol) -> Void)?
This usually happens with (actually, I think that it only happens with) Objective-C optional protocol methods.
You should be able to write manager?.addListener?(self) or manager?.addListener!(self).

Can I expose Swift default parameter values to Objective-C?

Say I have some Swift class with methods I'd like to expose to Objective-C:
#objc(Worker) class Worker : NSObject {
func performWork(label: String = "Regular Work") {
// Perform work
}
}
I can call this in two ways:
Worker().performWork()
Worker().performWork("Swift Development")
However, when called from Objective-C, I do not get the "default" version of this method - I must supply a label:
[[[Worker alloc] init] performWork:#"Obj-C Development"];
See the definition from my AppName-Swift.h header below:
SWIFT_CLASS_NAMED("Worker")
#interface Worker : NSObject
- (void)performWork:(NSString * __nonnull)label;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
I would expect to see a method with this signature:
- (void)performWork;
I realise ObjC doesn't support default parameter values, but my assumption was that the Swift default values simply generate additional Swift functions with trivial implementations calling the "real" function (i.e. they're syntactic sugar hiding the typical implementation we'd use in Obj-C).
Is there any way to expose this default value to Objective-C? If not, can anyone point me to any documentation on why this isn't available?
The answer is no, there is currently no way to expose that.
It is however, being considered for Swift 3.0 (see the proposal here)
EDIT: This is incorrect as pointed out by Adam S.

What is the correct way of accessing a Swift Delegate from Objective-C?

Environment: Xcode 6.1.1 & Xcode 6.2 Beta
Greetings:
I need to publish a NSString within a Swift doc from a neighboring Objective-C doc within the same project. For example, display "Hello World" generated in Objective-C upon a Swift page. I've made a proof-of-concept demo; based on feedback.
I'm thinking of using an ObjC --> Swift delegate via a protocol pattern as shown below:
Note: the Swift file is the delegate.
Here I'm calling the delegate method in Swift, from Objective-C:
#pragma mark - Action methods
- (IBAction)sendDelegateAction:(UIButton *)sender {
[_delegate radiusString:#"Hello World"];
}
I've instantiated the Objective-C file to link the delegate to the instance (I hope I got it right):
let geo32Controller = MyObjCTableViewController()
geo32Controller.delegate = self
So far, the compiler complained that the Swift protocol couldn't be found.
Here's the protocol (declared in Swift):
#objc protocol DiscoveryContributeProtocol {
// optional
func radiusString(radiusString:String)
}
And here's the delegate reference to that protocol in the Objective-C header file:
#interface MyObjCTableViewController : UIViewController<UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, weak) id<DiscoveryContributeProtocol> delegate;
#end
However, the compiler can't find the protocol:
BTW: when I put the bridge reference in the ObjC's header file, I get a compiler error:
Two Questions:
Do I have the correct pattern (did I instantiate the ObjC correctly) ?
How do I make the Objective-C portion see the Swift protocol for the delegate link?
You have the right idea, but have a few bugs that are preventing this from working.
You've declared Geo32Boundaries as conforming to the DiscoveryContributeProtocol, but it doesn't need to and doesn't actually implement it, it only has a property that conforms to that protocol. That's the source of the "Method 'radiusString:' not implemented" error:
#interface Geo32Boundaries: UIViewController // <-- that's all you need
You're setting the delegate incorrectly -- the code you have there looks like it's trying to set a class instance of Geo32Boundaries to self, but you're also trying to call it like a function. You'll need to set the delegate on a the instance of the Geo32Boundaries view controller that is being presented to the user. I don't know where that code lives, so I can't give a great example, but it'll be something like:
geo32Controller.delegate = self
Lastly, though not a bug, your protocol should really be called DiscoveryContributeDelegate -- we usually don't use "protocol" in the protocol name.

Are delegate methods privately declared?

In my view controller's viewDidLoad method, I create an NSURLConnection
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest
delegate:self]
You can see I set the delegate to self.
Then I implemented the delegate method
-(void) connection: (NSURLConnection*) connection didReceiveResponse: (NSURLResponse*) reponse {
//myImplementation;
}
This implementation is only defined in the #implementation ViewController #end block, and it is not declared in the ViewController's #interface.
So I guess this method is private? It compiles and runs well. But I just can't call this method like [self connection: connection didReceiveResponse: response] in the ViewController's own methods.
What's the explanation?
The methods are declared via your inclusion of the NSURLConnectionDelegate protocol in the class's interface definition:
#interface MyViewController : UIViewController <NSURLConnectionDelegate>
This tells the compiler that your class promises to implement all the required methods of that protocol, and that it may implement the optional methods (as it happens, this particular protocol has only optional methods). So the declarations exist publicly, they're just in another file.
The method is "private" inasmuch as it is possible to have a private method in Objective-C. You actually can call the method from outside of the class, even if it's not declared in the interface. This is possible by way of Objective-C's dynamism in how it handles method calls -- any object can receive any message (the obj-c lingo for method), but whether or not the object can actually do something with that method depends on any number of things. Check out Object Messaging in Apple's docs.
The short answer is that your code works as you have it because an implementation of your method exists in your class, and Objective-C knows how to find it at runtime regardless of your class interface.