How to check introduction of a feature at compile time in iOS? - objective-c

If I use a feature which is deprecated at current SDK version, compiler will warn about it.
I'm finding a feature just opposite against it.
If I use a class or method which is not defined in specific SDK version, compiler warn about it.
How can I gain this feature? I tried defining __IPHONE_OS_VERSION_MAX_ALLOWED by putting definition on prefix-header like this,
#undef __IPHONE_OS_VERSION_MAX_ALLOWED
#define __IPHONE_OS_VERSION_MAX_ALLOWED 40000
but it catches any of version mismatch even in SDK itself. (such as UIKit)
I'm making library code needs backward compatibility for iOS 4.0, and I want some automated method to check backward compatibility. I know feature availability test won't solve everything, but this will make the work a lot easier.
How can I check feature availability for specific SDK version with compiler?

This sounds like a job for respondsToSelector, where you can check to see if a selector exists in the OS you are currently running on.
But if you really want to future-proof your app, you should always go for the newest API defined for the minimum OS version you want to support.
Here is a related question about 'respondsToSelector' that may help you out.

I think what you need is to check for a class (feature) before attemp to use it by using the NSClassFromString.
Class myClass = NSClassFromString(#"CLassThatYouWantToUse"); //get the class object
if (myClass != nil)
{
// The device will support this class.. so carry on...
}
else
{
// The device doesn't support this class.
}

Related

Reachability 2.2 vs 3.0 (Tonymillion)

I have own static library that include Reachability for checking internet connection.
I use Reachability 2.2 version in my static library http://developer.apple.com/library/ios/#samplecode/Reachability/Introduction/Intro.html
Other developer added my static library to his(3rd party project) which already has other Reachability (tag 3.0 in Pod's spec)
He installed it through Cocoa Pods. see the spec.
As you see, this is last version in pods.
Ok, project was builded without errors.
BUT we got error:
+[Reachability reachabilityWithHostName:]: unrecognized selector sent to class 0x2c77fc
I spent lot of time to find out the issue.
So Reachability 2.2 has declaration:
+ (Reachability*) reachabilityWithHostName: (NSString*) hostName;
But, Reachability 3.0 has following declaration:
+(Reachability*)reachabilityWithHostname:(NSString*)hostname;
Is it different?! Compiling was successful! Look at "HostName"... yeap, small "n".
And when we check host reachability inside static library we get unrecognized selector.
My question is - what best way to avoid this issue? Should I force other developers who want to build my static library use Apple's Reachability or something else?
Thanks
I know this is WAY late, but its a problem I also face, as I am sure anyone else who is trying to build a redistributable library.
There isn't a "best way" to avoid this issue unfortunately, but there are ways to do it, just depends on your use case.
A low-tech, possibly high maintenance and effort way is to rename all of the classes in all of the pods you use. By giving all of the classes a unique prefix, you almost guarantee that your "customers" integrating your static lib will not use the same name, and specifically in your case, if they use pods, then the will not have the same class names, avoiding the conflict.
Other methods would involve post compilation class name munging which would be more maintainable, but required deeper technical knowledge in how to do such a thing to an object file and linker mapping. Sorry, i don't have this productized yet!
Hopefully I am not overlooking a more simple and elegant solution, but this is where I am at.

Is overriding Objective-C framework methods ever a good idea?

ObjC has a very unique way of overriding methods. Specifically, that you can override functions in OSX's own framework. Via "categories" or "Swizzling". You can even override "buried" functions only used internally.
Can someone provide me with an example where there was a good reason to do this? Something you would use in released commercial software and not just some hacked up tool for internal use?
For example, maybe you wanted to improve on some built in method, or maybe there was a bug in a framework method you wanted to fix.
Also, can you explain why this can best be done with features in ObjC, and not in C++ / Java and the like. I mean, I've heard of the ability to load a C library, but allow certain functions to be replaced, with functions of the same name that were previously loaded. How is ObjC better at modifying library behaviour than that?
If you're extending the question from mere swizzling to actual library modification then I can think of useful examples.
As of iOS 5, NSURLConnection provides sendAsynchronousRequest:queue:completionHandler:, which is a block (/closure) driven way to perform an asynchronous load from any resource identifiable with a URL (local or remote). It's a very useful way to be able to proceed as it makes your code cleaner and smaller than the classical delegate alternative and is much more likely to keep the related parts of your code close to one another.
That method isn't supplied in iOS 4. So what I've done in my project is that, when the application is launched (via a suitable + (void)load), I check whether the method is defined. If not I patch an implementation of it onto the class. Henceforth every other part of the program can be written to the iOS 5 specification without performing any sort of version or availability check exactly as if I was targeting iOS 5 only, except that it'll also run on iOS 4.
In Java or C++ I guess the same sort of thing would be achieved by creating your own class to issue URL connections that performs a runtime check each time it is called. That's a worse solution because it's more difficult to step back from. This way around if I decide one day to support iOS 5 only I simply delete the source file that adds my implementation of sendAsynchronousRequest:.... Nothing else changes.
As for method swizzling, the only times I see it suggested are where somebody wants to change the functionality of an existing class and doesn't have access to the code in which the class is created. So you're usually talking about trying to modify logically opaque code from the outside by making assumptions about its implementation. I wouldn't really support that as an idea on any language. I guess it gets recommended more in Objective-C because Apple are more prone to making things opaque (see, e.g. every app that wanted to show a customised camera view prior to iOS 3.1, every app that wanted to perform custom processing on camera input prior to iOS 4.0, etc), rather than because it's a good idea in Objective-C. It isn't.
EDIT: so, in further exposition — I can't post full code because I wrote it as part of my job, but I have a class named NSURLConnectionAsyncForiOS4 with an implementation of sendAsynchronousRequest:queue:completionHandler:. That implementation is actually quite trivial, just dispatching an operation to the nominated queue that does a synchronous load via the old sendSynchronousRequest:... interface and then posts the results from that on to the handler.
That class has a + (void)load, which is the class method you add to a class that will be issued immediately after that class has been loaded into memory, effectively as a global constructor for the metaclass and with all the usual caveats.
In my +load I use the Objective-C runtime directly via its C interface to check whether sendAsynchronousRequest:... is defined on NSURLConnection. If it isn't then I add my implementation to NSURLConnection, so from henceforth it is defined. This explicitly isn't swizzling — I'm not adjusting the existing implementation of anything, I'm just adding a user-supplied implementation of something if Apple's isn't available. Relevant runtime calls are objc_getClass, class_getClassMethod and class_addMethod.
In the rest of the code, whenever I want to perform an asynchronous URL connection I just write e.g.
[NSURLConnection sendAsynchronousRequest:request
queue:[self anyBackgroundOperationQueue]
completionHandler:
^(NSURLResponse *response, NSData *data, NSError *blockError)
{
if(blockError)
{
// oh dear; was it fatal?
}
if(data)
{
// hooray! You know, unless this was an HTTP request, in
// which case I should check the response code, etc.
}
/* etc */
}
So the rest of my code is just written to the iOS 5 API and neither knows nor cares that I have a shim somewhere else to provide that one microscopic part of the iOS 5 changes on iOS 4. And, as I say, when I stop supporting iOS 4 I'll just delete the shim from the project and all the rest of my code will continue not to know or to care.
I had similar code to supply an alternative partial implementation of NSJSONSerialization (which dynamically created a new class in the runtime and copied methods to it); the one adjustment you need to make is that references to NSJSONSerialization elsewhere will be resolved once at load time by the linker, which you don't really want. So I added a quick #define of NSJSONSerialization to NSClassFromString(#"NSJSONSerialization") in my precompiled header. Which is less functionally neat but a similar line of action in terms of finding a way to keep iOS 4 support for the time being while just writing the rest of the project to the iOS 5 standards.
There are both good and bad cases. Since you didn't mention anything in particular these examples will be all-over-the-place.
It's perfectly normal (good idea) to override framework methods when subclassing:
When subclassing NSView (from the AppKit.framework), it's expected that you override drawRect:(NSRect). It's the mechanism used for drawing views.
When creating a custom NSMenu, you could override insertItemWithTitle:action:keyEquivalent:atIndex: and any other methods...
The main thing when subclassing is whether or not your behaviour completes re-defines the old behaviour... or extends it (in which case your override eventually calls [super ...];)
That said, however, you should always stand clear of using (and overriding) any private API methods (those normally have an underscore prefix in their name). This is a bad idea.
You also should not override existing methods via categories. That's also bad. It has undefined behaviour.
If you're talking about categories, you don't override methods with them (because there is no way to call original method, like calling super when subclassing), but only completely replace with your own ones, which makes the whole idea mostly pointless. Categories are only useful for safely extending functionality, and that's the only use I have even seen (and which is a very good, an excellent idea), although indeed they can be used for dangerous things.
If you mean overriding by subclassing, that is not unique. But in Obj-C you can override everything, even private undocumented methods, not just what was declared 'overridable' like in other languages. Personally, I think it's nice, as I remember in Delphi and C++ I used to “hack” access to private and protected members to workaround an internal bug in framework. This is not a good idea, but at some moments it can be a life saver.
There is also method swizzling, but that's not standard language feature, that's a hack. Hacking undocumented internals is rarely a good idea.
And regarding “how can you explain why this can best be done with features in ObjC”, the answer is simple — Obj-C is dynamic, and this freedom is common to almost all dynamic languages (Javascript, Python, Ruby, Io, a lot more). Unless artificially disabled, every dynamic language has it.
Refer to the wikipedia page on dynamic languages for longer explanation and more examples. For example, an even more miraculous things possible in Obj-C and other dynamic languages is that an object can change it's type (class) in place, without recreation.

Can I use an application as a library (Mac, Objective-c)?

Is there a way to load classes from a mac app, and use them in a different mac app?
I'd like to make an Automator action that accesses some of the classes in my mac app, and this seems like the sort of way I'd ideally do it (means you have to have bought my app to use the Automator action, etc.)
Depending on what you want to do (I'm not quite clear), a Service might do the trick for you. You make a helper app which can pass data back and forth with your app, using a shared pasteboard. You can get a fairly wide range of action, because you can pass any object that conforms to the NSPasteboardWriting and NSPasteboardReading protocols; as it says there in the docs, NSString, NSAttributedString, NSURL, NSColor, NSSound, and NSImage are already available for you, and of course you can write a custom class that suits your needs exactly.
Have you tried creating a stand-alone Automator plugin project, or tried adding an Automator bundle target to your application's project?
I'm assuming that you want to create Automator actions for your main app, but are unclear how you get these actions to interact with your application (or with the classes present in your application).
There are 3 basic types of Automator actions: AppleScript-based, shell-script based, and Objective-C based. You'll most likely want to make yours Objective-C-based, which will allow you to easily incorporate other Objective-C code from your main application into the action itself (see Implementing an Objective-C Action). (Note that by default, when you add a new target for an automator bundle, it's an AMAppleScriptAction type).
To see how an Objective-C automator action is set up compared to an AppleScript-based action, you might want to try creating a separate standalone project.
Let's say your app is document-based, and uses the KWDocument class, which exposes a method named -duplicateObjects:(NSArray *)objects toDocument:(KWDocument *)destDocument;. You also have a KWRegistrationManager that knows whether your app is registered or not. And let's say you want to create an automator action that's called "Duplicate Objects to Document". The action will be implemented in KWDuplicateObjectsToDocument, which is as a subclass of AMBundleAction. In the Info.plist for Duplicate Objects to Document.action, the NSPrincipalClass will be KWDuplicateObjectsToDocument.
KWDuplicateObjectsToDocument.h will look something like:
#import <Cocoa/Cocoa.h>
#import <Automator/AMBundleAction.h>
#interface KWDuplicateObjectsToDocument : AMBundleAction {
}
- (id)runWithInput:(id)input fromAction:(AMAction *)anAction
error:(NSDictionary **)errorInfo;
#end
And your KWDuplicateObjectsToDocument.m will look something like this:
#import "KWDuplicateObjectsToDocument.h"
#import "KWDocument.h"
#import "KWRegistrationManager.h"
#implementation KWDuplicateObjectsToDocument
- (id)runWithInput:(id)input fromAction:(AMAction *)anAction
error:(NSDictionary **)errorInfo {
if (![[KWRegistrationManager defaultManager] isRegistered]) {
return nil;
}
// eventually you'll call
// duplicateObjects:toDocument:
return input;
}
#end
You'll need to make sure that the necessary classes you use (such as KWRegistrationManager, KWDocument, etc.) are compiled and included as part of the build process for this bundle.
Basically, no: you cannot link with an executable.
An application binary is in a specific format.
And that format is different from the static or shared library format.
It means you won't be able to load any code parts from an application binary, as you would with a library.
Take a look at distributed objects. Your application could vend one or more objects that your Automator action could use. I've never tried it with Automator, but it's a very elegant system that hasn't gotten a lot of attention in recent years. I think it's definitely worth a look.
One cool aspect of distributed objects is that the application could be running on the same computer if you wish, but it could just as easily be running on a different computer, perhaps even one that's very far away.
You could make certain behavior from you app accessible via Applescript, but accessing the the actual classes is not possible in the way I think you mean. I get the impression that you mean accessing the classes loaded into the memory of your running app. This is not possible on OS X (or any UNIX-like system). Applications run at the user level. Processess at the user level are not able to read memory from other processes. The components of the OS that need to do this sort of thing run at kernel level.
If you are just trying to reuse the code, you could build the parts you want to share into a static library, and others could link against it and share your code.
EDIT:
From NSGod's answer it seems that you can use the same approach that makes it accessible via Applescript and make it accessible via Obj-C. That looks pretty cool.

How to make iPhone universal binary for iOS4 and iOS3?

It is possible to make universal binary for os 2.x and 3.x, you can see sample code for MFMailComposer But how to make it for iOS4 and iOS3 and XCode 3.2.3?
yes, there's some documentation on the process but if you want to instantiate a class which might not be present, you must test first.
Using NSClassFromString returns null if you're running on 3.x without the class, and returns a class you can use to allocate a new instance if the class exists while running on a later OS. In this way, you can test and fallback to 3.x code and make use of 4.x classes and features if available.
Be sure you compile against newer (4.x) libraries while you also set your Target OS to be lower (3.x) so you can cast a new class in your code without compile time warning.
If you want to use a new API or support a change in API, you might test for method presence using respondsToSelector and optionally fire the method via performSelector etc.
There are many different cases, but the idea revolves around setting a the target OS and testing runtime events in order to fallback or avoid features only available in a later OS'
For instance, you might check for UILocalNotification class's existence and if it is available, you put a button on the toolbar and if it doesn't exist, you don't - thus altogether avoiding code which would crash at runtime.

Regular expressions in an Objective-C Cocoa application

Initial Googling indicates that there's no built-in way to do regular expressions in an Objective-C Cocoa application.
So four questions:
Is that really true?
Are you kidding me?
Ok, then is there a nice open-source library you recommend?
What are ways to get close enough without importing a library, perhaps with the NSScanner class?
I noticed that as of iOS 4.0 Apple provides a NSRegularExpression class. Additionally, as of 10.7, the class is available under OS X.
Yes, there's no regex support in Cocoa. If you're only interested in boolean matching, you can use NSPredicate which supports ICU regex syntax. But usually you're interested in the position of the match or position of subexpressions, and you cannot get it with NSPredicate.
As mentioned you can use regex POSIX functions. But they are considered slow, and the regex syntax is limited compared to other solutions (ICU/pcre).
There are many OSS libraries, CocoaDev has an extensive list.
RegExKitLite for example doesn't requires any libraries, just add the .m and .h to your project.
(My complaint against RegExKitLite is that it extends NSString via category, but it can be considered as a feature too. Also it uses the nonpublic ICU libraries shipped with the OS, which isn't recommended by Apple.)
RegexKit is the best I've found yet. Very Cocoa:y. I'm using the "Lite" version in several of our iPhone apps:
sourceforge
lingonikorg
You can use the POSIX Regular Expressions library (Yay for a POSIX compliant OS). Try
man 3 regex
The cheap and dirty hack solution that I use to solve REGEX and JSON parsing issues is to create a UIWebView object and inject Javascript function(s) to do the parsing. The javascript function then returns a string of the value (or list of values) I care about. In fact, you can store a small library set of functions customized for particular tasks and then just call them as needed.
I don't know if it this technique scales to huge volumes of repeated parsing requests, but for quick transactional stuff it gets the job done without depending on any extra external resources or code you might not understand.
I like the AGRegex framework which uses PCRE, handy if you are used to the PCRE syntax. The best version of this framework is the one in the Colloquy IRC client as it has been upgraded to use PCRE 6.7:
http://colloquy.info/project/browser/trunk/Frameworks/AGRegex
It's very lightweight, much more so than RegExKit (although not as capable of course).
NSRegularExpression is available since Mac OS X v10.7 and IOS 4.0.
During my search on this topic I came across CocoaOniguruma which uses Oniguruma, the Regular Expression engine behind Ruby1.9 and PHP5. It seems a bit newer compared to the existing OregKit (in Japanese). Not sure how these stack up against other bindings.
Googling alittle, found this library:
RegexOnNSString
Open source library, containing functions like:
-(NSString *) stringByReplacingRegexPattern:(NSString *)regex withString:(NSString *) replacement caseInsensitive:(BOOL)ignoreCase
and using NSRegularExpression class. Quite easy to use and no need to worry about anything.
Please, note that NSRegularExpression is available since Mac OS X v10.7 and IOS 4.0, as Datasmid mentioned.
I make it easy. I add a new C++ file to my Objective C project, rename it as .mm, and then create a standard C++ class inside. Then, I make a static class method in the "public:" section for a C++ function that takes an NSString and returns an NSString (or NSArray, if that's what you want). I then convert NSString to C++ std::string like so:
// If anyone knows a more efficient way, let me know in the comments.
// The "if" condition below is because ObjC crashes if converting to
// std::string if the string is nil or empty.
// assume #include <string>
std::string s = "";
if (([sInput != nil]) && (!([sInput isEqualTo:#""]))) {
std::string sTemp([sInput UTF8String]);
s = sTemp;
}
From there, I can use regex_replace like so:
// assume #include <regex>
std::string sResult = std::regex_replace(sSource,sRegExp,sReplaceWith);
Then, I can convert that std::string back into an NSString with:
NSString *sResponse2 = #(sResult.c_str());
If you're only using this C++ just for this function, then you may find it suitable to call this file extra.mm (class name Extra) and put this static class method in, and then add other static class methods when the situation arrives where it just makes sense to do it in C++ because it's less hassle in some cases. (There are cases where ObjC does something with less lines of code, and some cases where C++ does it with less lines of code.)
P.S. Still yet another way with this is to use a .mm file but make an Objective C wrapper around the use of std::string and std::regex_replace() (or regex_match()).