アプリ間コピー&ペーストの課題とその解決策
コピー&ペーストというシンプルなユーザー操作の裏側には、複雑なロジック、エッジケース、そしてシステムとの格闘が隠れています。Craftでこれらの問題をどのように解決しているかを解説します。

Craftは強力なメモ作成・ドキュメント作成アプリです。
シンプルでありながら強力な生産性ツールを愛する人のために設計されています。素早くメモを書き、AIを使って数秒で美しく仕上げましょう。ホワイトボード、タスク、リマインダーを使ってドキュメントを超えた作業もこなせます。すべてのデバイスに安全に同期されます。
テキストをタイトル、見出し、強調など一般的なスタイリングに変えたり、フォントの種類を変えたり、箇条書きや番号付きリストに整理したり、テキストの範囲を太字や斜体にするといったインラインスタイリングを適用できます。
さらにCraftはトグル、フォーカス/引用ブロック、テーブルといった高度な構造もサポートしています。
しかし、思考をページやカードに構造化し始めると、Craftの真価が発揮されます。背景、カバー画像、ページ区切りを設定して表現豊かなものにしましょう。
Craftの基本的な構成単位はブロックです。単語、文章、または段落になり得ます。ドラッグ、移動、コピー、スタイル変更、ページやカードへの変換を通じて、ドキュメントを構造化してより素晴らしいものにできます。これは(Notionでも採用されている)テキスト編集への新しいアプローチで、長い連続テキストに重点を置く既存のドキュメントエディターとは異なります。
Craftで美しいドキュメントを作成したら、別のアプリケーションで作業を続けたり、コンテンツを移動したりしたくなるかもしれません。コンテンツをエクスポートする方法のひとつは、Craftからコピーして対象のアプリケーションにペーストすることです。
ここで最初の問いが生じます:
Craftで作成されたコンテンツを、他のアプリケーションと互換性のある形式にするにはどうすればよいか?
そしてそれが解決できたとしても、まだ2番目の問いが残ります:
このコンテンツを正しく対象アプリケーションに移動するにはどうすればよいか?
コピー&ペーストというシンプルなユーザー操作の裏側には、複雑なロジック、エッジケース、そしてシステムとの闘いが隠れています。本記事では、その背景を説明し、Craftでこれらの問題をどのように解決しているかをお伝えします。
Craftが最初にサポートしたプラットフォームはiOSとMac(macCatalyst経由)であり、私はiOS/Mac製品エンジニアですので、これらのプラットフォームに焦点を当てます。WebおよびWindowsの開発者も似たような話を持っているはずです。
アプリケーション間でドキュメントのコンテンツを転送するという問題は複雑です。理解を助けるために、パーティーに参加して異なる言語を話す人々と会話するというアナロジーを使います。
パーティーに招待されたと想像してください
このパーティーはあなたのAppleデバイス上で開催されています。有名なゲスト(Notion、Microsoft Word、Slack、Bear、Obsidian)がいると聞いて、街の新参者として(まだ4年しか経っていませんが)、彼らと話して自分の知識や世界の見え方を共有したくてたまりません。
基本は分かっています。コピー&ペーストを通じてコンテンツを他のアプリと共有するには、コンテンツのフォーマットを登録してUIPasteBoardにアクセスする必要があります。
class BlockNSItemProvider: NSItemProvider {
init(){
registerDataRepresentation(forTypeIdentifier: kUTTypeUTF8PlainText as String, visibility: .all) { (completionBlock) -> Progress? in
let progress:Progress = Progress(totalUnitCount: 0.0)
//Convert the block data to String, then to UTF-8 data, also handle errors
let data:Data = ...
progress.completedUnitCount = 100
completionBlock(data, nil)
return progress
}
}
Craftがブロックをプレーンなコピー/UTF-8テキストとしてペーストボードに登録する方法の例です。
ゲストが複数の言語を話していると想像してください
他のゲストにアイデアを確実に伝えるために、自分を複数の形式で表現しなければならないと知って話は複雑になります。つまりCraftは自分のブロックを複数の「言語」に翻訳する必要があり、時には複数のバリエーションも必要です:
- シンプルなテキストブロックは、プレーンテキスト(シンプルなエディター用)、HTML(ブラウザー用)、リッチテキストフォーマット(Apple NotesとリッチエディターのためのRTF)、マークダウン(他の多くのエディター、Slackも含む)で利用できる必要があります。
- 画像、動画、描画は、対象アプリケーションがサポートしていればマルチメディアコンテンツとして、または便利な形式のURLとして利用できる必要があります。
- ページ、カード、トグルなどの構造化アイテムは線形エディター(Bear、Wordなど)では表現できないため、親コンテンツにネストされるか、親コンテンツの後に追加される形でフラット化されます。
各フォーマットには独自の表現レベルと制限があります。選択された各ブロックに対して、すべての一般的なUTTypeフォーマットで内部表現を変換します:
プレーンテキスト:ブロックのプレーンなStringコンテンツをコピーし、UTF-8およびUTF-16表現のデータとして取得します。
マークダウン:マークダウンパーサーを使用して、内部レンジ属性を持つブロックをマークダウンテキストに変換し、文字列をプレーンテキストとして登録します。
HTML/RTF:ブロックをNSAttributedStringに変換し、そのドキュメント関連APIを使ってHTMLまたはRTFフォーマットに変換します。HTML変換のプロセスを説明します(RTFも同様です):
let blockAttributedString:NSAttributedString = ... //Created from internal block representation
let htmlData:Data = try self.data(from: NSRange(location: 0, length: self.length), documentAttributes:[.documentType: NSAttributedString.DocumentType.html]);
let htmlString:String = String.init(data: htmlData, encoding: String.Encoding.utf8)
ここでは組み込みのApple APIを使用しています。サポートされているタイプからRTFとHTMLのみを使用しています。
「This answers our first question.」というサンプル文を使うと:
This answers our {
NSColor = "UIExtendedSRGBColorSpace 0.121569 0.133333 0.145098 1";
NSFont = "\".SFNS-Regular 15.00 pt. P [] (0x11592a450) fobj=0x11592a450, spc=3.98\"";
NSParagraphStyle = "Alignment Natural, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 22.5/0, LineHeightMultiple 1.25, LineBreakMode WordWrapping, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection Natural, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ''";
}first{
NSColor = "UIExtendedSRGBColorSpace 0.0784314 0.0862745 0.0941176 0.945098";
NSFont = "\".SFNS-Bold 15.00 pt. P [] (0x11b333990) fobj=0x11b333990, spc=3.63\"";
NSParagraphStyle = "Alignment Natural, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 22.5/0, LineHeightMultiple 1.25, LineBreakMode WordWrapping, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection Natural, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ''";
ourSpecialBoldFlag = 1;
} question.{
NSColor = "UIExtendedSRGBColorSpace 0.121569 0.133333 0.145098 1";
NSFont = "\".SFNS-Regular 15.00 pt. P [] (0x11592a450) fobj=0x11592a450, spc=3.98\"";
NSParagraphStyle = "Alignment Natural, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 22.5/0, LineHeightMultiple 1.25, LineBreakMode WordWrapping, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection Natural, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ''";
}
{
NSFont = "\".SFNS-Regular 16.00 pt. P [] (0x11b344de0) fobj=0x11b344de0, spc=4.19\"";
}
以下のHTMLに変換されます:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<title></title>
<meta name="Generator" content="Cocoa HTML Writer" />
<meta name="CocoaVersion" content="2487.5" />
<style type="text/css">
p.p1 {
margin: 0px 0px 0px 0px;
line-height: 22.5px;
font: 15.0px \'.AppleSystemUIFont\';
color: #1f2225;
}
span.s1 {
font-family: \'.SFNS-Regular\';
font-weight: normal;
font-style: normal;
font-size: 15px;
}
span.s2 {
font-family: \'.SFNS-Bold\';
font-weight: bold;
font-style: normal;
font-size: 15px;
color: rgba(20, 22, 24, 0.95);
}
</style>
</head>
<body>
<p class="p1">
<span class="s1">This answers our </span><span class="s2">first</span><span class="s1"> question.</span>
</p>
</body>
</html>
html、head、bodyタグを含む完全なHTMLページが生成されているのが分かります。属性はCSSスタイルに変換され、後でボディ部分でクラスを通じて参照されます。各クラスは元の属性付き文字列内の個別の属性レンジに対応しています。
- 画像/動画:画像/動画をダウンロードまたはキャッシュから取得し、適切なUTTypeタイプでペーストボードに配置します(画像には
public.pngを使用)。 - スマートURLと通常URL:
plainおよびpublic.urlタイプとして追加されます。
シンプルなテキストブロックを選択してコピーを押すと、これがクリップボードの状態です(クリップボードビューアーアプリで確認したもの)。
これが最初の問いへの答えです:Craft固有のブロックを、他のアプリが読み取れる標準的なペーストボードフォーマットに変換する方法についてです。
悪魔は細部に宿ります。ペーストボードにデータをどう配置するかは非常に重要です。パーティーのアナロジーを続けましょう。
いくつかの外国語はとても得意だが、学校でサボった言語もあると想像してください
Craftでは、まずユーザーに価値をもたらす最も重要・重大な機能に注力し、その後フィードバックを受けて何がユーザーにとって本当に重要かを見極めながら改善していきます。コピー&ペーストについても同様でした。これはすべてのドキュメントエディターに不可欠な機能であるため、2020年11月のCraft最初のリリース当初から追加されました。最初はプレーンテキストとマークダウンを正しくサポートすることに注力しました——当時はそれが重要に思えました。
マークダウンとプレーンテキストはドキュメントエディターの英語(共通言語)のようなもので、誰もがサポートすべきものですよね?
しかし、世界もCraftも変化しています。他のアプリがHTMLやRTFを追加・優先・サポートするようになりました。
一方、CraftはテーブルのようなさらにPy複雑な構造も導入しましたが、ペーストボードのサポートには正しく統合されていませんでした。
この2つが組み合わさって生じたシナリオ:テーブルをコピーしてHTMLを使用するアプリケーションにペーストすると、「Table」(文字通り)とだけ貼り付けられる状況が生まれました。
私たちはHTMLスキルを向上させるよう、また人気アプリへの対応推奨のフィードバックを受け始めました。
「[…] Craftが大好きです。実際に定着した初めてのアプリです——Notion、Roam、Evernoteなど試しましたが。ただ、チーム全体が苦しんでいる機能が1つあります:コピー&ペーストです。Craft上のテキストのスクリーンショットと、Google Docsにペーストしたときの見た目を添付しています。主な問題は箇条書きのスペースです——書式が保持されません。[…]」
「[…] ただし、チームはコミュニケーションや共同作業のために電子メール、Slack、Google Docsも使用しています。このため、メモをこれらのアプリにコピー&ペーストする必要がよく生じます。残念ながら、スタイル/書式が1:1で転送されず、常にタブなどを削除する必要があります。[…]」
推奨の一部はWebベース(Hey、Google Documents、Gmail web)でした。WebではClipboard APIはプレーンテキストとHTMLフォーマットを主にサポートしています。そのため、HTMLをより得意にすることにしました。
Craftバージョン2.7.9では、インラインスタイリング、リストアイテム、テーブルのためのより優れたHTMLサポートを導入しました。
インラインスタイリングのHTML変換をどのように改善しましたか?
元々の問題は、プラットフォームのネイティブAPIを使ってHTML文字列を作成しても、クロスプラットフォーム環境の別のアプリがHTML文字列の一部を単純に無視するということでした。これによりユーザーはコピーペースト中に書式を失っていました。悪意のあるアプリが実行可能なJavaScriptコードを<head>部分に隠すことがあるため、解析時にそれを実行したくないからです。そのため彼らはヘッド部分を削除してボディの内容だけを使います。
これを解決するために、すべてのスタイルをインラインにしました。少し見た目は悪いですが、シンプルな解決策です。上記の例は次のようになります:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<title></title>
<meta name="Generator" content="Cocoa HTML Writer">
<meta name="CocoaVersion" content="2487.5">
<style type="text/css"></style>
</head>
<body>
<p style="font:15.0px '.AppleSystemUIFont';color:#1f2225;margin:0.0px 0.0px 0.0px 0.0px;line-height:22.5px"><span style="font-style:normal;font-weight:normal;font-size:15.00px;font-family:'.SFNS-Regular'">This answers our </span><span style="font-family:'.SFNS-Bold';font-size:15.00px;font-style:normal;color:rgba(20, 22, 24, 0.95);font-weight:bold">first</span><span style="font-style:normal;font-weight:normal;font-size:15.00px;font-family:'.SFNS-Regular'"> question.</span></p>
</body>
</html>
</div>
これにより、ユーザーがHTMLとしてコピーした際にHeyやGmail(web)などのアプリケーションの書式保持が改善されました。
リストアイテムのHTML変換をどのように改善しましたか?
Craftはトグル、番号付きリスト、箇条書きリストをサポートしています。以前は、これらはすべてブロックコンテンツの前にダッシュを追加することでRTF/HTMLフォーマットに変換されていました。ユーザーはCraftエディター上で見えるような適切な交互のリストプレフィックス表現を期待します。NSTextListアイテムをブロック属性付き文字列の段落属性に追加することでこの問題を解決しました。
番号付きリストにはdecimalテキストリストアイテムを追加する必要があります:NSTextList(markerFormat: .decimal, options: 0)、箇条書きリストにはdisc/circleアイテムを追加できます:NSTextList(markerFormat: nested_level%2 == 0 ? .disc : .circle, options: 0)
let item:NSTextList = NSTextList(markerFormat: .decimal|.disc|.circle, options: 0)
let p:NSMutableParagraphStyle = NSMutableParagraphStyle()
p.textLists = [item]
blockAttributedString.addAttributes([.paragraphStyle : p])
例を第1レベルの箇条書きリストに入れると、変換された属性付き文字列は次のように変わります:
This is the answer for the {
NSColor = "UIExtendedSRGBColorSpace 0.121569 0.133333 0.145098 1";
NSFont = "\".SFNS-Regular 15.00 pt. P [] (0x12d166c90) fobj=0x12d166c90, spc=3.98\"";
NSParagraphStyle = "..., Lists (\n \"NSTextList 0x6000077e54a0 format <{circle}>\"\n)...";
}first{
NSColor = "UIExtendedSRGBColorSpace 0.0784314 0.0862745 0.0941176 0.945098";
NSFont = "\".SFNS-Bold 15.00 pt. P [] (0x36fc862c0) fobj=0x36fc862c0, spc=3.63\"";
NSParagraphStyle = "..., Lists (\n \"NSTextList 0x6000077e54a0 format <{circle}>\"\n)...";
lukiBold = 1;
} question
{
NSColor = "UIExtendedSRGBColorSpace 0.121569 0.133333 0.145098 1";
NSFont = "\".SFNS-Regular 15.00 pt. P [] (0x12d166c90) fobj=0x12d166c90, spc=3.98\"";
NSParagraphStyle = "..., Lists (\n \"NSTextList 0x6000077e54a0 format <{circle}>\"\n)...";
重要な点が2つあります:
- インデントを持つネストされたリストをサポートする場合、レンジの属性を設定するときに同じ
NSTextListオブジェクトを使用するようにしてください。これによりNSAttributedStringが属性レンジを統合でき、変換で適切なUL/LIリストアイテムが生成されます。 - 複数行を改行でつなげる場合、末尾の
\nもテキストリストが適用されるレンジの一部であることを確認してください。
別のネストされたリストをブロックに追加した後の結果の例:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<title></title>
<meta name="Generator" content="Cocoa HTML Writer">
<meta name="CocoaVersion" content="2487.5">
<style type="text/css"></style>
</head>
<body>
<ul style="list-style-type:circle">
<li style="font:15.0px '.AppleSystemUIFont';margin:0.0px 0.0px 0.0px 0.0px;color:#1f2225"><span style="font-size:15.00px;font-family:'.SFNS-Regular';font-weight:normal;font-style:normal">This is the answer for the </span><span style="font-size:15.00px;font-style:normal;font-weight:bold;color:rgba(20, 22, 24, 0.95);font-family:'.SFNS-Bold'">first</span><span style="font-size:15.00px;font-family:'.SFNS-Regular';font-weight:normal;font-style:normal"> question</span>
</li>
<ul style="list-style-type:disc">
<li style="font:15.0px '.AppleSystemUIFont';margin:0.0px 0.0px 0.0px 0.0px;color:#1f2225"><span style="font-size:15.00px;font-family:'.SFNS-Regular';font-weight:normal;font-style:normal">This is the answer for the </span><span style="font-size:15.00px;font-style:normal;font-weight:bold;color:rgba(20, 22, 24, 0.95);font-family:'.SFNS-Bold'">second</span><span style="font-size:15.00px;font-family:'.SFNS-Regular';font-weight:normal;font-style:normal"> question</span>
</li>
</ul>
</ul>
</body>
</html>
</div>
Web開発者はAppleのコンバーターの問題点をすでに見つけているかもしれません:標準的なネストされたリスト構文を生成していません!第2レベルの<ul>タグは第1レベルの<li>タグの内側から始まるべきです(w3schoolの例を参照)。一部のアプリケーションはこれで問題ありませんが、iOSのGmailなどは対応しておらず、新しいネストされたリストの各冒頭に余分な箇条書き点が表示されます。
テーブルのHTML変換をどのように改善しましたか?
Appleの組み込みAPIはテーブルを作成できません。なぜなら、iOS上のNSAttributedStringはそれをサポートしていないからです(macOSではNSParagraphStyleのNSTextTableオブジェクトでサポートされています)。独自のテーブル表現をHTMLテーブルフォーマットに変換することで解決しました。NSAttributedStringのHTML変換中にテーブルタグを保持するため、テーブルのHTML文字列を作成しつつ変換後にトークンで置き換えるという方法を取りました。通常のテキストブロックで解決したのと同じインラインフォーマット変換を個別のセルにも適用しました。

素晴らしい、CraftはHTMLの語学コースを修了しました。これで問題解決……ですよね?……ですよね……ですよね?
人気の言語を上手に話せても、あなたの最も苦手な言語を好むアプリがあるかもしれない
パーティーの電気を消してみましょう。聞こえるのは他の人が異なる言語で叫ぶ声ですが、誰が話しているかは分かりません。さまざまな言語を認識するだけです。Appleプラットフォームでは、何をどの順番で受け取るかは受け取り側が決めます。
最高のHTMLで話しても、受け取り側のアプリケーションが先にRTFを探していればどうにもなりません。
受け取り側はコピー操作のソースアプリケーションを推測できます。なぜならアプリケーションはペーストボードに追加のコンテンツを置いて、自分のアイデンティティの一端を明らかにするからです:
- Apple iWorkアプリケーション(Pages、Numbers)はiWorkマーク付きの追加コンテンツ(例:
com.apple.iWork.TSPNativeData)を配置します。 - Adobeは追加の
com.adobe.pdfを配置します。 - Safariは追加の
com.apple.webarchiveを配置します。
良心的なアプリは、より良い連携を提供するためにUIPasteboardのスキャン順序を変えるヒューリスティックや知識を使うかもしれません。そうでないアプリもあり、その結果が最終的に貧しいユーザー体験につながります。
私たちも、ユーザーが自分のデータをどのフォーマットで持ちたいかを決めることを許可することで、受け取り側がペーストボードデータを使う方法をある程度コントロールしようとしました。プレーンテキスト/マークダウン/HTML/RTFのシングルコピーをサポートしており、ペーストボードには選択したフォーマットのデータのみが配置されます。これにより受け取り側は私たちが得意な言語に集中せざるを得なくなります(またはそのフォーマットをサポートしていない場合は全く認識できません)。
すべてが正常でも、まだ問題を引き起こす可能性のある要因があります:
- 新しいOSバージョンがペーストボードへの異なるコンテンツ配置を壊すことがあります:iOS 17では
URL(string:)APIが変更され、提供された文字列がURLでなくてもnilを返さなくなりました。これがRayCastアプリのバグの原因となり、コピーされたデータが単純なテキストであってもURLコンテンツをペーストボードに配置するようになりました。CraftはこれをエスケープされたURLコンテンツとしてブロックにペーストしようとしました。 - 受け取り側アプリの新しいバージョンがデータのパース方法を変えることがあり、またプラットフォームによって実装が異なることもあります(GmailウェブはApple HTMLコンテンツをうまく解析できますが、Gmail iOSアプリはネストされた
NSTextListに苦労しています)。
声が仲介者を通じて伝わると想像してください
パーティーの電気が消え、他の人に聞こえやすいように1つの言語だけで話しますが、あなたが言うことはすべて実際には別の人によって聞かれて伝達されます。
これがMacアプリケーションのケースで、macCatalystフレームワークを使用しています。このフレームワークはiPadコードを移植してmacOSサポートを充実させるのに役立ちました。この移行から多くのことを学びました(こちらのブログ記事をご覧ください)。
HTMLやRTFとしてのコピーを試み始めたとき、フレームワークのもう一つの興味深い動作について知りました。iOSでは、ペーストボードのためにNSItemProviderを登録するとき、フォーマットタイプとロードハンドラーを定義します。このロードハンドラーは受け取り側アプリがそのフォーマットを使用することを決定したときに非同期でデータを収集します。Macでは、macCatalystフレームワークが非同期コードを同期AppKit APIに変換する必要があります。つまり、フォーマットを登録するたびに、ロードハンドラーが即座に呼び出され、メインスレッドをブロックして、くるくる回るビーチボールをパーティーに招待することになります。
さらに、ペーストボードに配置されるアイテムは、ネイティブAppKit APIがデフォルトで追加するものとは異なります。2つのケースのペーストボードの内容を比較してみましょう:


これにより、一部のネイティブアプリおよびElectronアプリ(SlackやVSCodeなど)が、開発者が正しいフォーマットでペーストボードに配置しているにもかかわらず、HTMLおよびRTFコンテンツを正しく認識しないという結果になります。
アプリケーションのこの部分をAppKitフレームワークを使用するように変更する予定です。
これが2番目の問いへの答えです:コンテンツを正しい方法で転送するために、特定の言語のサポートを改善し、データのコピー方法をユーザーがコントロールできるようにし、よりネイティブなソリューションを使用しました。
まとめ
この記事がコピーペーストを正しく処理することの複雑さを示せていれば幸いです。最近のリリースでの決定の背景と機能の設計方法をできる限り明らかにしようとしました。
アプリのコピー体験を改善したい場合、以下のポイントを参考にしてください:
- ペーストボードの現在の状態を把握してください。どんなアプリでも使えますが、私たちは無料のClipboard Viewerを使用しました。ペーストボードに配置されているコンテンツの種類を確認するのに役立ちます。
- サポートすべきフォーマットやオプションは多くあります。ユーザーが不満に思っている、または毎日依存しているものを改善することに注力してください(ドキュメントエディターではプレーンテキスト、マークダウン、HTML、RTFが最もよく使われます)。
NSItemProviderのregisterDataRepresentationロードハンドラーでは長時間かかる処理やCPU集中型の処理を避けてください。macCatalystではアプリケーションがフリーズする可能性があります。- 他のアプリがコンテンツをどのように消費するかをコントロールするために、「名前を付けてコピー」機能を提供してください。これは上級/プロ向け機能かもしれませんが、ユーザーが意思決定プロセスに参加できるようになります。
- 受け取り側アプリがデータをどのように処理するか分からないため、標準的で一般的で安全なフォーマットに変換してください(AppleのHTML文字列からはhead部分ではなくbody部分に注目するなど)。
上記の話は、Craftからコンテンツをコピーする際のコインの一面に過ぎません。ペーストの問題もこれと同様に難しいものです。今度は自分が暗い部屋で多言語の叫び声を理解しようとする側になります。将来の記事でそちらについても詳しく説明します。
このブログ記事に関するご意見、アイデア、技術的な質問やコメントがあれば、peter@craft.doまでメールでお知らせください。お読みいただきありがとうございました!
