I'm trying to use Combine with a view but am getting this error message at runtime:
Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
I tried adding ".receive(on: RunLoop.main)" before each ".eraseToAnyPublisher()", but that didn't resolve the issue.
Here the code block that I'm assuming is causing the problem:
func emailAvailable(_ email: String, completion: @escaping (Bool) -> ()) -> () {
if email != "" {
print("SignupModel: emailAvailable(): Validating email '" + email + "'...")
let pattern = try! Regex(pattern: emailRegex)
if pattern.matches(email) {
guard let url = URL(string: "https://api.myapi.network/users/email/availability?email=" + email.lowercased()) else { fatalError("emailAvailable(): Missing URL") }
var request = URLRequest(url: url)
request.setValue(K.apiKey, forHTTPHeaderField: "x-api-key")
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("SignupModel: emailAvailable(): Request error: ", error)
completion(false)
}
guard let response = response as? HTTPURLResponse else { return }
if response.statusCode == 200 {
guard let data = data else {
print("SignupModel: emailAvailable(): Response contained no data!")
return
}
DispatchQueue.main.async {
do {
let decodedResp = try JSONDecoder().decode(EmailAvailResponse.self, from: data)
if (decodedResp.available) {
print("SignupModel: emailAvailable(): Email available, returning true!")
completion(true)
} else {
print("SignupModel: emailAvailable(): Email NOT available, returning false!")
completion(false)
}
} catch let error {
print("SignupModel: emailAvailable(): Error decoding: ", error)
completion(false)
}
}
} else {
print("SignupModel: emailAvailable(): Response status = \(response.statusCode)")
completion(true)
}
}
dataTask.resume()
} else {
print("SignupModel: emailAvailable(): Exiting gracefully because email '" + email + "' is not a valid email!")
completion(true)
}
} else {
print("SignupModel: emailAvailable(): Exiting because email is nil!")
completion(true)
}
}
lazy var emailValidationAvailable: ValidationPublisher = {
return $email
.debounce(for: 0.5, scheduler: RunLoop.main)
.removeDuplicates()
.flatMap { email in
return Future { promise in
self.emailAvailable(email) { available in
promise(.success(available ? .success : .failure(message: "Email is already registered!")))
}
}
}
.dropFirst()
.receive(on: RunLoop.main) // <<—— run on main thread
.eraseToAnyPublisher()
}()
Anyone know what I'm missing? Thanks in advance!
Aaron