Opening an App Conditionally with Swift and App Intents in iOS Shortcuts

278 views Asked by At

I am trying to create a simple app that "blocks" other apps if a certain condition is not met. I am currently using the IOS shortcuts and have set up an automation that opens my app A whenever another app B opens.

If the condition is not met i imagine the flow to look like:

  1. Open app A.
  2. My app B opens instead.
  3. I check a box in my app B.
  4. I navigate back to app A and it works as expected.

If the condition already is met the app A would work as expected from the beginning.

What is have tried so far

My first attempt involved using an AppIntent and changing the openAppWhenRun programmatically based on the condition. I did however learn pretty quickly that changing the value of openAppWhenRun does not change if the AppIntent actually opens my app. The code for this looked like this where the value of openAppWhenRun is changed in another function.

struct BlockerIntent: AppIntent {
        static let title: LocalizedStringResource = "Blocker App"
        static let description: LocalizedStringResource = "Blocks an app until condition is met"

        static var openAppWhenRun: Bool = false
        
        @MainActor
        func perform() async throws -> some IntentResult {
            return .result()
        }
}

Another attempt involved setting openAppWhenRun to false in an outer AppIntent and opening another inner AppIntent if the condition is met. If the condition in my app is met openAppWhenRun is set to true and instead of opening the inner AppIntent an Error is thrown. This functions as expected but there is an error notification showing every time I open the "blocked" app.

struct BlockerIntent: AppIntent {
        static let title: LocalizedStringResource = "Blocker App"
        static let description: LocalizedStringResource = "Blocks an app until condition is met"
        
        static var openAppWhenRun: Bool = false
        
        func perform() async throws -> some IntentResult & OpensIntent {
            if (BlockerIntent.openAppWhenRun) {
                throw Error.notFound
            }
            return .result(opensIntent: OpenBlockerApp())
        }
        
        enum Error: Swift.Error, CustomLocalizedStringResourceConvertible {
            case notFound
            
            var localizedStringResource: LocalizedStringResource {
                switch self {
                case .notFound: return "Ignore this message"
                }
            }
        }
}


struct OpenBlockerApp: AppIntent {
        static let title: LocalizedStringResource = "Open Blocker App"
        static let description: LocalizedStringResource = "Opens Blocker App"
        
        static var openAppWhenRun: Bool = true
        
        @MainActor
        func perform() async throws -> some IntentResult {
            return .result()
        }
}

My third attempt look similar to the previous one but instead I used two different inner AppIntents. The only difference between the two were that on had openAppWhenRun = false and the other had openAppWhenRun = true.

struct BlockerIntent: AppIntent {
        static let title: LocalizedStringResource = "Blocker App"
        static let description: LocalizedStringResource = "Blacks an app until condition is met"
        
        static var openAppWhenRun: Bool = false
        
        func perform() async throws -> some IntentResult & OpensIntent {
            if (BlockerIntent.openAppWhenRun) {
                return .result(opensIntent: DoNotOpenBlockerApp())
            } else {
                return .result(opensIntent: OpenBlockerApp())
            }
        }
}

Trying this gives me this error:

Function declares an opaque return type 'some IntentResult & OpensIntent', but the return statements in its body do not have matching underlying types

I have also tried opening the app with a URL link with little to no success often ending up in an infinity loop, I did try the ForegroundContinuableIntent but it did not function as expected since it relies on the users input.

Is there any way to do what I am trying to accomplish? I have seen other apps using a similar concept so I feel like this should be possible.

Many thanks!

0

There are 0 answers