ネイティブアプリのAPI更新対応備忘録 〜iOS編〜
概要
↑を対応しつつ、その他 deprecated なコードの修正対応をメモがてら記載していく。
なお、修正前の時点で Project の iOS Deployment Target は iOS8 だった。
今回はそれを iOS11 まで引き上げることとする。というのも、WKWebView自体は iOS8からサポートされているが、 iOS10までと11以降でほぼ別物のため、保守管理の観点からiOS11以降に限定させた。
対応時のXcodeバージョン: 11.5
また、この記事は公開後も更新があった場合は追記する予定です。
- 概要
- UIWebView → WKWebView
- 通信周り
- UIAlertView → UIAlertController
- プッシュ通知
- URLエンコード/デコードAPI
- topLayoutGuide.length
- ステータスバーの表示・非表示API修正
- ステータスバーのサイズを除いた全画面サイズ取得API
- カメラ周り
- Custom URL scheme起動用のメソッド
- sizeWithFont:constrainedToSize
- openURL
- 課金レシート情報
- MobileCoreServices.framework → CoreServices.framework
- OpenGLES.framework → Metal.framework
- FacebookSDKの更新
- ローカルライブラリのパス整理
- モーダル表示方法の修正
- 感想
UIWebView → WKWebView
UIWebView
をプロジェクト内から完全に駆逐する。
ターミナルでプロジェクトへ移動し、以下を入力して使われている箇所を把握する
grep -r "UIWebView" .
そして上記の箇所をすべて WKWebView
に変えていく。
また、Storyboard の View で使われている Web View (deprecated)
コンポーネントを WebKit View
に変更。
Twitter 連携用のコードでも UIWebView
が使われていたので WKWebView
に変更。
またFacebookSDKが古すぎてそちらでもUIWebView
が使われていたので最新のSDKにバージョンアップさせた。具体的な最新作業については 「FacebookSDKの更新」欄を参照。
参考サイト:
通信周り
NSURLConnection
で同期通信をしていたが、 このクラスは iOS9 で deprecated になり、 NSURLSession
へ修正。
NSURLSession
は非同期しか対応しておらず、今からガッツリ変えるのはリスキーなのでディスパッチセマフォで同期処理にする。
Before
-(BOOL)sendSynchronousRequest:(NSString *)ua { NSString* version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]; NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@?p=%@", ROOT_URL, ENVIRONMENT_URL, version]]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [request setValue:ua forHTTPHeaderField:@"User-Agent"]; NSURLResponse *response = nil; NSError *error = nil; NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; NSString *errorStr = [error localizedDescription]; if ([errorStr length] > 0) { DLOG(@"error message: %@", errorStr); } return [self decrypt:data]; }
After
-(BOOL)sendSynchronousRequest:(NSString *)ua { NSString* version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]; NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@?p=%@", ROOT_URL, ENVIRONMENT_URL, version]]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [request setValue:ua forHTTPHeaderField:@"User-Agent"]; // 同期通信 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block BOOL isSuccess = YES; __block NSData *resData = nil; NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; NSURLSessionDataTask *task = [session dataTaskWithRequest:(NSURLRequest *)request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error){ // error NSString *errorStr = [error localizedDescription]; if ([errorStr length] > 0) { DLOG(@"error message: %@", errorStr); isSuccess = NO; dispatch_semaphore_signal(semaphore); return; } resData = data; dispatch_semaphore_signal(semaphore); }]; [task resume]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); if(!isSuccess) { return NO; } return [self decrypt:resData]; }
デリゲート利用クラス
また、NSURLConnection
のデリゲートを利用していたクラスはNSURLSession
のデリゲートへ変更する。
/** * HTTPリクエスト */ - (void)post:(PostData * )postData_ { // 初期化 webData = [NSMutableData data]; // セッション作成 NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; // 通信設定 NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]]; NSURLSessionDataTask *task = [session dataTaskWithRequest:theRequest]; // 通信開始 [task resume]; } /** * HTTPリクエストのデリゲートメソッド(データ受け取り初期処理) */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; if (httpResponse.statusCode == 200) { // 保持していたレスポンスのデータを初期化 [webData setLength: 0]; // didReceivedData と didCompleteWithError が呼ばれるように、通常継続の定数をハンドラーに渡す completionHandler(NSURLSessionResponseAllow); // 続ける } else { // error.code = -999で終了メソッドが呼ばれる completionHandler(NSURLSessionResponseCancel); // 止める } } /** * HTTPリクエストのデリゲートメソッド(受信の度に実行) */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { // 1つのパケットに収まらないデータ量の場合は複数回呼ばれるので、データを追加していく [webData appendData:data]; } /** * HTTPリクエストのデリゲートメソッド(完了処理) [正常終了、エラー終了、途中終了でもこのメソッドが呼ばれる] */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { if (error) { // エラー終了 } else { // 完了通知 } [session invalidateAndCancel]; }
参考サイト:
- NSURLConnection から NSURLSession への移行例 - Qiita
- iOS 弱者が NSURLConnection を NSURLSession に置き換えた話 - Qiita
UIAlertView → UIAlertController
UIAlertView
は iOS8 で deprecated になったため、 UIAlertController
へ変更。
これにより、アラート表示処理が大きく変わってしまい、表示元の UIViewController
のインスタンスが必要になった。
親 ViewController
をなんとか検索して取得する方法を下記参考サイトを見つつ対応。
Before
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show];
After
//コントローラーの生成 UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert]; //ボタンとその機能を生成 UIAlertAction *OKAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; //コントローラーにボタンを追加 [alert addAction:OKAction]; //アラートを表示 [self presentViewController:alert animated:YES completion:nil];
参考サイト:
- [iOS 8] UIAlertView と UIActionSheet が deprecated になった | Developers.IO
- iOS8でUIAlertViewからUIAlertControllerへ置き換えるのは大変だ - Qiita
プッシュ通知
プッシュ通知のコードをUNUserNotificationCenter
を利用したものへ変更。
気をつける点としては以下の2点。
- 通知のデリゲートを受け取る場合は
UNUserNotificationCenterDelegate
をヘッダーに宣言し、今まで利用してきた通知デリゲートメソッドはUNUserNotificationCenterDelegate
で用意されているやつに差し替える必要がある。 - 通知をキャンセルする場合、古いやり方だと
cancelAllLocalNotifications
を呼ぶだけで全部削除されたが、新しい方法ではremoveAllPendingNotificationRequests
とremoveAllDeliveredNotifications
を呼ぶ必要がある。前者は未送信の通知をすべて削除し、後者は送信済みの通知を削除する。
Before
MyAppDelegate.h
@interface MyAppDelegate : NSObject <UIApplicationDelegate> {
}
MyAppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { CGFloat currentVersion = [[[UIDevice currentDevice] systemVersion] floatValue]; if (currentVersion >= 8.0) { // iOS8.0以降の処理 //UserNotificationの種類(バッヂ、アラート、サウンド) UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound; // User Notificationの設定を登録 UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil]; [application registerUserNotificationSettings:settings]; // Remote Notificationの設定を登録 [application registerForRemoteNotifications]; } else { // iOS7以前の処理 // Remote Notificationの種類 UIRemoteNotificationType types = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound; // Remote Notificationの設定を登録 [application registerForRemoteNotificationTypes:types]; } // アプリを起動したら、ローカル通知設定をクリア [application cancelAllLocalNotifications]; }
After
MyAppDelegate.h
@interface MyAppDelegate : NSObject <UIApplicationDelegate, UNUserNotificationCenterDelegate> {
}
MyAppDelegate.m
#import <UserNotifications/UserNotifications.h> // 略... - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // プッシュ通知受信用の処理 UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter]; //UserNotificationの種類 UNAuthorizationOptions options = UNAuthorizationOptionAlert | UNAuthorizationOptionAlert | UNAuthorizationOptionSound; // デリゲートを接続 notificationCenter.delegate = self; // User Notificationを登録 [notificationCenter requestAuthorizationWithOptions:options completionHandler: ^(BOOL granted, NSError * _Nullable error) { if (error) { NSLog(@"requestAuthorizationWithOptions error: %@", error); return; } if (granted) { // ユーザが Push 通知を許可するとここに来る dispatch_async(dispatch_get_main_queue(), ^ { [[UIApplication sharedApplication] registerForRemoteNotifications]; }); } }]; // アプリを起動したら、ローカル通知設定をクリア [notificationCenter removeAllPendingNotificationRequests]; [notificationCenter removeAllDeliveredNotifications]; }
参考サイト
URLエンコード/デコードAPI
エンコード・デコード周りのコードも古かったので差し替え。
エンコードは CFURLCreateStringByAddingPercentEscapes
を利用していたので stringByAddingPercentEncodingWithAllowedCharacters
に変更。
- (NSString *)stringByEncodingURLFormat { return [(NSString *)self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]]; }
デコードの方は stringByReplacingPercentEscapesUsingEncoding
から stringByRemovingPercentEncoding
へ変更。
- (NSString *)stringByDecodingURLFormat { return [result stringByRemovingPercentEncoding]; }
参考サイト:
[Objective-C] iOS9からのURLエンコード・デコード | Tips of Rubbish
topLayoutGuide.length
self.topLayoutGuide.length
が iOS11 でdeprecated なので view.safeAreaInsets
で取得するように変更。
Before
topOffset = self.topLayoutGuide.length;
After
topOffset = self.view.safeAreaInsets.top;
参考サイト:
ステータスバーの表示・非表示API修正
UIApplication
の setstatusbarhidden
が iOS9 で deprecated になり、作り方が大きく変わっていた。
最初にInfo.plistの「View controller-based status bar appearance」をYESに変更後、以下のようにステータスバーを隠すコードを修正。
上記の変更により、複数Viewがある場合はそれぞれのクラスで設定が必要なので注意。
Before
// ステータスバーを隠す - (void)hideStatusBar { [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide]; // hogehoge }
After
// インスタンス変数 bool shouldBeHidingStatusBar = NO; UIStatusBarAnimation statusBarAnimationType = UIStatusBarAnimationSlide; // ステータスバーを隠す - (void)hideStatusBar { shouldBeHidingStatusBar = YES; statusBarAnimationType = UIStatusBarAnimationSlide; [UIView animateWithDuration:0.5 animations:^{ [self setNeedsStatusBarAppearanceUpdate]; } completion:^(bool isFinished) { // hogehoge }]; } //YESでステータスバーを非表示(NOなら表示) - (BOOL)prefersStatusBarHidden { return shouldBeHidingStatusBar; } //スライドするアニメーションを指定 - (UIStatusBarAnimation)preferredStatusBarUpdateAnimation { return statusBarAnimationType; } //ステータスバーの色を指定 - (UIStatusBarStyle)preferredStatusBarStyle { // 白で固定 return UIStatusBarStyleLightContent; }
上記のように、 prefersStatusBarXXX
系のメソッドは
UIViewControllerののAPIとして予め用意されているものであり、これらのメソッドを宣言して返り値に適切な変数を返してやる必要がある。
これらの設定の反映を適用するには setNeedsStatusBarAppearanceUpdate
メソッドを呼ぶ必要がある。
参考サイト:
iOSのステータスバーを自在に扱う - Qiita
ステータスバーのサイズを除いた全画面サイズ取得API
UIScreen:applicationFrame
の差し替え対応。
Before
size = [[UIScreen mainScreen] applicationFrame].size;
After
// 全画面サイズ size = [[UIScreen mainScreen] bounds].size; // ステータスバー分のサイズを除く size.height = size.height - UIApplication.sharedApplication.statusBarFrame.size.height; size.width = size.width - UIApplication.sharedApplication.statusBarFrame.size.width;
参考サイト:
カメラ周り
写真データ取得に使っていた AVCaptureStillImageOutput
が iOS9 で deprecated になったので AVCapturePhotoOutput
に修正。
CaptureSessionManager.h
Before
@interface CaptureSessionManager : NSObject<CaptureSessionManagerDelegate> { } @property (nonatomic, retain) AVCaptureStillImageOutput *stillImageOutput;
After
@interface CaptureSessionManager : NSObject<CaptureSessionManagerDelegate, AVCapturePhotoCaptureDelegate> { } @property (nonatomic, retain) AVCapturePhotoOutput *stillImageOutput;
CaptureSessionManager.m
Before
- (void)addVideoOutput{ stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG, AVVideoCodecKey, nil]; [stillImageOutput setOutputSettings:outputSettings]; } // 撮影 - (void)capture{ // 略 [stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { CFDictionaryRef exifAttachments = CMGetAttachment( imageDataSampleBuffer, kCGImagePropertyExifDictionary, NULL); if (imageDataSampleBuffer == NULL) return; NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; UIImage *image = [[UIImage alloc] initWithData:imageData]; // hogehoge }]; }
After
- (void)addVideoOutput{ stillImageOutput = [[AVCapturePhotoOutput alloc] init]; [captureSession addOutput:stillImageOutput]; } // 撮影 - (void)capture{ AVCapturePhotoOutput * output = (AVCapturePhotoOutput *)stillImageOutput; videoConnection = [output connectionWithMediaType:AVMediaTypeVideo]; AVCapturePhotoSettings * settings = [AVCapturePhotoSettings photoSettings]; [output capturePhotoWithSettings:settings delegate:self]; } // 撮影時のコールバック - (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(nullable NSError *)error { NSData *imageData = [photo fileDataRepresentation]; UIImage* resutImage = [[UIImage alloc] initWithData:imageData]; // hogehoge }
参考サイト:
- [iOS 10] iOS 10以降のAVFoundationでの撮影方法 | Developers.IO
- iOS 10 - Objective-C : How to implement AVCapturePhotoOutput() to capture image and videos? - Stack Overflow
- AVFoundation-自定义拍照 - 代码先锋网 ( 拍照按钮点击-获取图片 の欄)
Custom URL scheme起動用のメソッド
iOS9 以下で使っていた、
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
はdeprecatedになったので、以下のメソッドで対応する。
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
参考サイト:
公式ドキュメント
sizeWithFont:constrainedToSize
iOS7 で deprecatedになった sizeWithFont:constrainedToSize
を変更。
Before
CGSize dims = [self.labelText sizeWithFont:self.labelFont]; // 中略 CGFloat maxHeight = frame.size.height - self.height - 2*margin; CGSize labelSize = [detailsLabel.text sizeWithFont:detailsLabel.font constrainedToSize:CGSizeMake(frame.size.width - 4*margin, maxHeight) lineBreakMode:detailsLabel.lineBreakMode]; lHeight = labelSize.height; lWidth = labelSize.width;
After
NSDictionary *labelDatas = @{NSFontAttributeName: self.labelFont}; CGSize dims = [self.labelText sizeWithAttributes:labelDatas]; // 中略 CGFloat maxHeight = frame.size.height - self.height - 2*margin; NSDictionary *detailsLabelData = @{NSFontAttributeName: detailsLabel}; CGRect labelSize = [detailsLabel.text boundingRectWithSize:CGSizeMake(frame.size.width - 4*margin, maxHeight) options:NSStringDrawingUsesLineFragmentOrigin attributes:detailsLabelData context:nil]; lHeight = labelSize.size.height; lWidth = labelSize.size.width;
参考サイト: 非推奨になった sizeWithFont:constrainedToSize:lineBreakMode を置き換える - Qiita
openURL
UIApplication
の openURL
を更新する。
Brefore
[[UIApplication sharedApplication] openURL:url];
After
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
参考サイト: [iOS 10] UIApplication の openURL: が Deprecated になりました | Developers.IO
課金レシート情報
課金した時のレシートを受け取るAPIがtransactionReceipt
だったので変更する。
レシート情報の構造が変わるのでサーバー側のレシート検証周りのコードも合わせて修正する必要有り。
Before
NSString * data = [transaction.transactionReceipt base64EncodingWithLineLength:0]; // サーバーへ送る
After
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL]; if (!receiptData) { NSLog(@"no receipt"); // エラーハンドリング } else { NSString *data = [receiptData base64EncodedStringWithOptions:0]; // サーバーへ送る }
レシートのレスポンス
どこよりもわかりやすいiOS最強課金まとめ - Qiita より引用しました。
{ "status": 0, "environment": "Sandbox", "receipt": { "receipt_type": "ProductionSandbox", "adam_id": 0, "app_item_id": 0, "bundle_id": " ", "application_version": "1", "download_id": 0, "version_external_identifier": 0, "receipt_creation_date": "2019-11-12 08:55:19 Etc/GMT", "receipt_creation_date_ms": "1573548919000", "receipt_creation_date_pst": "2019-11-12 00:55:19 America/Los_Angeles", "request_date": "2019-11-18 12:58:25 Etc/GMT", "request_date_ms": "1574081905053", "request_date_pst": "2019-11-18 04:58:25 America/Los_Angeles", "original_purchase_date": "2013-08-01 07:00:00 Etc/GMT", "original_purchase_date_ms": "1375340400000", "original_purchase_date_pst": "2013-08-01 00:00:00 America/Los_Angeles", "original_application_version": "1.0", "in_app": [ { "quantity": "1", "product_id": " ", "transaction_id": "1000000590999315", "original_transaction_id": "1000000590999315", "purchase_date": "2019-11-12 08:55:18 Etc/GMT", "purchase_date_ms": "1573548918000", "purchase_date_pst": "2019-11-12 00:55:18 America/Los_Angeles", "original_purchase_date": "2019-11-12 08:55:19 Etc/GMT", "original_purchase_date_ms": "1573548919000", "original_purchase_date_pst": "2019-11-12 00:55:19 America/Los_Angeles", "expires_date": "2019-11-12 08:58:18 Etc/GMT", "expires_date_ms": "1573549098000", "expires_date_pst": "2019-11-12 00:58:18 America/Los_Angeles", "web_order_line_item_id": "1000000048178827", "is_trial_period": "false", "is_in_intro_offer_period": "false" } ] }, "latest_receipt_info": [ { "quantity": "1", "product_id": " ", "transaction_id": "1000000590999315", "original_transaction_id": "1000000590999315", "purchase_date": "2019-11-12 08:55:18 Etc/GMT", "purchase_date_ms": "1573548918000", "purchase_date_pst": "2019-11-12 00:55:18 America/Los_Angeles", "original_purchase_date": "2019-11-12 08:55:19 Etc/GMT", "original_purchase_date_ms": "1573548919000", "original_purchase_date_pst": "2019-11-12 00:55:19 America/Los_Angeles", "expires_date": "2019-11-12 08:58:18 Etc/GMT", "expires_date_ms": "1573549098000", "expires_date_pst": "2019-11-12 00:58:18 America/Los_Angeles", "web_order_line_item_id": "1000000048178827", "is_trial_period": "false", "is_in_intro_offer_period": "false", "subscription_group_identifier": "20567235" } ], "latest_receipt": " ", "pending_renewal_info": [ { "expiration_intent": "1", "auto_renew_product_id": " ", "original_transaction_id": "1000000590999315", "is_in_billing_retry_period": "0", "product_id": " ", "auto_renew_status": "0" } ] }
参考サイト:
MobileCoreServices.framework → CoreServices.framework
タイトル通り。
OpenGLES.framework → Metal.framework
タイトル通り。
FacebookSDKの更新
FacebookSDKのバージョンが v.3 系と相当古かったので更新。
執筆時点での最新版であるv.7にアップグレードさせた。
更新方法は公式ドキュメントを参考にしつつ、Swift Packagesでダウンロードする方式に差し替えた。
また、今回は元々Obj-cなプロジェクトのため、Swiftライブラリが利用できるように Project の Build Settings で以下の設定を行った。
Build Option -> Always Embed Swift Standartd Libraries
をYES
に変更Linking -> Runpath Search Paths
に@executable_path/Frameworks
を追加- 念の為
Product -> Clean Build
で過去のビルドキャッシュを削除
上記を設定しないと、ビルドは成功するが端末だと起動直後、
dyld: Library not loaded: @rpath/libswiftCore.dylib
というエラーで動かないので注意が必要。
参考サイト:
- スタートガイド - iOS SDK - ドキュメンテーション - Facebook for Developers
- ios — dyld:ライブラリがロードされていません:@ rpath/libswiftCore.dylib
ローカルライブラリのパス整理
ビルド時に発生する、
ld: warning: directory not found for option
の解消対応。
1. Build Settings -> Search Paths -> Always Search User Paths (Deprecated)
を NO
に変更
参考サイト:
xcode - ‘ld: warning: directory not found for option’ - Stack Overflow
モーダル表示方法の修正
iOS13以降ではモーダル表示方法の設定が特に記載されていない場合、カード型の UIで表示されるようになりました。
自分のプロジェクトではこれまで通りフルスクリーンで表示させたいため、以下のように設定。
vc.modalPresentationStyle = UIModalPresentationFullScreen; //表示形式の選択 vc.modalInPopover = YES; //YESにするとスワイプで消えなくなる // モーダル表示 [self presentViewController:vc animated:YES completion:nil];
参考サイト:
iOS 13 以降でモーダルを fullScreen 表示させる3つの方法 - Qiita
感想
iOS8 から 11 に変えるだけでも結構な変更がありますね。
最近発表されたiOS14ではホーム画面のUIに大きなテコ入れがあり、また広告周りのセキュリティが厳しくなるのでAdjustとか使っている方は更新する必要があるかもです。
ページトップのロゴは、Apple Inc.の商標です。