Is it possible to hide code based on SDK version - ios

Because my build machine is still using the Xcode 12.5 , So the UITabBar's scrollEdgeAppearance(which will not exist in the Xcode 12.5's SDK) will make the build fail even i'am using the #available to check .
if (#available(iOS 15.0, *)) {
UINavigationBarAppearance* navBarAppearance = [UINavigationBarAppearance new];
navBarAppearance.backgroundColor = [UIColor colorNamed:#"navbar_bg"];
[UINavigationBar appearance].standardAppearance = navBarAppearance;
[UINavigationBar appearance].scrollEdgeAppearance = navBarAppearance;
UITabBarAppearance* tabBarAppearance = [UITabBarAppearance new];
tabBarAppearance.backgroundColor = [UIColor colorNamed:#"second_bg"];
[UITabBar appearance].standardAppearance = tabBarAppearance;
[UITabBar appearance].scrollEdgeAppearance = tabBarAppearance;
[UITableView appearance].sectionHeaderTopPadding = 0;
}
So is it possible to do this kind of SDK checking in code ,when the build SDK is not the newest SDK , these code will not be involved to build? like this
if (BuilDSDK >= someversion)
{
[UITabBar appearance].scrollEdgeAppearance = tabBarAppearance;
}

#available is a runtime availability check, not really usable for compile-time stuff in this situation.
In Objective-C, you can wrap the code part applied on iOS 15 SDK into another, macro condition:
#ifdef __IPHONE_15_0
if (#available(iOS 15.0, *)) {
...
} else {
#endif
// possible legacy branch code
#ifdef __IPHONE_15_0
}
#endif
__IPHONE_15_0 is defined starting with iOS 15 SDK, thus being omitted when building in Xcode 12/iOS 14 SDK. 👍

Another similar solution for Swift classes can be found here: https://stackoverflow.com/a/69583441/1195661:
#if swift(>=5.5) // Only run on Xcode version >= 13 (Swift 5.5 was shipped first with Xcode 13).
if #available(iOS 15.0, *) {
UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance
}
#endif

Related

After upgrading to Xcode 11.2 from Xcode 11.1, app crashes due to _UITextLayoutView

After upgrading to Xcode 11.2 from Xcode 11.1 my app crashes:
*** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: 'Could not instantiate class named _UITextLayoutView because no class named _UITextLayoutView was found; the class needs to be defined in source code or linked in from a library (ensure the class is part of the correct target)'
Why is this happening? How can I prevent this crash?
Update: Fixed! 🎉🎊
The ONLY Solution is to update
This bug is fixed in Xcode 11.2.1. So you can download and use it from here.
Storyboards containing a UITextView will no longer cause the app to crash on operating system versions earlier than iOS 13.2, tvOS 13.2, or macOS 10.15.2. (56808566, 56873523)
Xcode 11.2 is deprecated by Apple on November 5, 2019
if you ever try to submit your app that build with Xcode 11.2 to the AppStore, you will be rejected:
App Store Connect Operation Warning
WARNING ITMS-90703: "Deprecated Xcode Build. Due to resolved app archives issues, we have deprecated Xcode 11.2 on November 5, 2019. Download Xcode 11.2.1 or newer, rebuild your app and resubmit."
So all workarounds done with the Xcode 11.2 is useless
It's a bug for Xcode 11.2, and fixed in Xcode 11.2.1.
Solution(s)
Roll back to previous Xcode release version from: Rollback is not an option anymore and AppStore will reject any build with Xcode below 11.2.1 take a look at this
https://developer.apple.com/services-account/download?path=/Developer_Tools/Xcode_11.1/Xcode_11.1.xip
Note that you should use Safari to download it and you must first login to Apple developer portal.
You can find all other Xcode versions and other resources link (including release and beta versions) here at https://developer.apple.com/download/more
The workaround
This is very hard but working workaround.
Replace all UITextViews in storyboards and Xibs with the pure code version.
Note that this bug is found and fixed by Apple
Also earlier, the bug was confirmed by Apple Staff edford
For those with iOS 13.2 and can not use Xcode 11.1 anymore:
Update macOS to 10.15.1 or later
Install Xcode 11.2.1 or later
It should be work now on the updated device.
For those with storyboard:
Subclass UITextView
Assign it to all UITextView objects
Don't forget to update any property changes that may lose in the subclassing.
For those comfortable with method swizzling (Objc and dynamic behavior)
Head to the #aftab muhammed khan answer for Objective-C and #MikRo answer for Swift adapted version
Just don't do it anymore:
Even if these last two swizzling workarounds are not using Apple private API, they will be rejected in AppStore because Apple will not accept builds with Xcode versions under 11.2.1!
And once again:
Xcode 11.2 is deprecated by Apple on November 5, 2019
Congratulation
The New version of Xcode (11.2.1) is available now which is the best way to get rid off this issue.
Workarounds
#Mojtaba Hosseini the solution I proposed was from the help and the participation from my side to my fellow developers over StackOverflow. You, me and all the rest of the developer here already know that when the new version is announced by Apple, this issue will be gone.
But Beside Everything
The solution aforementioned was definitely accepted by Apple Review as there is no private API involved at all. This approach is very similar to the creating property like
#interface UITextView (Layout)
Or
UITextView+Layout.h
So when you are creating property you are directly using APPLE Private Components and re-moduling them as per you depends or requirement.
The Simple Example is AMFNetworking classes
- (void)setImageWithURL:(NSURL *)url {
[self setImageWithURL:url placeholderImage:nil];
}
Hope I am done with the Allegation
The answer below was just some help from my side to enable developer to continue developing as you we initially proposed developer to roll back Xcode. This was a bad practice to download 8 GB Xcode again since we all know that the new version of Xcode will be released soon.
While it is fixed in Xcode 11.2.1, I got one solution for Xcode 11.2 by which you can get rid off this crash:
*** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: 'Could not instantiate class named _UITextLayoutView because no class named _UITextLayoutView was found; the class needs to be defined in source code or linked in from a library (ensure the class is part of the correct target)'
SOLUTION
Go to the Build Setting search for "DEAD_CODE_STRIPPING" and set it to NO
DEAD_CODE_STRIPPING = NO
Then
create files UITextViewWorkaround
UITextViewWorkaround.h
#import <Foundation/Foundation.h>
#interface UITextViewWorkaround : NSObject
+ (void)executeWorkaround;
#end
UITextViewWorkaround.m
#import "UITextViewWorkaround.h"
#import <objc/runtime.h>
#implementation UITextViewWorkaround
+ (void)executeWorkaround {
if (#available(iOS 13.2, *)) {
}
else {
const char *className = "_UITextLayoutView";
Class cls = objc_getClass(className);
if (cls == nil) {
cls = objc_allocateClassPair([UIView class], className, 0);
objc_registerClassPair(cls);
#if DEBUG
printf("added %s dynamically\n", className);
#endif
}
}
}
#end
execute it in the app delegate
#import "UITextViewWorkaround.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[UITextViewWorkaround executeWorkaround];
return yes;
}
Compile the code and you will have a running app :)
The issue was fixed in Xcode 11.2.1.
EDIT: As the fix is now released, you should switch to that Xcode version and comment out this workaround. As Mojtaba Hosseini in his answer mentioned:
... these last two swizzling workarounds are using Apple private API and will be reject from Apple review!
For the time until the fix was released by Apple, this was a good workaround to continue developing and testing.
For Xcode 11.2, based on the idea of Aftab Muhammed Khan and with the help of John Nimis I just tested the following code.
No change in the storyboard files necessary!
Edited my AppDelegate.swift file and added this class
//******************************************************************
// MARK: - Workaround for the Xcode 11.2 bug
//******************************************************************
class UITextViewWorkaround: NSObject {
// --------------------------------------------------------------------
// MARK: Singleton
// --------------------------------------------------------------------
// make it a singleton
static let unique = UITextViewWorkaround()
// --------------------------------------------------------------------
// MARK: executeWorkaround()
// --------------------------------------------------------------------
func executeWorkaround() {
if #available(iOS 13.2, *) {
NSLog("UITextViewWorkaround.unique.executeWorkaround(): we are on iOS 13.2+ no need for a workaround")
} else {
// name of the missing class stub
let className = "_UITextLayoutView"
// try to get the class
var cls = objc_getClass(className)
// check if class is available
if cls == nil {
// it's not available, so create a replacement and register it
cls = objc_allocateClassPair(UIView.self, className, 0)
objc_registerClassPair(cls as! AnyClass)
#if DEBUG
NSLog("UITextViewWorkaround.unique.executeWorkaround(): added \(className) dynamically")
#endif
}
}
}
}
and within the delegate call for "didFinishLaunchingWithOptions" call the workaround
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// This is the workaround for Xcode 11.2
UITextViewWorkaround.unique.executeWorkaround()
}
I've adapted khan's Obj-C solution to Swift:
import UIKit
#objc
class UITextViewWorkaround : NSObject {
static func executeWorkaround() {
if #available(iOS 13.2, *) {
} else {
let className = "_UITextLayoutView"
let theClass = objc_getClass(className)
if theClass == nil {
let classPair: AnyClass? = objc_allocateClassPair(UIView.self, className, 0)
objc_registerClassPair(classPair!)
}
}
}
}
Call it at the end of didFinishLaunchingWithOptions in AppDelegate.
Thanks #Aftab!
A quicker fix:
///Substitute class for _UITextLayoutView bug
class FixedTextView: UITextView {
required init?(coder: NSCoder) {
if #available(iOS 13.2, *) {
super.init(coder: coder)
}
else {
let rect = CGRect(origin: .zero, size: CGSize(width: 100, height: 44*3))
super.init(frame: rect, textContainer: nil)
}
}
}
Add this code somewhere and then replace all storyboard instances to FixedTextView.
Note: you will lose any attributes created in the storyboards. This could have serious implications (e.g. delegate setting, size, etc.)
Updated Solution:
Update to Xcode 11.2.1. It works on iOS 11, 12 or 13 devices for me.
Refer to apple's documentation
This update fixes a critical issue that could cause apps using UITextView to crash.
Old Solution:
Downloaded Xcode 11.1 from https://developer.apple.com/download/more/
Switching back from 11.2 to 11.1 fixed the crash.
Also, for me even with Xcode 11.2, when I upgraded my iPhone to 13.2, that fixed the crash.
11.2.1 GM seed resolves this issue
(and it can be used to publish to App Store)
Go to https://developer.apple.com/download/. Download Xcode 11.2.1 GM seed
Release notes confirm it fixes this error:
You can go to download the last Xcode beta release (11.2.1 GM) from Apple developer website.
Here the direct link
Improving on #garafajon answer. For me it works in most cases.
///Substitute class for _UITextLayoutView bug
class FixedTextView: UITextView {
required init?(coder: NSCoder) {
if #available(iOS 13.2, *) {
super.init(coder: coder)
}
else {
super.init(frame: .zero, textContainer: nil)
self.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.contentMode = .scaleToFill
self.isScrollEnabled = false // causes expanding height
// Auto Layout
self.translatesAutoresizingMaskIntoConstraints = false
self.font = UIFont(name: "HelveticaNeue", size: 18)
}
}
}
As a "quick" fix you can add the UITextView directly from the code and not via IB. At least it worked for me. Though from my point of view it's better to rollback to previous Xcode/wait for the new one.
It is a bug with Xcode 11.2. Subclassed Textviews are crashing on all devices not having the neweset iOS build (13.2) installed. You probably better not build a release with that build.
You can now:
downgrade Xcode to 11.1 or
upgrade your device to iOS 13.2
I used a successful workaround, but it was painful. This is the process that I followed:
Open the XIB in a text editor
Find the offending TextView. In my case:
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="782-j1-88c" customClass="LCAnsiConsoleTextView">
<rect key="frame" x="16" y="20" width="343" height="589"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" name="Menlo-Regular" family="Menlo" pointSize="12"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
Note its id (in my case: id="782-j1-88c")
Override the class as noted in the answers above and recreate the options (mine is Objective-C, sorry):
#implementation FixedTextView
- (id) initWithCoder:(NSCoder*)coder
{
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){13,2,0}])
self = [super initWithCoder:coder];
else {
self = [super initWithFrame:CGRectMake(16, 3, 343, 605)];
self.editable = YES;
self.selectable = YES;
self.insetsLayoutMarginsFromSafeArea = YES;
self.clipsToBounds = YES;
self.clearsContextBeforeDrawing = YES;
self.autoresizesSubviews = YES;
self.contentMode = UIViewContentModeScaleToFill;
self.scrollEnabled = YES;
self.userInteractionEnabled = YES;
self.multipleTouchEnabled = YES;
self.translatesAutoresizingMaskIntoConstraints = NO;
self.font = [UIFont fontWithName:#"Menlo-Regular" size:12.0];
}
return self;
}
Note the constraints that include your text view id, and recreate those constraints against the other element ids in your view or view controller. In my case:
- (id) initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self xibSetup];
[self initView];
/*
<constraint firstItem="75C-lt-YtE" firstAttribute="top" secondItem="782-j1-88c" secondAttribute="bottom" constant="8" symbolic="YES" id="8SH-5l-FAs"/>
<constraint firstItem="782-j1-88c" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leadingMargin" id="Mve-aZ-HCe"/>
<constraint firstItem="782-j1-88c" firstAttribute="leading" secondItem="75C-lt-YtE" secondAttribute="leading" id="dPG-u3-cCi"/>
<constraint firstItem="782-j1-88c" firstAttribute="trailing" secondItem="iN0-l3-epB" secondAttribute="trailingMargin" id="sjT-0Q-hNj"/>
<constraint firstItem="782-j1-88c" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" id="vic-vZ-osR"/>
*/
[self.command.topAnchor constraintEqualToAnchor:self.console.bottomAnchor constant:8].active = YES;
[self.console.leadingAnchor constraintEqualToAnchor:self.layoutMarginsGuide.leadingAnchor].active = YES;
[self.console.leadingAnchor constraintEqualToAnchor:self.command.leadingAnchor].active = YES;
[self.console.trailingAnchor constraintEqualToAnchor:self.trailingAnchor].active = YES;
[self.console.topAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.topAnchor].active = YES;
}
return self;
}
Doing this fixed the issue for me with no loss in desired functionality. Fortunately I had only one UITextView to replace. Otherwise, this gets untenable.
I had the same issue I just upgraded my Xcode 11.2 to 11.2.1 it worked fine.
After upgrade I have tested the same on iOs 13 and iOS 12 and it was working fine.
1. Problem:
There is an issue with Xcode 11.2 wherein Storyboards containing a UITextView will cause the app to crash on OS versions earlier than iOS 13.2 if compiled with Xcode 11.2.
Check this apple documentation.
2. Solution:
The only solution is to update your Xcode to 11.2.1 or 11.3.
Xcode 11.2.1 was particularly released to fix this crash issue.
Check this apple documentation.
3. Suggestion:
I would suggest you to go with latest version of Xcode 11.3 since this supports developing apps for iOS 13.3 and also there are many new features.
Check this apple documentation.
This issue was fixed in Xcode version 11.2.1, and called out in the release notes:
This update fixes a critical issue that could cause apps using UITextView to crash

How to change wallpaper programmatically in iOS 10?

I use the privite API to change wallpapper. It works well in iOS 9.3, but in iOS 10 nothing happend, no crash.
Class class = NSClassFromString(#"PLStaticWallpaperImageViewController");
_wallPaper = [[class alloc] performSelector:NSSelectorFromString(#"initWithUIImage:") withObject:_image];
int wallpaperMode = 0;
[_wallPaper setValue:#(wallpaperMode) forKey:#"wallpaperMode"];
[_wallPaper setValue:#(YES) forKeyPath:#"allowsEditing"];
[_wallPaper setValue:#(YES) forKeyPath:#"saveWallpaperData"]; //
NSString *selector = #"setImageAsLockScreenClicked:";
[_wallPaper performSelector:NSSelectorFromString(selector) withObject:nil];
Does anyone know how to do this in iOS 10?

How to detect iOS version in Objective C?

I'm using Firebase 3.7.x to store my database. Firebase 3.7.x is support iOS 7.0 or higher but my project supports from iOS 6.0. So I want to detect iOS version in device to call #import Firebase. Something like that:
if IOS_7_OR_HIGHER
#import Firebase
else
//do nothing
if IOS_7_OR_HIGHER
- (void)dosomething{}
else
- (void)donothing {}
I know about if #available in swift. Is there any code like if #available in Objective C? Or is there any way to import Firebase for iOS 7 or higher and disable disable for iOS6?
Thanks.
You can get device system version by using
-(NSString*)getDeviceVersion{
return [[UIDevice currentDevice] systemVersion];
}
it will return you device version as string e.g. #"4.0" .
Hope it help you.
Try below code:
NSArray *osVersion = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:#"."];
if ([[osVersion objectAtIndex:0] intValue] >= 7) {
// iOS-7 or greater
} else if ([[osVersion objectAtIndex:0] intValue] == 6) {
// iOS-6 code
} else if ([[osVersion objectAtIndex:0] intValue] > 2) {
// iOS-3,4,5 code
} else {
// iOS-1,2... code
}
To answer your question you can do it like this:
#ifdef __IPHONE_6_0
//Do something patchy!
#else
#import Firebase
#endif
Humble suggestion: You can consider upgrading your app.
A recent iOS version stats counter from Apple showing that there are only 5% devices which are still having iOS 8, 7 or <= 6. Means, you should drop out support for all those versions or you should start supporting iOS9 onwards.
By doing this you will get all the latest iOS features and you will never have to make this kind of patch in future.
Source: https://developer.apple.com/support/app-store/

Use AVSpeechSynthesizer on iOS 7 but retain compatibility for iOS 6

Hello I Understand TTS is only available in iOS 7 but i used many apis in the past by checking if the class is available and managed to retain compatibility on previous versions but with AVSpeechSynthesizer it doesn't seem to work, can you please help me use TTS for iOS 7 and retain compatibility by disabling it in iOS 6, thank you very much.
Here is my code but it doesn't seem to work
if (([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)) {
if([AVSpeechSynthesizer class]) {
AVSpeechSynthesizer *synth = [[AVSpeechSynthesizer alloc] init];
AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:text];
utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:#"en-US"];
utterance.rate = AVSpeechUtteranceDefaultSpeechRate/2;
utterance.pitchMultiplier = 0.9;
utterance.preUtteranceDelay = 0;
utterance.postUtteranceDelay = 0;
[synth speakUtterance:utterance];
} else {
// Feature not available, do something else
}
}
i've already linked avfoundation in my project and set deployment target iOS 6 and it seems to work only when runing on iOS 7 devices if its iOS 6 it crashes.
here is the error message i get
dyld: Symbol not found: _AVSpeechUtteranceDefaultSpeechRate
The framework needs to be be weak linked. Do the following:
Click your project file in the Project Navigator (on the left) to open your project in the editor
Click the Build Phases tab in the editor
Expand the Link Binary With Libraries section
Set AVFoundation.framework from Required to Optional
Additionally, you might want to update your version check with one that's safer (and recommended by Apple).

Check if device is running iOS 5 or higher [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Check iPhone iOS Version
One of the changes made in iOS 5 is the ability to override the drawrect methods. This means I need to change the appearance of the navigationBar and tabBar on a different way. I am able to use apple new methods:
[[UINavigationBar appearance]setBackgroundImage:[UIImage imageNamed:#"navigationBarBackgroundRetro.png"] forBarMetrics:UIBarMetricsDefault];
//I create my TabBar controlelr
tabBarController = [[UITabBarController alloc] init];
// I create the array that will contain all the view controlers
[[UITabBar appearance] setBackgroundImage:
[UIImage imageNamed:#"navigationBarBackgroundRetroTab.png"]];
[[UITabBar appearance] setSelectionIndicatorImage:
[UIImage imageNamed:#"tab_select_indicator"]];
I'm developing an app for iOS 4.3 and 5.0. However, iOS 5 ignores the drawrect method that I'm overriding, so it should run the above code. How can I check the iOS version so I can use the above code if the device is on iOS 5?
The samples below work for any version number. e.g.: to detect iOS 5 instead 7, replace 7 with a 5 in the code.
AvailabilityInternal.h macros
This detects the SDK you are building with:
#ifdef __IPHONE_7_0
// iOS 7.0
#endif
This detects the version set as Deployment Target in the General tab of your target configuration:
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
// iOS 7.0 or later
#else
// less than 7
#endif
NSFoundation version
BOOL isAtLeastIOS61 = NSFoundationVersionNumber >= NSFoundationVersionNumber_iOS_6_1;
BOOL isAtMost61 = NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_6_1;
BOOL is7x = floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1;
If you ⌘ click NSFoundationVersionNumber, you'll see version constants for iOS and OSX. The constant for the current SDK is always missing.
Core Foundation version
BOOL atLeastIOS61 = kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_6_1;
As with NSFoundationVersionNumber, the SDK version is missing.
Device system version
NSString *version = [[UIDevice currentDevice] systemVersion];
BOOL isAtLeast6 = [version floatValue] >= 6.0;
BOOL isAtLeast7 = [version floatValue] >= 7.0;
An alternative way:
BOOL isAtLeast6 = [version hasPrefix:#"6."];
BOOL isAtLeast7 = [version hasPrefix:#"7."];
An alternative way:
BOOL isAtLeast6 = [version compare:#"6.0" options:NSNumericSearch] != NSOrderedAscending
BOOL isAtLeast7 = [version compare:#"7.0" options:NSNumericSearch] != NSOrderedAscending
In case of concerns about float/string conversion, let it be know that everything above reports correctly if the version is equal or greater, for any possible iOS version (6.0, 6.0.1, 6.1, etc.).

Resources