Ethan D'Mello
Ethan D'Mello

Reputation: 119

Trying to retrieve Pasteboard value after 10 s with background task

Context: The goal of my code is to copy text to the pasteboard and to wipe it out after 10s if it hasn't been overwritten with a new value. If a new value is copied that doesn't equal the previous value, the value on the pasteboard should NOT be wiped out. This requirement must run even if you background the app and move to a different app.

Implementation: To accomplish this task I used UIBackgroundTaskIdentifier, Timer, and UIPasteboard.

Results Simulator: This solution works perfectly

Results Physical Test Device iPhone 8 plus: This implementation works when I don't leave the app the background task was created in, however when I background the app and move to a different app and I haven't copied anything new, the timer activates but UIPasteboard.general.hasStrings returns false, when I believe it should return true as the original string copied should still be there. Is it because I am using an iPhone 8 plus, instead an iPhone 11? Am I accessing the Pasteboard correctly? Is what I am trying to do even possible?

import UIKit

class TimerViewController: UIViewController {

    private var backgroundTaskTimer: Timer?
    private var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier.invalid

    @IBOutlet weak var copiedLabel: UILabel!

    func didCopyText(_ text: String) {
        UIPasteboard.general.string = text
    }

    func clearTextAfterDelay(_ originalCopiedText: String) {
        backgroundTask = UIApplication.shared.beginBackgroundTask { [unowned self] in
            //end background task
            UIApplication.shared.endBackgroundTask(self.backgroundTask)
            self.backgroundTask = UIBackgroundTaskIdentifier.invalid
        }

        backgroundTaskTimer?.invalidate()
        backgroundTaskTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: false) { [unowned self] _ in
            if UIPasteboard.general.hasStrings {
                let currentTextOnClipBoard = UIPasteboard.general.string
                if currentTextOnClipBoard == originalCopiedText {
                    UIPasteboard.general.string = ""
                }
            }
            //end background task
            UIApplication.shared.endBackgroundTask(self.backgroundTask)
            self.backgroundTask = UIBackgroundTaskIdentifier.invalid
        }
    }


    @IBAction func copiedTapped(_ sender: Any) {
        copiedLabel.isHidden = false
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            self.copiedLabel.isHidden = true
        }
        let text = "HelloWorld"
        didCopyText(text)
        clearTextAfterDelay(text)
    }    
}

Upvotes: 1

Views: 648

Answers (1)

Ethan D'Mello
Ethan D'Mello

Reputation: 119

After speaking with technical support at Apple, they gave me the following explanation:

"For security reason, we intentionally prevent an app running in the background from accessing the system pasteboard, so it is as-designed that UIPasteboard.general.hasStrings returns false in your case"

Instead they suggested the using the setItems method with an expiration date

import CoreServices

UIPasteboard.general.setItems([[kUTTypeUTF8PlainText as String: text]], options: [.expirationDate : Date(timeIntervalSinceNow: 10)])

Upvotes: 4

Related Questions