So I am attempting to bring the iOS 5 SDK to the Theos Makefile system. I have the following things working: Private Frameworks, Multitasking from UIKit. Since Theos uses SDK 3. This jump to SDK 5 breaks a few methods as they're deprecated. Keep in mind that I'll be using RPetrich's headers on GitHub for this. With that in mind, I'm trying to add the following dismissal code to the UIViewController header.
- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
This makes my compiler complain about the (void (^) (void)) part. I've tried replacing it with BOOL and just a regular void and then just calling nil when using the method. Doesn't work that way. I wanted to know if there was a way I can successfully compile with this method, or if I can somehow use dismissModalViewControllerAnimated: again. Some reason I can still use presentModalViewController:animated: and it says they both WILL be deprecated. Any suggestions?
change default compiler gcc/g++ to apple's clang/clang++
Related
In my Swift 2 project, targeting iOS 9.2 and above, in Xcode 8.2.1, I have code that shows the mail-compose screen like so:
if MFMailComposeViewController.canSendMail() {
let composeMailVC = MFMailComposeViewController()
composeMailVC.mailComposeDelegate = self
composeMailVC.setSubject("Test")
// etc
}
Originally I had a reference to the MessageUI.framework in my project properties, but after removing the framework reference and cleaning the project, it still builds fine and when I run the code on my device the mail compose window still appears and seems fully functional.
I cannot find any explicit references to MessageUI.framework in the raw text of my .xcodeproj file, nor is there anything in my Objective-C bridging header.
I know that Swift does make some implicit framework references, but I couldn't find anything that suggests MessageUI.framework is one of them.
Curiously when I jump to the definition of MFMailComposeViewController XCode shows it in the MessageUI module.
The compiler automatically added the frame work in given its previous direction - IE. Import.
There are 2 "writeImageToSavedPhotosAlbum" methods in ALAssetsLibrary Class:
- (void)writeImageToSavedPhotosAlbum:(CGImageRef)imageRef
metadata:(NSDictionary *)metadata
completionBlock:(ALAssetsLibraryWriteImageCompletionBlock)completionBlock
(available on iOS 4.1+)
- (void)writeImageToSavedPhotosAlbum:(CGImageRef)imageRef
orientation:(ALAssetOrientation)orientation
completionBlock:(ALAssetsLibraryWriteImageCompletionBlock)completionBlock
(available on iOS 4.0+)
I am using the 1st one (need iOS 4.1) in my code and it will crash on iOS 4.0 device. I am trying to use respondsToSelector to check which method is supported, however looks like the selector only check the method name, not the parameters.
I read some suggestions and feel it might not good by purely check on OS version, so is there anything similar to respondstoselector that can help me solve this problem?
You misunderstand the Objective-C method naming system. The selector is the combination of all foo:bar:baz: combined.
So, in this case, there is no method called writeImageToSavedPhotosAlbum. The first one is, as a selector, corresponds to
#selector(writeImageToSavedPhotosAlbum:metadata:completionBlock:)
and the second one is
#selector(writeImageToSavedPhotosAlbum:orientation:completionBlock:)
In your code, check whether the first selector is available or not, as in
if([obj respondsToSelector:#selector(writeImageToSavedPhotosAlbum:metadata:completionBlock:)]){
....
}
This should distinguish whether the first one is available or not.
These methods have different names, so you can test them separately.
if ([assetsLibrary respondsToSelector:
#selector(writeImageToSavedPhotosAlbum:metadata:completionBlock:)]) {
// Now you can safely use this method.
}
If you wanted to test the other one you would use #selector(writeImageToSavedPhotosAlbum:orientation:completionBlock:).
You can then differentiate them with os version. How about it?
Hi
I am using SDK 4.1 to build an iPhone app and I set the target OS to 3.1.3.
When I install the app on devices running iOS4.1. everything goes smoothly.
When I try to run the app on devices running 3.1.3 I get the stacktrace below.
dyld: Symbol not found: _OBJC_CLASS_$_UINib
Referenced from: /var/mobile/Applications/BDD67A1E-9B40-43E7-A012-7D92036B2E24/ThisIsMy.app/ThisIsMy
Expected in: /System/Library/Frameworks/UIKit.framework/UIKit
in /var/mobile/Applications/BDD67A1E-9B40-43E7-A012-7D92036B2E24/ThisIsMy.app/ThisIsMy
My guess is that it's because UINib was only added to the SDK in 4.0.
What I would like to know is how you mitigate this problem. What should I do to support 3.1.3?
Cheers..
I strongly recommend you not weakly link UIKit. This may hide future linker issues and result in crashes. I'm also simply not comfortable with telling the linker that UIKit is optional when it plainly isn't. Big hack.
Instead, initialize and call UIPopoverController indirectly using NSClassFromString:
Class popover = NSClassFromString(#"UIPopoverController");
if (nil != popover)
{
self.myPopover = [[popover alloc] initWithContentViewController:myContent];
}
If you still have linker errors, you may need to call UIPopoverController's messages using NSSelectorFromString:
Class popover = NSClassFromString(#"UIPopoverController");
if (nil != popover)
{
SEL myInit = NSSelectorFromString(#"initWithContentViewController:");
self.myPopover = [[popover alloc] performSelector:myInit withObject:myContent];
}
For portability, I recommend writing a proxy object to handle these implementation details.
First, you need to change the framework reference to a weak link (aka "optional"). Then use the techniques described here to test that a class exists before calling your code that uses it.
How do you deal with deprecated methods in iPhone that require you to use a newer method, not available in older versions?
Consider the case of setStatusBarHidden:animated:, which was deprecated in iOS 3.2. The documentation points you to use setStatusBarHidden:withAnimation:, which is only available in iOS 3.2 or later.
If I understand correctly, this means that to target all devices (iOS 3.0 or later), I have to ask first if setStatusBarHidden:withAnimation: is available. If it is, use it. If not, use the deprecated method. But I would still get a warning of deprecation.
Is this correct (please say it isn't!)? If it is, is there any way to suppress this deprecation warning, or to indicate the compiler that I have already handled the problem?
I found a similar question that assumes that yes, this is the correct way of dealing with deprecated methods, and no, there is no way to suppress deprecation warnings on a per-case basis, but there are hacks to mislead the compiler.
To deal with the example case, I decided to create an util class using one of these hacks:
#protocol UIApplicationDeprecated
- (void) setStatusBarHidden:(BOOL)hidden animated:(BOOL)animated;
#end
#implementation UIUtils
+ (void) setStatusBarHidden:(BOOL)hidden animated:(BOOL)animated {
if([[UIApplication sharedApplication] respondsToSelector:#selector(setStatusBarHidden:withAnimation:)]) {
[[UIApplication sharedApplication] setStatusBarHidden:hidden withAnimation:animated ? UIStatusBarAnimationSlide : UIStatusBarAnimationNone];
} else {
id<UIApplicationDeprecated> app = (id)[UIApplication sharedApplication];
[app setStatusBarHidden:hidden animated:animated];
}
}
#end
If I'm not mistaken using respondsToSelector is costly. This could be optimized for performance to remember if the new selector is present after the first query, thus avoiding the need for reflection in subsequent calls.
Coming from a Java background, I find this way of dealing with deprecation appalling and I still can't believe that this is how iOS designers expect us to deal with this problem. More thoughts on the subject will be much appreciated.
Probably there is a better answer, but what I did once was:
1 check if the deprecatedMethod is available. (using respondsToSelector: method)
2 if yes then call that method using objective-c runtime function:
id objc_msgSend(id theReceiver, SEL theSelector, ...)
when using this function the compiler won't give you any warnings :)
3 other wise use the new method
Invoking method this way:
id objc_msgSend(id theReceiver, SEL theSelector, ...)
will be better choise in case you want to omit warning that UIApplication may not respond to setStatusBarHidden:withAnimation: method (in iOS 3.0 or later).
well to begin with I'm sure this is a simple question.
I am developing an iPhone app with the iAd Framework, which only runs for iOS 4.0 or higher.
Still, I wanna choose a iPhone OS 3.0 deployment target, which causes everything to crash.
How do I conditionally include the iAd framework?
...I mean, it would be something like:
...if([[UIDevice currentDevice] systemVersion]>=4.0]) #import
Obviously this won't work because I don't know the correct syntax. Also:
How do I conditionally declare an AdView* variable?
How do I conditionally handle this AdView* variable in my implementation file.
If you guys could help me, I will be very well impressed.
Thanks
You don't need to change your include, you need to make the iAd (or any other new framework) linked weakly:
In your target, find iAd in the linked frameworks and change its "Role" from "Required" to "Weak".
To handle the variable conditionally, use NSClassFromString function, like this:
Class AdClass = NSClassFromString(#"ADBannerView");
if(AdClass) {//if the class exists
ADBannerView* myAd = [[AdClass alloc] initWithFrame:CGRectZero];
// do something with the ad
}
If OS is older than iOS 4.0, AdClass will be nil and the code won't execute. Note that using ADBannerView* as the type of the variable shouldn't cause any problems, as it's just a hint for a compiler and is the same as id after compilation.