I have the following code I use to grab text that a user has copied to the clipboard from outside the app so they can paste it in within the app:
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
if ([pasteboard hasStrings])
{
NSString *text = pasteboard.string;
}
This has always worked fine, until iOS14
I notice I've been getting crashes because pasteboard.string
is nil
despite hasStrings
being true.
I looked into the documentation and discovered that indeed, it's possible for pasteboard.string
to be nil
:
The value stored in this property is an NSString object. The associated array of representation types is UIPasteboardTypeListString, which includes type kUTTypeUTF8PlainText. Setting this property replaces all current items in the pasteboard with the new item. If the first item has no value of the indicated type, nil is returned.
I take this mean to that some sort of string that is not kUTTypeUTF8PlainText
is in the clipboard and that's why pasteboard.string
is nil
, but is this the correct interpretation?
I'm just confused as to what exactly is happening here and am unsure what to tell my user if I reach the case where pasteboard.string
is nil
?
-[UIPasteboard hasStrings] == YES
only means the items in pasteboard have type of public.utf8-plain-text
or any other types that indicates it's a string.
But -[UIPasteboard string]
can still return nil
if object of class NSString
cannot be constructed from any data provided by itemProviders
.
Here is an example to reproduce the situation you are in:
First implement a test class that conforms to NSItemProviderWriting
#import <Foundation/Foundation.h>
static NSString *const UTTypeUTF8PlainText = @"public.utf8-plain-text";
@interface TestObject : NSObject <NSItemProviderWriting>
@end
@implementation TestObject
- (NSData *)randomDataWithLength:(NSUInteger)length {
NSMutableData *data = [NSMutableData dataWithLength:length];
SecRandomCopyBytes(kSecRandomDefault, length, data.mutableBytes);
return data;
}
#pragma mark - NSItemProviderWriting
+ (NSArray<NSString *> *)writableTypeIdentifiersForItemProvider {
return @[UTTypeUTF8PlainText];
}
- (nullable NSProgress *)loadDataWithTypeIdentifier:(nonnull NSString *)typeIdentifier forItemProviderCompletionHandler:(nonnull void (^)(NSData * _Nullable, NSError * _Nullable))completionHandler {
// random data that an utf8 string may not be constructed from
NSData *randomData = [self randomDataWithLength:1];
completionHandler(randomData, nil);
return nil;
}
@end
Then put the test object into pastboard
if (@available(iOS 11.0, *)) {
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
TestObject *item = [TestObject new];
[pasteboard setObjects:@[item]];
if ([pasteboard hasStrings]) {
// text may be nil
NSString *text = pasteboard.string;
}
}