Search code examples
iosswiftclipboarduipasteboardpasteboard

UIPasteboard unable to copy text with expiration time multiple times


I'm calling:

UIPasteboard.general.setItems([[kUTTypePlainText as String: text]], options: [.localOnly: true, .expirationDate: expirationTime])

to copy text per button click. However, after the expiration time has elapsed (30 seconds), the copy functionality stops working. After looking in the debugger, the second time (or after) this line is called, the items array in the UIPasteboard comes back as empty. Why is this happening? Other apps like Lastpass, allow text to be copied multiple times with expiration times.

I have a hunch that it might be something to do with the key being used, any ideas?


Solution

  • After spending too much time, I wasn't able to figure out why the expirationDate option in setItems(_:options:) isn't working for subsequent usages of that function. There isn't any other documentation on this. Either this is a basic trivial issue that I'm not able to figure out or is something more complex with the API.

    Anyway, I've implemented this solution using a Timer. This will work for all iOS versions, we're simply clearing the items array of UIPasteboard after 30 seconds. The expirationDate option only works for iOS 10.3 and above whereas this functionality is more robust and will work for all versions.

    class MyPasteboard {
        private static let shared = MyPasteboard()
        private let pasteboard = UIPasteboard.general
        private var expirationTimer: Timer?
    
        private init() {}
    
        static func copyText(_ text: String, expirationTime: TimeInterval = 30) {
            shared.pasteboard.string = text
            shared.expirationTimer?.invalidate()
            shared.expirationTimer = Timer.scheduledTimer(
                timeInterval: expirationTime,
                target: self,
                selector: #selector(expireText),
                userInfo: nil,
                repeats: false
            )
        }
    
        @objc private static func expireText() {
            shared.expirationTimer = nil
            shared.pasteboard.items = []
        }
    }
    

    There are many different ways to architect this but I chose to do it this way as it allows me to abstract out the UIPasteboard copy functionality and for simple usage via a static function which is what I require.