Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

はじめに

iOS ではデバイス上のファイルに対して、属性のキー 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 で保存した値が取得できていて、ファイルに紐づいた値が永続化できていることが分かります。

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away