iPhone Xの発売まであと1週間ほどとなり、
弊社のiOSアプリ「高速バス比較」でも、Swift4とiPhone Xの対応を行いました。
今回は、Swift3からSwift4への作業内容をまとめていきたいと思います。
準備
Xcode上でビルドするためにコードやライブラリの更新をします。
Xcodeの設定
ローカルのSwiftのバージョンをSwift3からSwift4へ変更する必要があります。 Swiftenv等を使用していなければ、Xcodeの設定から変更可能です。
SwiftのバージョンをSwift 4に変更
Build Settings の Swift Language Version を Swift 4 に設定します。
Update to recommended settingsを実行
新しく追加されたコンパイル時の警告などが有効化されます。
ライブラリをアップデート
Swift4に対応していないライブラリがある場合は、削除して代わりのものを見つけましょう。
今回はCarthageで管理しているライブラリに未対応のライブラリがあったので以下の作業を行いました。
- Cartfileから削除
- Build Phasesの Link Binary With Libraries からリファレンスを削除
- Frameworksからリファレンスを削除
マイグレーション
ビルドできる環境が整ったので次はコードをSwift4へ対応します。
Auto Conversion
バージョンの違いで変更があるコードの一部は自動変換が行えます。
言語設定をSwift4へ変更した際にダイアログが出ると思いますが、 そこで操作をキャンセルした場合は Edit > Convert > To Current Swift Syntax でも実行可能です。
弊社の環境では以下の変更が行われました。
- NSObjectを継承していたクラスや
dynamic
で暗黙的に付与されていた@objc
が明示的に付与されるようになった- Realmで扱っていたプロパティ
#selector()
で参照しているメソッド 等
NSMuteAttributedString
、kCTForegroundColorAttributeName
で定義していた箇所をNSAttributedStringKey
で定義するようになった
NSAttributedStringKeyに変更
この変換は既存のコードで書き方が整っていなかった関係か、一部正常に動作しなかったので手動で対応しました。
NSAttributed~~ as String
やkCTLanguageAttributeName
などのString型で設定していた箇所をNSAttributedStringKey
型を使用するように変更します。
let textAttributes: [NSAttributedStringKey: Any] = [ NSAttributedStringKey.foregroundColor: UIColor.red, NSAttributedStringKey.font: UIFont.systemFont(ofSize: 12), NSAttributedStringKey.paragraphStyle: textStyle, kCTLanguageAttributeName as NSAttributedStringKey: "ja" ]
Objective-Cの initilize()
一部でMethod Swizzlingが使われており、Objective-Cのinitilize()
メソッドの記述が残っていました。
(Method Swizzlingとは既存のメソッドを自前のメソッドに差し替える手法)
簡易的な対応として、Swizzleの処理をメソッドで括り
AppDelegateのdidFinishLaunchingWithOptions
で実行することにしました。
@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, AdjustDelegate, TAGContainerOpenerNotifier { ... func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { UIFont.swizzleSystemFont() ... } extension UIFont { func swizzleSystemFont() { ... } }
レイアウト崩れ、iPhone Xへの対応
UITableViewのドリルダウンでアニメーションが崩れる
iOS11からUIScrollViewへadjustedContentInset
というプロパティが追加され、自動でInsetを付けてくれるようになりました。
しかし、自前でContentInsetsを設定している場合は このプロパティ追加の影響でレイアウト崩れを起こしてしまうようです。
弊社のアプリでも所々で該当する箇所があったので、下記を記述して無効化しました。
if #available(iOS 11.0, *) { tableView.contentInsetAdjustmentBehavior = .never }
UITableViewのHeader、Footerの設定
上記のadjustedContentInset
と直接の関係はないと思いますが、
NavigationBarに隣接して設置していたUITableViewにおいて、
余白として設けていたHeaderViewが表示されずに、上に詰まってしまう崩れが起きていました。
原因はHeaderViewの設置にwillDisplayHeaderView
メソッドを使用していたことで
上記のメソッドではなく、viewForHeaderInSection
を使うようにし、対応しました。
// Swift 3 func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) { let headerView = view as! UITableViewHeaderFooterView headerView.backgroundView?.backgroundColor = UIColor.gray headerView.layer.borderColor = UIColor.red.cgColor headerView.layer.borderWidth = borderWidth } // Swift 4 func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let headerView = UIView() headerView.frame.size = CGSize(width: self.view.frame.width, height: tableHeaderViewHeight) headerView.backgroundColor = UIColor.glay headerView.layer.borderColor = UIColor.red.cgColor headerView.layer.borderWidth = borderWidth return headerView }
NavigationBarのサイズが小さくなる問題に対応
ナビゲーションバーに表示しているViewの表示内容が見切れていました。 対象のUIViewに下記を記述することによって、以前と同じようにサイズが一杯に広がります。
override var intrinsicContentSize: CGSize { return UILayoutFittingExpandedSize }
Use Safe Area Layout Guidesを有効にする
iPhone Xへの対応で、新しく追加されたSafeAreaを有効にします。 StoryboardのInterface Builderからインスペクタを選択し、該当の項目にチェックを付けると有効になります。
コードのみで実装している場合はUIEdgeInsetsのsafeAreaInsets
からマージンを取得できるので この値を利用します。
最後に
Swift3の時と比べると簡単にバージョンアップすることができました。 Codableなどの新しいプロトコルも試しつつ、コード内容にもSwift4の書き方を取り入れていきたいです。
iPhone Xへの対応については、まだまだ調整を加えたり、サイズを活かしたレイアウトにする必要がありそうです。
特にSafeAreaはiPhone Xのみの概念なので、レイアウトを変更する際にしっかり確認する必要があります。OSや端末など環境による条件分岐が増え続けるのはツラいですね。