The ability to cut, copy and paste items within and between documents is one of the most fundamental features of modern computer systems. For the macOS user, this is handled through the Clipboard, but internally (and to developers) it’s confusingly known as the Pasteboard instead. This article explains how it works, and how you can see inside it.
As one of the oldest features of the Mac, going back to its precursor the Lisa, the pasteboard is one of the most mature parts of macOS. It’s all run by a tiny background service or daemon /usr/libexec/pboard, with which each app communicates using XPC messages. When you cut or copy an item, its data are converted into one or more standard formats and passed to one of the pasteboards managed by pboard
, normally the general pasteboard to make it accessible to everything else. When you paste an item from the clipboard, its data are passed to the requesting app, which then performs any conversion necessary, and inserts it in the document.
The details are a bit more complex, and can be followed in the log by watching entries from the sub-system com.apple.CFPasteboard
. If you want to browse the full sequence of entries for the summaries I provide here, I have put them into an appendix.
Pasteboards can hold multiple items, each of which can have one or more representations which are known here as flavors and conform to UTI types such as public.jpeg
for a JPEG image. In addition to a range of standard flavors covering the most common types of data, apps can define custom flavors to deal with other types. For example, if your app allows you to select multiple sections of rich text, it would then copy a list of those sections, each of which might contain a rich text version (UTI public.rtf
) and a plain text version (public.utf8.plain-text
).
There are several other standard public pasteboards: general is that most widely used for cut-copy-paste, and there’s a Find pasteboard used in Find operations, and dedicated pasteboards for rulers, fonts, and drag-and-drop. The best way to explore these is using the free Pasteboard Viewer from the App Store.
When copying, the first log entry you’ll see declares which pasteboard is being used, normally Apple CFPasteboard general
, and a UUID is assigned to this:
BeginGeneration('Apple CFPasteboard general' (<CFUUID 0x6000024f75c0> 4F359174-D592-4132-9F29-08B166B6E78D))
following which a com.apple.pboard.begin-generation
message is passed to pboard
. That’s followed by the promise of some data for that pasteboard, which specifies its flavor using a UTI, such as public.utf8-plain-text
. That particular flavor has a well-known pasteboard type of NSStringPboardType, and conforms to the more generic UTI of public.plain-text
.
The data for the pasteboard is then supplied with a message of com.apple.pboard.has-entries
, where you’ll see the flavor and size of the data, after which data is registered with a message of com.apple.pboard.register-entries
. Here, the type alias of public.plain-text
is added, ensuring the contents of the pasteboard can be accessed as either plain text or UTF8 plain text. The pasteboard item(s) are then ‘advertised’ as being available.
When an app wants to paste items from the specified pasteboard, this starts with a com.apple.pboard.get-counts
message which triggers the cache to be rebuilt, and returns counts of the items and flavors for that pasteboard. A com.apple.pboard.refresh-cache
message refreshes the cache again, and returns updated counts. The sequence is completed by a com.apple.pboard.request-data
message, which then returns the requested items with their flavors. It’s then up to the requesting app to convert that data to whatever internal form it requires before inserting it in the document.
This sequence is summarised in this diagram.
with a tear-out PDF version from here: pasteboard2
If you want to do fancy things with the clipboard as a user, there are many managers and enhancers available, among which the best-known is Pastebot, available from the App Store.
Appendix
Log sequence for a simple copy:
1.102706 com.apple.CFPasteboard BeginGeneration('Apple CFPasteboard general' (<CFUUID 0x6000024f75c0> 4F359174-D592-4132-9F29-08B166B6E78D))
1.102796 com.apple.CFPasteboard Handling com.apple.pboard.begin-generation message
1.102982 com.apple.CFPasteboard result: 0 generation: 57
1.103082 com.apple.CFPasteboard PromiseDataUsingBlock('Apple CFPasteboard general' (<CFUUID 0x6000024f75c0> 4F359174-D592-4132-9F29-08B166B6E78D) gen: 57 item: 789514 flavor: 'public.utf8-plain-text')
1.103091 com.apple.CFPasteboard Successfully promised data (new-entry)
1.103114 com.apple.CFPasteboard result: 0
1.103144 com.apple.CFPasteboard Handling com.apple.pboard.has-entries message
1.103210 com.apple.CFPasteboard SetData('Apple CFPasteboard general' (<CFUUID 0x6000024f75c0> 4F359174-D592-4132-9F29-08B166B6E78D) gen: 57 item: 789514 flavor: 'public.utf8-plain-text') local-gen: 57
1.103214 com.apple.CFPasteboard _SetData('Apple CFPasteboard general' (<CFUUID 0x6000024f75c0> 4F359174-D592-4132-9F29-08B166B6E78D) gen: 57 item: 789514 flavor: 'public.utf8-plain-text' data: 17 bytes flags: 20000000) local-gen: 57
1.103216 com.apple.CFPasteboard Promise fufilled - 17 bytes for 'Apple CFPasteboard general' gen: 57 of flavor: 'public.utf8-plain-text'
1.103217 com.apple.CFPasteboard Successfully set data (cache)
1.103223 com.apple.CFPasteboard Flushing 1 pending entries synchronously for pboard 'Apple CFPasteboard general' (<CFUUID 0x6000024f75c0> 4F359174-D592-4132-9F29-08B166B6E78D) generation: 57.
1.103268 com.apple.CFPasteboard Handling com.apple.pboard.register-entries message
1.103314 com.apple.CFPasteboard Installing local data provider: (uuid:7F4F8F60-6BFA-461E-83DD-35C39575F15D gen: 57 item: 789514 flavor: 'public.utf8-plain-text') for gen: 57 item: 789514
1.103318 com.apple.useractivity PBOARD CLIENT: add type for index: 0/<private>
1.103338 com.apple.useractivity Sending pasteboard update to server: <private>
1.103345 com.apple.useractivity [Local Pasteboard] Adding alias: public.text for type: public.utf8-plain-text
1.103393 com.apple.CFPasteboard result: 0
1.103445 com.apple.useractivity Did send update to server for generation: 57
1.103533 com.apple.useractivity [PBOARD CONTROLLER] Got type update from client, gen: 57, <private>
1.105476 com.apple.useractivity DETERMINE: 3F9DC9E2-9673-46B9-9922-6CF15AFB8C29/<private> as the current item-to-advertise.
1.105546 com.apple.useractivity self.advertisableItems is <private>, itemToAdvertise is <private>
1.105607 com.apple.useractivity ADVERTISING: Advertising new item or updating user-idle time in previous advertisement, 3F9DC9E2-9673-46B9-9922-6CF15AFB8C29/<private>
1.105671 com.apple.useractivity ADVERTISING: $a1072cb8bf1d98/*32 pb-1 (old=$a1072cb8bf1d98/*32 pb-0)
1.105692 com.apple.useractivity advertising <private>, when = Mon May 11 07:44:01 2020
1.105745 com.apple.useractivity ADVERTISING:$a1072cb8bf1d98/*32 pb-1 3F9DC9E2-9673-46B9-9922-6CF15AFB8C29 type=<private><private> at Mon May 11 07:44:01 2020 opts={ UAPasteboardAvailable = 1; UAPasteboardVersionBit = 1; UAUserActivityItemIsNotActiveKey = 0;}
1.105949 com.apple.sharing Request to advertise <a1072cb8bf1d9818a0> with options {SFActivityAdvertiserOptionFlagCopyPasteKey = 1;SFActivityAdvertiserOptionMinorVersionKey = 0;SFActivityAdvertiserOptionVersionKey = 0;}
Times given in seconds.
Log sequence for a simple paste:
3.812258 com.apple.CFPasteboard Handling com.apple.pboard.get-counts message
3.812313 com.apple.CFPasteboard CopyItemsAndFlavors('Apple CFPasteboard general (<CFUUID 0x6000037f0780> E77DB031-76E8-4841-8226-D0AA6FC8962A)' gen: -1)
3.812317 com.apple.CFPasteboard Rebuilding cache for 'Apple CFPasteboard general' (<CFUUID 0x6000037f0780> E77DB031-76E8-4841-8226-D0AA6FC8962A)
3.812327 com.apple.CFPasteboard CopyItemsAndFlavors('Apple CFPasteboard general (<CFUUID 0x6000024f75c0> 4F359174-D592-4132-9F29-08B166B6E78D)' gen: -1)
3.812331 com.apple.CFPasteboard result: 0, gen: -1 item-count: 1 flavor-count: 1 (-1 means nil)
3.812338 com.apple.CFPasteboard GetDataFlags('Apple CFPasteboard general' (<CFUUID 0x6000024f75c0> 4F359174-D592-4132-9F29-08B166B6E78D) gen: -1 item: 789514, flavor: 'public.utf8-plain-text')
3.812339 com.apple.CFPasteboard result: 0 flags: 20000000
3.812355 com.apple.CFPasteboard Handling com.apple.pboard.refresh-cache message
3.812394 com.apple.CFPasteboard result: 0, gen: -1 item-count: 1 flavor-count: 1 (-1 means nil)
3.812401 com.apple.CFPasteboard GetDataFlags('Apple CFPasteboard general' (<CFUUID 0x6000037f0780> E77DB031-76E8-4841-8226-D0AA6FC8962A) gen: -1 item: 789514, flavor: 'public.utf8-plain-text')
3.812402 com.apple.CFPasteboard result: 0 flags: 20000000
3.812488 com.apple.CFPasteboard GetItemCount('Apple CFPasteboard general' (<CFUUID 0x6000037f0780> E77DB031-76E8-4841-8226-D0AA6FC8962A) gen: -1)
3.812492 com.apple.CFPasteboard result: 0 gen: 57 count: 1
3.812495 com.apple.CFPasteboard CopyItemsAndFlavors('Apple CFPasteboard general (<CFUUID 0x6000037f0780> E77DB031-76E8-4841-8226-D0AA6FC8962A)' gen: -1)
3.812496 com.apple.CFPasteboard result: 0, gen: -1 item-count: 1 flavor-count: 1 (-1 means nil)
3.812499 com.apple.CFPasteboard CopyData('Apple CFPasteboard general' (<CFUUID 0x6000037f0780> E77DB031-76E8-4841-8226-D0AA6FC8962A) gen: 57 item: 789514 flavor: 'public.utf8-plain-text')
3.812500 com.apple.CFPasteboard Will request data from daemon
3.812546 com.apple.CFPasteboard Handling com.apple.pboard.request-data message
3.813097 com.apple.CFPasteboard (not-in-cache) - result: 0 generation: 57 data (17 bytes) flags: 20000000
3.813776 com.apple.CFPasteboard GetItemCount('Apple CFPasteboard general' (<CFUUID 0x6000024f75c0> 4F359174-D592-4132-9F29-08B166B6E78D) gen: -1)
3.813778 com.apple.CFPasteboard result: 0 gen: 57 count: 1
3.813813 com.apple.CFPasteboard GetItemAtIndex('Apple CFPasteboard general' (<CFUUID 0x6000024f75c0> 4F359174-D592-4132-9F29-08B166B6E78D) gen: -1 idx: 1)
3.813814 com.apple.CFPasteboard result: 0 gen: 57 item: 789514
3.813817 com.apple.CFPasteboard CopyFlavorsForItem('Apple CFPasteboard general' (<CFUUID 0x6000024f75c0> 4F359174-D592-4132-9F29-08B166B6E78D) gen: -1 item: 789514)
3.813819 com.apple.CFPasteboard result: 0 gen: 57 flavor-count: 1 (-1 means nil)
3.813823 com.apple.CFPasteboard CopyData('Apple CFPasteboard general' (<CFUUID 0x6000024f75c0> 4F359174-D592-4132-9F29-08B166B6E78D) gen: 57 item: 789514 flavor: 'public.utf8-plain-text')
3.813825 com.apple.CFPasteboard (cache) - result: 0 generation: 57 data (17 bytes) flags: 20000000
List of requests supported by pboard
:
com.apple.pboard.create
com.apple.pboard.get-counts
com.apple.pboard.barrier
com.apple.pboard.begin-generation
com.apple.pboard.has-entries
com.apple.pboard.register-entries
com.apple.pboard.request-data
com.apple.pboard.refresh-cache
com.apple.pboard.release
com.apple.pboard.unique-promise-file
com.apple.pboard.resolve-all-promises
com.apple.pboard.resolve-pboard-promises
com.apple.pboard.set-data-flags
com.apple.pboard.make-generation-local
Thanks to Rob Mayoff for compiling this.