Files
swift-composable-architectu…/Examples/TicTacToe/tic-tac-toe/Sources/NewGameUIKit/NewGameViewController.swift
2021-12-01 10:12:39 -05:00

183 lines
5.5 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Combine
import ComposableArchitecture
import GameUIKit
import NewGameCore
import UIKit
public class NewGameViewController: UIViewController {
let store: Store<NewGameState, NewGameAction>
let viewStore: ViewStore<ViewState, ViewAction>
private var cancellables: Set<AnyCancellable> = []
struct ViewState: Equatable {
let isGameActive: Bool
let isLetsPlayButtonEnabled: Bool
let oPlayerName: String?
let xPlayerName: String?
public init(state: NewGameState) {
self.isGameActive = state.game != nil
self.isLetsPlayButtonEnabled = !state.oPlayerName.isEmpty && !state.xPlayerName.isEmpty
self.oPlayerName = state.oPlayerName
self.xPlayerName = state.xPlayerName
}
}
enum ViewAction {
case gameDismissed
case letsPlayButtonTapped
case logoutButtonTapped
case oPlayerNameChanged(String?)
case xPlayerNameChanged(String?)
}
public init(store: Store<NewGameState, NewGameAction>) {
self.store = store
self.viewStore = ViewStore(store.scope(state: ViewState.init, action: NewGameAction.init))
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "New Game"
self.navigationItem.rightBarButtonItem = UIBarButtonItem(
title: "Logout",
style: .done,
target: self,
action: #selector(logoutButtonTapped)
)
let playerXLabel = UILabel()
playerXLabel.text = "Player X"
playerXLabel.setContentHuggingPriority(.required, for: .horizontal)
let playerXTextField = UITextField()
playerXTextField.borderStyle = .roundedRect
playerXTextField.placeholder = "Blob Sr."
playerXTextField.setContentCompressionResistancePriority(.required, for: .horizontal)
playerXTextField.addTarget(
self, action: #selector(playerXTextChanged(sender:)), for: .editingChanged)
let playerXRow = UIStackView(arrangedSubviews: [
playerXLabel,
playerXTextField,
])
playerXRow.spacing = 24
let playerOLabel = UILabel()
playerOLabel.text = "Player O"
playerOLabel.setContentHuggingPriority(.required, for: .horizontal)
let playerOTextField = UITextField()
playerOTextField.borderStyle = .roundedRect
playerOTextField.placeholder = "Blob Jr."
playerOTextField.setContentCompressionResistancePriority(.required, for: .horizontal)
playerOTextField.addTarget(
self, action: #selector(playerOTextChanged(sender:)), for: .editingChanged)
let playerORow = UIStackView(arrangedSubviews: [
playerOLabel,
playerOTextField,
])
playerORow.spacing = 24
let letsPlayButton = UIButton(type: .system)
letsPlayButton.setTitle("Lets Play!", for: .normal)
letsPlayButton.addTarget(self, action: #selector(letsPlayTapped), for: .touchUpInside)
let rootStackView = UIStackView(arrangedSubviews: [
playerXRow,
playerORow,
letsPlayButton,
])
rootStackView.isLayoutMarginsRelativeArrangement = true
rootStackView.layoutMargins = .init(top: 0, left: 32, bottom: 0, right: 32)
rootStackView.translatesAutoresizingMaskIntoConstraints = false
rootStackView.axis = .vertical
rootStackView.spacing = 24
self.view.addSubview(rootStackView)
NSLayoutConstraint.activate([
rootStackView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
rootStackView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
rootStackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
])
self.viewStore.publisher.isLetsPlayButtonEnabled
.assign(to: \.isEnabled, on: letsPlayButton)
.store(in: &self.cancellables)
self.viewStore.publisher.oPlayerName
.assign(to: \.text, on: playerOTextField)
.store(in: &self.cancellables)
self.viewStore.publisher.xPlayerName
.assign(to: \.text, on: playerXTextField)
.store(in: &self.cancellables)
self.store
.scope(state: \.game, action: NewGameAction.game)
.ifLet(
then: { [weak self] gameStore in
self?.navigationController?.pushViewController(
GameViewController(store: gameStore),
animated: true
)
},
else: { [weak self] in
guard let self = self else { return }
self.navigationController?.popToViewController(self, animated: true)
}
)
.store(in: &self.cancellables)
}
public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !self.isMovingToParent {
self.viewStore.send(.gameDismissed)
}
}
@objc private func logoutButtonTapped() {
self.viewStore.send(.logoutButtonTapped)
}
@objc private func playerXTextChanged(sender: UITextField) {
self.viewStore.send(.xPlayerNameChanged(sender.text))
}
@objc private func playerOTextChanged(sender: UITextField) {
self.viewStore.send(.oPlayerNameChanged(sender.text))
}
@objc private func letsPlayTapped() {
self.viewStore.send(.letsPlayButtonTapped)
}
}
extension NewGameAction {
init(action: NewGameViewController.ViewAction) {
switch action {
case .gameDismissed:
self = .gameDismissed
case .letsPlayButtonTapped:
self = .letsPlayButtonTapped
case .logoutButtonTapped:
self = .logoutButtonTapped
case let .oPlayerNameChanged(name):
self = .oPlayerNameChanged(name ?? "")
case let .xPlayerNameChanged(name):
self = .xPlayerNameChanged(name ?? "")
}
}
}