Search code examples
swiftdateswiftuilogic

onChange swift to update variables, loop forever


I'm stuck on the following issue, I have created 2 text field in an Hstack where the user can insert the local time in the format of HH:mm and it is converted automatically in UTC time in the second textfield and vice-versa. TO do so I used onchange method, when the variable "localTime" changes, it updates the variable "utcTime" and vice-versa, but when I run the code it enters in an infinite loop because they keep updating each other... any solution how to avoid this?

import SwiftUI

struct TimeView: View {
    @Binding var localDate : Date?
    @Binding var utcDate : Date?
    
    /// continete l'ora in formato HH:mm
    @Binding var localTime : String
    @Binding var utcTime : String
    
    @Binding var selectedDate: Date
    
    @State var shouldUpdateUTCTime = true
    @State var shouldUpdateLocalTime = true
   
    let dateTimeFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm"
        formatter.timeZone = TimeZone(identifier: "UTC")
        return formatter
    }()
    
    @FocusState var isFocused: Bool

    var body: some View {
        HStack {
            TimeTextField(time: $localTime, description: "Local Time")
                .multilineTextAlignment(.center)
                .keyboardType(.numbersAndPunctuation)
                .onChange(of: localTime) { oldValue, newValue in
                    if shouldUpdateUTCTime {
                        updateUTCTime()
                    }
                }
                
            
            TimeTextField(time: $utcTime, description: "UTC")
                .multilineTextAlignment(.center)
                .keyboardType(.numbersAndPunctuation)
                .onChange(of: utcTime) { oldValue, newValue in
                      if shouldUpdateLocalTime {
                        updateLocalTime()
                    }
                  
                }
                
        }
    }
    
    func updateUTCTime() {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "HH:mm"
        if let utcDate = dateFormatter.date(from: localTime) {
            dateFormatter.timeZone = TimeZone(identifier: "UTC") // convert to utc the local
            let utcDateString = dateTimeFormatter.string(from: utcDate)
            utcTime = utcDateString
        }
    }
    func updateLocalTime() {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "HH:mm"
        if let local = dateFormatter.date(from: utcTime) {
            dateFormatter.timeZone = TimeZone.current // convert to current the local
            let localDateString = dateTimeFormatter.string(from: local)
            localTime = localDateString
        }
    }

}



Solution

  • enum Field: Hashable {
        case universalTime
        case localTime
    }
    
    @FocusState var focusedState: Field?
    

    Use focusState, add focused modifiers to each of textFields like this:

    TextField("", text: $localTime)
         .focused($focusedState, equals: .localTime)
    
    TextField("", text: $universalTime)
         .focused($focusedState, equals: .universalTime)
    

    and than .onChange modifier check focusState and call the methods based on current / active focusState,

    For LocalTime TextField:

    .onChange(of: localTime) { _ in
      if focusedState == .localTime {
          updateUTCTime()
         }
      }
    

    For UniversalTime TextField:

    .onChange(of: universalTime) { _ in
       if focusedState == .universalTime {
           updateLocalTime()
         }
       }
    

    Now it will not go into infinite loop.