iOS 13 兼容性适配检查
复制下面的关键字在 Xcode 中正则搜索
(_UINavigationBarContentView|forKeyPath|_placeholderLabel|boolForKey|stringForKey|deviceToken|CNCopyCurrentNetworkInfo|presentViewController|UISearchDisplayController|UIWebView|MPMoviePlayerController|@available|_cancelButtonText|_searchField)

crash: [searchBar setValue:@”取消” forKey:@”_cancelButtonText”];
for (UIView *subview in self.subviews) {
if ([NSStringFromClass([subview class]) containsString:@”_UINavigationBarContentView”]) {
subview.layoutMargins = UIEdgeInsetsZero;
break;
}
}
这种做法在 iOS 13 中会导致崩溃,崩溃信息如下
*** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Client error attempting to change layout margins of a private view’
解决方案
使用设置 frame 的方式,让 _UINavigationBarContentView 向两边伸展,从而抵消两边的边距。
[_textField setValue:[UIColor redColor] forKeyPath:@”_placeholderLabel.textColor”];///崩溃
[_textField setValue:[UIFont systemFontOfSize:14] forKeyPath:@”_placeholderLabel.font”];///崩溃解决方案
_textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@”姓名” attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:14],NSForegroundColorAttributeName:[UIColor redColor]}];在Xcode10上编译不会有问题,但在Xcode11上编译的会崩溃。并且- (void)setValue:(nullable id)value forKey:(NSString *)key方法没问题,- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath会崩溃
第三方 NSDictionary 扩展重名
打印日志:objc[5337]: REPLACED: -[NSDictionary boolForKey:] by category HMFoundation (IMP was 0x104ae8280 (/var/containers/Bundle/Application/B1486820-C2CC-4339-9D41-DBF1AC77EBF2/iHome4iPhone.app/iHome4iPhone), now 0x186d95a8c (/System/Library/PrivateFrameworks/HMFoundation.framework/HMFoundation))解决方案
Category 换名字
stringForKey 返回的是默认值是@“”并非nil.
if([dict stringForKey:@”key”]){} 会发生逻辑错误解决方案
[dict stringForKey:@”key”].length > 0
见 <Category 私有函数>
– (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSString *token = [deviceToken description];
for (NSString *symbol in @[@” “, @”<“, @”>”, @”-“]) {
token = [token stringByReplacingOccurrencesOfString:symbol withString:@””];
}
}
在 iOS 13 中,这种方法已经失效,NSData类型的 deviceToken 转换成的字符串变成了:
{length = 32, bytes = 0xd7f9fe34 69be14d1 fa51be22 329ac80d … 5ad13017 b8ad0736 }
解决方案
#include <arpa/inet.h>
– (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
if (![deviceToken isKindOfClass:[NSData class]]) return;
const unsigned *tokenBytes = [deviceToken bytes];
NSString *hexToken = [NSString stringWithFormat:@”%08x%08x%08x%08x%08x%08x%08x%08x”,
ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
NSLog(@”deviceToken:%@”, hexToken);
}
苹果将 UIViewController 的 modalPresentationStyle 属性的默认值改成了新加的一个枚举值 UIModalPresentationAutomatic,对于多数 UIViewController,此值会映射成 UIModalPresentationPageSheet。
这种效果弹出来的页面导航栏部分是会被砍掉的,在 storyboard 中也可以看到,页面布局时需要注意导航栏的内容不要被遮挡。注意,我们原来以全屏的样式弹出一个页面,那么将这个页面弹出的那个 ViewController 会依次调用 viewWillDisappear 和 viewDidDisappear。然后在这个页面被 dismiss 的时候,将他弹出的那个 ViewController 的 viewWillAppear 和 viewDidAppear 会被依次调用。然而使用默认的视差效果弹出页面,将他弹出的那个 ViewController 并不会调用这些方法,原先写在这四个函数中的代码以后都有可能会存在问题。解决方案:如果视差效果的样式可以接受的话,就不需要修改;如果需要改回全屏显示的界面,需要手动设置弹出样式:
– (UIModalPresentationStyle)modalPresentationStyle {
return UIModalPresentationFullScreen;
}
或
self.modalPresentationStyle = .fullScreen
CNCopyCurrentNetworkInfo 文档说明,应用还需要符合下列三项条件中的至少一项才能得到正确的值:使用 Core Location 的应用, 并获得定位服务权限。
使用 NEHotspotConfiguration 来配置 WiFi 网络的应用。
目前正处于启用状态的 VPN 应用。解决方案:
获取 wift信息还要先获得位置授权
在 iOS 8 之前,我们在 UITableView 上添加搜索框需要使用 UISearchBar + UISearchDisplayController 的组合方式,而在 iOS 8 之后,苹果就已经推出了 UISearchController 来代替这个组合方式。在 iOS 13 中,如果还继续使用 UISearchDisplayController 会直接导致崩溃,崩溃信息如下:
*** Terminating app due to uncaught exception ‘NSGenericException’, reason: ‘UISearchDisplayController is no longer supported when linking against this version of iOS. Please migrate your application to UISearchController.’
解决方案:
使用 UISearchController 替换 UISearchBar + UISearchDisplayController 的组合方案。
UIWebView 将被禁止提交审核
如果开发者将包含 UIWebView api 的应用更新上传到 App Store 审核后,其将会收到包含 ITMS-90809 信息的回复邮件解决方案
用 WKWebView 替代 UIWebView,确保所有 UIWebView 的 api 都要移除,如果需要适配 iOS 7 的可以通过 openURL 的方式在 Safari 打开。
在 iOS 9 之前播放视频可以使用 MediaPlayer.framework 中的MPMoviePlayerController类来完成,它支持本地视频和网络视频播放。但是在 iOS 9 开始被弃用,如果在 iOS 13 中继续使用的话会直接抛出异常:*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘MPMoviePlayerController is no longer available. Use AVPlayerViewController in AVKit.’
解决方案
使用 AVFoundation 里的 AVPlayer 作为视频播放控件。
使用 @available 导致旧版本 Xcode 编译出错
在 Xcode 11 的 SDK 工程的代码里面使用了 @available 判断当前系统版本,打出来的包放在 Xcode 10 中编译,会出现一下错误:Undefine symbols for architecture i386:
“__isPlatformVersionAtLeast”, referenced from:
…
ld: symbol(s) not found for architecture i386复制代码从错误信息来看,是 __isPlatformVersionAtLeast 方法没有具体的实现,但是工程里根本没有这个方法。实际测试无论在哪里使用@available ,并使用 Xcode 11 打包成动态库或静态库,把打包的库添加到 Xcode 10 中编译都会出现这个错误,因此可以判断是 iOS 13 的 @available 的实现中使用了新的 api。
解决方案
如果你的 SDK 需要适配旧版本的 Xcode,那么需要避开此方法,通过获取系统版本来进行判断:
if ([UIDevice currentDevice].systemVersion.floatValue >= 13.0) {
…
}另外,在 Xcode 10 上打开 SDK 工程也应该可以正常编译,这就需要加上编译宏进行处理:
#ifndef __IPHONE_13_0
#define __IPHONE_13_0 130000
#endif
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
…
#endif
UITextField *textField = [searchBar valueForKey:@”_searchField”]; // Crash
解决方案
// 替代方案 1,使用 iOS 13 的新属性 searchTextField
searchBar.searchTextField.placeholder = @”search”;
[searchBar setValue:@”取消” forKey:@”_cancelButtonText”]; // Crash
解决方案
// 替代方案,用同上的方法找到子类中 UIButton 类型的属性,然后设置其标题
UIButton *cancelButton = [self findViewWithClassName:NSStringFromClass([UIButton class]) inView:searchBar];
[cancelButton setTitle:@”取消” forState:UIControlStateNormal];
类 | Category函数 | 系统Category仓库 |
NSObject | safeValueForKey | AccessibilityUtilities |
NSDictionary | boolForKey | HMFoundation |
NSDictionary | stringForKey | HMFoundation |
NSDictionary | arrayForKey | HMFoundation |
NSDictionary | dictionaryForKey | HMFoundation |
NSDictionary | dataForKey | HMFoundation |
NSDictionary | numberForKey | HMFoundation |
NSDictionary | mutableDictionaryForKey | HMFoundation |
NSDictionary | mutableArrayForKey | HMFoundation |
NSString | unsignedLongLongValue | HearingUtilities |
NSString | unsignedLongLongValue | HearingUtilities |
NSString | stdStringForString | libwebrtc |
NSString | stringForStdString | libwebrtc |
NSString | unsignedIntValue | IMFoundation |
NSString | stdString | GeoServices |
NSString | containsString | Foundation |
NSString | unsignedIntValue | IMFoundation |
NSString | hexValue | IMFoundation |
NSString | hasPrefixCaseInsensitive | CalendarFoundation |
NSString | urlEncodedString | Social |
NSMutableArray | nonRetainingArray | IMFoundation |
(safeValueForKey|boolForKey|stringForKey|arrayForKey|dictionaryForKey|dataForKey|numberForKey|mutableDictionaryForKey|mutableArrayForKey|unsignedLongLongValue|stdStringForString|stringForStdString|unsignedIntValue|stdString|hexValue|hasPrefixCaseInsensitive|urlEncodedString|nonRetainingArray)
Last modified 3yr ago