Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-23797] iOS: Improve Pasteboard support

GitHub Issuen/a
TypeImprovement
PriorityHigh
StatusClosed
ResolutionFixed
Resolution Date2016-08-30T21:47:10.000+0000
Affected Version/sRelease 5.5.0
Fix Version/sRelease 5.5.0
ComponentsiOS
Labelsqe-5.5.0
ReporterEric Wieber
AssigneeHans Knöchel
Created2016-08-22T05:18:41.000+0000
Updated2016-09-06T21:58:04.000+0000

Description

There are several features of Pasteboards described in https://developer.apple.com/reference/uikit/uipasteboard that we currently do not support: Named pasteboards (TIMOB-23799), localOnly, expiration time/date, etc. Ideally, we would support these features (and avoid the deprecations introduced with iOS 10).

Comments

  1. Hans Knöchel 2016-08-22

    Good one. Named pasteboards are a big thing and need further investigation to be integrated in the existing API's, so I would like to get that in a new ticket like "iOS: Support named pasteboards". For the new setItems method that also takes UIPasteboardOption options, I just implemented the new API (for 6.1.0). PR coming shortly.
  2. Hans Knöchel 2016-08-22

    PR (master): https://github.com/appcelerator/titanium_mobile/pull/8247 PR (5_5_X): https://github.com/appcelerator/titanium_mobile/pull/8290 PR (6_0_X): https://github.com/appcelerator/titanium_mobile/pull/8289 Demo:
       var win = Ti.UI.createWindow({
           backgroundColor : "#fff"
       }); 
       
       var btn1 = Ti.UI.createButton({
           title : "Set clipboard items",
           top: 40
       });
       
       var btn2 = Ti.UI.createButton({
           title : "Get clipboard items",
           top: 80
       });
        
       btn1.addEventListener("click", function() {
       	var localOnly = Ti.UI.CLIPBOARD_OPTION_LOCAL_ONLY;
       	var expirationDate = Ti.UI.CLIPBOARD_OPTION_EXPIRATION_DATE;
       	
           Ti.UI.Clipboard.setItems({
       	    items: [{
       	        "text/plain": "John",
       	    },{
       	        "text/plain": "Doe"
       	    }],
       	    options: {
       	        localOnly: true,
       	        expirationDate: new Date(2020, 04, 20)
       	    }
       	});
       });
       
       btn2.addEventListener("click", function() {
           alert(Ti.UI.Clipboard.getItems());
       });
        
       win.add(btn1);
       win.add(btn2);
       win.open();
       
  3. Eric Wieber 2016-08-22

    [~hansknoechel], sounds good. Created TIMOB-23799 for Named Pasteboard support.
  4. Eric Wieber 2016-09-01

    The getItems() method is throwing an error when there are no items in the Pasteboard:
       [ERROR] :  2016-09-01 06:35:19.000 PasteboardCheck[12358:22821338] -[_NSZeroData boundBridge:withKrollObject:]: unrecognized selector sent to instance 0x79e63d80
       [ERROR] :  Script Error {
       [ERROR] :      column = 39;
       [ERROR] :      line = 58;
       [ERROR] :      message = "-[_NSZeroData boundBridge:withKrollObject:]: unrecognized selector sent to instance 0x79e63d80";
       [ERROR] :      stack = "[native code]\n";
       [ERROR] :  }
       
    *Steps to reproduce issue* 1. Create a new project 2. Add the below lines to the code, preferably on a button press: Ti.UI.Clipboard.clearData(); alert(Ti.UI.Clipboard.getItems()); 3. Run the project and press the button or call the methods *Expected results* getItems() returns an empty array, without error *Actual results* The above error is thrown *Notes* This is stated in the docs for setItems(), but is there a way we can handle it better? Perhaps checking numberOfItems and using that to make sure the Pasteboard is not empty first? Although a runtime error is thrown, you can see an empty array returned from getItems() if you dismiss the error and call it again. However, If you clear the data with clearData() and then call getItems() too many times (sometimes 2), then the app will crash with the below error:
       [ERROR] :  The application has crashed with an uncaught exception 'NSInvalidArgumentException'.
       [ERROR] :  Reason:
       [ERROR] :  -[_NSZeroData unboundBridge:]: unrecognized selector sent to instance 0x79191700
       [ERROR] :  Stack trace:
       [ERROR] :  
       [ERROR] :  0   CoreFoundation                      0x06f851f0 __exceptionPreprocess + 160
       [ERROR] :  1   libobjc.A.dylib                     0x065fae66 objc_exception_throw + 52
       [ERROR] :  2   CoreFoundation                      0x06ffe3dc -[NSObject(NSObject) doesNotRecognizeSelector:] + 172
       [ERROR] :  3   CoreFoundation                      0x06f0434c ___forwarding___ + 1052
       [ERROR] :  4   CoreFoundation                      0x06f03f0e _CF_forwarding_prep_0 + 14
       [ERROR] :  5   PasteboardCheck                     0x00061d6e -[KrollBridge unregisterProxy:] + 158
       [ERROR] :  6   PasteboardCheck                     0x00070839 KrollFinalizer + 489
       [ERROR] :  7   PasteboardCheck                     0x0042c42c _ZN2TI20JSCallbackObjectData8finalizeENS_6HandleINS_7UnknownEEEPv + 108
       [ERROR] :  8   PasteboardCheck                     0x005fb4ae _ZN2TI9WeakBlock5sweepEv + 110
       [ERROR] :  9   PasteboardCheck                     0x005fd358 _ZN2TI7WeakSet5sweepEv + 40
       [ERROR] :  10  PasteboardCheck                     0x004eedbc _ZN2TI11MarkedBlock5sweepENS0_9SweepModeE + 28
       [ERROR] :  11  PasteboardCheck                     0x003c08fa _ZN2TI18IncrementalSweeper7doSweepEd + 154
       [ERROR] :  12  PasteboardCheck                     0x003c084b _ZN2TI18IncrementalSweeper6doWorkEv + 27
       [ERROR] :  13  PasteboardCheck                     0x003bf566 _ZN2TI9HeapTimer12timerDidFireEP16__CFRunLoopTimerPv + 166
       [ERROR] :  14  CoreFoundation                      0x06f115d6 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
       [ERROR] :  15  CoreFoundation                      0x06f110ed __CFRunLoopDoTimer + 1213
       [ERROR] :  16  CoreFoundation                      0x06f10bff __CFRunLoopDoTimers + 255
       [ERROR] :  17  CoreFoundation                      0x06f086c0 __CFRunLoopRun + 2208
       [ERROR] :  18  CoreFoundation                      0x06f07bab CFRunLoopRunSpecific + 395
       [ERROR] :  19  CoreFoundation                      0x06f07a0b CFRunLoopRunInMode + 123
       [ERROR] :  20  GraphicsServices                    0x08241b4c GSEventRunModal + 177
       [ERROR] :  21  GraphicsServices                    0x082419c7 GSEventRun + 80
       [ERROR] :  22  UIKit                               0x025480eb UIApplicationMain + 148
       [ERROR] :  23  PasteboardCheck                     0x000462e8 main + 408
       [ERROR] :  24  libdyld.dylib                       0x07d96799 start + 1
       [ERROR] :  25  ???                                 0x00000001 0x0 + 1
       [ERROR] :  
       [ERROR] :  2016-09-01 06:37:27.651 PasteboardCheck[12715:22825481] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_NSZeroData unboundBridge:]: unrecognized selector sent to instance 0x79191700'
       [ERROR] :  *** First throw call stack:
       [ERROR] :  (
       [ERROR] :  	0   CoreFoundation                      0x06f85212 __exceptionPreprocess + 194
       [ERROR] :  	1   libobjc.A.dylib                     0x065fae66 objc_exception_throw + 52
       [ERROR] :  	2   CoreFoundation                      0x06ffe3dc -[NSObject(NSObject) doesNotRecognizeSelector:] + 172
       [ERROR] :  	3   CoreFoundation                      0x06f0434c ___forwarding___ + 1052
       [ERROR] :  	4   CoreFoundation                      0x06f03f0e _CF_forwarding_prep_0 + 14
       [ERROR] :  	5   PasteboardCheck                     0x00061d6e -[KrollBridge unregisterProxy:] + 158
       [ERROR] :  	6   PasteboardCheck                     0x00070839 KrollFinalizer + 489
       [ERROR] :  	7   PasteboardCheck                     0x0042c42c _ZN2TI20JSCallbackObjectData8finalizeENS_6HandleINS_7UnknownEEEPv + 108
       [ERROR] :  	8   PasteboardCheck                     0x005fb4ae _ZN2TI9WeakBlock5sweepEv + 110
       [ERROR] :  	9   PasteboardCheck                     0x005fd358 _ZN2TI7WeakSet5sweepEv + 40
       [ERROR] :  	10  PasteboardCheck                     0x004eedbc _ZN2TI11MarkedBlock5sweepENS0_9SweepModeE + 28
       [ERROR] :  	11  PasteboardCheck                     0x003c08fa _ZN2TI18IncrementalSweeper7doSweepEd + 154
       [ERROR] :  	12  PasteboardCheck                     0x003c084b _ZN2TI18IncrementalSweeper6doWorkEv + 27
       [ERROR] :  	13  PasteboardCheck                     0x003bf566 _ZN2TI9HeapTimer12timerDidFireEP16__CFRunLoopTimerPv + 166
       [ERROR] :  	14  CoreFoundation                      0x06f115d6 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
       [ERROR] :  	15  CoreFoundation                      0x06f110ed __CFRunLoopDoTimer + 1213
       [ERROR] :  	16  CoreFoundation                      0x06f10bff __CFRunLoopDoTimers + 255
       [ERROR] :  	17  CoreFoundation                      0x06f086c0 __CFRunLoopRun + 2208
       [ERROR] :  	18  CoreFoundation                      0x06f07bab CFRunLoopRunSpecific + 395
       [ERROR] :  	19  CoreFoundation                      0x06f07a0b CFRunLoopRunInMode + 123
       [ERROR] :  	20  GraphicsServices                    0x08241b4c GSEventRunModal + 177
       [ERROR] :  	21  GraphicsServices                    0x082419c7 GSEventRun + 80
       [ERROR] :  	22  UIKit                               0x025480eb UIApplicationMain + 148
       [ERROR] :  	23  PasteboardCheck                     0x000462e8 main + 408
       [ERROR] :  	24  libdyld.dylib                       0x07d96799 start + 1
       [ERROR] :  	25  ???                                 0x00000001 0x0 + 1
       [ERROR] :  )
       [ERROR] :  libc++abi.dylib: terminating with uncaught exception of type NSException
       [TRACE] :  [PasteboardCheck] -[_NSZeroData unboundBridge:]: unrecognized selector sent to instance 0x79191700
       [TRACE] :  [PasteboardCheck] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_NSZeroData unboundBridge:]: unrecognized selector sent to instance 0x79191700'
       
  5. Hans Knöchel 2016-09-01

    [~ewieber] As far as I understood, the iOS 10 method setItems persists the item at a different internal location to enable options like the expire- and localOnly configuration. So using clearData will only clear the items that have been set with the known methods: {quote} Starting in iOS 10 there is a new Handoff feature which allows the general pasteboard contents to automatically transfer between devices. You can control Handoff behavior for pasteboard contents, and can set a pasteboard to expire, by using the setItems(_:options:\) method, as follows: To exclude a pasteboard from Handoff, call the setItems(_:options:\) method with the localOnly option. To indicate an expiration time and date for copied data, call the setItems(_:options:\) method with the expirationDate option. At the time and date that you set, the system removes the pasteboard items from the pasteboard. {quote} So setItems is primary for enabling handoff with those items and clearData is specific to the system-wide pasteboard. See the [official docs](https://developer.apple.com/reference/uikit/uipasteboard).
  6. Eric Wieber 2016-09-01

    I see, but there is either some connection between the two or there is an issue with calling getItems on an empty Pasteboard, hence the crash. What are your thoughts on using numberOfItems to make sure that the Pasteboard has >0 items before calling \[\[\[UIPasteboard generalPasteboard\] items\] retain\]?
  7. Hans Knöchel 2016-09-01

    Well, the number of items is > 0, but those items are invalid from previous setItems calls that had an invalid mime-type specified. The whole ticket should have gone into 6.1.0. But for now, I would suggest an improvement-ticket to summarize your finding that can go into 6.0.0, so I have more time to investigate the different use-cases you want.
  8. Eric Wieber 2016-09-01

    TIMOB-23858 created to continue the discussion/investigation on the clearData and getItems issue
  9. Eric Wieber 2016-09-06

    Since TIMOB-23799, TIMOB-23857, and TIMOB-23858 cover the issue above and the implementation of other APIs, this can be considered done. Tested using: MacOS 10.12 (16A239m) Studio 4.7.1.201608190732 Ti SDK 5.5.0.v20160904203801 Appc NPM 4.2.7 Appc CLI 5.5.0-5 Alloy 1.9.1 Xcode 8.0 (8S201h) I am now able to get and set items on Pasteboards, using the localOnly and expiration date properties, without error. Tested using the provided samples as well as my own test apps that get/set multiple items on a Pasteboard and use one or both of the localOnly and expiration date properties.

JSON Source