LoginSignup
3
4

More than 5 years have passed since last update.

RealmファイルをwatchOSにfileTransferしたときの実機で生じるエラーと解決方法

Last updated at Posted at 2017-03-19

はじめに

RealmファイルをiOSからwatchOSにfileTransferで送信しようとしたときに、実機ランでmake_dir() failed: Operation not permittedというエラーが生じました。この解決方法について備忘のためにメモを残します。

現象

実機ランすると、iOSがwatchOSにRealmファイルをfileTransferしたときに、default.realm.management': make_dir() failed: Operation not permittedというエラーが生じました。シミュレータでランしたときには、このエラーが発生しません。

Realm Swift 2.4.4、Xcode 8.2、iOS10.2.1、watchOS 3.1.1、シミュレータ(iOS/Watch)Version 10.0(SimulatorApp-236.7)という環境でした。

原因

watchOSがRealmファイルを受け取った後、そのファイルは読取り専用領域に保持されます。一方、Realmは管理のためにその領域に書き込みをしようとします。ここで権限がない操作が行われようとしたため、Operation not permittedというエラーが発生していました。

このエラーがAppleWatch実機のみで発生し、一方でシミュレータで発生しないのは、シミュレータが書込み権限の動作を完全にシミュレートできていないためだと考えられます。

プログラマが意図してwatchOS側のRealmファイルを変更しようとしなくても、Realm自体が書込みをしようとするので、この挙動は意識しておく必要があります。

解決

エラーが出てしまう従来のコードは、こちらです。読み取り専用領域にファイルが作られるので、Realmが管理用に編集しようとしたときにエラーが発生します。

before
// When the file was received
    func session(_ session: WCSession, didReceive file: WCSessionFile) {

        //set the recieved file to default Realm file
        var config = Realm.Configuration()
        config.fileURL = file.fileURL
        Realm.Configuration.defaultConfiguration = config

        // display the first of realm objects
        let realm = try! Realm()
        if let firstField = realm.objects(Field.self).first{
            realmLabel.setText(firstField.text)
        }
    }

解決のために、受信したファイルをそのまま利用するのではなく、そのコピーを書込み権限のあるdocumentDirectory配下に作り、そのパスを使うことにします。

加えて、同名のファイルがあるとFileManagerの重複ファイルエラーが発生してしまうので、その対応が必要になります。今回は、既存ファイルがあればそれを削除した後で、コピーするように実装します。

具体的には次のようなコードになります。

after
// When the file was received
    func session(_ session: WCSession, didReceive file: WCSessionFile) {

        //set the recieved file to default Realm file
        var config = Realm.Configuration()
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let documentsDirectory = paths[0]
        let realmURL = documentsDirectory.appendingPathComponent("data.realm")
        if FileManager.default.fileExists(atPath: realmURL.path){
            try! FileManager.default.removeItem(at: realmURL)
        }
        try! FileManager.default.copyItem(at: file.fileURL, to: realmURL)
        config.fileURL = realmURL
        Realm.Configuration.defaultConfiguration = config

        // display the first of realm objects
        let realm = try! Realm()
        if let firstField = realm.objects(Field.self).first{
            realmLabel.setText(firstField.text)
        }
    }

これで解決できました。

なお、FileManager.default.fileExists(atPath:)の引数には、realmURL.pathを代入します。引数がString型ですが、.absoluteStringや.relativeStringではありません。

参考リンク

今回のソースファイルは全部こちらのリポジトリで公開しています。
SampleRealmOnWatchOS3

基本的にこちらのコードの考え方を参考にしています。ただし、既存ファイルがあると重複エラーが発生するので、今回はその対応を加えて実装しました。
Apple WatchにRealmのデータを転送できない

3
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4