4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ファイルに拡張属性( Extended Attributes )を付与する

Posted at

はじめに

iOS ではデバイス上のファイルに対して、属性のキー [FileAttributeKey] (https://developer.apple.com/documentation/foundation/fileattributekey) に対応した値を取得することができます。これには、ファイルの作成日や更新日、ファイルのサイズといった情報が含まれています。 FileAttributeKey に定義されている値については FileManager に用意されているメソッド attributesOfItem(atPath:) を通して値が取得できますが、これとは別に独自に属性を設定して付加情報を保存/取得することもできます。
利用シーンの例として、S3 上のファイルをローカルにダウンロードした際に、ファイルに更新時間に関する情報 LastModified の値を付与して次回以降のファイルの同期管理に使用する、といったことなどが浮かびます。

実装例

簡単の為に以下のコードでは保存する情報は String としています。 String ではなく Codable に準拠した型を対象にするのでもいいと思います。

extension URL {
  /// 拡張属性の取得
  func extendedAttribute(for name: String) -> String?  {
    let result = withUnsafeFileSystemRepresentation { fileSystemPath -> String? in
      // name に対応する拡張属性のデータの大きさを取得
      let length = getxattr(fileSystemPath, name, nil, 0, 0, 0)
      guard length >= 0 else {
        return nil
      }
      // データを格納する領域を確保
      var data = Data(count: length)
      let result = data.withUnsafeMutableBytes { [count = data.count] in
        // name に対応する拡張属性のデータを取得
        getxattr(fileSystemPath, name, $0.baseAddress, count, 0, 0)
      }
      guard result >= 0 else {
        return nil
      }
      return String(data: data, encoding: .utf8)
    }
    return result
  }

  /// 拡張属性の保存
  @discardableResult
  func setExtendedAttribute(value: String, for name: String) -> Bool {
    guard let data = value.data(using: .utf8) else {
      return false
    }
    let result = withUnsafeFileSystemRepresentation { fileSystemPath -> Bool in
      let result = data.withUnsafeBytes {
        // name に対応する拡張属性のデータを保存
        setxattr(fileSystemPath, name, $0.baseAddress, data.count, 0, 0)
      }
      return result >= 0
    }
    return result
  }
}

## 動作を確認してみる

// 設定する拡張属性の名前
let key = "com.example.extended_attribute"
// 拡張属性を設定するファイルの場所
let url = FileManager.default.temporaryDirectory.appendingPathComponent("example_file")

// ファイルがなかったら作成して拡張属性を付与する
if !FileManager.default.fileExists(atPath: url.path) {
  let data = "example_content".data(using: .utf8)!
  try! data.write(to: url)
  // 拡張属性の付与
  url.setExtendedAttribute(value: "example_value", for: key)
}

// 拡張属性が取得できるか確認
print(url.extendedAttribute(for: key))

コンソールの出力

Optional("example_value")

アプリを再起動しても setExtendedAttribute で保存した値が取得できていて、ファイルに紐づいた値が永続化できていることが分かります。

参考

4
3
0

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
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?