LCL Engineers' Blog

バス比較なび・格安移動・バスとりっぷを運営する LCLの開発者ブログ

Swift 4とiPhone Xの対応をしました

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() で参照しているメソッド 等
  • NSMuteAttributedStringkCTForegroundColorAttributeNameで定義していた箇所をNSAttributedStringKeyで定義するようになった
NSAttributedStringKeyに変更

この変換は既存のコードで書き方が整っていなかった関係か、一部正常に動作しなかったので手動で対応しました。

NSAttributed~~ as StringkCTLanguageAttributeNameなどの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や端末など環境による条件分岐が増え続けるのはツラいですね。