【Swift】Realm で CGFloat が非推奨なことに気がついたので Float にマイグレーションした話

どうも。Reoです。

Realm を使っていたアプリ(OKAIMO)が、Xcode11.5 + iOS13.5 環境で、起動後クラッシュするようになってしまいました。

CGFloatを使っていた部分がクラッシュしてしまうので、CGFloat → Float へのマイグレーション処理を行いました。

せっかくなので記事として残しておきます。

 

環境

  • macOS Catalina (10.15.5)
  • Xcode 11.5
  • Swift 5.1.3
  • iOS 13.5.1

事象

Terminating app due to uncaught exception ‘RLMException’, reason: ‘Property Color.hue is declared as CGFloat, which is not a supported managed Object property type. If it is not supposed to be a managed property, either add it to `ignoredProperties()` or do not declare it as `@objc dynamic`. See https://realm.io/docs/swift/latest/api/Classes/Object.html for more information.’

というエラーでクラッシュ。

 

原因

クラッシュしている原因は、Realm オブジェクトのプロパティにCGFloatを使用していることでした。

CGFloatはプラットフォーム依存で定義が変わってしまうようで、RealmのドキュメントにもCGFloatは使うなと書いてありました。

CGFloat properties are discouraged, as the type is not platform independent.

CGFloat型はプラットフォーム(CPUアーキテクチャ)によって実際の定義が変わるため、使用しないようにしてください。

https://realm.io/docs/swift/latest

[iOS]RealmのモデルクラスのプロパティにCGFloatを指定してはいけない – Qiita も参考にさせていただきました。

 

マイグレーション処理

CGFloat を Float にマイグレーションしていきます。

まずはプロパティを CGFloat から Float に変更します。

final class Color: Object, Codable {

  // 変更前
    @objc dynamic var hue: CGFloat = 0
    @objc dynamic var saturation: CGFloat = 0
    @objc dynamic var brightness: CGFloat = 0
    @objc dynamic var alpha: CGFloat = 1

    // 以下に変更
    @objc dynamic var hue: Float = 0
    @objc dynamic var saturation: Float = 0
    @objc dynamic var brightness: Float = 0
    @objc dynamic var alpha: Float = 1

    // 略
}

実際に使っているところは Float(hue), CGFloat(hue) のように変換しておきます。

このまま実行すると、マイグレーションをしてねと言われるのでマイグレーション処理をします。

RealmMigration.swift というファイルを作成しました。全体は以下。

final class RealmMigration {} 部分はどのプロジェクトでも使えるようにしてあります。

実際のマイグレーション処理はextensionで書いた部分です。

extension RealmMigration {

    private func migrationTo2(_ migration: Migration) {
        migration.enumerateObjects(ofType: "Color") { old, new in
            guard
                let hue: CGFloat = old?["hue"] as? CGFloat,
                let saturation: CGFloat = old?["saturation"] as? CGFloat,
                let brightness: CGFloat = old?["brightness"] as? CGFloat,
                let alpha: CGFloat = old?["alpha"] as? CGFloat else {
                fatalError("Migration is Failed.")
            }
            new!["hue"] = Float(hue)
            new!["saturation"] = Float(saturation)
            new!["brightness"] = Float(brightness)
            new!["alpha"] = Float(alpha)
        }
    }
}

単にそれぞれ古い値をFloatに変換して入れ直しています。

最後に、アプリ起動時にマイグレーション処理が走るように AppDelegate 内で呼び出します。


@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        RealmMigration.shared.configure()
        return true
    }

これで実行して無事起動することができました。

 

おわりに

CGFloat が非推奨なんて全然知らなかったです。普通に使えるから、使えると思ってしまうじゃんか…こんなトラップがあるなんて…

久々に触ると本当に予期せぬクラッシュが多くてビビりますね。

今回は、自分が書いたAppAppのリファクタリング記事に助けられました。この時より汎用性ある感じにはなったハズ…

 

それではでは。

Swift
コメントは認証制です。詳しくは下記の注意をお読みください。お気軽にコメントお願いします!

Write a Comment

コメント時の注意

「Twitter」「Facebook」「Google+」「WordPress」のいずれかのアカウントをお持ちの方は各アカウントと連携することでコメントできます。 コメントしたことはSNSに流れませんので、アカウントをお持ちの方はこちらの方法でコメントを投稿して下さると嬉しいです。 アカウントをお持ちでない方はメールアドレスで投稿することができます。 初回コメント時は承認後に表示されます。

Related Memo...

UITableView.RowAnimation の .none はアニメーションするよ

UITableView.RowAnimation の .none はアニメーションがnoneなわけじゃなく、デフォルトの設定を使うよという意味らしい。

The inserted or deleted rows use the default animations.

なのでアニメーションしちゃう。今更の気づき。

 

iOS

UINavigationController + UIScrollView の組み合わせで使っている時に謎の余白ができる時

UINavigationController + UIScrollView の組み合わせで使っていて、UIScrollView 上に AutoLayout で上下左右0で View を設置しているのに、30px程度上にずれてしまうとき。

`navigationController.navigationBar.isTranslucent = false` にすると直るかもしれない。

ScrollView上のコンテンツとNavigationBarの重なっているところが透過していたら多分これで直せるはず。

通常のターゲットではちゃんと動いているのに、iOSSnapshotTestCase を用いたテストでだけこの対応が必要なのよくわからないけれど。。。

iOS

記事を書くほどでもないけれどメモっておきたいこと

テスト投稿。

例えばiphone7 の画面サイズ

750 × 1334
半分375 × 667

iOS
more