The pasteboard server is shared by all running apps. It contains data that the user has cut or copied, as well as other data that one application wants to transfer to another.
NSPasteboard objects are the only interface to the server for an application and to all pasteboard operations.
An NSPasteboard object is also used to transfer data between apps and service providers listed in each application’s Services menu.
For transferring data with drag and drop, the drag pasteboard is used to transfer data that is being dragged by the user.
A pasteboard can contain multiple items. We can directly write or read any object that implements the NSPasteboardWriting or NSPasteboardReading protocol, respectively.
This allows us to write and read common items such as URLs, colors, images, strings, attributed strings, and sounds without an intermediary object.
Custom classes can also implement these protocols for use with the pasteboard.
Writing methods provide a convenient means of writing to the first pasteboard item, without having to create the first pasteboard item.
The general pasteboard, available by way of the general class method, automatically participates with the Universal Clipboard feature in Mac OS 10.12 and later.
IPC Using Pasteboard
A pasteboard is a standard way to exchange data between applications. The most common example of IPC using a pasteboard is copy-paste functionality provided by macOS.
When a user copies the data, it gets placed in the pasteboard, and when the user pastes it, the data is made available to the application from the pasteboard.
Another common example of IPC through a pasteboard is drag and drop features supported by some applications.
Pasteboards exist in a special global memory separate from application processes. A pasteboard object accesses this shared repository where reader and writer applications read or write, respectively.
When moving data between two different applications, the memory space assigned for a pasteboard gets involved, so the data persists even in the absence of the source.
A pasteboard could be public or private depending on the use case. A private pasteboard is recommended if IPC is to be implemented only between predefined client-server apps. If we have to provide a generic copy-paste or drag feature, we need to use public pasteboards.
Let’s see an example of IPC using copy/paste of a string through a private pasteboard.
First, create two projects with the macOS App template as shown in Figures 5-10 and 5-11 in Chapter 5.
Objective-C Implementation
Listing
7-1 declares the
pasteboard name which is common for server and client apps.
#define PrivatePasteboard @”PrivatePasteboard”
Listing 7-1Common to both server and client
Now we will look into the code to write to the pasteboard from a
server application (Listing
7-2).
– (void)copyToPrivatePasteboard
{
NSPasteboard* privatePateboard = [NSPasteboard pasteboardWithName:PrivatePasteboard];
//Clear pasteboard from previously copied items
[privatePateboard clearContents];
//Create a array of objects that needs to be copied
//I have used a hard-coded string for example. It could be a input from UI
NSArray* objectTobeCopied = [NSArray arrayWithObject:@”IPC using private pasteboard”];
//Write array of objects to pasteboard
[privatePateboard writeObjects:objectTobeCopied];
}
Next, we will look into the reader code for the
client application. So, the client will gain access to the private pasteboard by name and will read the copied values (Listing
7-3).
– (void)readFromPrivatePasteboard
{
NSPasteboard* privatePateboard = [NSPasteboard pasteboardWithName:PrivatePasteboard];
//Specify the type of objects to be read from pasteboard
NSArray *typeArray = [NSArray arrayWithObject:[NSString class]];
NSDictionary *optionsDic = [NSDictionary dictionary];
//Check if objects of specified type can be read from pasteboard
BOOL canRead = [privatePateboard canReadObjectForClasses:typeArray options:optionsDic];
if (canRead)
{
NSArray *objectsToPaste = [privatePateboard readObjectsForClasses:typeArray options:optionsDic];
NSLog(@”%@”,objectsToPaste);
}
}
Reading/Writing Custom Data
We might want to read/write a custom object to the pasteboard. This is also possible if the custom
object conforms to the NSPasteboardWriting and NSPasteboardReading protocols. Let’s understand with one example how it can be done. First, let’s declare an interface for the custom object (Listing
7-4).
#import <Cocoa/Cocoa.h>
@interface CustomItem : NSObject<NSPasteboardWriting, NSPasteboardReading>
@property (atomic) NSInteger objectId;
@property (atomic, strong) NSString *text;
In the implementation file, first synthesize properties “objectId” and “text” as shown in Listing
7-5.
@implementation CustomItem
@synthesize objectId;
@synthesize text;
@end
Listing 7-5Synthesize properties in the implementation
Now we will implement pasteboard delegate functions in the implementation file. Listing
7-6 shows the implementation for write support for the
server, whereas Listing
7-7 shows the implementation for read support for the
client
.
#pragma mark –
#pragma mark NSPasteboardWriting support
– (NSArray *)writableTypesForPasteboard:(NSPasteboard *)pasteboard
{
// These are the types we can write.
NSArray *types = [NSArray arrayWithObjects:NSPasteboardTypeString, nil];
return types;
}
– (NSPasteboardWritingOptions)writingOptionsForType:(NSString *)type pasteboard:(NSPasteboard *)pasteboard
{
return 0;
}
– (id)pasteboardPropertyListForType:(NSString *)type
{
return [NSString stringWithFormat:@"%ld , %@ ",self.objectId, self.text];
}
Listing 7-6Delegate functions for write support
#pragma mark –
#pragma mark NSPasteboardReading support
+ (NSArray *)readableTypesForPasteboard:(NSPasteboard *)pasteboard
{
return [NSArray arrayWithObjects:(id)kUTTypeURL, NSPasteboardTypeString, nil];
}
+ (NSPasteboardReadingOptions)readingOptionsForType:(NSString *)type pasteboard:(NSPasteboard *)pasteboard
{
if ([type isEqualToString:NSPasteboardTypeString] || UTTypeConformsTo((__bridge CFStringRef)type, kUTTypeURL))
{
return NSPasteboardReadingAsString;
}
else
{
return NSPasteboardReadingAsData;
}
}
Listing 7-7Delegate functions for read support
Now, as shown in Listing
7-8, create an initializer for the
class.
– (id)initWithPasteboardPropertyList:(id)propertyList ofType:(NSString *)type
{
if ([type isEqualToString:NSPasteboardTypeString])
{
self = [super init];
NSArray* prArr = [propertyList componentsSeparatedByString:@","];
self.objectId = [[prArr objectAtIndex:0] integerValue];
self.text = [prArr objectAtIndex:1];
}
else
{
NSAssert(NO, @”internal error: type not supported”);
}
return self;
}
Swift Implementation
First, declare a pasteboard
name to be used by server and client apps (Listing
7-9).
let privatePasteboardName = "PrivatePasteboard"
Listing 7-9Common to both server and client
Now we will look into the code to write to the pasteboard from a server application (Listing
7-10).
func copyToPrivatePasteboard()
{
//Pasteboard
let privatePasteboard = NSPasteboard(name: NSPasteboard.Name(rawValue: privatePasteboardName))
//Clear pasteboard from previously copied items
privatePasteboard.clearContents()
//Create a array of objects that needs to be copied
//I have used a hard-coded string for example. It could be a input from UI
let objectTobeCopied : [NSPasteboardWriting] = ["IPC using private pasteboard" as NSPasteboardWriting]
//Write array of objects to pasteboard
privatePasteboard.writeObjects(objectTobeCopied)
}
Next, we will look into the reader code for the client application. So, the client will gain access to the private pasteboard by name and will read the copied values (Listing
7-11).
func pasteFromPrivatePasteboard()
{
//Pasteboard
let privatePasteboard = NSPasteboard(name: NSPasteboard.Name(rawValue: privatePasteboardName))
let objects = privatePasteboard.readObjects(forClasses: [NSString.self], options: nil)
print(objects as Any)
}
Reading/Writing Custom Data
We might want to
read/write a custom object to the pasteboard. This is also possible if the custom object conforms to the NSPasteboardWriting and NSPasteboardReading protocols. Let’s understand with one example how it can be done. Create a class for the custom
object and implement the pasteboard delegate functions (Listing
7-12).
class CustomItem : NSObject, NSPasteboardWriting, NSPasteboardReading
{
var objectId : Int?
var text : String?
func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType]
{
// These are the types we can write.
let types = [NSPasteboard.PasteboardType.string]
return types
}
func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any?
{
return "(objectId!),(text!)"
}
static func readableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType]
{
return [NSPasteboard.PasteboardType.string]
}
required init?(pasteboardPropertyList propertyList: Any, ofType type: NSPasteboard.PasteboardType)
{
super.init()
if type == NSPasteboard.PasteboardType.string, let prArr = (propertyList as? String)?.components(separatedBy: ",")
{
objectId = Int(prArr.first!)!
text = prArr.last!
}
else
{
print("internal error: type not supported")
}
}
}
Listing 7-12Custom data in the pasteboard
Time to Run the Code!
Run the server app first to write some data into the pasteboard. Then run the client app to read the data from the pasteboard. The client app should be able to read the content written by the server app.
With this, we are done with the implementation of IPC using pasteboards. Next, let’s check the pros and cons of IPC using pasteboards.
Pros and Cons of IPC Using Pasteboards
Whenever we copy-paste a text from a document into another document, we perform IPC using pasteboards. It is a hassle-free way to copy some data from one location to another.
With the feature of creating private pasteboards and the ability to store custom objects in them, it gives a great flexibility for implementing a custom copy-paste from one application to another application sharing the private pasteboard.
Another biggest advantage is that a pasteboard server resides outside an application’s process, and so a value copied into the pasteboards remains there even if the application which copied the content quits.
The client application can read the copied data from the pasteboard even when the originating application is not running.
On the other hand, the disadvantage is that the client did not get any notification about the data being available in the pasteboard. The client needs to check the data in the desired pasteboard, and that means this IPC technique is more suitable where data is read and written on specific commands from the client and the server like copy/cut and paste operations.
Real-World Scenarios for IPC Using Pasteboards
As discussed earlier, whenever we copy-paste a text from a document into another document, we perform IPC using pasteboards. Other examples include operations such as copy-pasting a file/image from one location to another location or into an application.
We can also use pasteboards where we need the application to read data which was dragged from a location or from within other applications and dropped on the concerned application.
What’s Next?
This is all about IPC using pasteboards. In the next chapter, we will discuss IPC using XPC. So, stay tuned and enjoy coding!