I have scaled down one of the Apps I am working on to use to learn how to program App Intents and utilize Siri in my apps. I will post the scaled-down version of my code below in its simplest form. This is a simple app that merely keeps a total in an @State var
named counter
. The app shows the current total along with 2 buttons, one button being labeled "minus" and the other labeled "add".
When someone taps the minus button, 1 is subtracted from counter
if counter
is greater than 0. When someone taps the plus button, 1 is added to counter
as long as it is not greater than 10,000.
The buttons actually call functions called decrementCounter
and incrementCounter
which do the math and update the value of the state variable counter
.
The app works just fine as is. Minus and plus buttons work and the view is updated to reflect the current value of counter
as buttons are pushed.
The problem is when I try to use an App Intent to add or subtract from counter
. In this example, I only put in two App Intents, one to add to counter
and the other to have Siri tell you what the current value of counter
is.
The app intent called SiriAddOne
calls the same function as is used when a button is pressed, however, counter
does not get incremented.
Also, the app intent SiriHowMany
will always tell you the counter is zero.
It's like the App Intents are not able to access the counter
variable used in the view.
I do know that the functions are being called because, in my main program where I extracted this from, the incrementCounter
and decrementCounter
functions do others things as well. Those other things all work when I use the App Intent to call the function, but the counter
variable remains unchanged.
Hopefully, someone can tell me what I am doing wrong here or how I need to go about doing this correctly. Thank you.
import SwiftUI
import AppIntents
struct ContentView: View {
// This variable counter is the only thing that changes
// and should be what forces the view to update
@State var counter = 0
var body: some View {
VStack {
Spacer()
Text("Total")
// Displays the current value of the counter
Text(String(counter))
Spacer()
// When button is pressed call decrementCounter function
Button(action: {
decrementCounter()
}, label: {
Text("Minus")
})
Spacer()
// When button is pressed call incrementCounter function
Button(action: {
incrementCounter()
}, label: {
Text("Add")
})
Spacer()
}
.padding()
}
// subtract 1 from the counter
// when this happens the view should update to
// to reflect the new value.
func decrementCounter() {
if counter > 0 {
counter -= 1
}
return
}
// Add 1 to the counter
// when this happens the view should update to
// to reflect the new value.
func incrementCounter() {
if counter <= 9999 {
counter += 1
}
return
}
// Set up App Intent, perform action when matched
// and have siri state it has been done.
@available(iOS 16, *)
struct SiriAddOne: AppIntent {
static var title: LocalizedStringResource = "Add 1"
static var description = IntentDescription("Adds 1 to the counter")
@MainActor
func perform() async throws -> some IntentResult {
ContentView().incrementCounter()
return .result(dialog: "Okay, added 1 to counter.")
}
}
// Set up App Intent, perform action when matched
// and have siri state the current value of the counter.
@available(iOS 16, *)
struct SiriHowMany: AppIntent {
static var title: LocalizedStringResource = "How Many"
static var description = IntentDescription("How Many?")
func perform() async throws -> some IntentResult {
return .result(dialog: "You have \(ContentView().counter).")
}
}
// Defines the two shortcut phrases to be used to call the two AppIntents
@available(iOS 16, *)
struct SiriAppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: SiriAddOne(),
phrases: ["Add one to \(.applicationName)"]
)
AppShortcut(
intent: SiriHowMany(),
phrases: ["How many \(.applicationName)"]
)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
When you do
ContentView()
, you're creating a newContentView
object, which has a separate counter from the mainContentView
created by your app.I'd recommend moving the counter to a class conforming to the
ObservableObject
protocol. Make only one instance of it and store that instance in a global variable, then add an@ObservedObject
property toContentView
whose value is that object. For example: