[Swift] [iOS][MacOS] Document App を作る(その1:UTI と Document Type の定義)

SwiftUI

     
⌛️ 3 min.
ドキュメントタイプのアプリ(Document-based App)を作るまでのステップを順に説明していきます。

Step0: Document-based Application とは

通常の iOS アプリ、macOS アプリ共に、アプリケーション内部に保存領域を持つことができて、CoreData や Realm を使ったアプリであっても、特にデータを外部ファイルに書き出すということをしなくても良いです。

ユーザー視点では、アプリケーションを立ち上げただけで、データがそこにある状態です。ただし、別のデータを選択することはできません。

Word や Excel のようにアプリケーションによっては、複数の目的に向けてデータを扱うことがあり、それぞれを独立して扱うことが必要となります。

このようなときに使用されるアプリケーションのタイプが、Document-based application です。データを、そのアプリケーションとは別に、分離して持つことができます。
Document-based application で使われる ドキュメントは、テキストファイルのように、複数のアプリケーションから開くことができるようなものもありますし、特定のアプリケーションからのみ開くことができるドキュメントとすることもできます。

Step1: UTI を理解して、定義する

Document-based Application を作るためには、UTI と ドキュメント を定義する必要があります。最初に、UTI を説明します。

UTI とは

UTI とは、Universal Type Identifier の省略形です。

Apple のドキュメントは、こちら

Windows PC 等では、拡張子でファイルのタイプやどのアプリケーションで開くかを決めていますが、macOS, iOS では、この UTI によって制御されます。
開くアプリケーションの情報だけではなく、ファイルのフォーマットやその内容タイプの情報を含めることができます。

UTI には、物理階層と機能階層の両方を記述することができます。

例えば、PNG ファイルを表す UTType.png(public.png) は、UTType.image(public.image) のサブタイプという扱いになっていて、画像イメージの一種類でもあることがわかります。

UTType.image は、UTType.content(public.content) のサブタイプであり、書類の1つであることを表しています。public.content が機能階層を表すタイプの基底にあるタイプです。

物理階層を表すために、UTType.image は、UTType.content のサブタイプであると同時に、UTType.data(public.data) のサブタイプとなっています。

UTType.data は、UTTypeItem(public.item) のサブタイプとなっていて、UTTypeItem は、物理階層を表す、基底タイプです。

上記のように、UTIは、複数の UTI を継承するように定義することができます。この継承関係があることで、拡張子からの情報より、多くの機能を提供できるようになっています。

UTType.png のように、既存のタイプの多くは、すでにその継承関係を含め定義されていますので、自分が定義したいタイプがどのような特徴を持つのかで当てはめれば良いことになります。

どのようなタイプがすでに定義されているかは、Apple のドキュメントで確認できます。

Apple のドキュメントは、こちら

public. で始まる UTI は、Apple のみが定義を追加することができます。

通常、新しく定義する際には、逆DNSのパターンで定義します。
例えば、smalldesksoftware.com のアプリケーションで、myappdoc というタイプを定義するのであれば、ID は、com.smalldesksoftware.myappdoc となります。

UTI には、2種類あります。imported と exported です。それぞれを説明します。

Exported

アプリケーション独自のタイプを使うためには、そのタイプを Exported として定義します。

この定義を持つアプリケーションがあることで、システムは、そのドキュメントのタイプを識別することができます。

Imported

独自のタイプを使わずに、すでに使用されているタイプを使用して動作するアプリケーションの場合は、Imported タイプの UTI を定義します。
例えば、テキストエディタがその1例です。

なお、既存のタイプであっても、(開発する)アプリケーションの中で使用することを定義する必要があります。

定義してみる

Xcode のプロジェクトで設定画面を開く

「プロジェクト」ー「ターゲット」ー「Info」を開いて、定義していきます。

UI for defining UTI

「UI for defining UTI」

Exported UTI を定義する

Exported Type Identifiers を開いて、”+” ボタンを押すことで新しい Exported UTI を追加することができます。

追加された UTI の Description、Identifier、Conforms To、Extensions を以下のように定義します。

Description: New exported UTI from my app
Identifier: com.smalldesksoftware.myappdoc
Conforms To: public.data
Extensions: myappdoc

Defined exported UTI for example

「Defined exported UTI for example」

これで、新しい自アプリ用のタイプが定義できたことになります。

コードでも定義する

上記は、Info.plist での定義ですが、ソースコードでも使用する箇所があります。

Document App として作成されているテンプレートのコードを以下のように変更します。

UTI 定義を更新したコード


//
//  SampleAppDocument.swift
//  Shared
//
//  Created by Tomoaki Yagishita on 2020/10/15.
//

import SwiftUI
import UniformTypeIdentifiers

extension UTType {
  static var myappdoc: UTType {
    UTType(exportedAs: "com.smalldesksoftware.myappdoc")    // define Exported UTI as static var
  }
}

struct SampleAppDocument: FileDocument {
  var text: String
  
  init(text: String = "Hello, world!") {
    self.text = text
  }
  
  static var readableContentTypes: [UTType] { [.myappdoc] }  // define readable type
  
  init(configuration: ReadConfiguration) throws {
    guard let data = configuration.file.regularFileContents,
          let string = String(data: data, encoding: .utf8)
    else {
      throw CocoaError(.fileReadCorruptFile)
    }
    text = string
  }
  
  func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
    let data = text.data(using: .utf8)!
    return .init(regularFileWithContents: data)
  }
}

Info.plist で定義した UTI を 改めて定義して、FileDocument の readableContentTypes に設定しています。

# FileDocument については、別記事で説明予定です。

Step2: Document Type を定義する

アプリケーションが動作対象とするドキュメントを、Document Type として定義します。

Document Type

UTI を定義することで、システム上に存在するファイルのタイプを定義したことになりますが、Document Type を定義することで、アプリケーションが開くドキュメントを宣言することができます。

定義してみる

Xcode のプロジェクトで設定画面を開く

Document Type 設定画面

「UI for defining Document Type」

プロジェクト作成時に、Document App を選択していると 1つの Document Type が定義されています。既に定義されている Document Type を変更していきます。

Document Type を定義(変更)する

Document Type の Name, Types, Handler Rank を以下のように設定します。

Name: My App Doc
Types: com.smalldesksoftware.myappdoc
Handler Rank: Owner

Types は、先ほど定義した Exported UTI と同じ値にします。

Defined Document type

「Defined Document type」

これで、自アプリが使用するドキュメントのタイプを定義できたことになります。

Step3: 動作確認してみる

Simulator で確認

iOS simulator を使って動作確認してみます。

Document App の動作

Document App テンプレートから作成したアプリは、最初は、DocumentGroup ビューが開かれた状態になります。一見すると Files アプリ のような画面です。

起動直後は、Recents タブにいますので、Browse タブに切り替えると、新規作成のための ”+” ボタンがあることがわかります。この + を押して新規ドキュメントを作成します。作成したら、Simulator のホームボタンを押して、アプリを閉じます。

Simulator にも入っている Files アプリを使って、作成されたファイルを確認します。

拡張子と属性

Files アプリで、アプリごとに作成されているフォルダ内を確認すると、ファイルが作成されていることが確認できます。

Created file (in Files)

「Created file (in Files)」

ここで、先ほど指定した拡張子 “myappdoc” となっていることが確認できます。

ファイルを長押しするとプルダウンメニューが表示されますので、そこから、”Info” を選択するとファイルの属性を確認できます。

File attribute

「File attribute」

Kind が、先ほど指定した “New exported UTI from my app” になっていることが確認できます。

まとめ

Document App のための準備
  • UTI を定義する
  • Document Type を定義する
  • FileDocument の readableContentType を設定する

説明は以上です。
不明な点やおかしな点ありましたら、ご連絡いただけるとありがたいです。

SwiftUI おすすめ本

SwiftUI を理解するには、以下の本がおすすめです。

# SwiftUI2.0 が登場したことで少し古くなってしまいましたが、いまでも 定番本です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です