Is it OK to implement a protocol that's unavailable in my deployed OS version? - objective-c

Say I'm writing an app to run on iOS7 and iOS6. I've a class that implements a protocol - a protocol only available in iOS7 (in this case, NSLayoutManagerDelegate).
Everything seems to work on iOS6 - my code that instantiates the class implementing the protocol is skipped on iOS6 - so it appears I'm good to go.
I was a little surprised not to see an error during app load though; apparently the loader is OK with me implementing a protocol that doesn't exist. Luckily for me!
Is there any Apple documentation that discusses this scenario? Is it safe? Any gotchas I should watch for?

No problem with that, a bit like implementing a method that is only called by the system for some version upwards.
At worst you can say that you have generated some code that takes up space somewhere (the "disk" if you can call it that on iOS and probably also the RAM) without being used on the older version(s), but that is minimal.
This all requires that the code is compiled with an SDK that includes the protocol in question - if conformance to the protocol is declared by using the <ProtocolName> construct and including the corresponding header. But that is kind of a given, since it won't compile otherwise.

Related

NSCoder Availability

trying to use the initwithcoder init method in my custom NSControl Class.
It works just fine and does what I need it to do. However and this leads me to asking this question on this here forum -> in the class reference of NCControl when you scroll down to initWithCoder and click on it it states SDKs
macOS 10.10+ which leads me to believe that it would not work and do what I need it to do on versions prior to that... Unless Apple's documentation once again is wrong...
BTW - from Apple's own reference (Online as well as offline) apparently the NSCoder Class is also suffering from this SDKs macOS 10.10+
https://developer.apple.com/reference/foundation/nscoder
The strange thing is that in the documentation describing how a NIB is loaded and which init methods it calls on various objects, it describes initWithCoder as being the designated initializer but that documentation is from before 10.10.
Thanks to anyone who can set my mind at ease ;-)
Yes it will not work before defined version. I think you are checking versions of Swift, if you select objective-c you should see version 10.0+ which i think should work fine for your need. If you change the language, you can put your mind to ease :)
I am assuming if you would like to support things that far back you are going to use objective-c over swift.
Check the below image for NSCoder documentation after you select objective-c on right hand side.

NSUUID conflict (iOS6.0 and Above)

We have a NSUUID class (we provide the declaration and implementation). We used it successfully up to iOS 6.0. We implemented it because UIDevice uniqueIdentifier was banned long before Apple deprecated it, and returning a NSUUID was a natural choice.
At iOS 6.0, we had to guard the define because Apple introduced the same class:
#if __IPHONE_OS_VERSION_MAX_ALLOWED <= __IPHONE_5_1
#interface NSUUID : NSObject {
...
}
#endif
iOS 5.1 and lesser are now broken. On iOS 5.1, we get back nil after alloc/init.
I tried to remove the #if/#end, but I get duplicate names when using the latest iPhone SDK.
Apple's lack of a stable API is a bug, not a feature. This "try it at runtime" crap is not cutting it. It makes it very difficult to write high integrity software.
From Tommy's response below, I can't instruct Apple's toolchain to use our implementation of NSUUID all the time. How do I provide an implementation of NSUUID for iOS 5.1 and lower (that might be compiled using the latest SDK)?
You can't. You've explicitly broken the rules:
Objective-C classes must be named uniquely [...] In order to keep
class names unique, the convention is to use prefixes on all classes.
You’ll have noticed that Cocoa and Cocoa Touch class names typically
start either with NS or UI. Two-letter prefixes like these are
reserved by Apple for use in framework classes.
You'll need to rename your own class. The quickest way is quite probably to right click on the class name, select "Refactor -> Rename..." and use a correct prefix this time. Xcode may not be able to refactor fully automatically since it'll obviously be ambiguous which NSUUID you're referring to in other parts of your code.
EDIT: regardless of grandstanding, if you want to implement code that provides a self-implemented replacement for NSUUID where it's not available then the solution is to "try it at runtime".
Assuming you've implemented NDRUUID, which implements the same interface as NSUUID then the quickest solution is to add something like this to your prefix header:
#define NSUUID (NSClassFromString(#"NSUUID") ? [NSUUID class] : [NDRUUID class])
You can then use [NSUUID UUID], etc, everywhere else in your code as though you were targeting iOS 6 only; the only difference is that when running under 5 you'll actually be addressing NDRUUID. Whenever you stop supporting 5 just remove that line from your prefix header and delete your own class from the project.
Hopefully you can see this is a much better way to handle introducing new APIs and backwards compatibility than, say, not using Apple's NSUUID at all anywhere until it's available everywhere.
I have built exactly what you asked for: an implementation of NSUUID for iOS 5.1 and lower that might be compiled using the latest SDK. See my NSUUID project on GitHub.

localStorage not persisting in OSX app (Xcode 4.3)

From what I have seen, if you are building a OSX desktop HTML5 app and want localStorage to persist in your WebView wrapper, you need to do something like this:
WebPreferences* prefs = [webView preferences];
[prefs _setLocalStorageDatabasePath:#"~/Library/Application Support/MyApp"];
[prefs setLocalStorageEnabled:YES];
Taken from: How do I enable Local Storage in my WebKit-based application?
But this doesn't seem to work for me in Xcode 4.3. Instead I get
"No visible #interface for 'WebPreferences' declares the selector '_setLocalStorageDatabasePath:'
"No visible #interface for 'WebPreferences' declares the selector 'setLocalStorageEnabled:'
I'm very new to Objective C, and are probably doing something silly like not including some header or something.
I've included the WebKit framework and both of these headers:
#import <WebKit/WebKit.h>
#import <WebKit/WebPreferences.h>
And what's weird is that I can access other methods of prefs, i.e. [prefs setDefaultFontSize:10] - but just not the two above that I listed.
Any ideas? Is this something that has been removed in Xcode 4.3?
OK, I have a solution. I looked at the source code to macgap and noticed how they were dealing with this issue.
It turns out the error message I was getting does make a little sense - I needed to declare an interface for WebPreferences first.
#interface WebPreferences (WebPreferencesPrivate)
- (void)_setLocalStorageDatabasePath:(NSString *)path;
- (void) setLocalStorageEnabled: (BOOL) localStorageEnabled;
#end
...
WebPreferences* prefs = [WebPreferences standardPreferences];
[prefs _setLocalStorageDatabasePath:"~/Library/Application Support/MyApp"];
[prefs setLocalStorageEnabled:YES];
[webView setPreferences:prefs];
Like I said, I'm new to Objective-C. I don't really get why the interface is needed in order to call those two methods (i.e. when I can call the other methods without the interface).
There's good news and there's bad news; I'm going to make the assumption that you want the bad news first (it'd be easier if I answered your question with the bad news first anyway).
The Bad News
The only answer to why this is happening is that Xcode 4.3 doesn't offer those methods anymore. That question that you linked to, "How do I enable Local Storage in my WebKit-based application?" was last active over a year ago (with the accepted answer being edited in early 2011). There have been at least two updates to Xcode since then (probably more and I'm just not remembering them), and it seems feasible to me that Apple would want to keep their private methods private, so it's safe to assume that they removed them as well as the support for setLocalStorageEnabled:.
The reasons that I don't think that there is any other answer to your problem are the following:
Both methods that you call on the WebPreferences instance are not supported. It's not just the private method, so Apple must have modified the WebPreferences class, removing not only setLocalStorageEnabled: but also support for private methods such as _setLocalStorageDatabasePath:. Why they supported private methods to begin with, I don't know, but they've definitely cracked down on their support because I haven't seen an opportunity to implement a private method in quite some time.
If implementing the private method (or even the other, public method) were possible, it'd be as easy as your code makes it out to be. If one looks at the linked question, they don't mention any difficult steps to getting the code to be supported. There isn't any way to import a private part of a framework such as WebKit without doing some heavy-lifting with regards to not only finding the private part but getting it into your code as well. Even if you can get those methods supported in your code after all of that heavy-lifting, it'd be highly unlikely that Apple would be very happy with it and they'd probably deny your app from the app store.
Sorry to be a Debbie-downer about it all, but I just don't think that your code would work anymore without some deep diggging and large workarounds. If you want it to work easily, you'll probably have to go back to early 2011 and make your app then instead.
The Good News
There is probably some solution that doesn't involve private and unsupported methods that I'm just not aware of because of my lack of experience using WebKit. Instead of looking for an answer for why your code isn't working, I'd start looking for alternatives for what your code is supposed to do.
I have found a solution to the persistence problem. see my post at
Local Storage in webview is not persistent

What determines the process by which unimplemented methods are resolved?

As I understand it, an unimplemented method gets resolved in the following way:
resolveInstanceMethod: / resolveClassMethod: gets a chance to implement the method
forwardingTargetForSelector: gets a chance to forward to a delegate
forwardInvocation: gets a chance to handle the method as it sees fit.
Where is this three-step process defined? I'd like to handle it myself as NSInvocation might be too heavyweight for my needs. I've had a poke around the runtime source and can't really see anything.
It looks like the old runtime would call forward:args: on the receiver, to do this but that seems to have gone from the new one. I'm guessing that the process must be defined by the framework rather than the runtime, since it'd be strange if the runtime depended on Cocoa to the extent of requiring NSInvocation to handle messages. Is it maybe an undocumented method that gets called on NSObject / NSProxy?
Edit:
It looks like the runtime declares, but never defines, a C function which is called when objc_msgSend can't find an implementation:
id objc_msgForward(id object,SEL message,...);
I don't work for Apple, so I don't know how Foundation implements this, but at least in the case of Cocotron, they use:
id objc_msgForward(id object,SEL message,...)
{
Class class=object->isa;
struct objc_method *method;
void *arguments=&object;
if((method=class_getInstanceMethod(class,#selector(forwardSelector:arguments:)))!=NULL)
return method->method_imp(object,#selector(forwardSelector:arguments:),message,arguments);
else
{
OBJCRaiseException("OBJCDoesNotRecognizeSelector","%c[%s %s(%d)]", class_isMetaClass(class) ? '+' : '-', class->name,sel_getName(message),message);
return nil;
}
}
adding a forwardSelector:arguments: method doesn't seem to work, so I'm guessing this is specific to Cocotron. Anyone know what objc_msgForward does in Foundation?
I'm writing something a bit like a
scripting language that uses message
forwarding to interface with
objective-c. For now, I'm using
NSInvocation, but it could end up
doing this thousands of times per
second, so the overhead would be
noticeable. But I guess I'm also just
curious...
As far as message forwarding is concerned, the behavior is [often subtly] different across different platforms and versions of the runtime.
In any case, don't re-invent the wheel. There are two language bridges available today that do pretty close to full-fidelity bridging from which you can learn a ton. Both have liberal licenses specifically to allow for such re-use.
Specifically, the MacRuby project offers a Ruby implementation that sits on top of CoreFoundation and the Objective-C Garbage Collector. It is the "most native" bridge available (and not terribly portable as a result -- not a goal of the project).
The PyObjC bridge is the best example available of a high fidelity bridge between the Objective-C runtime and another dynamic OO language's runtime; Python. It is a bit more portable, though the non-Mac OS X bits have likely rotted somewhat.
(I would be remiss in not mentioning F-Script; a new language built on Objective-C for which, I believe, the source is/was available?)
All of the bridges deal with both method forwarding, subclassing and cross-runtime proxying, all of which sound like they are applicable to your particular needs.

what is the program flow in Cocoa Applcation

I am new to mac os X development ,I downloaded an open source mac application ,but i couldn't able to understand the flow of execution of cocoa program.so any one can explain the program flow of a general cocoa program briefly.
Thanks in advance
Start in main. It's not likely to contain anything interesting, but worth checking just in case. Most probably, it will contain only a call to NSApplicationMain, which will create the NSApplication object and send it a run message. That's what gets the application running, and this method will run for the rest of the rest of the process.
Then look in the MainMenu nib. Loading this is one of the first things the application will do. Any windows here that are set as “Visible on Launch” will come up immediately; more importantly, the application delegate will probably be here. Check the application's or File's Owner's (the application is both of them in this nib, so you need to check both) delegate outlet, and if one of them is connected, follow the connection. See what class that object is an instance of.
Once you've found the application delegate class, open it up in Xcode. Look through the list of application delegate methods and find which ones are implemented, and read the ones that are. The application:…FinishLaunching: twins will be particularly important at the start of the process.
From there, it's all just reading code, seeing what it does, and going where it takes you.
Peter's answers are good - I'd also say to check for implementations of 'awakeFromNib', especially for object loaded from MainMenu.nib. You often find interesting things stashed away in that method, rightly or wrongly.