I am writing an app that has a minimum deployment target of 10.7. As such, whenever I try to include a protocol that conforms to JSExport, the compiler says that it cannot find the protocol declaration for 'JSExport'.
If I set the minimum deployment target up to 10.9 it compiles just fine. Here is an example of noncompiling code.
#import <Cocoa/Cocoa.h>
#import <JavaScriptCore/JavaScriptCore.h>
#import <WebKit/WebKit.h>
#protocol MyJSExport <JSExport>
-(void)doSomething;
#end
I've tried dynamically creating MyJSExport protocol at runtime by following the example set here, but their conclusion was that it didn't work.
So how do you write a protocol that conforms to JSExport while maintaining a 10.7 minimum deployment target?
The JSExport protocol causes Javascript getter/setters to be defined for properties in your protocol and Javascript wrapper functions to be defined for methods defined in your protocol.
It didn't exist before 10.9.
The answer depends on what behaviour are you expecting for targets less than 10.9
Assuming you just expect to silently ignore this functionality in older targets, then inspecting <JSExport.h> provides a clue on how you might do this - by inserting your own empty protocol for JSExport (just as <JSExport.h> does) if it is not already defined (as shown below), gives you code that doesn't need to change when you compile for 10.7 vs 10.9
But: inspecting the JavaScriptCore headers, I would expect that the rest of the Objective-C Javascript bridge functionality introduced with 10.9 will be disabled until you compile for a target of 10.9 - are you using JSContext or JSValue?
#import <Cocoa/Cocoa.h>
#import <JavaScriptCore/JavaScriptCore.h>
#import <WebKit/WebKit.h>
// Check if JSExport functionality is available
#if !JSC_OBJC_API_ENABLED
#protocol JSExport
#end
#endif
#protocol MyJSExport <JSExport>
-(void)doSomething;
#end
Related
It appears that if I declare an Objective-C protocol in a project but no classes in the project/target actually adopt the protocol, the runtime doesn't know about the protocol at runtime. (i.e. NSProtocolFromString(#"MyProtocol") returns NULL) However, if I add a minimal "dummy" class, like this:
#interface Dummy : NSObject < MyProtocol >
#end
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wprotocol"
#implementation Dummy
#end
#pragma clang diagnostic pop
...the runtime will know about the existence of the protocol at runtime.
Is there a more elegant/less-hacky way to ensure that an otherwise-unadopted protocol will be "known" at runtime?
Try #protocol(MyProtocol) instead of NSProtocolFromString(#"MyProtocol"). I found it near the bottom of this page http://nshipster.com/at-compiler-directives/. Does that help answer the question?
I am integrating Swift into a large existing Objective C project and have run into what I think is a circular reference.
The classes in question are as follows:
Objective C Controller
#import "Hopscotch-Swift.h"
#interface MyController : UIViewController<MyProtocol>
...
#end
Swift Protocol
#objc protocol MyProtocol: NSObjectProtocol {
...
}
Bridging Header
#import "MyController.h"
This code fails to compile because the Hopscotch-Swift.h file will not generate.
I think this is due to a circular reference error as I can import Hopscotch-Swift.h into objective c headers that are not included in Hopscotch-Bridging-Header.h and it works fine.
Is there a workaround for this issue or should I file a radar with Apple?
Forward declaration should work, in your case.
In your .h:
#protocol MyProtocol;
#interface MyController : UIViewController<MyProtocol>
#end
In your .m:
#import "HopScotch-Swift.h"
From How can I add forward class references used in the -Swift.h header? and the Swift interoperability guide:
If you use your own Objective-C types in your Swift code, make sure to import the Objective-C headers for those types prior to importing the Swift generated header into the Objective-C .m file you want to access the Swift code from.
I ran into this when trying to use Swift classes inside Objective-C protocols, where the protocol was also implemented by another Swift class. It reeked of circular references and I guessed that it might be a problem trying to circularly generate the bridging headers, rather than a 'normal' circular include problem.
The solution, for me, was to just use forward declarations before the protocol declaration:-
// don't include the MyProject-Swift.h header
// forward declaration of Swift classes used
#class SwiftClass;
#protocol MyProtocol <NSObject>
- (SwiftClass *)swiftClass;
#end
The forward declaration by itself didn't work for me. It compiled without errors but still had warnings that the protocol couldn't be found. I treat all warnings as errors, so this isn't good enough.
I was able to fix it by moving the protocol implementation into another category header.
So here's what worked for me:
In my MyOtherSwiftFile.swift:
#objc protocol MyProtocol: class {
func viewController(didFinishEditing viewController: MyViewController)
}
In my MyViewController.h:
#interface MyViewController // Removed protocol implementation declaration here
#end
Added MyViewController+MyProtocol.h to project, and put this in there:
#interface MyViewController (MyProtocol) <MyProtocol>
#end
The methods themselves can stay where they are if you want.
After you implement the above and compile, you'll get compiler warning(s) somewhere in your code that requires that MyViewController implements MyProtocol. In that file, you will #import "MyViewController+MyProtocol.h"
Alternatively you can convert your protocol to an Objective-C protocol MyProtocol.h and then use it in Swift by including MyProtocol.h in your bridging header.
You could something like this in the .h file you suspect to trigger the circular reference:
#ifndef MY_HEADER_H
#define MY_HEADER_H
your header file
#endif
I was tinkering with XCode 4.5.2 this morning and wanting to make a table view I naturally added the UITableViewDataSource and UITableViewDelegate protocols to one on my view controller definitions.
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#end
#import "MyViewController.h"
#implementation MyViewController
#end
Now I know at this stage I need to implement the #required methods of the UITableViewDataSource protocol but out of (presumably TDD) habit I decided to perform a build first with the expectation that the compiler would throw up warnings about the unimplemented #required methods (indicating to me which ones to implement).
But NO the build completed without a single error or warning from the compiler which has confused and concerned me slightly.
So I realize this question is normally the other way round i.e. 'why I am getting this warning', instead of 'why am I NOT getting this warning' but this really simple issue has really baffled me. Clearly I'm doing something wrong but I've no idea what. Any ideas how this might be possible?
FYI this a new project with no build/project setting customisation on a brand new clean install of XCode 4.5.2 on a new Mac Book.
In XCode 4.5.x there is a option for setting different warnings to display (YES/NO). You can check it under build setting Compiler Warnings for LLVM 4.1 compiler.
I hope it'll resolve your issue.
I'm writing an Universal App that will run natively on both iPad and iPhone. I also need it to be targeted to older devices (those that cannot run 4.0) so 3.1 is a must.
I have already set up the Base SDK to the latest available version (4.2), and the Deployment Target to 3.1. I am making lots of runtime checks in order to call the corresponding methods only on the right device/version.
One of the things I am making use of in the iPad is an UISplitViewController. When assigning the splitViewController delegate, the compiler throws a warning because the class interface isn't explicitly adopting the UISplitViewControllerDelegate protocol and I'm afraid that if I declare it to make it so, the App will crash on older devices where there is no UISplitViewController/UISplitViewControllerDelegate.
What is the best way to supress the compiler warning? Should I declare an 'empty' UISplitViewControllerDelegate? If so, can I make it conditionally at runtime? Or should I just make the corresponding class interface conform to the protocol and not worry about older devices?
Best,
You can suppress the warning with a simple C cast:
foo.delegate = (id<UISplitViewControllerDelegate>)self;
I haven't tried this, but I'm pretty sure you can just go ahead and adopt the protocol unconditionally, even if the class may be used on an older runtime that does not have the protocol. And here's why:
All the information for defining a protocol is contained in the .h file that declares the #protocol. When you adopt a protocol, that protocol declaration is imported at some point in your .h file (presumably by #import <UIKit/UIKit.h>).
When the runtime needs to know something about a protocol, it references a "Protocol Object", which you would normally reference in source code by doing #protocol(MyProtocolName). And the compiler creates this protocol object (at compile time) when such a protocol reference is encountered, according to the section titled Protocol Objects in The Objective-C Programming Language documentation.
So knowing all that, if you adopt a protocol and you write any code that references the protocol object, that protocol object will be created by the compiler. Even if you're running on an older device, the compiler should've created that protocol object for you, so I don't think it'll cause a crash.
Hope that makes sense. If I have some time I can try this out to see if it holds in practice because I have devices running a range of iOS versions from 3.1 to 4.2.
Same problem if you want to use printing and stil have the app runnig before 4.2. Basically it works with this
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40200
// code for iOS 4.2++
#interface PersonDetailViewController : UITableViewController <EditViewControllerDelegate, EditPickerViewControllerDelegate, UITextFieldDelegate, UIActionSheetDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UIPrintInteractionControllerDelegate>{
#else
// code for iOS til 4.1
#interface PersonDetailViewController : UITableViewController <EditViewControllerDelegate, EditPickerViewControllerDelegate, UITextFieldDelegate, UIActionSheetDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate>{
#endif
But... It looks like the InterfaceBuilder can't handle this. All outlets defines after this conditional definition ar gone in IB.
So any better solution?
Regards
Gerd
The short version is that I have a protocol which has an optional parameter. When I build a class that implements it with the iPhone SDK 3.x it compiles just fine, with no errors or warnings. When I used the 2.x SDK I get the following warning:
Class.m:68: warning: property 'field' requires method '-field' to be defined - use #synthesize, #dynamic or provide a method implementation
It works just fine in both cases.
So two questions:
What is the right way to fix the warning? I added #dynamic to the implementation which isn't really correct as the property really isn't there.
Why does work in SDK 3.x but not 2.x? The docs say "On Mac OS X v10.5, protocols may not include optional declared properties." Clearly that's not exactly the case here.
Here's a quick sample of the kind of code I have to make things a little more obvious if I wasn't completely clear.
#protocol MyProtocol
#required
- (void) method:(NSString*)param;
#optional
#property (nonatomic,retain) NSString* field;
#end
#interface MyClass : NSObject<MyProtocol> {
}
- (void) method:(NSString*)param;
#end
The iPhone SDK is not exactly identical to any paricular version of Mac OS X. Clearly a newer version of the toolset is included with SDK 3 that's more similar to the one from Snow Leopard.
The simnple way to remove the warning is to add
#dynamic field;
to your implementation. That tells the compiler that you will provide the implementation dynamically, which you wont, becuase its an optional property, but that should shut the compiler up.
The #optional was introduced in Objective-C 2.0 so it won't be applicable for older versions of the SDK. Your best bet is to determine whether it should be present (probably not) and then #ifdef that around with
#if __OBJC2__
#optional
#property ...
#endif
Then it should only compile under an OBJC2, and it won't be present in the older systems as part of the protocol itself.