Delegate written in Swift not matching selector expected by Objective-C - objective-c

I'm attempting to use an Objective C wrapper for a Flickr API (ObjectiveFlickr). The wrapper works with an Objective-C code example. I'm trying to use it with Swift and as soon as the wrapper tries to find the first delegate method, it fails on this assertion.
NSAssert([delegate respondsToSelector:#selector(flickrAPIRequest:didObtainOAuthRequestToken:secret:)],
#"Delegate must implement the method -flickrAPIRequest:didObtainOAuthRequestToken:secret: to handle OAuth request token callback");
[delegate flickrAPIRequest:self didObtainOAuthRequestToken:oat secret:oats];
Here's the delegate method signature.
class FlickrDelegate : NSObject, OFFlickrAPIRequestDelegate
{
func flickrAPIRequest(inRequest:OFFlickrAPIRequest,
didObtainOAuthRequestToken inToken:String,
secret inSecret:String) {
...
}
}
I'm a complete novice with what I'm trying to do, so perhaps I've made some terribly dumb mistake.

Did you set your delegate property on flickrAPIRequest class in viewdidload to receive messages from Flikr?

Related

_cmd in Swift for selector use

I'm trying to write the following ObjC code in Swift 3:
- (void)scrollViewScroll:(UIScrollView*)scrollView {
// some code
if ([_userDelegate respondsToSelector:_cmd]) {
[_userDelegate scrollViewDidEndDecelerating:scrollView];
}
}
But do not know what to replace _cmd with. I'm trying function, but it doesn't work:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// some code
if (userDelegate?.responds(to: #function))! {
userDelegate?.scrollViewDidScroll!(scrollView)
}
}
using #selector(scrollViewDidScroll(_:)) works, but is there a way to keep it generic?
Edit: Possible duplicate answer is about getting function name which isn't what I'm asking above
Swift doesn't have selectors.
Objective-C sends messages to objects while Swift calls functions. So checking if object can respond to selector is part of Objective-C and NSObject.
Swift protocol functions are required by default. Swift compiler doesn't let you skip those function implementations. But you can make them optional, and you have to check, if these functions implemented before calling.
In this case, just call function with question mark at the end, like this
if let returnValue = userDelegate?.theOptionalFunction?(arguments) {
// you got value
} else {
// delegate returned nil or delegate function isn't implemented
}
Source: The Swift Programming Language
An optional protocol requirement can be called with optional chaining, to account for the possibility that the requirement was not implemented by a type that conforms to the protocol. You check for an implementation of an optional method by writing a question mark after the name of the method when it is called, such as someOptionalMethod?(someArgument).

How can you implement the NSDocument method -canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo: in Swift?

In my application, a NSDocument subclass mission-critical hardware – users really don’t want to close a document by accident! So, I’ve implemented canCloseDocumentWithDelegate… to show an NSAlert and ask before closing.
I am now trying to implement this same thing in an application written in Swift.
Since the answer comes asynchronously, the “should close” result is passed to a callback on a delegate, and not simply returned. In the documentation for -canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo:, it says:
The shouldCloseSelector callback method should have the following signature:
- (void)document:(NSDocument *)doc shouldClose:(BOOL)shouldClose contextInfo:(void *)contextInfo
So, as there’s 3 arguments of different types, I cannot use the simple performSelector:withObject: style methods – you have to use NSInvocation. Note that the delegate is of type id, and the signature above does not appear in any formal protocol – you can’t simply call the method normally. (See this mailing list post for example of how this should be done)
Now, the issue is, NSInvocation is not allowed in Swift! See Swift blog “What Happened to NSMethodSignature”:
Bringing the Cocoa frameworks to Swift gave us a unique opportunity to look at our APIs with a fresh perspective. We found classes that we didn't feel fit with the goals of Swift, most often due to the priority we give to safety. For instance, some classes related to dynamic method invocation are not exposed in Swift, namely NSInvocation and NSMethodSignature.
That sounds like a good thing, but falls down when a simple NSDocument API requires NSInvocation still! The real solution to this whole problem would be for Apple to introduce a new canCloseDocument… API using a block callback. But until that happens, what’s the best solution?
You can solve this with some low level runtime functions:
override func canCloseDocumentWithDelegate(delegate: AnyObject, shouldCloseSelector: Selector, contextInfo: UnsafeMutablePointer<Void>) {
let allowed = true // ...or false. Add your logic here.
let Class: AnyClass = object_getClass(delegate)
let method = class_getMethodImplementation(Class, shouldCloseSelector)
typealias signature = #convention(c) (AnyObject, Selector, AnyObject, Bool, UnsafeMutablePointer<Void>) -> Void
let function = unsafeBitCast(method, signature.self)
function(delegate, shouldCloseSelector, self, allowed, contextInfo)
}
If you need to move this behaviour to another method (eg. after a sheet gets confirmation from the user), simply store the delegate and shouldCloseSelector in properties so you can access them later.
So, my current solution to this, is to keep using Objective-C to perform the NSInvocation. The NSDocument subclass is written in Swift, and calls an Objective-C category to do this bit of work.
Since NSInvocation does not exist in Swift, I really don’t see any other way.
- (void)respondToCanClose:(BOOL)shouldClose delegate:(id)delegate selector:(SEL)shouldCloseSelector contextInfo:(void *)contextInfo
{
NSDocument *doc = self;
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[delegate methodSignatureForSelector:shouldCloseSelector]];
invocation.target = delegate;
invocation.selector = shouldCloseSelector;
[invocation setArgument:&doc atIndex:2]; // Note index starts from 2 - 0 & 1 are self & selector
[invocation setArgument:&shouldClose atIndex:3];
[invocation setArgument:&contextInfo atIndex:4];
[invocation invoke];
}
You can see my sample project:
https://github.com/DouglasHeriot/canCloseDocumentWithDelegate
Another option is to use Objective-C to wrap around objc_msgSend, which is also unavailable in Swift. http://www.cocoabuilder.com/archive/cocoa/87293-how-does-canclosedocumentwithdelegate-work.html#87295
At least as of Swift 4.1, you can do something like:
// Application Logic
myDocument.canClose(
withDelegate: self,
shouldClose: #selector(MyClass.document(_:_:_:)),
contextInfo: nil)
...
// Handler
#objc
private func document(_ doc: NSDocument, _ shouldClose: Bool, _ contextInfo: UnsafeMutableRawPointer) {
...
}
Here is a Swift solution to this issue that I received from Apple Developer Technical Support:
override func canCloseDocumentWithDelegate(delegate: AnyObject, shouldCloseSelector: Selector, contextInfo: UnsafeMutablePointer<Void>) {
super.canCloseDocumentWithDelegate(self, shouldCloseSelector: "document:shouldClose:contextInfo:", contextInfo: contextInfo)
}
func document(doc:NSDocument, shouldClose:Bool, contextInfo:UnsafeMutablePointer<Void>) {
if shouldClose {
// <Your clean-up code>
doc.close()
}
}

EXC_BAD_ACCESS - NSURLConnection

I have a class that uses NSURLConnection to fire a POST request.
I have other classes use a delegate on this class that it uses to fire an event when a response has been received.
When I've parsed the response, I call the delegate like so:
- (void)connectionDidFinishLoading:(NSURLConnection*)conn { ...
if (delegate)
{
[delegate serverDataLayerResponse:entity];
} ... }
I'm getting "EXC_BAD_ACCESS(code=1, address-..." on the line inside the if block.
I've even tried #try and #catch around that part but it stills kills my app.
I'm suspecting that the delegate is still pointing to as object in memory that has been released? How can I guard from this?
Thanks for any help.
You've got a bad pointer. delegate is nonzero, so the test passes, but doesn't point to a valid object. You could put a breakpoint in the delegate's -dealloc to detect whether the object was deallocated. Also, try breaking where you assign the delegate and make sue you've got a valid object at that point.

Possible? NSThread request URL by singleton Objective-C iOS

Situation.
XMLProxy Class is singleton. XMLRequest Class too.
Normally, when I want to request URL.
Example :
XMLProxy *xmlProxy = [XMLProxy sharedInstance];
[xmlProxy tryGetDataWithParameter:example_parameter];
// Method "tryGetDataWithParameter" will call method "requestURL" from XMLRequest Class
// Assume that is [xmlRequest requestURL];
// and XMLProxy Class (XMLProxy delegate) will create xml parser for each "example_parameter"
type to response xml data receive.
It's no problem.
Question?
I want to create visual background process to request URL and I create AutoSending Class that is singleton.
I want to use AutoSending Class to call method
"tryGetDataWithParameter" (from XMLProxy class). Example to call :
[autoSending start];
-(void)start{ XMLProxy *xmlProxy = [XMLProxy sharedInstance]; [xmlProxy tryetDataWithParameter:example_parameter]; }
Can I use NSThread to call statement "[autoSending start]";
It is the result of a visual Background Process.
Rather than try to code all this up by yourself, why not use a library like AFNetworking which is built on top of NSURLConnection, NSOperation, and other standard IOS technologies. You can then use NSOperationQueue to run your request on another thread.

Objective C custom class methods not being called

So I have this custom class with just a test method that does nslog. I am going to reuse this method many times in my app. The interface looks like this.
#interface TestViewController: UIViewController { CMImageMover * imageMover }
Then in the view did load I:
imageMover = [[CmImageMover alloc] init];
If I do:
[imageMover testMethod];
Right after the alloc and init it works in the viewDidLoad function but if I call it again from another function in the view controller nothing works and the class method does not get called.
What am I doing wrong here. Every other var I declare like NSArray/NSTimer, I do the say way and I am able to access and use it throughout my controller.
When you say "if I call it again from another function in the view controller nothing works" then first thing to check is what you are sending the testMethod. It could be nil, in which case nothing will happen. In objective C sending a message to nil does nothing. Add an NSLog to find out, e.g.
NSLog(#"imageMover object is: %#", imageOver);
[imageMover testMethod];
If the NSLog shows it is nil - or something crazy - then follow up what you are doing with the imageMover ivar.
You mention a class method in your question, but don't refer to it in your code snippets.
If you have defined testMethod as a class method it will, of course, fail if you send that message to an instance. (And it will fail noisily.) A class method would be introduced like this:
+ (void) testMethod
{
NSLog(#"CMImageMover testMethod called on Class");
}
An instance method would be introduced like this:
- (void) testMethod
{
NSLog(#"testMethod called on an instance of CMImageMover");
}
Apologies if this is all screamingly obvious to you and missing the point of the question. It's not that clear from your question where the issue lies.