Home > database >  Using a class inside a struct is giving an error: "partial application of 'mutating'
Using a class inside a struct is giving an error: "partial application of 'mutating'

Time:11-05

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 Views 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...")
        //...
    }
}
  • Related