I am creating a class inside a struct to create a timer that sends information between an Apple Watch and the paired phone. When trying to run the timer with a button the error:
Partial application of the 'mutating' method is not allowed
The way I'm creating the class is the following:
import SwiftUI
struct ContentView: View {
//Timer to send information to phone
var timerLogic: TimerLogic!
var body: some View {
Button(action: startTimer, //"partial application of 'mutating' method is not allowed"
label: {
Image(systemName: "location")
})
}
// Class with the timer logic
class TimerLogic {
var structRef: ContentView!
var timer: Timer!
init(_ structRef: ContentView) {
self.structRef = structRef
self.timer = Timer.scheduledTimer(
timeInterval: 3.0,
target: self,
selector: #selector(timerTicked),
userInfo: nil,
repeats: true)
}
func stopTimer() {
self.timer?.invalidate()
self.structRef = nil
}
// Function to run with each timer tick
@objc private func timerTicked() {
self.structRef.timerTicked()
}
}
mutating func startTimer() {
self.timerLogic = TimerLogic(self)
}
// Function to run with each timer tick, this can be any action
func timerTicked() {
let data = ["latitude": "\(location.coordinate.latitude)", "longitud": "\(location.coordinate.longitude)"]
connectivity.sendMessage(data)
}
}
The closest solution that might solve the error is this one or is there another one?
CodePudding user response:
SwiftUI View
s should not have mutating
properties/functions. Instead, they should use property wrappers like @State
and @StateObject
for state.
Besides the mutating
function, you're fighting the principals of SwiftUI a bit. For example, you should never try to keep a reference to a View
and call a function on it. Views are transient in SwiftUI and should not be expected to exist again if you need to call back to them. Also, SwiftUI tends to go hand-in-hand with Combine, which would be a good fit for implementing in your Timer
code.
This might be a reasonable refactor:
import SwiftUI
import Combine
// Class with the timer logic
class TimerLogic : ObservableObject {
@Published var timerEvent : Timer.TimerPublisher.Output?
private var cancellable: AnyCancellable?
func startTimer() {
cancellable = Timer.publish(every: 3.0, on: RunLoop.main, in: .default)
.autoconnect()
.sink { event in
self.timerEvent = event
}
}
func stopTimer() {
cancellable?.cancel()
}
}
struct ContentView: View {
//Timer to send information to phone
@StateObject var timerLogic = TimerLogic()
var body: some View {
Button(action: timerLogic.startTimer) {
Image(systemName: "location")
}
.onChange(of: timerLogic.timerEvent) { _ in
timerTicked()
}
}
// Function to run with each timer tick, this can be any action
func timerTicked() {
print("Timer ticked...")
//...
}
}