SwiftUI - избегайте AnyPublisher в первый раз - PullRequest
1 голос
/ 01 августа 2020

Я создал издателей для проверки вводимых пользователем данных, в данном случае просто проверяю, что они имеют длину 32 символа.

ConnectionVM

import UIKit
import Combine

class ConnectionVM: ObservableObject {
    private var cancellableSet: Set<AnyCancellable> = []
    //INPUT
    @Published var uuid1: String = ""
    @Published var uuid2: String = ""
    //OUTPUT
    @Published var uuid1Message = ""
    @Published var uuid2Message = ""
    
    init() {
        isUUID1ValidPublisher
            .receive(on: RunLoop.main)
            .map { valid in
                valid ? "" : "UUID1 must have 32 characters"
            }
            .assign(to: \.uuid1Message, on: self)
            .store(in: &cancellableSet)
        
        isUUID2ValidPublisher
            .receive(on: RunLoop.main)
            .map { valid in
              valid ? "" : "UUID2 must have 32 characters"
            }
            .assign(to: \.uuid2Message, on: self)
            .store(in: &cancellableSet)
    }
    
    private var isUUID1ValidPublisher: AnyPublisher<Bool, Never> {
      $uuid1
        .debounce(for: 0.8, scheduler: RunLoop.main)
        .removeDuplicates()
        .map { input in
          return input.count == 32
        }
        .eraseToAnyPublisher()
    }
    private var isUUID2ValidPublisher: AnyPublisher<Bool, Never> {
      $uuid2
        .debounce(for: 0.8, scheduler: RunLoop.main)
        .removeDuplicates()
        .map { input in
          return input.count == 32
        }
        .eraseToAnyPublisher()
    }
    private var isFormValidPublisher: AnyPublisher<Bool, Never> {
      Publishers.CombineLatest(isUUID1ValidPublisher, isUUID2ValidPublisher)
        .map { uuid1IsValid, uuid2IsValid in
          return uuid1IsValid && uuid2IsValid
        }
      .eraseToAnyPublisher()
    }
}

ConnectionView

import SwiftUI

let lightGreyColor = Color(red: 239.0/255.0, green: 243.0/255.0, blue: 244.0/255.0, opacity: 1.0)

struct ConnectionView: View {
    @ObservedObject var keyboardResponder = KeyboardResponder()
    @ObservedObject var viewModel = ConnectionVM()
//    @State var uuid1: String = ""
//    @State var uuid2: String = ""
    @State var authenticationDidFail: Bool = false
    
    var body: some View {
        return VStack {
            WelcomeText()
            LogoImage()
            UUIDTextField(uuid: $viewModel.uuid1)
            if !viewModel.uuid1Message.isEmpty {
                Text(viewModel.uuid1Message)
                .offset(y: -10)
                .foregroundColor(.red)
            }
            UUIDTextField(uuid: $viewModel.uuid2)
            if !viewModel.uuid2Message.isEmpty {
                Text(viewModel.uuid2Message)
                .offset(y: -10)
                .foregroundColor(.red)
            }
            Button(action: {
                print("Button tapped")
            }) {
               LoginButtonContent()
            }
        }
        .padding()
        .offset(y: -keyboardResponder.currentHeight*0.5)
    }
    struct WelcomeText : View {
        var body: some View {
            return Text("Welcome!")
                .font(.largeTitle)
                .fontWeight(.semibold)
                .padding(.bottom, 20)
        }
    }
    struct LogoImage : View {
        var body: some View {
            return Image("logo")
                .resizable()
                .aspectRatio(contentMode: .fill)
                .frame(width: 150, height: 150)
                .clipped()
                .cornerRadius(150)
                .padding(.bottom, 75)
        }
    }
    struct UUIDTextField : View {
        @Binding var uuid: String
        var body: some View {
        return TextField("UUID", text: $uuid)
                    .padding()
                    .background(lightGreyColor)
                    .cornerRadius(5.0)
                    .padding(.bottom, 20)
            }
    }
    struct LoginButtonContent : View {
        var body: some View {
            return Text("LOGIN")
                .font(.headline)
                .foregroundColor(.white)
                .padding()
                .frame(width: 220, height: 60)
                .background(Color.green)
                .cornerRadius(15.0)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ConnectionView()
    }
}

Проблема в том, что при первом открытии экрана сообщения об ошибках появляются автоматически.

введите описание изображения здесь

1 Ответ

0 голосов
/ 02 августа 2020
• 1000 Bool в зависимости от того, изменялся ли он более одного раза (т.е. сверх начального назначения "").
let hasUUID1Changed = $uuid1.scan(0, { a, _ in a + 1}).map { $0 > 1 }
let hasUUID2Changed = $uuid2.scan(0, { a, _ in a + 1}).map { $0 > 1 }

Publishers.Scan в этом случае действует как счетчик / аккумулятор. Время жизни - это время жизни модели представления.

Затем вы можете использовать этого издателя в сочетании с isUUID1ValidPublisher, чтобы определить, отображать ли сообщение:

isUUID1ValidPublisher
    .receive(on: RunLoop.main)
    .combineLatest(hasUUID1Changed) // here
    .map { (valid, changed) in
        valid && !changed ? "" : "UUID1 must have 32 characters"
    }
    .sink { [weak self] self?.uuid1Message = $0 }
    .store(in: &cancellableSet)
...