0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【開発日誌 Day17】ObsidianとNotion、メモの行き先を手で選んだ

0
Posted at

どちらを選んだかは、先に言わない。どう選んだかを、判断が動いた順番のまま書く。題材は、ワンタップで送った1行のメモが、最終的にどこへ着地するか、だ。ObsidianのMarkdownファイルに1行足すのか、NotionのデータベースにAPIで書き込むのか。画面を何で描くかをUIKitとSwiftUIで選んだ前回の続きで、今回は道具そのものではなく、書いたものの行き先を選ぶ。

「メモアプリ Obsidian Notion どっち」で検索すると、たいていは編集体験や見た目の比較が出てくる。だが自分がここで選ぶのは編集の場所ではない。ワンタップで送った思いつきが、最初に着地する場所の設計だ。そこは編集画面より前の、もっと乾いたレイヤの話になる。

何を選ぶ問題なのか、をまず狭める

キャプチャ専用のメモアプリにとって、「行き先」は2つの意味に分かれる。1つは、ユーザーが最終的に読み返す場所。もう1つは、送信ボタンを押した直後にデータが物理的に書き込まれる場所。この2つは同じとは限らない。

今回決めるのは後者だ。前者、つまり「どこで整理して読むか」は、正直ユーザーの好みでいい。ObsidianでもNotionでも、Apple純正メモでも、本人が心地よい場所ならそれが正解になる。問題は、その手前の「送った瞬間、どこに、どうやって、確実に1行を刻むか」の部分で、ここだけはアプリの設計者が引き受けないといけない。

だから比較の土俵を、機能の多さやUIの好みから、いったん引きはがす。比べるのは2つの書き込みパラダイムだ。片方はローカルのMarkdownファイルに追記する方式。もう片方は、ネットワーク越しにホスト型データベースのAPIを叩く方式。Obsidianは前者の代表で、Notionは後者の代表になる。この2つは、思想がかなり違う。

候補を並べる

行き先の候補は、実際には4つあった。

  1. Obsidianの保管庫(vault)に、ローカルのMarkdownファイルとして追記する
  2. NotionのAPIを呼び、指定データベースのページやブロックとして追記する
  3. メールだけで完結させ、外部ツール連携は持たない
  4. 1と2を両方やる(二重書き込み)

3のメール単独は、このアプリがもともと採っている土台だ。送信は自分宛メールとして届く。この時点で「消えない」は担保できている。だが受信箱は、PKMのように構造化して育てる場所ではない。セカンドブレインやZettelkastenの入口として使うなら、メールの外にもう1つ、積み上がる置き場が要る。だから連携先を1つ足す判断になった。

4の二重書き込みは魅力的に見えて、最初に落ちた。書き込み先が2つあると、片方が成功して片方が失敗したときに、どちらを正とするかが決まらない。「メールには届いたがNotionには入っていない」という半端な状態を検知して、片側だけ再送する仕組みを、キャプチャの速さを守りながら作るのは割に合わない。真実が2つある設計は、どちらかが遅れた瞬間に、ユーザーへ「入ったのか、入っていないのか」を答えられなくなる。連携先は1つに絞る、と早い段階で決めた。

残ったのは1と2、Obsidianファイル追記か、NotionのAPI追記か、という一騎打ちになった。

比べる軸を、キャプチャの都合で立てる

軸は7本立てた。ここで大事なのは、軸に重みをつける前に、この道具が「キャプチャ専用」だという前提を思い出すことだ。Day8で書いたInbox一本の設計や、Day3で書いた送信の到達契約と同じで、評価軸は用途から逆算しないと意味を持たない。

Obsidian(ローカルMarkdown追記) Notion(ホストDBへAPI追記)
書き込み遅延 ローカルファイルI/O、ほぼ即時 ネット往復あり、数百ms〜秒
オフライン 圏外でもその場で書ける ネット必須、圏外は不可
データ所有 手元の.md、ロックインなし Notionサーバ上、エクスポート前提
設定の手間 フォルダを一度選ぶだけ 連携トークン発行+DB選択+項目対応
失敗モード iCloud同期の遅延、ファイル競合 429レート制限、トークン失効、API変更
構造・検索 平文の時刻付き行、構造は弱い プロパティ・リレーションで強い
長期の寿命 アプリが消えてもファイルは残る アカウントとAPIに依存

この表を眺めると、NotionはNotionで明確に強い列を持っている。構造・検索の行だ。プロパティを持ったデータベースに入れられれば、タグやステータスや締切で後から絞り込める。これはMarkdownの平文追記には出せない強さだ。

だが、キャプチャの瞬間に効く軸はそこではない。効くのは上の3行——遅延、オフライン、所有——になる。ここが今回の判断の中心だった。

iPhoneのクイックキャプチャ先は、ObsidianとNotionのどちらが向くのか

この問いに一般解はない。ただ「キャプチャ専用の入口」という条件を付けると、答えは片方に寄る。理由は、キャプチャという行為の仕事が「思いついたことを、信頼できる場所に、考えを止めずに置く」で終わっているからだ。整理はそのあとの、別フェーズの仕事になる。

キャプチャと整理の分業、という言い方をしている。外に出ているときはiPhoneで捕まえるだけ捕まえて、机に戻ってからObsidianなりNotionなりでじっくり畳む。この分け方を採ると、入口に求める性質がくっきりする。速いこと。圏外でも死なないこと。書いたものが自分の手元に残ること。この3つだ。

Notionのデータベースは、整理フェーズでは頼れる。しかしキャプチャの入口に置くと、3つとも引っかかる。APIはネットワークを要求するから、電車のトンネルや山では書けない。1本の往復に数百ミリ秒かかることもあり、送ってすぐ次を書きたいテンポに割り込む。そしてデータはNotionのサーバ側にある。どれもNotionの欠陥ではなく、ホスト型DBという構造から来る、当然の帰結だ。

一方、Obsidianの保管庫は、ただのフォルダに置かれた.mdファイルの集まりでしかない。Obsidianはアカウントを要求せず、既定ではネットワークにも出ない。だから追記は、iCloud Drive上のローカルファイルに1行を足すだけの、ネット不要の操作になる。キャプチャの入口が欲しがる性質と、ローカルMarkdownの性質が、ちょうど噛み合っていた。

Obsidian側は、ファイルに1行足すだけになる

言葉で書くと単純だが、実装はいくつか気をつける点がある。追記先は、その日のyyyy-MM-dd.mdか、固定のInboxノートのどちらか。形式は- HH:mm メモのタイムスタンプ付きリスト行。既存の中身は壊さず、末尾に1行だけ増やす。

フォルダへのアクセスは、ユーザーが一度選んだvaultのsecurity-scoped bookmarkで持っておく。書き込みは、他プロセス(Obsidian本体やiCloud同期)と競合しないようNSFileCoordinatorで調停する。

func appendLine(_ line: String, to noteURL: URL) throws {
    var coordError: NSError?
    let coordinator = NSFileCoordinator()
    coordinator.coordinate(writingItemAt: noteURL,
                           options: .forMerging,
                           error: &coordError) { url in
        let existing = (try? String(contentsOf: url, encoding: .utf8)) ?? ""
        // 末尾に改行がなければ足してから1行追記する
        let joined = existing.isEmpty || existing.hasSuffix("\n")
            ? existing + line + "\n"
            : existing + "\n" + line + "\n"
        try? joined.data(using: .utf8)?.write(to: url, options: .atomic)
    }
    if let coordError { throw coordError }
}

タイムスタンプ行の組み立ては、ロケール非依存で固定する。ユーザーの端末設定に引っ張られてHHが12時間表記になると、あとで並べたとき崩れるからだ。

private let stamp: DateFormatter = {
    let f = DateFormatter()
    f.locale = Locale(identifier: "en_US_POSIX") // 端末設定に依存させない
    f.dateFormat = "HH:mm"
    return f
}()

func formattedLine(for memo: String, at date: Date) -> String {
    "- \(stamp.string(from: date)) \(memo)"
}

この経路には、意図的にネットワーク呼び出しを1つも置いていない。キャプチャのホットパスは、ローカルファイルI/Oだけで閉じる。ここが今回の設計でいちばん守りたかった線だ。保管庫がObsidian Sync専用でiCloud Driveに出てこない場合だけ、例外としてobsidian:// URLスキームで一瞬開いて追記するフォールバックに切り替える。詳しい分解はDay9で書いた。

Notionに自動でメモを追記するのと、Obsidianに追記するのは何が違うのか

同じ「自動追記」でも、Notion側は絵がまるで変わる。2021年に公開されたNotionのAPIは、REST型のよくできたAPIだ。だが叩くには、連携トークンによる認証と、生きたネットワークが要る。書き込みは、ローカルI/Oではなく、こういうHTTPリクエストになる。

func appendToNotion(_ memo: String, databaseId: String,
                    token: String) async throws {
    var req = URLRequest(url: URL(string: "https://api.notion.com/v1/pages")!)
    req.httpMethod = "POST"
    req.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
    req.setValue("2022-06-28", forHTTPHeaderField: "Notion-Version")
    req.setValue("application/json", forHTTPHeaderField: "Content-Type")
    req.httpBody = try JSONSerialization.data(withJSONObject: [
        "parent": ["database_id": databaseId],
        "properties": ["Name": ["title": [["text": ["content": memo]]]]]
    ])
    let (_, resp) = try await URLSession.shared.data(for: req)
    // 429 が返るので、Retry-After を尊重してキューし直す必要がある
    guard (resp as? HTTPURLResponse)?.statusCode == 200 else {
        throw CaptureError.notionRejected
    }
}

コードの行数はさほど変わらない。だが背後に抱えるものが違う。まず、書き込みにかかる時間の内訳がまるで別物だ。ローカルのファイル追記は、フォルダを開いて調停し、既存の内容に1行足して原子的に書き戻すだけで、実測では数ミリ秒の世界に収まる。対してNotion経路は、名前解決とTLSの確立、リクエストの往復、JSONの直列化と応答の解釈がぶら下がり、電波が良くても合計で数百ミリ秒、悪ければ1秒を超える。10回連続で送るテンポの入力では、この差は誤差ではなく体感になる。

抱えるものの違いは、遅延だけではない。Notionのレート制限は、1つの連携あたり平均で毎秒3リクエスト、15分あたり2,700回が目安とされる。個人が数分に1回メモを送るぶんには当たらない。しかし「当たらないから無視していい」ではなく、当たったときに429とRetry-Afterを見て、送信済みキューを崩さずに待って再送する経路を、キャプチャの速さと両立させて実装する義務が生まれる。トークンが失効すれば書けなくなるし、Notion-Versionが上がればペイロードの追従も要る。オフラインなら、そもそも1バイトも書けない。加えて、使い始めるまでの段取りも重い。Notion側で連携(インテグレーション)を作ってトークンを発行し、書き込みたいデータベースにその連携を共有し、どのプロパティにメモ本文を入れるかを対応づける。ここまでやって、ようやく最初の1行が入る。Obsidian側は、iCloud Drive上の保管庫フォルダを一度選ぶだけで終わる。

つまりNotion追記は、送信の裏にもう1つ、小さな配送システムを建てるのに近い。Day3やDay11で、メール配送の到達契約と後処理の分離に苦労した話を書いたが、Notion経路を採ると、あの苦労がもう一式増える。Obsidianのファイル追記には、その配送システムがまるごと要らない。差の本質は、機能ではなく、抱える運用の重さの側にあった。

選び、そして何を捨てたか

選んだのは、Obsidianのローカルファイル追記だ。キャプチャの入口としては、遅延・オフライン・所有の3軸が支配的で、ローカルMarkdownがその3つを素で満たしていた。ネットワーク越しのAPIを、いちばん速くありたいホットパスに置く理由が、この用途では見つからなかった。

だが、捨てたものは正直に数える。Notionを選ばなかったことで、構造化された取り込みを最初から諦めている。送った瞬間にステータスを「未整理」にし、プロジェクトのリレーションを張り、締切プロパティを埋める——そういう「入口で構造を持たせる」運用は、平文の- HH:mm追記では出せない。もしあなたの母艦がNotionで、ワークフローの全部がそのデータベースの上で回っているなら、キャプチャもNotionのInbox用DBへ直接入れるほうが、後の手数は確実に減る。そこはNotionが正しく勝つ場所だ。

もう1つ正直に書くと、ローカルファイル追記にも弱点はある。iCloud同期はときどき数秒遅れるし、複数端末が同じ.mdを同時に触れば競合の目もある。構造がない平文なので、増えすぎたInboxノートは、結局あとで人間が仕分ける前提になる。この「あとで仕分ける」問題は、Day12で思考実験として掘った通り、消えたわけではなく、整理フェーズへ先送りしただけだ。

それでも釣り合っていると判断した。キャプチャ専用という形にとっては、入口の軽さと手元に残る安心のほうが、入口での構造化より重い。この重みづけは、Day16でUIKitを選んだときと同じ筋で出している。良し悪しは道具単体では決まらず、いつも「何のための入口か」とセットで決まる。用途がNotion中心に変われば、この答えも当然ひっくり返る。

3ヶ月後に、この判断を見直すとしたらどこか

決めたことより、決めた前提が崩れる場所を書いておくほうが、あとで役に立つ。次にこの選択を疑うなら、たぶん次の3点のどれかが引き金になる。

1つ目、Notionのようなホスト型ツールが、オフラインの書き込みキューをAPI側で正式に持ったとき。いま「ネット必須」を弱点に数えた根拠が消えるので、重みづけをやり直す必要が出る。2つ目、ユーザーの保管庫がObsidian Sync専用で、iCloud Driveに出てこないケースが想定より多かったとき。その場合は主役がファイル追記ではなくobsidian://フォールバックになり、設計の重心がずれる。3つ目、キャプチャ時のファイルI/Oが、保管庫の肥大で目に見えて遅くなったとき。ローカルが速いという前提そのものが揺らぐ。

この3つのどれかが起きたら、たぶん自分はまたこの表を引っぱり出して、軸の重みを引き直す。そのときのために、判断の中身ではなく、判断の手順を残しておく——というのが、この記事の本当の目的だった。

最後に、答えを持っていない問いを1つ置く。あなたがiPhoneで思いつきを捕まえるとき、その最初の1行は、手元のファイルに落ちてほしいだろうか、それともクラウドのデータベースに構造ごと入ってほしいだろうか。そしてその選択を決めるのは、「圏外でも書けること」だろうか、それとも「入れた瞬間から検索で引けること」だろうか。自分はキャプチャの入口では前者を取ったが、整理の母艦では後者が正しい日も多い。あなたの入口では、どちらが重いか、コメントで一言もらえると次の見直しのいい目印になる。

参考にした仕様とドキュメント


Obsidian連携シンプルメモ
捕まえるのはiPhone、寝かせて整理するのはObsidian。ワンタップで送った1行が、iCloud Drive上の保管庫のノートへローカル追記で積み上がる、起動0.3秒のキャプチャ専用メモアプリ(プラグイン不要)。
App Store:https://apps.apple.com/jp/app/captio%E5%BC%8F%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%83%A1%E3%83%A2/id6749649498

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?