Skip to content

Commit

Permalink
change to x-callback-url shortcuts for error handling
Browse files Browse the repository at this point in the history
- Also helps fixing Bug/feature introduced in iOS 17.6, where shortcut runs unreliable if first action in the shortcut is to immediatey switch back to the app calling the shortcut deeplink (LoopFollow in this case). Now ive changed my remote shortcuts to switch back to LF after completion: success/error/cancel/wrong passcode (which is made possible with x-callback-url)
  • Loading branch information
dsnallfot committed Sep 5, 2024
1 parent 6f5ee66 commit ba2f6b4
Show file tree
Hide file tree
Showing 6 changed files with 444 additions and 15 deletions.
32 changes: 32 additions & 0 deletions LoopFollow/Application/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,38 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
}
}
}

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let urlContext = URLContexts.first else { return }

let url = urlContext.url
print("Received URL in SceneDelegate: \(url.absoluteString)")

guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let host = components.host else {
print("Invalid URL or missing host")
return
}

print("URL Host: \(host)")

switch host {
case "success":
NotificationCenter.default.post(name: NSNotification.Name("ShortcutSuccess"), object: nil)
print("Posted success notification")
case "error":
NotificationCenter.default.post(name: NSNotification.Name("ShortcutError"), object: nil)
print("Posted error notification")
case "cancel":
NotificationCenter.default.post(name: NSNotification.Name("ShortcutCancel"), object: nil)
print("Posted cancel notification")
case "passcode":
NotificationCenter.default.post(name: NSNotification.Name("ShortcutPasscode"), object: nil)
print("Posted passcode notification")
default:
print("Unhandled URL scheme host: \(host)")
}
}

// The following method is called when the user taps on the Home Screen Quick Action
func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
Expand Down
83 changes: 81 additions & 2 deletions LoopFollow/ViewControllers/BolusViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ class BolusViewController: UIViewController, UITextFieldDelegate, TwilioRequesta
bolusEntryField.delegate = self
self.focusBolusEntryField()

// Register observers for shortcut callback notifications
NotificationCenter.default.addObserver(self, selector: #selector(handleShortcutSuccess), name: NSNotification.Name("ShortcutSuccess"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleShortcutError), name: NSNotification.Name("ShortcutError"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleShortcutCancel), name: NSNotification.Name("ShortcutCancel"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleShortcutPasscode), name: NSNotification.Name("ShortcutPasscode"), object: nil)

// Create a NumberFormatter instance
let numberFormatter = NumberFormatter()
numberFormatter.minimumFractionDigits = 0
Expand Down Expand Up @@ -221,11 +227,31 @@ class BolusViewController: UIViewController, UITextFieldDelegate, TwilioRequesta
print("Failed to encode URL string")
return
}
let urlString = "shortcuts://run-shortcut?name=Remote%20Bolus&input=text&text=\(encodedString)"

// Define your custom callback URLs
let successCallback = "loop://completed" // Add completed for future use when the shortcut has run, but for instance the passcode was wrong. NOTE: not to mixed up with loop://success that should be returned by the remote meal shortcut to proceed with the meal registration)
let errorCallback = "loop://error"
let cancelCallback = "loop://cancel"

// Encode the callback URLs
guard let successEncoded = successCallback.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let errorEncoded = errorCallback.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let cancelEncoded = cancelCallback.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
print("Failed to encode callback URLs")
return
}

/*let urlString = "shortcuts://run-shortcut?name=Remote%20Bolus&input=text&text=\(encodedString)"
if let url = URL(string: urlString) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)*/
let urlString = "shortcuts://x-callback-url/run-shortcut?name=Remote%20Bolus&input=text&text=\(encodedString)&x-success=\(successEncoded)&x-error=\(errorEncoded)&x-cancel=\(cancelEncoded)"

if let url = URL(string: urlString) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
dismiss(animated: true, completion: nil)

print("Waiting for shortcut completion...")
//dismiss(animated: true, completion: nil)
} else {
// If method is "SMS API", proceed with sending the request
twilioRequest(combinedString: combinedString) { result in
Expand Down Expand Up @@ -254,6 +280,59 @@ class BolusViewController: UIViewController, UITextFieldDelegate, TwilioRequesta
}
}

@objc private func handleShortcutSuccess() {
print("Shortcut succeeded")

// Play a success sound
AudioServicesPlaySystemSound(SystemSoundID(1322))

// Show success alert with "Lyckades"
showAlert(title: NSLocalizedString("Lyckades", comment: "Lyckades"), message: NSLocalizedString("Bolusregistreringen skickades", comment: "Bolusregistreringen skickades"), completion: {
self.dismiss(animated: true, completion: nil) // Dismiss the view controller after showing the alert
})
}

@objc private func handleShortcutError() {
print("Shortcut failed, showing error alert...")

// Play a error sound
AudioServicesPlaySystemSound(SystemSoundID(1053))

showAlert(title: NSLocalizedString("Misslyckades", comment: "Misslyckades"), message: NSLocalizedString("Ett fel uppstod när genvägen skulle köras. Du kan försöka igen.", comment: "Ett fel uppstod när genvägen skulle köras. Du kan försöka igen."), completion: {
self.handleAlertDismissal() // Re-enable the send button after error handling
})
}

@objc private func handleShortcutCancel() {
print("Shortcut was cancelled, showing cancellation alert...")

// Play a error sound
AudioServicesPlaySystemSound(SystemSoundID(1053))

showAlert(title: NSLocalizedString("Avbröts", comment: "Avbröts"), message: NSLocalizedString("Genvägen avbröts innan den körts färdigt. Du kan försöka igen.", comment: "Genvägen avbröts innan den körts färdigt. Du kan försöka igen.") , completion: {
self.handleAlertDismissal() // Re-enable the send button after cancellation
})
}

@objc private func handleShortcutPasscode() {
print("Shortcut was cancelled due to wrong passcode, showing passcode alert...")

// Play a error sound
AudioServicesPlaySystemSound(SystemSoundID(1053))

showAlert(title: NSLocalizedString("Fel lösenkod", comment: "Fel lösenkod"), message: NSLocalizedString("Genvägen avbröts pga fel lösenkod. Du kan försöka igen.", comment: "Genvägen avbröts pga fel lösenkod. Du kan försöka igen.") , completion: {
self.handleAlertDismissal() // Re-enable the send button after cancellation
})
}

private func showAlert(title: String, message: String, completion: @escaping () -> Void) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
completion() // Call the completion handler after dismissing the alert
}))
present(alert, animated: true, completion: nil)
}

@IBAction func editingChanged(_ sender: Any) {
print("Value changed in bolus amount")

Expand Down
84 changes: 82 additions & 2 deletions LoopFollow/ViewControllers/CustomActionViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ class CustomActionViewController: UIViewController, UIPickerViewDataSource, UIPi
// Hide warning symbol
minPredBGView.isHidden = true
}

// Register observers for shortcut callback notifications
NotificationCenter.default.addObserver(self, selector: #selector(handleShortcutSuccess), name: NSNotification.Name("ShortcutSuccess"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleShortcutError), name: NSNotification.Name("ShortcutError"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleShortcutCancel), name: NSNotification.Name("ShortcutCancel"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleShortcutPasscode), name: NSNotification.Name("ShortcutPasscode"), object: nil)
}

// MARK: - UIPickerViewDataSource
Expand Down Expand Up @@ -212,11 +218,32 @@ class CustomActionViewController: UIViewController, UIPickerViewDataSource, UIPi
print("Failed to encode URL string")
return
}
let urlString = "shortcuts://run-shortcut?name=Remote%20Custom%20Action&input=text&text=\(encodedString)"


// Define your custom callback URLs
let successCallback = "loop://completed" // Add completed for future use when the shortcut has run, but for instance the passcode was wrong. NOTE: not to mixed up with loop://success that should be returned by the remote meal shortcut to proceed with the meal registration)
let errorCallback = "loop://error"
let cancelCallback = "loop://cancel"

// Encode the callback URLs
guard let successEncoded = successCallback.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let errorEncoded = errorCallback.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let cancelEncoded = cancelCallback.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
print("Failed to encode callback URLs")
return
}

/*let urlString = "shortcuts://run-shortcut?name=Remote%20Custom%20Action&input=text&text=\(encodedString)"
if let url = URL(string: urlString) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)*/
let urlString = "shortcuts://x-callback-url/run-shortcut?name=Remote%20Custom%20Action&input=text&text=\(encodedString)&x-success=\(successEncoded)&x-error=\(errorEncoded)&x-cancel=\(cancelEncoded)"

if let url = URL(string: urlString) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
dismiss(animated: true, completion: nil)

print("Waiting for shortcut completion...")
//dismiss(animated: true, completion: nil)

} else {
// If method is "SMS API", proceed with sending the request
Expand Down Expand Up @@ -245,6 +272,59 @@ class CustomActionViewController: UIViewController, UIPickerViewDataSource, UIPi
}
}
}

@objc private func handleShortcutSuccess() {
print("Shortcut succeeded")

// Play a success sound
AudioServicesPlaySystemSound(SystemSoundID(1322))

// Show success alert with "Lyckades"
showAlert(title: NSLocalizedString("Lyckades", comment: "Lyckades"), message: NSLocalizedString("Förvalsregistreringen skickades", comment: "Förvalsregistreringen skickades"), completion: {
self.dismiss(animated: true, completion: nil) // Dismiss the view controller after showing the alert
})
}

@objc private func handleShortcutError() {
print("Shortcut failed, showing error alert...")

// Play a error sound
AudioServicesPlaySystemSound(SystemSoundID(1053))

showAlert(title: NSLocalizedString("Misslyckades", comment: "Misslyckades"), message: NSLocalizedString("Ett fel uppstod när genvägen skulle köras. Du kan försöka igen.", comment: "Ett fel uppstod när genvägen skulle köras. Du kan försöka igen."), completion: {
self.handleAlertDismissal() // Re-enable the send button after error handling
})
}

@objc private func handleShortcutCancel() {
print("Shortcut was cancelled, showing cancellation alert...")

// Play a error sound
AudioServicesPlaySystemSound(SystemSoundID(1053))

showAlert(title: NSLocalizedString("Avbröts", comment: "Avbröts"), message: NSLocalizedString("Genvägen avbröts innan den körts färdigt. Du kan försöka igen.", comment: "Genvägen avbröts innan den körts färdigt. Du kan försöka igen.") , completion: {
self.handleAlertDismissal() // Re-enable the send button after cancellation
})
}

@objc private func handleShortcutPasscode() {
print("Shortcut was cancelled due to wrong passcode, showing passcode alert...")

// Play a error sound
AudioServicesPlaySystemSound(SystemSoundID(1053))

showAlert(title: NSLocalizedString("Fel lösenkod", comment: "Fel lösenkod"), message: NSLocalizedString("Genvägen avbröts pga fel lösenkod. Du kan försöka igen.", comment: "Genvägen avbröts pga fel lösenkod. Du kan försöka igen.") , completion: {
self.handleAlertDismissal() // Re-enable the send button after cancellation
})
}

private func showAlert(title: String, message: String, completion: @escaping () -> Void) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
completion() // Call the completion handler after dismissing the alert
}))
present(alert, animated: true, completion: nil)
}

@IBAction func cancelButtonPressed(_ sender: Any) {
dismiss(animated: true, completion: nil)
Expand Down
Loading

0 comments on commit ba2f6b4

Please sign in to comment.