© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2021
H. DuttInterprocess Communication with macOShttps://doi.org/10.1007/978-1-4842-7045-5_7

7. IPC Through Pasteboards

Hem Dutt1  
(1)
North Delhi, Delhi, India
 

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

Common 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];
}
Listing 7-2

Writer code

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);
         }
}
Listing 7-3

Reader code

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;
@end
Listing 7-4

Interface

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

Synthesize 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-6

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

Delegate 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;
}
Listing 7-8

Initializer

Swift Implementation

First, declare a pasteboard name to be used by server and client apps (Listing 7-9).
let privatePasteboardName = "PrivatePasteboard"
Listing 7-9

Common 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)
}
Listing 7-10

Writer code

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)
}
Listing 7-11

Reader code

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

Custom 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!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.243.32