IOS 13 supports adaptive models

  • IPhone 11, iPhone 11 Pro, iPhone 11 Pro Max
  • IPhone X, iPhone XR, iPhone XS, iPhone XS Max
  • IPhone 8, iPhone 8 Plus
  • IPhone 7, iPhone 7 Plus
  • IPhone 6S, iPhone 6s Plus
  • iPhone SE
  • IPod Touch (7th generation)
  • A new version of the iPhone was released later

Adapt to time node requirements

November 5, 2019

Apple officially deprecated Xcode version 11.2 on the same day, and will need to update to Xcode 11.2.1 or later for packaging and review.

30 June 2020

Apple delayed the adaptive time node at 2020.03.26, as can be seenDeadline for App Updates has Been Extended

  1. All submissions toAppStoreNew applications and application updates must be usediOS 13The SDKCompile the package. It also supports all screen sizes corresponding to all devices, please see for detailsSubmit Your iPhone Apps to the App Store As well asSubmit Your iPad Apps to the App Store ;
  2. All applications must use itXcode storyboardAvailable to support all screen sizesStart the interface, please see for detailsBuilding Adaptive User Interfaces for iPhone and iPad;
  3. existingApp and app updates that comply with Apple’s approval rules regarding accessSign In With AppleFunction requirements must be connected. For details, see the following.
  4. All AppWatch apps must be usedwatchOS 6Or later versions of the SDKCompile the package.
  5. Guideline 1.3, Guideline 5.1.4 must be complied with in Kids Category applications.
  6. Applications using HTML 5 must comply with Guideline 4.7 sections 4, 5, and 6.

In April 2020

Updating Apps that Use Web Views must Use WKWebView instead of UIWebView.

In December 2020

Updating Apps must Use WKWebView instead of UIWebView, Updating Apps that Use Web Views.

New feature adaptation

1. Dark Mode

IOS 13 introduces Dark Mode. UIKit provides new system colors and apis for adapting different color modes. Xcassets also make adjustments to the material adaptation, as shown in the following: Implementing Dark Mode on iOS.

Dark Mode doesn’t have to be compatible, but you need to make sure that switching the theme doesn’t affect the user’s use of the app (for example, if the text and background color are the same). If you don’t want to use Dark Mode, you can disable Dark Mode in your app by adding a column in info.plist: User Interface Style: Light.

In addition, even if the color scheme is set, the popup window of the system requesting permission will still display according to the color of the system, but the UIAlertController created by yourself will not.

2. Sign In with Apple

In iOS 13, Apple introduced a fast and easy way to Sign In to apps and websites: Sign In With Apple. This is a new feature in iOS 13, so it needs to be developed using Xcode 11. As for whether an App requires access to this login method, Apple says in the App Store App Approval Guidelines:

Apps that exclusively use a third-party or social login service (such as Facebook Login, Google Sign-In, Sign in with Twitter, Sign In with LinkedIn, Login with Amazon, Or WeChat Login) to set up or authenticate the user’s primary account with the app must also offer Sign in with Apple as an equivalent option.

If your app uses a third party or social account login service (Facebook, Google, Twitter, LinkedIn, Amazon, wechat, etc.) to set up or verify a user’s primary account, you must add Sign In With Apple as an equivalent option to your app. The following types of applications do not need to be added:

  • Apps that use only internal company accounts to register and log in;
  • Educational, corporate, or business-type applications that require users to log in using an existing educational or corporate account;
  • Applications that use government or industry-supported citizenship identification systems or electronic identifiers to authenticate users;
  • An application of a particular third-party service that requires users to log directly into their email, social media, or other third-party accounts to access its content.

Note also that Apple mentions In News and Updates when it requires access to Sign In With Apple:

Starting today, new apps submitted to the App Store must follow these guidelines. Existing apps and app updates must follow them by April 2020.

From September 12, 2019, new apps submitted to the App Store must be accessed according to the criteria set out in the App Approval Guidelines; Existing apps and app updates must also be available by April 2020.

API adaptation

1. The private method KVC may cause a crash

In iOS 13, valueForKey and setValue:forKey: are not allowed to be used for some method attributes to obtain or set private attributes. Specifically, it will crash directly at runtime, and the following crash message will be displayed:

*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Access to UISearchBar's _searchField ivar is prohibited. This is an application bug'
Copy the code

The solution

At present, the private API that will cause crash and corresponding alternatives are sorted as follows. Thank you for your feedback, and welcome your comments and corrections:

// Crash API UITextField *textField = [searchBar valueForKey:@"_searchField"]; / / alternative 1, 13 new properties using iOS searchTextField searchBar. SearchTextField. Placeholder = @"search"; // alternative 2, iterate over the property of the specified type - (UIView *)findViewWithClassName:(NSString *)classNameinView:(UIView *)view{
    Class specificView = NSClassFromString(className);
    if ([view isKindOfClass:specificView]) {
        return view;
    }

    if (view.subviews.count > 0) {
        for (UIView *subView in view.subviews) {
            UIView *targetView = [self findViewWithClassName:className inView:subView];
            if(targetView ! = nil) {returntargetView; }}}returnnil; } // call method UITextField *textField = [self findViewWithClassName:@"UITextField" inView:_searchBar];
Copy the code

// Crash API [searchBar]setValue:@"Cancel" forKey:@"_cancelButtonText"]; // Instead, use the same method as above to find UIButton properties in subclasses, And then set its title UIButton * cancelButton = [self findViewWithClassName: NSStringFromClass ([UIButton class])inView:searchBar];
[cancelButton setTitle:@"Cancel" forState:UIControlStateNormal];
Copy the code

// Crash API. Loading a placeholderLabel does not crash, but loading the properties in the placeholderLabel will return textFieldsetValue:[UIColor blueColor] forKeyPath:@"_placeholderLabel.textColor"];
[textField setValue:[UIFont systemFontOfSize:20] forKeyPath:@"_placeholderLabel.font"]; // Replace the placeholderLabel [textField] with an empty callbacksetValue:[UIColor blueColor] forKeyPath:@"placeholderLabel.textColor"];
[textField setValue:[UIFont systemFontOfSize:20] forKeyPath:@"placeholderLabel.font"]; / / alternative 2 textField. AttributedPlaceholder = [[NSAttributedString alloc] initWithString: @"Input" attributes:@{
    NSForegroundColorAttributeName: [UIColor blueColor],
    NSFontAttributeName: [UIFont systemFontOfSize:20]
}];
Copy the code

2. The format of the deviceToken pushed is changed

We could have simply converted the deviceToken of type NSData to an NSString string, and then replaced the extra symbols:

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    NSString *token = [deviceToken description];
    for (NSString *symbol in@ [@""The @"<"The @">"The @"-"]) {
        token = [token stringByReplacingOccurrencesOfString:symbol withString:@""];
    }
    NSLog(@"deviceToken:%@", token);
}
Copy the code

In iOS 13, this method is no longer valid, and the deviceToken of type NSData converts to a string instead:

{length = 32, bytes = 0xd7f9fe34 69be14d1 fa51be22 329ac80d ... 5ad13017 b8ad0736 } 
Copy the code

The solution

A data format process is required, and UmENG provides a way to adapt to old and new systems:

#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);
}
Copy the code

APNs Device tokens are of variable length. Do not hard-code their size. Therefore, the data format processing part can be optimized:

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    if(! [deviceToken isKindOfClass:[NSData class]]) {return; } const unsigned char *tokenBytes = deviceToken.bytes; NSInteger count = deviceToken.length; NSMutableString *hexToken = [NSMutableString string];for (int i = 0; i < count; ++i) {
        [hexToken appendFormat:@"%02x", tokenBytes[i]];
    }
    NSLog(@"deviceToken:%@", hexToken);
}
Copy the code

3. The default style of the modal view has changed

On iOS 13, the presentViewController method is used to open the modal view, which defaults to the parallax effect shown in the image below, returned by sliding.

This is because apple will UIViewController modalPresentationStyle attribute default value changed to new UIModalPresentationAutomatic an enumeration values, for the majority of UIViewController, This value will map into UIModalPresentationPageSheet.

Note that the navigation bar of the page that pops up with this effect will be cut off. This can be seen in storyboards as well.

One other thing to notice is that when we pop up a page in full screen, the ViewController that pops up that page will call viewWillDisappear and viewDidDisappear in turn. And then when the page is dismissed, the viewWillAppear and viewDidAppear of the ViewController that popped it up will be called. However, using the default parallax effect to pop up the page, the ViewController that pops it up doesn’t call these methods, and the code originally written in these four functions can have problems later.

The solution

If the parallax style is acceptable, there is no need to change it; If you want to change back to full screen display, you need to manually set the pop-up style:

- (UIModalPresentationStyle)modalPresentationStyle {
    return UIModalPresentationFullScreen;
} 
Copy the code

4. UISearchBar black line processing crashes

In order to deal with the problem of black lines in the search box, the searchBar subViews were usually traversed to find and delete the UISearchBarBackground.

for (UIView *view in _searchBar.subviews.lastObject.subviews) {
    if ([view isKindOfClass:NSClassFromString(@"UISearchBarBackground")]) {
        [view removeFromSuperview];
        break; }}Copy the code

Doing so in iOS13 will cause UI rendering to fail and then crash, with the following crash message:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Missing or detached view for search bar layout'
Copy the code

The solution

Set the background image of UISearchBar to empty:

[_searchBar setBackgroundImage:[UIImage new]];
Copy the code

5. UITabBarButton has different structures in different states

In iOS 13 UITabBarButton control structure changes with the change of the selected, mainly embodied in UITabBarSwappableImageView and UITabBarButtonLabel position change. Is a child of UITabBarButton, as before, when selected. And not selected in the devolved into UIVisualEffectView _UIVisualEffectContentView inside. Thanks for the reminder from @Lightman. See the comparison below for details:

When we are in the custom UITabBar, usually will traverse UITabBarButton get UITabBarSwappableImageView child controls, such as custom red dot when added to the right upper corner of this ImageView This can cause an exception in iOS 13.

The solution

Can use recursive traversal UITabBarButton all get UITabBarSwappableImageView subviews, specific can consult a private method above KVC can result in collapse Chapter the recursive traversal method is given.

Note also that when unchecked, the added red dot will turn gray like the tabBar image, probably due to its structural changes. See the following figure for details:

If you want to not when selected is red, same as before is also very simple, add red dots to UITabBarButton, position is adjusted according to UITabBarSwappableImageView again can.

6. UINavigationBar setting button margins crashes

Starting with iOS 11, the UINavigationBar uses an automatic layout with 16 or 20 margins between the buttons on the left and right and the screen.

UINavigationBar
layoutSubviews
_UINavigationBarContentView
layoutMargins
UIEdgeInsetsZero

- (void)layoutSubviews {
    [super layoutSubviews];
    
    for (UIView *subview in self.subviews) {
        if ([NSStringFromClass([subview class]) containsString:@"_UINavigationBarContentView"]) {
            subview.layoutMargins = UIEdgeInsetsZero;
            break; }}}Copy the code

However, this will cause a crash in iOS 13, with the following crash message:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Client error attempting to change layout margins of a private view'
Copy the code

The solution

The way of using set frame, to each side _UINavigationBarContentView, so as to offset the margin on both sides of the.

- (void)layoutSubviews {
    [super layoutSubviews];
    
    for (UIView *subview in self.subviews) {
        if ([NSStringFromClass([subview class]) containsString:@"_UINavigationBarContentView"]) {
            if([UIDevice currentDevice]. SystemVersion. FloatValue > = 13.0) {UIEdgeInsets margins. = subview layoutMargins; subview.frame = CGRectMake(-margins.left, -margins.top, margins.left + margins.right + subview.frame.size.width, margins.top + margins.bottom + subview.frame.size.height); }else {
                subview.layoutMargins = UIEdgeInsetsZero;
            }
            break; }}}Copy the code

7. The interface modified by the child thread crashes (the album’s first authorization callback must appear)

The PHPhotoLibrary requestAuthorization:] method is called when using the album, and the result is called back through a block with The PHAuthorizationStatus information.

[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {// Determine different status}];Copy the code

In iOS 13, if you modify the interface directly in the callback of obtaining permissions for the first time, it will crash. The crash information is as follows:

This application is modifying the autolayout engine from a background thread after the engine was accessed from the main  thread. This can lead to engine corruption and weird crashes. *** Terminating app due to uncaught exception'NSInternalInconsistencyException', reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'
Copy the code

In the actual test, the first authorization crash must precede, and the second authorization occasionally occurs. In addition, as the crash message indicates, not only the album authorization callback thread, but also other child threads that modify the interface have a certain probability of crashing, and the probability seems to be higher in iOS 13.

The solution

When debugging runs in Xcode, there is a purple exclamation mark on the child thread modification screen. Note that it is changed to return to the main thread.

8. The default pop-up style opens the page in WKWebView to get photos crashed

Because iOS 13 mode to view the default styles change, if the default popup a ViewController UIModalPresentationPageSheet style, and use WKWebView through HTML photo acquisition system:

[_webView loadHTMLString:@"<input accept='image/*' type='file'>" baseURL:nil];
Copy the code

When clicking the select button, a crash will appear according to @easyman’s feedback. The crash message is as follows:

*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Your application has presented a UIDocumentMenuViewController (<UIDocumentMenuViewController: 0x101226860>). In its current trait environment, the modalPresentationStyle of a UIDocumentMenuViewController with this style is UIModalPresentationPopover. You must provide location information for this popover through the view controller's popoverPresentationController. You must provide either a sourceView and sourceRect or a barButtonItem.  If this information is not known when you present the view controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.'
Copy the code

Specific reason for this is that photos, click the acquisition system will pop up a modal view style for UIModalPresentationPopover UIDocumentMenuViewController, under this kind of style, If the parent UIViewController is presented in a non-full-screen manner, then you need to specify its sourceView and sourceRect, as iPad does, or a barButtonItem, otherwise the crash will occur. Using pop-up UIModalPresentationFullScreen way, I wouldn’t have this problem.

The solution

The first method is to specify sourceView, sourceRect, and barButtonItem.

- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
    [self setUIDocumentMenuViewControllerSoureViewsIfNeeded:viewControllerToPresent];
    [super presentViewController:viewControllerToPresent animated:flag completion:completion];
}

- (void)setUIDocumentMenuViewControllerSoureViewsIfNeeded:(UIViewController *)viewControllerToPresent{
    if (@available(iOS 13, *)) {
        if([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone && [viewControllerToPresent isKindOfClass:UIDocumentMenuViewController.class]){ viewControllerToPresent.popoverPresentationController.sourceView = self.webView; viewControllerToPresent.popoverPresentationController.sourceRect = CGRectMake(15, 5, 1, 1); }}} // If the top layer has a UINavigationController, You need to specify - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {if([self.viewControllers.lastObject isKindOfClass:WKWebViewController.class]){
        WKWebViewController *vc = self.viewControllers.lastObject;
        [vc setUIDocumentMenuViewControllerSoureViewsIfNeeded:viewControllerToPresent];
    }
    [super presentViewController:viewControllerToPresent animated:flag completion:completion];
}
Copy the code

The second method is to use the full screen popup mode (practice has proved that the default popup style is full screen in landscape)

- (UIModalPresentationStyle)modalPresentationStyle {
    return UIModalPresentationFullScreen;
}
Copy the code

9. WKWebView displays desktop web pages by default on the iPad

On iPadOS, I use WKWebView to open a web page using the default Desktop version of the web page.

Mozilla / 5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko)Copy the code

The reason is that In iOS 13.0, Apple added a way to switch between desktop and mobile versions of WKWebView, which is controlled by a new enumerated value:

typedef NS_ENUM(NSInteger, WKContentMode) { WKContentModeRecommended, WKContentModeMobile, WKContentModeDesktop} API_AVAILABLE (ios (13.0));Copy the code

This enumeration defaults to WKContentModeRecommended, maps to WKContentModeMobile on iPhone and iPad Mini, and WKContentModeDesktop on other ipads, So when you open a web page on the iPad, the desktop version is displayed by default.

The solution

  1. throughWKWebViewConfigurationThe new attributedefaultWebpagePreferencesTo set, which currently contains only oneWKContentModeType attributespreferredContentMode, the default value isWKContentModeRecommendedYou can modify the version displayed by changing its value:
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
if(@ the available (iOS 13.0. *)) {configuration. DefaultWebpagePreferences. PreferredContentMode = WKContentModeMobile; }Copy the code
  1. In addition to initialization, switching between Desktop and Mobile versions can be achieved Using a new proxy method. An example of this method is given by Apple: Viewing Desktop or Mobile Web Content Using a Web View.
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction preferences:(WKWebpagePreferences *)preferences decisionHandler:(void (^)(WKNavigationActionPolicy, WKWebpagePreferences * _Nonnull))decisionHandler {
    preferences.preferredContentMode = WKContentModeMobile;
    decisionHandler(WKNavigationActionPolicyAllow, preferences);
}
Copy the code

Method is deprecated

1. UIWebView will be prohibited from submitting for review

After the release of iOS 13, Apple fixed the range of supported systems in iOS 2 to iOS 12 in the UIWebView description. In its December 23, 2019, News and Updates update, Apple gave the exact timing:

The App Store will no longer accept new apps using UIWebView as of April 2020 and app updates using UIWebView as of December 2020.

From April 2020, new applications containing UIWebView will no longer be submitted. From December 2020, application updates containing UIWebView will no longer be submitted.

The solution

Replace UIWebView with WKWebView, make sure all UIWebView apis are removed, and open Safari openURL if needed for iOS 7.

2. Use the UISearchDisplayController lead to collapse

Before the iOS 8, we add a search box on the UITableView needs to use UISearchBar + UISearchDisplayController combinations, and after the iOS 8, Apple has introduced UISearchController to replace this combination. In iOS 13, if you still continue to use the UISearchDisplayController can directly lead to collapse, collapse information is as follows:

*** 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.' 
Copy the code

The solution

Using UISearchController replace UISearchBar + UISearchDisplayController combination plan.

3. MPMoviePlayerController deprecated

Playing videos before iOS 9 can be done using the MPMoviePlayerController class in MediaPlayer.framework, which supports both local and network video playback. It was deprecated in iOS 9 and will raise an exception if it continues in iOS 13:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'MPMoviePlayerController is no longer available. Use AVPlayerViewController in AVKit.'
Copy the code

The solution

Use AVPlayer in AVFoundation as a video player control.

Engineering adaptation

1. Bluetooth permission field update caused a crash and failed to submit for review

In the iOS 13, apple will apply for permission to use original bluetooth NSBluetoothPeripheralUsageDescription fields, replace with NSBluetoothAlwaysUsageDescription fields.

For apps with a deployment target of iOS 13 and later, use NSBluetoothAlwaysUsageDescription instead.

Thank you @Dengchaojie for your feedback. If you use the old permission field to obtain Bluetooth permission in iOS 13, it will cause a crash. The crash message is as follows:

This app has crashed because it attempted to access privacy-sensitive data without a usage description.  The app's Info.plist must contain an NSBluetoothAlwaysUsageDescription key with a string value explaining to the user how the app uses this data.
Copy the code

In addition, if you submit a package without the new fields for review, you will receive an email containing ITMS-90683 with a message indicating that the review was not approved.

Dear Developer,

We identified one or more issues with a recent delivery for your app, “xxx”. Please correct the following issues, then upload again.

ITMS-90683: Missing Purpose String in Info.plist – Your app’s code references one or more APIs that access sensitive user data. The app’s Info.plist file should contain a NSBluetoothAlwaysUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. Starting Spring 2019, all apps submitted to the App Store that access user data are required to include a purpose string. If you’re using external libraries or SDKs, they may reference APIs that require a purpose string. While your app might not use these APIs, a purpose string is still required. You can contact the developer of the library or SDK and request they release a The version of their code that doesn ‘t contain the APIs. Learn more (developer.apple.com/documentati…). .

Best regards,

The App Store Team

The solution

Add both fields to info.plist.

For deployment targets earlier than iOS 13, Add to both NSBluetoothAlwaysUsageDescription and NSBluetoothPeripheralUsageDescription to app ‘s Information Property List file.

2. CNCopyCurrentNetworkInfo is stricter

As of iOS 12, CNCopyCurrentNetworkInfo will return the correct value only after Access WiFi Information is enabled. In iOS 13, the use of this function has become more stringent. According to the CNCopyCurrentNetworkInfo documentation, the application also needs to meet at least one of the following three conditions to get the correct value:

  • Use Core Location applications and get Location service privileges.
  • Use NEHotspotConfiguration to configure WiFi network applications.
  • The VPN application is in the enabled state.

Apple made the change to protect the security of its users, who can easily predict their current geographic location based on their MAC addresses. Similarly, Bluetooth devices also have MAC addresses, so Apple has added new permissions for Bluetooth, as you can see above.

The solution

Add one of the three requirements based on the application requirements. You can choose the first option to obtain location rights, because the cost of adding is not too high. You only need to allow the application to use location services.

3. LaunchImage is deprecated

Before iOS 8, we used LaunchImage to set the startup image. Whenever Apple launched a device with a new screen size, we needed to put the startup image of the corresponding size into assets, which was a very tedious step. So in iOS 8, Apple introduced LaunchScreen, which allows you to set up the launch interface directly in the Storyboard, making it easy to adapt to different screens.

In the Modernizing Your UI for iOS 13 section, apple says that starting in April 2020, all apps that support iOS 13 must provide launchscreen.storyboard. Otherwise, it will not be submitted to the App Store for approval.

The solution

Use launchscreen. storyboard to set up the launch page and discard LaunchImage.

4. UISegmentedControl default style changes

The default style is black text on a white background. If the color has been changed, the page needs to be changed.

The original tintColor setting for the selected color has been disabled. The selectedSegmentTintColor property has been added to modify the selected color.

5. Projects created by Xcode 11 run black screen on earlier versions of devices

For a project created using Xcode 11, a black screen will appear when you run the application on a device with iOS 13.0 or lower. This is because Xcode 11 creates an application that manages multiple UIWindows via UIScene by default. In addition to the AppDelegate, there will be a SceneDelegate:

This is for multi-process iPadOS, which means UIWindow is no longer managed in UIApplication, but the old version doesn’t have UIScene at all.

The solution

Add the following to the AppDelegate header:

@property (strong, nonatomic) UIWindow *window;
Copy the code

6. Projects compiled by Xcode 11.2 will crash on devices prior to iOS 13.2.

If the project storyboard contains UITextView and is compiled and packaged using Xcode 11.2, the App will crash on devices prior to iOS 13.2. The crash message is as follows:

*** 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)'
Copy the code

This issue was the bane of Xcode, and Apple later released Xcode 11.2.1 as an emergency fix. Xcode 11.2 was officially deprecated on November 5, 2019. ERROR ITMS-90534 and WARNING ITMs-90703 will be rejected if you use Xcode 11.2.

The solution

Update to Xcode 11.2.1 package review.

The SDK adaptation

1. Use@availableThe old version of Xcode fails to compile

In the code of SDK project of Xcode 11, @available is used to determine the current system version. The printed package is compiled in Xcode 10, and the following error will occur:

Undefine symbols for architecture i386:
    "__isPlatformVersionAtLeast", referenced from:
        ...
ld: symbol(s) not found for architecture i386
Copy the code

From the error message, the __isPlatformVersionAtLeast method has no concrete implementation, but there is no such method in the project. This error occurred wherever the @available implementation of iOS 13 used the new API and was packaged into a dynamic or static library using Xcode 11.

The solution

If your SDK needs to be compatible with older versions of Xcode, avoid this method and get the system version to determine:

if([UIDevice currentDevice]. SystemVersion. FloatValue > = 13.0) {... }Copy the code

In addition, open the SDK project on Xcode 10 should also be able to compile normally, which needs to add compilation macro to handle:

#ifndef __IPHONE_13_0
#define __IPHONE_13_0 130000
#endif

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0.#endif
Copy the code

Refer to the article

This article summarizes common adaptation problems based on personal problems and the following parts of the article

  • IOS 13 adaptation
  • IOS13 adaptation
  • Adapter iOS13
  • Xcode11 works with iOS13
  • IOS13 UI & feature adaptation
  • Fixed a black screen issue with xcode11-Beta when creating new iOS projects for lower versions
  • Modernizing Your UI for iOS13
  • CNCopyCurrentNetworkInfo is a new feature in iOS 13 and a workaround for apps that can’t get Wi-Fi information
  • IOS13 adaptation