うるおいらんど

【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 というファイルを作成しました。全体は以下。

https://gist.github.com/uruly/a799e5f4cdafbe9585945d7506f93780

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のリファクタリング記事に助けられました。この時より汎用性ある感じにはなったハズ...

 

それではでは。

Comments

コメントはありません。

現在コメントフォームは工事中です。