skip to Main Content

I am trying to dynamically determine whether the AppIntent opens the app, as dictated by the openAppWhenRun property. Sometimes it should open the app and sometimes it shouldn’t.

What I have tried

  1. I tried having nested AppIntents so the main AppIntent would either run the AppIntent which opens the app or run the AppIntent which doesn’t. However, it gave me the following error: Function declares an opaque return type 'some IntentResult & OpensIntent', but the return statements in its body do not have matching underlying types
    func perform() async throws -> some IntentResult & OpensIntent {
        if (condition) {
                return .result(opensIntent: OtherIntent())
        }
            
        return .result(opensIntent: NestedIntent())
    }
  1. I tried making openAppWhenRun not static and changing it at runtime but this didn’t work

It’s important to note that I also don’t want to use needsToContinueInForegroundError() or requestToContinueInForeground() since they require user confirmation.

2

Answers


  1. The error you are getting is caused by trying to return different types as some Type.

    This is the same sort of thing a @ViewBuilder takes care of in SwiftUI by wrapping the results in a single type. However, since this concerns static properties, a wrapper type (I believe) can’t work here.

    So it seems you can’t call different intent types based on a condition. This discussion would also indicate what you are asking for is impossible.

    Now you can of course, chain intents. But as far as I’m aware, the only way to break this chain is by throwing an error. This gives us one incredibly hacky solution:

    enum DoneError: Error, LocalizedError {
        case done
        var errorDescription: String?  { "All done!" }
    }
    
    func perform() async throws -> some IntentResult {
        // do things...
        if condition {
            throw DoneError.done
        } else {
            return .result(opensIntent: OpeningAppIntent())
        }
    }
    

    …so you should probably just label the intents differently.

    Login or Signup to reply.
  2. I kind of fixed the problem, even though I didn’t manage to get it working within one AppIntent.

    I created two AppIntents one which returns a boolean and is set to openAppWhenRun = false:

    import AppIntents
    import UIKit
    
    struct CheckAppIntent: AppIntent {
      
      static var title: LocalizedStringResource = "Check Open App"
      
      static var openAppWhenRun: Bool = false
      
      @MainActor
      func perform() async throws -> some IntentResult & ReturnsValue<Bool> {
        let shouldOpenApp = true
        return .result(
          value: shouldOpenApp)
      }
    }
    

    The second Intent which only opens the app:

    import AppIntents
    import UIKit
    
    struct OpenAppIntent: AppIntent {
      
      static var title: LocalizedStringResource = "Open App"
      
      static var openAppWhenRun: Bool = true
      
      @MainActor
      func perform() async throws -> some IntentResult {
        return .result()
      }
    }
    

    These AppIntents are than linked together in a shortcut within an if-statement.
    In my case, I made the Shortcut available for users to download

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search