Graceful degradation on iPhone - iphone

How do I write a program for iPhone (Objective C++) that runs on OS 2.0 but takes advantage of 3.0 features if they're available?
Example: copy&paste (class UIPasteboard). Nice feature to have, but I don't want to kill backward compatibility. Do I compile against SDK v. 3 or v. 2? If the latter, how do I create an instance of UIPasteboard, considering it's not declared in the headers? If the former, won't some C-linkage functions cause "unresolved reference" upon loading under OS 2.0?

Edit your Target's build settings like this:
Set the Base SDK to the version whose APIs you want to use (e.g. 3.0 or 3.1).
Set the Deployment Target to the lowest OS version you want to support (e.g. 2.2.1).
When building, you compile against the Base SDK. For all symbols that defined in your Base SDK but not available in your Deployment Target, you need to add runtime checks to your code to check for their availability. Examples:
Check if UIPasteboard is available:
Class PasteboardClass = NSClassFromString(#"UIPasteboard");
if (PasteboardClass != nil) {
// UIPasteboard is available
}
Check if a specific method is available:
if ([UITableViewCell respondsToSelector:#selector(initWithStyle:reuseIdentifier:)]) {
// ...
}

I didn't try this but i would recommend building against the most recent (3.x) SDK release. So you get any class and method definitions that might be available on the target device.
And in your application you have to check the OS release your application runs on. Based on the target OS you have to decide which Class and Method you should use. After all it is a big mess of conditional code, probably with a lot of additional code to provide missing functionality (i.e. direct access to SQLite instead of using Core-Data). In my experience that should not lead to problems, because most type information is erased at runtime.

Related

Only factor methods can have swift_name attribute Mapbox

I'm using cocoa pods and map box was working fine but I installed an update and this message appeared:
Now I can't run my project. I'm using map box iOS sdk 3.3.4. What should I do to fix this issue?
According to apple documentation:
The Swift compiler automatically imports Objective-C code as conventional Swift code. There may be edge cases in your code that are not automatically handled. If you need to change the name imported by Swift of an Objective-C method, enumeration case, or option set value, you can use the NS_SWIFT_NAME macro to customize how a declaration is imported. See more.
So all what I did was delete the implementation of the NS_SWIFT_NAME and with that I was able to build the project. I don't know what made this error appear but this was the best solution I found.
Example:
From this:
- (instancetype)recordWithRPM:(NSUInteger)RPM NS_SWIFT_NAME(init(RPM:));
To this:
- (instancetype)recordWithRPM:(NSUInteger)RPM;

The new servicestack client for Xcode 7 and swift 2.0 can't successfully compile code it generates?

Basically we have met a lot of problems to even compile the code that works in Xcode 6.4
Our entire stack of APIs are written with servicestack but we didn't use many of them with servicestack swift client. Only a few of them, so many DTOs are not usable even in xcode 6.4 But it compiles fine and we get to use those APIs that we specifically modified to accomandate servicestack swift client.
And now after a few days we have tested the latest servicestack swift client for XCode 7. And immediately there are 500+ error. Mostly can be categoried into 2-3 error types.
Type 'xxxxxx' does not conform to protocol 'Has Metadata' (because we don't have ireturn?)
Type alias 'Return' must be declared public because it matches a requirement in public protocol 'IReturn' (This one we do have a return type set, and this should be the one that we really care about and want to be able to call)
Static member 'toJson' cannot be used on instance of type 'xxxxx'
Static member 'fromObject' cannot be used on instance of type 'xxxxx'
Also, can we just ignore everything else and ask the plugin to include only 3 APIs that we really care about? I saw there is a commented area at the top called //IncludeTypes:
//ExcludeTypes:
Can we just use that? IF we can, how should we?
Please see the v4.0.46 Release Notes on Swift 2.0 Support.
The just released Xcode 7 only comes with Swift 2.0 which is a breaking language change that was only first added in ServiceStack v4.0.46. So if you want to use Xcode 7 you need to upgrade to v4.0.46.
You'll also need to download the latest ServiceStack Xcode 7 Plugin.
If you only want to include a few types you can use IncludeTypes.

dyld: Symbol not found problem (NSMutableAttributedString seems to be strongly linked)

[edited] I edited the question to isolate the problem and help other people better.
I'm using NSMutableAttributedString class in my app, which is available in iOS 3.2 and later. I'm also targeting 3.1.2-version devices though; for the backward compatibility, I used the following code:
CFAttributedStringRef attributedString;
if (NSClassFromString(#"NSMutableAttributedString")) {
attributedString = (CFAttributedStringRef)[[[NSMutableAttributedString alloc]
/* init... to initialize an object */ ] autorelease];
} else {
attributedString = CFAttributedStringCreate(kCFAllocatorDefault,
(CFStringRef)NSLocalizedString(#"MessageInEllipse",
#"Message to show in an ellipse"),
(CFDictionaryRef)attributes);
}
}
In line 3, I directly use the class name NSMutableAttributedString, but I expected this to be weakly linked by the linker, so it merely means nil here and the app would work without problems.
However, my app crashes on 3.1.2 devices when it launches, complaining that it can't find symbol NSMutableAttributedString. It seems like this class symbol is strongly linked. Why would this happen?
You need to change the framework linking configuration to "weak" link to the framework you are testing in the code.
Weak linking to a specific class is not available in all cases. In order to weakly link a class symbol,
The base SDK must be iOS 4.2 or newer.
The deployment target must be iOS 3.1 or newer.
The compiler must be the LLVM-GCC 4.2 or newer, or LLVM-Clang 1.5 or newer.
The class to which you want to weakly link must be declared using NS_CLASS_AVAILABLE macro.
The framework that the class belongs to must exist in the version for deployment, and if otherwise the framework itself must be weakly linked.
The third condition was my problem because I wrongfully thought I was using LLVM (I only found this with a help in the Apple's forum). GCC is the Xcode 3 default, so you must be careful.
If these condition doesn't hold, you cannot use weak linking. In this case, instead of using [NSMutableAttributedString alloc], for example, I should do like [NSClassFromString(#"NSMutableAttributedString") alloc].
There's one thing left to mention. As in #sza's answer, if I weakly link against the framework itself (Foundation in this case), I can use weak linking to the missing class even with GCC 4.2. Although it can solve the problem right away, in my opinion, it seems like a practice that should be avoided. I'm cautious in this because I'm not sure how weak linking to a framework does work in runtime, but wouldn't it impose more performance overhead than strongly linking to a framework, because all the information about the framework need to be acquired in runtime? Therefore, if I weakly link against a framework that is frequently used (sure does Foundation), I guess I could have a performance problem. At least, the references are very specific to say weakly link against a framework if that framework is not available for some of your deployment targets.
Therefore, I think the best practice here is:
always strongly link against frameworks that are available in my deployment target
and if I'm using a class of the framework that becomes available after the deployment target,
use weak linking if I can meet the requirements, OR
always use NSClassFromString() to refer to the class, no matter it would be executed or not in the older versions of iOS.

How to check if not available methods are used if deployment target < base sdk?

I would like to know how you check that your code do not call not available methods when the deployment target is inferior to base SDK ?
It is possible to run the application on a device with the SDK equal to deployment target, but I search a way more 'automatic'. Any idea ?
Regards,
Quentin
The easiest way to do this is to use the __IPHONE_OS_VERSION_MAX_ALLOWED preprocessor define.
You do this by adding
__IPHONE_OS_VERSION_MAX_ALLOWED=__IPHONE_4_2
or something similar to your "Preprocessor Macros" option in Build Settings of your target. You can look up versions available in <Availability.h>.
Unfortunately if you add this define it will cause mismatch errors with your precompiled header. So, to fix that you need to turn off the "Precompile Prefix Header" option in your build settings as well.
Once you do this you'll get a bunch of errors for classes that don't exist on your targeted SDK (for instance NSOrderedSet doesn't exist in iOS 4.2). If you're trying to go back pre-iOS 4 you'll probably get so many errors that the compiler bails--I don't know of a workaround for this. In any case, ignore the errors about missing classes in the UIKit headers, and go to the bottom of the error list; there you should find an error for each time you use a method or class that isn't included in the SDK pointed to by __IPHONE_OS_VERSION_MAX_ALLOWED. Make sure each of these methods is enclosed in an
if( [targetObject respondsToSelector:#selector(thePossiblyMissingSelector:)]
and you should be safe. Classes that may be missing should be tested as well
if ([NSOrderedSet class] != nil)
These settings aren't something you want to accidentally forget to flip back however. To make this an automatic option for testing, do the following:
Create a new build configuration called something like "Old SDK Testing".
Define __IPHONE_OS_VERSION_MAX_ALLOWED and the precompiled head option only for this configuration (hit the disclosure arrow beside each line in Build Settings to access per configuration settings).
Duplicate your current Scheme and set its name to something like "Old SDK Check".
Set the Build Configuration of the Run item in this new scheme to the build configuration you created in step 1.
Select the new Scheme and build.
Notes:
I make no guarantee that this will catch any/all of your issues.
Anything outside of UIKit will not be caught by this check.
This is not a substitute for testing your code on the versions of iOS you
plan to support.
use NSClassFromString();
Class cls = NSClassFromString(#"YourClass");
if (cls == nil)
is this you are looking for?
best way to do that which i found: compile code with old SDK :) link which can help
I think this question is releated with next
I belive that someday Apple allow to compile project for old SDK by simple defining #define __IPHONE_OS_VERSION_MAX_ALLOWED __IPHONE_3_0
upd: I found solution here
4.3 5.0 and 5.1 SDK just fail to compile after trying to redefine this macro
Are you looking for something like
- (BOOL)respondsToSelector:(SEL)aSelector
If you have an instance of a class, you can use the following to see if it understands the method you want to call:
if ([mipmapBrowserView respondsToSelector:#selector(setBackgroundColor:)]) {
// set the background layer since IKImageView supports it
}
Here, mipmapBrowserView is an instance of IKImageView, which was first introduced in Mac OS X 10.5. The setBackgroundColor: method of IKImageView was only added in 10.6, however, so I need to check before I call it. This allows me to build against the 10.6 SDK, and take advantage of the new features, yet still support OS X 10.5 as well. While this example involves OS X rather than iOS, the same method (pun intended?) works in iOS as well.
Note that things are slightly different when you are subclassing a class, and you want to know whether the superclass responds to a certain selector:
"You cannot test whether an object inherits a method from its superclass by sending respondsToSelector: to the object using the super keyword. This method will still be testing the object as a whole, not just the superclass’s implementation. Therefore, sending respondsToSelector: to super is equivalent to sending it to self. Instead, you must invoke the NSObject class method instancesRespondToSelector: directly on the object’s superclass...."

EXC_BAD_ACCESS when sending messages to blocks on 4.0 -> 3.2

I've been using http://code.google.com/p/plblocks/ for a while now to get blocks support in our 3.2 iPad app. It was recently pointed out to me that you can set xcode to use the 4.0 SDK, then set the OS Deployment target to 3.2.
If I do, the following code works.
void (^world)() = ^ {
NSLog(#"Hello World");
};
NSLog(#"Hello?");
world();
However, any time I sent a message to a block I get an EXC_BAD_ACCESS. For example, if I add the following line:
void (^acopy)() = [world copy];
This is a problem, since you have to copy blocks in order for them to keep their scope around later. Any idea why blocks would work, but the messages wouldn't? Am I missing some setting or something? Am I mistaken about the need to copy?
It seems you can still use Block_copy(). I don't know why PLBlocks would be able to use the Objective-C and the built-in compilers can't.
Here's an intro article:
http://developer.apple.com/mac/articles/cocoa/introblocksgcd.html
It says:
Importantly, block objects are laid
out in such a way that they are also
Objective-C objects if that runtime is
present.
I can't really see how you could be developing an iPad app without the Objective-C runtime present. As a sanity check you could make sure the C version (Block_copy()) works.