diff --git a/.travis.yml b/.travis.yml index 15c07027..93a86f7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,12 +10,18 @@ matrix: script: cd Generator && swift test -Xswiftc -DDEBUG - name: "NeedleGeneratorBinary" script: cd Generator && swift build -c release - - name: "NeedleSampleMVCApp" - install: cd Sample/MVC && carthage update --platform ios - script: xcodebuild build -project ../../Sample/MVC/TicTacToe/TicTacToe.xcodeproj -scheme TicTacToe -destination 'platform=iOS Simulator,OS=13.2.2,name=iPhone 11' - - name: "NeedleSampleMVCTests" - install: cd Sample/MVC && carthage update --platform ios - script: xcodebuild test -project ../../Sample/MVC/TicTacToe/TicTacToe.xcodeproj -scheme TicTacToeTests -destination 'platform=iOS Simulator,OS=13.2.2,name=iPhone 11' + - name: "NeedleSampleMVCApp-Rx" + install: cd Sample/MVC-rx && carthage update --platform ios + script: xcodebuild build -project ../../Sample/MVC-rx/TicTacToe/TicTacToe.xcodeproj -scheme TicTacToe -destination 'platform=iOS Simulator,OS=13.2.2,name=iPhone 11' + - name: "NeedleSampleMVCTests-Rx" + install: cd Sample/MVC-rx && carthage update --platform ios + script: xcodebuild test -project ../../Sample/MVC-rx/TicTacToe/TicTacToe.xcodeproj -scheme TicTacToeTests -destination 'platform=iOS Simulator,OS=13.2.2,name=iPhone 11' + - name: "NeedleSampleMVCApp-Plain" + install: cd Sample/MVC-plain && carthage update --platform ios + script: xcodebuild build -project ../../Sample/MVC-plain/TicTacToe/TicTacToe.xcodeproj -scheme TicTacToe -destination 'platform=iOS Simulator,OS=13.2.2,name=iPhone 11' + - name: "NeedleSampleMVCTests-Plain" + install: cd Sample/MVC-plain && carthage update --platform ios + script: xcodebuild test -project ../../Sample/MVC-plain/TicTacToe/TicTacToe.xcodeproj -scheme TicTacToeTests -destination 'platform=iOS Simulator,OS=13.2.2,name=iPhone 11' - name: "NeedleSamplePluginizedApp" install: cd Sample/Pluginized && carthage update --platform ios script: xcodebuild build -project ../../Sample/Pluginized/TicTacToe/TicTacToe.xcodeproj -scheme TicTacToe -destination 'platform=iOS Simulator,OS=13.2.2,name=iPhone 11' diff --git a/Generator/Tests/NeedleFrameworkTests/Generating/AbstractGeneratorTests.swift b/Generator/Tests/NeedleFrameworkTests/Generating/AbstractGeneratorTests.swift index cf677fcb..a3288d9d 100644 --- a/Generator/Tests/NeedleFrameworkTests/Generating/AbstractGeneratorTests.swift +++ b/Generator/Tests/NeedleFrameworkTests/Generating/AbstractGeneratorTests.swift @@ -44,7 +44,7 @@ class AbstractGeneratorTests: XCTestCase { for _ in 0 ..< 5 { url = url.deletingLastPathComponent() } - url.appendPathComponent("Sample/MVC/TicTacToe/Sources/") + url.appendPathComponent("Sample/MVC-rx/TicTacToe/Sources/") let path = url.absoluteString.replacingOccurrences(of: "file://", with: "") return URL(path: path) diff --git a/Sample/MVC/.gitignore b/Sample/MVC-plain/.gitignore similarity index 100% rename from Sample/MVC/.gitignore rename to Sample/MVC-plain/.gitignore diff --git a/Sample/MVC-plain/Cartfile b/Sample/MVC-plain/Cartfile new file mode 100644 index 00000000..6228b765 --- /dev/null +++ b/Sample/MVC-plain/Cartfile @@ -0,0 +1 @@ +github "SnapKit/SnapKit" ~> 4.0 diff --git a/Sample/MVC-plain/Cartfile.resolved b/Sample/MVC-plain/Cartfile.resolved new file mode 100644 index 00000000..f6927ec1 --- /dev/null +++ b/Sample/MVC-plain/Cartfile.resolved @@ -0,0 +1 @@ +github "SnapKit/SnapKit" "4.2.0" diff --git a/Sample/MVC/TicTacToe/Sources/AppDelegate.swift b/Sample/MVC-plain/TicTacToe/Sources/AppDelegate.swift similarity index 100% rename from Sample/MVC/TicTacToe/Sources/AppDelegate.swift rename to Sample/MVC-plain/TicTacToe/Sources/AppDelegate.swift diff --git a/Sample/MVC/TicTacToe/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json b/Sample/MVC-plain/TicTacToe/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Sample/MVC/TicTacToe/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Sample/MVC-plain/TicTacToe/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Sample/MVC/TicTacToe/Sources/Assets.xcassets/Contents.json b/Sample/MVC-plain/TicTacToe/Sources/Assets.xcassets/Contents.json similarity index 100% rename from Sample/MVC/TicTacToe/Sources/Assets.xcassets/Contents.json rename to Sample/MVC-plain/TicTacToe/Sources/Assets.xcassets/Contents.json diff --git a/Sample/MVC/TicTacToe/Sources/Base.lproj/LaunchScreen.storyboard b/Sample/MVC-plain/TicTacToe/Sources/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from Sample/MVC/TicTacToe/Sources/Base.lproj/LaunchScreen.storyboard rename to Sample/MVC-plain/TicTacToe/Sources/Base.lproj/LaunchScreen.storyboard diff --git a/Sample/MVC-plain/TicTacToe/Sources/Game/GameComponent.swift b/Sample/MVC-plain/TicTacToe/Sources/Game/GameComponent.swift new file mode 100644 index 00000000..39b00c20 --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Sources/Game/GameComponent.swift @@ -0,0 +1,45 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import NeedleFoundation +import UIKit + +protocol GameDependency: Dependency { + var mutableScoreStore: MutableScoreStore { get } + var playersStore: PlayersStore { get } +} + +class GameComponent: Component, GameBuilder { + + var gameViewController: UIViewController { + return GameViewController(mutableScoreStore: dependency.mutableScoreStore, playersStore: dependency.playersStore, scoreSheetBuilder: scoreSheetBuilder) + } + + var scoreSheetBuilder: ScoreSheetBuilder { + return ScoreSheetComponent(parent: self) + } + + // This should not be used as the provider for GameDependency. + var mutableScoreStore: MutableScoreStore { + return ScoreStoreImpl() + } +} + +// Use a builder protocol to allow mocking for unit tests. At the same time, +// this allows GameViewController to be initialized lazily. +protocol GameBuilder { + var gameViewController: UIViewController { get } +} diff --git a/Sample/MVC-plain/TicTacToe/Sources/Game/GameViewController.swift b/Sample/MVC-plain/TicTacToe/Sources/Game/GameViewController.swift new file mode 100644 index 00000000..bf88c2c9 --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Sources/Game/GameViewController.swift @@ -0,0 +1,302 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SnapKit +import UIKit + +private let rowCount = 3 +private let colCount = 3 +private let sectionCount = 1 +private let cellSize: CGFloat = UIScreen.main.bounds.width / CGFloat(colCount) +private let cellIdentifier = "TicTacToeCell" + +private enum Players: Int { + case player1 = 1 + case player2 + + var color: UIColor { + switch self { + case .player1: + return UIColor.red + case .player2: + return UIColor.blue + } + } +} + +class GameViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, ScoreSheetListener { + + private let mutableScoreStore: MutableScoreStore + private let playersStore: PlayersStore + private let scoreSheetBuilder: ScoreSheetBuilder + private let collectionView: UICollectionView = { + let layout = UICollectionViewFlowLayout() + layout.minimumLineSpacing = 0 + layout.minimumInteritemSpacing = 0 + layout.itemSize = CGSize(width: cellSize, height: cellSize) + return UICollectionView(frame: CGRect.zero, collectionViewLayout: layout) + }() + + init(mutableScoreStore: MutableScoreStore, playersStore: PlayersStore, scoreSheetBuilder: ScoreSheetBuilder) { + self.mutableScoreStore = mutableScoreStore + self.playersStore = playersStore + self.scoreSheetBuilder = scoreSheetBuilder + super.init(nibName: nil, bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = UIColor.purple + + buildCollectionView() + buildScoerButton() + initBoard() + } + + private func buildCollectionView() { + collectionView.dataSource = self + collectionView.delegate = self + collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellIdentifier) + view.addSubview(collectionView) + collectionView.snp.makeConstraints { (maker: ConstraintMaker) in + maker.center.equalTo(self.view.snp.center) + maker.size.equalTo(CGSize(width: CGFloat(colCount) * cellSize, height: CGFloat(rowCount) * cellSize)) + } + } + + private func announce(_ winner: Players, withCompletionHandler handler: @escaping () -> ()) { + performOnPlayerNames { [weak self] (player1Name: String, player2Name: String) in + let winnerName: String + switch winner { + case .player1: + winnerName = player1Name + case .player2: + winnerName = player2Name + } + self?.showAlert(with: "\(winnerName) Won!", completionHandler: handler) + } + } + + private func announceDraw(withCompletionHandler handler: @escaping () -> ()) { + showAlert(with: "It's a Tie", completionHandler: handler) + } + + private func showAlert(with title: String, completionHandler handler: @escaping () -> ()) { + let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert) + let closeAction = UIAlertAction(title: "Close Game", style: UIAlertAction.Style.default) { _ in + handler() + } + alert.addAction(closeAction) + present(alert, animated: true, completion: nil) + } + + private func performOnPlayerNames(with handler: @escaping (String, String) -> ()) { + if let names = playersStore.names { + handler(names.0, names.1) + } + } + + // MARK: - High Scores + + private func buildScoerButton() { + let scoreButton = UIButton() + view.addSubview(scoreButton) + scoreButton.snp.makeConstraints { (maker: ConstraintMaker) in + maker.bottom.equalTo(self.view.snp.bottom).offset(-70) + maker.leading.trailing.equalTo(self.view).inset(40) + maker.height.equalTo(50) + } + scoreButton.setTitle("High Scores", for: .normal) + scoreButton.setTitleColor(UIColor.white, for: .normal) + scoreButton.backgroundColor = UIColor.black + scoreButton.addTarget(self, action: #selector(didTapScoreButton), for: .touchUpInside) + } + + @objc + private func didTapScoreButton() { + if let scoreSheetVC = scoreSheetBuilder.scoreSheetViewController as? ScoreSheetViewController { + scoreSheetVC.listener = self + present(scoreSheetVC, animated: true) + } + } + + func done() { + dismiss(animated: true, completion: nil) + } + + // MARK: - Game Logic + + private var currentPlayer = Players.player1 + private var board = [[Players?]]() + + private func initBoard() { + for _ in 0 ..< rowCount { + board.append([nil, nil, nil]) + } + } + + private func placeCurrentPlayerMark(at row: Int, col: Int) { + guard board[row][col] == nil else { + return + } + + let currentPlayer = getAndFlipCurrentPlayer() + board[row][col] = currentPlayer + setCell(at: row, col: col, withPlayerType: currentPlayer) + + let endGame = checkEndGame() + if endGame.didEnd { + if let winner = endGame.winner { + performOnPlayerNames { [weak self] (player1Name: String, player2Name: String) in + let winnerName = winner == .player1 ? player1Name : player2Name + let loserName = winner != .player1 ? player1Name : player2Name + self?.announce(winner) { [weak self] in + self?.mutableScoreStore.updateScore(withWinner: winnerName, loser: loserName) + } + } + } else { + announceDraw { [weak self] in + self?.mutableScoreStore.updateDraw() + } + } + } + } + + private func setCell(at row: Int, col: Int, withPlayerType playerType: Players) { + let indexPathRow = row * colCount + col + let cell = collectionView.cellForItem(at: IndexPath(row: indexPathRow, section: sectionCount - 1)) + cell?.backgroundColor = playerType.color + } + + private func getAndFlipCurrentPlayer() -> Players { + let currentPlayer = self.currentPlayer + self.currentPlayer = currentPlayer == .player1 ? .player2 : .player1 + return currentPlayer + } + + private func checkEndGame() -> (winner: Players?, didEnd: Bool) { + let winner = checkWinner() + if let winner = winner { + return (winner, true) + } + let isDraw = checkDraw() + if isDraw { + return (nil, true) + } + + return (nil, false) + } + + private func checkWinner() -> Players? { + // Rows. + for row in 0 ..< rowCount { + guard let assumedWinner = board[row][0] else { + continue + } + var winner: Players? = assumedWinner + for col in 1 ..< colCount { + if assumedWinner.rawValue != board[row][col]?.rawValue { + winner = nil + break + } + } + if let winner = winner { + return winner + } + } + + // Cols. + for col in 0 ..< colCount { + guard let assumedWinner = board[0][col] else { + continue + } + var winner: Players? = assumedWinner + for row in 1 ..< rowCount { + if assumedWinner.rawValue != board[row][col]?.rawValue { + winner = nil + break + } + } + if let winner = winner { + return winner + } + } + + // Diagnals. + guard let p11 = board[1][1] else { + return nil + } + if let p00 = board[0][0], let p22 = board[2][2] { + if p00.rawValue == p11.rawValue && p11.rawValue == p22.rawValue { + return p11 + } + } + + if let p02 = board[0][2], let p20 = board[2][0] { + if p02.rawValue == p11.rawValue && p11.rawValue == p20.rawValue { + return p11 + } + } + + return nil + } + + private func checkDraw() -> Bool { + for row in 0 ..< rowCount { + for col in 0 ..< colCount { + if board[row][col] == nil { + return false + } + } + } + return true + } + + // MARK: - UICollectionViewDataSource + + func numberOfSections(in collectionView: UICollectionView) -> Int { + return sectionCount + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return rowCount * colCount + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let reusedCell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) + reset(cell: reusedCell) + return reusedCell + } + + private func reset(cell: UICollectionViewCell) { + cell.backgroundColor = UIColor.white + cell.contentView.layer.borderWidth = 2 + cell.contentView.layer.borderColor = UIColor.lightGray.cgColor + } + + // MARK: - UICollectionViewDelegate + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let row = indexPath.row / colCount + let col = indexPath.row - row * rowCount + placeCurrentPlayerMark(at: row, col: col) + } +} diff --git a/Sample/MVC/TicTacToe/Sources/Info.plist b/Sample/MVC-plain/TicTacToe/Sources/Info.plist similarity index 100% rename from Sample/MVC/TicTacToe/Sources/Info.plist rename to Sample/MVC-plain/TicTacToe/Sources/Info.plist diff --git a/Sample/MVC-plain/TicTacToe/Sources/LoggedIn/LoggedInComponent.swift b/Sample/MVC-plain/TicTacToe/Sources/LoggedIn/LoggedInComponent.swift new file mode 100644 index 00000000..4183f46f --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Sources/LoggedIn/LoggedInComponent.swift @@ -0,0 +1,51 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import NeedleFoundation +import UIKit + +class LoggedInComponent: Component, LoggedInBuilder { + + var scoreStore: ScoreStore { + return mutableScoreStore + } + + var loggedInViewController: UIViewController { + return LoggedInViewController(gameBuilder: gameComponent, scoreStore: scoreStore, scoreSheetBuilder: scoreSheetComponent) + } + + var gameComponent: GameComponent { + return GameComponent(parent: self) + } + + var scoreSheetComponent: ScoreSheetComponent { + return ScoreSheetComponent(parent: self) + } +} + +// Use a builder protocol to allow mocking for unit tests. At the same time, +// this allows LoggedInViewController to be initialized lazily. +protocol LoggedInBuilder { + var loggedInViewController: UIViewController { get } +} + +// Use extension to show parsing of component extensions. +extension LoggedInComponent { + + var mutableScoreStore: MutableScoreStore { + return shared { ScoreStoreImpl() } + } +} diff --git a/Sample/MVC-plain/TicTacToe/Sources/LoggedIn/LoggedInViewController.swift b/Sample/MVC-plain/TicTacToe/Sources/LoggedIn/LoggedInViewController.swift new file mode 100644 index 00000000..843d0106 --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Sources/LoggedIn/LoggedInViewController.swift @@ -0,0 +1,106 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SnapKit +import UIKit + +class LoggedInViewController: UIViewController, ScoreSheetListener { + + private let gameBuilder: GameBuilder + private let scoreStore: ScoreStore + private let scoreSheetBuilder: ScoreSheetBuilder + + init(gameBuilder: GameBuilder, scoreStore: ScoreStore, scoreSheetBuilder: ScoreSheetBuilder) { + self.gameBuilder = gameBuilder + self.scoreStore = scoreStore + self.scoreSheetBuilder = scoreSheetBuilder + super.init(nibName: nil, bundle: nil) + + scoreStore.add(listener: self) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = UIColor.yellow + + let scoreButton = buildScoreButton() + buildGameButton(with: scoreButton) + } + + private func buildScoreButton() -> UIButton { + let scoreButton = UIButton() + view.addSubview(scoreButton) + scoreButton.snp.makeConstraints { (maker: ConstraintMaker) in + maker.bottom.equalTo(self.view.snp.bottom).inset(30) + maker.leading.trailing.equalTo(self.view).inset(40) + maker.height.equalTo(50) + } + scoreButton.setTitle("High Scores", for: .normal) + scoreButton.setTitleColor(UIColor.white, for: .normal) + scoreButton.backgroundColor = UIColor.black + scoreButton.addTarget(self, action: #selector(didTapScoreButton), for: .touchUpInside) + return scoreButton + } + + private func buildGameButton(with previousButton: UIView) { + let gameButton = UIButton() + view.addSubview(gameButton) + gameButton.snp.makeConstraints { (maker: ConstraintMaker) in + maker.bottom.equalTo(previousButton.snp.top).offset(-20) + maker.leading.trailing.equalTo(self.view).inset(40) + maker.height.equalTo(50) + } + gameButton.setTitle("Play TicTacToe", for: .normal) + gameButton.setTitleColor(UIColor.white, for: .normal) + gameButton.backgroundColor = UIColor.black + gameButton.addTarget(self, action: #selector(didTapGameButton), for: .touchUpInside) + } + + @objc + private func didTapScoreButton() { + if let scoreSheetVC = scoreSheetBuilder.scoreSheetViewController as? ScoreSheetViewController { + scoreSheetVC.listener = self + present(scoreSheetVC, animated: true) + } + } + + @objc + private func didTapGameButton() { + let viewController = gameBuilder.gameViewController + present(viewController, animated: true, completion: nil) + } + + func done() { + dismiss(animated: true) + } + + deinit { + scoreStore.remove(listener: self) + } +} + +extension LoggedInViewController: ScoreStatusListener { + func gameDidEnd() { + dismiss(animated: true, completion: nil) + } + + func scoreUpdated(player1Score: PlayerScore, player2Score: PlayerScore) {} +} diff --git a/Sample/MVC-plain/TicTacToe/Sources/LoggedIn/ScoreStore.swift b/Sample/MVC-plain/TicTacToe/Sources/LoggedIn/ScoreStore.swift new file mode 100644 index 00000000..3e15ec14 --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Sources/LoggedIn/ScoreStore.swift @@ -0,0 +1,91 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +struct PlayerScore { + let name: String + var score: Int +} + +protocol ScoreStatusListener: class { + func gameDidEnd() + func scoreUpdated(player1Score: PlayerScore, player2Score: PlayerScore) +} + +protocol ScoreStore { + var scores: (PlayerScore, PlayerScore) { get } + func add(listener: ScoreStatusListener) + func remove(listener: ScoreStatusListener) +} + +protocol MutableScoreStore: ScoreStore { + func updateDraw() + func updateScore(withWinner winner: String, loser: String) +} + +class ScoreStoreImpl: MutableScoreStore { + private var listeners: [ScoreStatusListener] = [] + + private var player1Score: PlayerScore? + private var player2Score: PlayerScore? + + var scores: (PlayerScore, PlayerScore) { + if let player1Score = player1Score, let player2Score = player2Score { + return (player1Score, player2Score) + } else { + return (PlayerScore(name: "None", score: 0), PlayerScore(name: "None", score: 0)) + } + } + + func updateDraw() { + listeners.forEach { $0.gameDidEnd() } + } + + func updateScore(withWinner winner: String, loser: String) { + if var player1Score = player1Score, var player2Score = player2Score { + if winner == player1Score.name { + player1Score.score += 1 + } else { + player2Score.score += 1 + } + self.player1Score = player1Score + self.player2Score = player2Score + } else { + player1Score = PlayerScore(name: winner, score: 1) + player2Score = PlayerScore(name: loser, score: 0) + } + + listeners.forEach { $0.scoreUpdated(player1Score: scores.0, player2Score: scores.1) } + listeners.forEach { $0.gameDidEnd() } + } + + func add(listener: ScoreStatusListener) { + let listenerAlreadyExists = listeners.contains { $0 === listener } + if listenerAlreadyExists { + return + } + + listeners.append(listener) + } + + func remove(listener: ScoreStatusListener) { + let index = listeners.firstIndex { $0 === listener } + if let index = index { + listeners.remove(at: index) + } + } +} diff --git a/Sample/MVC-plain/TicTacToe/Sources/LoggedOut/LoggedOutComponent.swift b/Sample/MVC-plain/TicTacToe/Sources/LoggedOut/LoggedOutComponent.swift new file mode 100644 index 00000000..d8b5541e --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Sources/LoggedOut/LoggedOutComponent.swift @@ -0,0 +1,35 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import NeedleFoundation +import UIKit + +protocol LoggedOutDependency: Dependency { + var mutablePlayersStore: MutablePlayersStore { get } +} + +class LoggedOutComponent: Component, LoggedOutBuilder { + + var loggedOutViewController: UIViewController { + return LoggedOutViewController(mutablePlayersStore: dependency.mutablePlayersStore) + } +} + +// Use a builder protocol to allow mocking for unit tests. At the same time, +// this allows LoggedOutViewController to be initialized lazily. +protocol LoggedOutBuilder { + var loggedOutViewController: UIViewController { get } +} diff --git a/Sample/MVC-plain/TicTacToe/Sources/LoggedOut/LoggedOutViewController.swift b/Sample/MVC-plain/TicTacToe/Sources/LoggedOut/LoggedOutViewController.swift new file mode 100644 index 00000000..f2d2732c --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Sources/LoggedOut/LoggedOutViewController.swift @@ -0,0 +1,92 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SnapKit +import UIKit + +class LoggedOutViewController: UIViewController { + + init(mutablePlayersStore: MutablePlayersStore) { + self.mutablePlayersStore = mutablePlayersStore + super.init(nibName: nil, bundle: nil) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = UIColor.white + let playerFields = buildPlayerFields() + buildLoginButton(withPlayer1Field: playerFields.player1Field, player2Field: playerFields.player2Field) + } + + // MARK: - Private + + private let mutablePlayersStore: MutablePlayersStore + private var player1Field: UITextField? + private var player2Field: UITextField? + + private func buildPlayerFields() -> (player1Field: UITextField, player2Field: UITextField) { + let player1Field = UITextField() + self.player1Field = player1Field + player1Field.borderStyle = UITextField.BorderStyle.line + view.addSubview(player1Field) + player1Field.placeholder = "Player 1 name" + player1Field.snp.makeConstraints { (maker: ConstraintMaker) in + maker.top.equalTo(self.view).offset(100) + maker.leading.trailing.equalTo(self.view).inset(40) + maker.height.equalTo(40) + } + + let player2Field = UITextField() + self.player2Field = player2Field + player2Field.borderStyle = UITextField.BorderStyle.line + view.addSubview(player2Field) + player2Field.placeholder = "Player 2 name" + player2Field.snp.makeConstraints { (maker: ConstraintMaker) in + maker.top.equalTo(player1Field.snp.bottom).offset(20) + maker.left.right.height.equalTo(player1Field) + } + + return (player1Field, player2Field) + } + + private func buildLoginButton(withPlayer1Field player1Field: UITextField, player2Field: UITextField) { + let loginButton = UIButton() + view.addSubview(loginButton) + loginButton.snp.makeConstraints { (maker: ConstraintMaker) in + maker.top.equalTo(player2Field.snp.bottom).offset(20) + maker.left.right.height.equalTo(player1Field) + } + + loginButton.setTitle("Login", for: .normal) + loginButton.setTitleColor(UIColor.white, for: .normal) + loginButton.backgroundColor = UIColor.black + loginButton.addTarget(self, action: #selector(didTapLoginButton), for: .touchUpInside) + } + + @objc + private func didTapLoginButton() { + guard let player1Field = player1Field, let player2Field = player2Field else { + return + } + mutablePlayersStore.update(player1: player1Field.text, player2: player2Field.text) + dismiss(animated: true, completion: nil) + } +} diff --git a/Sample/MVC-plain/TicTacToe/Sources/NeedleGenerated.swift b/Sample/MVC-plain/TicTacToe/Sources/NeedleGenerated.swift new file mode 100644 index 00000000..58dbf1a5 --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Sources/NeedleGenerated.swift @@ -0,0 +1,92 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import NeedleFoundation +import UIKit + +let needleDependenciesHash : String? = nil + +// MARK: - Registration + +public func registerProviderFactories() { + __DependencyProviderRegistry.instance.registerDependencyProviderFactory(for: "^->RootComponent->LoggedInComponent->GameComponent") { component in + return GameDependency1ab5926a977f706d3195Provider(component: component) + } + __DependencyProviderRegistry.instance.registerDependencyProviderFactory(for: "^->RootComponent->LoggedInComponent->GameComponent->ScoreSheetComponent") { component in + return ScoreSheetDependency97f2595a691a56781aaaProvider(component: component) + } + __DependencyProviderRegistry.instance.registerDependencyProviderFactory(for: "^->RootComponent->LoggedInComponent->ScoreSheetComponent") { component in + return ScoreSheetDependencycbd7fa4bae2ee69a1926Provider(component: component) + } + __DependencyProviderRegistry.instance.registerDependencyProviderFactory(for: "^->RootComponent->LoggedOutComponent") { component in + return LoggedOutDependencyacada53ea78d270efa2fProvider(component: component) + } + __DependencyProviderRegistry.instance.registerDependencyProviderFactory(for: "^->RootComponent->LoggedInComponent") { component in + return EmptyDependencyProvider(component: component) + } + __DependencyProviderRegistry.instance.registerDependencyProviderFactory(for: "^->RootComponent") { component in + return EmptyDependencyProvider(component: component) + } + +} + +// MARK: - Providers + +/// ^->RootComponent->LoggedInComponent->GameComponent +private class GameDependency1ab5926a977f706d3195Provider: GameDependency { + var mutableScoreStore: MutableScoreStore { + return loggedInComponent.mutableScoreStore + } + var playersStore: PlayersStore { + return rootComponent.playersStore + } + private let loggedInComponent: LoggedInComponent + private let rootComponent: RootComponent + init(component: NeedleFoundation.Scope) { + loggedInComponent = component.parent as! LoggedInComponent + rootComponent = component.parent.parent as! RootComponent + } +} +/// ^->RootComponent->LoggedInComponent->GameComponent->ScoreSheetComponent +private class ScoreSheetDependency97f2595a691a56781aaaProvider: ScoreSheetDependency { + var scoreStore: ScoreStore { + return loggedInComponent.scoreStore + } + private let loggedInComponent: LoggedInComponent + init(component: NeedleFoundation.Scope) { + loggedInComponent = component.parent.parent as! LoggedInComponent + } +} +/// ^->RootComponent->LoggedInComponent->ScoreSheetComponent +private class ScoreSheetDependencycbd7fa4bae2ee69a1926Provider: ScoreSheetDependency { + var scoreStore: ScoreStore { + return loggedInComponent.scoreStore + } + private let loggedInComponent: LoggedInComponent + init(component: NeedleFoundation.Scope) { + loggedInComponent = component.parent as! LoggedInComponent + } +} +/// ^->RootComponent->LoggedOutComponent +private class LoggedOutDependencyacada53ea78d270efa2fProvider: LoggedOutDependency { + var mutablePlayersStore: MutablePlayersStore { + return rootComponent.mutablePlayersStore + } + private let rootComponent: RootComponent + init(component: NeedleFoundation.Scope) { + rootComponent = component.parent as! RootComponent + } +} diff --git a/Sample/MVC-plain/TicTacToe/Sources/Root/PlayersStore.swift b/Sample/MVC-plain/TicTacToe/Sources/Root/PlayersStore.swift new file mode 100644 index 00000000..e208b4d0 --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Sources/Root/PlayersStore.swift @@ -0,0 +1,73 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +protocol PlayersStoreListener: class { + func didUpdate(names: (String, String)) +} + +protocol PlayersStore { + var names: (String, String)? { get } + func add(listener: PlayersStoreListener) + func remove(listener: PlayersStoreListener) +} + +protocol MutablePlayersStore: PlayersStore { + func update(player1: String?, player2: String?) +} + +class PlayersStoreImpl: MutablePlayersStore { + + private(set) var names: (String, String)? + private var listeners: [PlayersStoreListener] = [] + + func update(player1: String?, player2: String?) { + let player1Name: String + if let player1 = player1, !player1.isEmpty { + player1Name = player1 + } else { + player1Name = "Player 1" + } + let player2Name: String + if let player2 = player2, !player2.isEmpty { + player2Name = player2 + } else { + player2Name = "Player 2" + } + + let newNames = (player1Name, player2Name) + names = newNames + + listeners.forEach { $0.didUpdate(names: newNames) } + } + + func add(listener: PlayersStoreListener) { + let listenerAlreadyExists = listeners.contains { $0 === listener } + if listenerAlreadyExists { + return + } + + listeners.append(listener) + } + + func remove(listener: PlayersStoreListener) { + let index = listeners.firstIndex { $0 === listener } + if let index = index { + listeners.remove(at: index) + } + } +} diff --git a/Sample/MVC-plain/TicTacToe/Sources/Root/RootComponent.swift b/Sample/MVC-plain/TicTacToe/Sources/Root/RootComponent.swift new file mode 100644 index 00000000..03f4434f --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Sources/Root/RootComponent.swift @@ -0,0 +1,41 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import NeedleFoundation +import UIKit + +class RootComponent: BootstrapComponent { + + var playersStore: PlayersStore { + return mutablePlayersStore + } + + var mutablePlayersStore: MutablePlayersStore { + return shared { PlayersStoreImpl() } + } + + var rootViewController: UIViewController { + return RootViewController(playersStore: playersStore, loggedOutBuilder: loggedOutComponent, loggedInBuilder: loggedInComponent) + } + + var loggedOutComponent: LoggedOutComponent { + return LoggedOutComponent(parent: self) + } + + var loggedInComponent: LoggedInComponent { + return LoggedInComponent(parent: self) + } +} diff --git a/Sample/MVC-plain/TicTacToe/Sources/Root/RootViewController.swift b/Sample/MVC-plain/TicTacToe/Sources/Root/RootViewController.swift new file mode 100644 index 00000000..a48833af --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Sources/Root/RootViewController.swift @@ -0,0 +1,81 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class RootViewController: UIViewController { + + private let playersStore: PlayersStore + private let loggedOutBuilder: LoggedOutBuilder + private let loggedInBuilder: LoggedInBuilder + + init(playersStore: PlayersStore, loggedOutBuilder: LoggedOutBuilder, loggedInBuilder: LoggedInBuilder) { + self.playersStore = playersStore + self.loggedOutBuilder = loggedOutBuilder + self.loggedInBuilder = loggedInBuilder + super.init(nibName: nil, bundle: nil) + + self.playersStore.add(listener: self) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = UIColor.white + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + updateChildViewController(names: nil) + } + + private func updateChildViewController(names: (String, String)?) { + if names == nil { + present(viewController: loggedOutBuilder.loggedOutViewController) + } else { + present(viewController: loggedInBuilder.loggedInViewController) + } + } + + private func present(viewController: UIViewController) { + if presentedViewController == viewController { + return + } + + if presentedViewController != nil { + dismiss(animated: true) { + self.present(viewController, animated: true, completion: nil) + } + } else { + present(viewController, animated: true, completion: nil) + } + } + + deinit { + playersStore.remove(listener: self) + } +} + +extension RootViewController: PlayersStoreListener { + func didUpdate(names: (String, String)) { + updateChildViewController(names: names) + } +} diff --git a/Sample/MVC-plain/TicTacToe/Sources/ScoreSheet/ScoreSheetComponent.swift b/Sample/MVC-plain/TicTacToe/Sources/ScoreSheet/ScoreSheetComponent.swift new file mode 100644 index 00000000..410c9c75 --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Sources/ScoreSheet/ScoreSheetComponent.swift @@ -0,0 +1,34 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import NeedleFoundation +import UIKit + +protocol ScoreSheetDependency: Dependency { + var scoreStore: ScoreStore { get } +} + +class ScoreSheetComponent: Component, ScoreSheetBuilder { + + var scoreSheetViewController: UIViewController { + return ScoreSheetViewController(scoreStore: dependency.scoreStore) + } +} + +// Use a builder protocol to allow mocking for unit tests +protocol ScoreSheetBuilder { + var scoreSheetViewController: UIViewController { get } +} diff --git a/Sample/MVC-plain/TicTacToe/Sources/ScoreSheet/ScoreSheetViewController.swift b/Sample/MVC-plain/TicTacToe/Sources/ScoreSheet/ScoreSheetViewController.swift new file mode 100644 index 00000000..128b0e97 --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Sources/ScoreSheet/ScoreSheetViewController.swift @@ -0,0 +1,117 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SnapKit +import UIKit + +protocol ScoreSheetListener { + func done() +} + +class ScoreSheetViewController: UIViewController { + + public var listener: ScoreSheetListener? + + init(scoreStore: ScoreStore) { + self.scoreStore = scoreStore + super.init(nibName: nil, bundle: nil) + + scoreStore.add(listener: self) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .blue + + buildScoreSheet() + setupInitialValue() + } + + // MARK: - Private + private let scoreStore: ScoreStore + private var player1Score: UILabel? + private var player2Score: UILabel? + private var okButton: UIButton? + + private func buildScoreSheet() { + let player1Score = UILabel() + self.player1Score = player1Score + player1Score.backgroundColor = .lightGray + view.addSubview(player1Score) + player1Score.snp.makeConstraints { (maker: ConstraintMaker) in + maker.height.equalTo(44) + maker.top.equalTo(view).offset(100) + maker.leading.equalTo(view).offset(50) + maker.trailing.equalTo(view).offset(-50) + } + + let player2Score = UILabel() + self.player2Score = player2Score + player2Score.backgroundColor = .lightGray + view.addSubview(player2Score) + player2Score.snp.makeConstraints { (maker: ConstraintMaker) in + maker.height.equalTo(player1Score) + maker.top.equalTo(player1Score.snp.bottom).offset(20) + maker.leading.trailing.equalTo(player1Score) + } + + let okButton = UIButton() + self.okButton = okButton + okButton.backgroundColor = .black + view.addSubview(okButton) + okButton.snp.makeConstraints { (maker: ConstraintMaker) in + maker.height.equalTo(44) + maker.width.equalTo(100) + maker.bottom.equalTo(view).offset(-50) + maker.centerX.equalTo(view) + } + + okButton.setTitle("OK", for: .normal) + okButton.setTitleColor(.white, for: .normal) + okButton.addTarget(self, action: #selector(didTapOkButton), for: .touchUpInside) + } + + private func setupInitialValue() { + updateScore(player1Score: scoreStore.scores.0, player2Score: scoreStore.scores.1) + } + + @objc + private func didTapOkButton() { + listener?.done() + } + + deinit { + scoreStore.remove(listener: self) + } +} + +extension ScoreSheetViewController: ScoreStatusListener { + func gameDidEnd() {} + + func scoreUpdated(player1Score: PlayerScore, player2Score: PlayerScore) { + updateScore(player1Score: player1Score, player2Score: player2Score) + } + + private func updateScore(player1Score: PlayerScore, player2Score: PlayerScore) { + self.player1Score?.text = "\(player1Score.name): \(player1Score.score)" + self.player2Score?.text = "\(player2Score.name): \(player2Score.score)" + } +} diff --git a/Sample/MVC/TicTacToe/Tests/Info.plist b/Sample/MVC-plain/TicTacToe/Tests/Info.plist similarity index 100% rename from Sample/MVC/TicTacToe/Tests/Info.plist rename to Sample/MVC-plain/TicTacToe/Tests/Info.plist diff --git a/Sample/MVC-plain/TicTacToe/Tests/RootViewControllerTests.swift b/Sample/MVC-plain/TicTacToe/Tests/RootViewControllerTests.swift new file mode 100644 index 00000000..34cc801a --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/Tests/RootViewControllerTests.swift @@ -0,0 +1,78 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@testable import TicTacToe +import XCTest + +class RootViewControllerTests: XCTestCase { + + private var playersStore: PlayersStoreMock! + private var loggedOutBuilder: LoggedOutBuilderMock! + private var loggedInBuilder: LoggedInBuilderMock! + private var rootViewController: RootViewController! + + override func setUp() { + super.setUp() + + playersStore = PlayersStoreMock() + loggedOutBuilder = LoggedOutBuilderMock() + loggedInBuilder = LoggedInBuilderMock() + rootViewController = RootViewController(playersStore: playersStore, loggedOutBuilder: loggedOutBuilder, loggedInBuilder: loggedInBuilder) + } + + func test_viewDidAppear_presentsLoggedOut() { + rootViewController.viewDidAppear(true) + + XCTAssertEqual(loggedOutBuilder.loggedOutViewControllerCallCount, 1) + XCTAssertEqual(loggedInBuilder.loggedInViewControllerCallCount, 0) + } + + func test_playerNamesUpdated_presentsLoggedIn() { + playersStore.names = ("blah", "haha") + rootViewController.didUpdate(names: playersStore.names!) + + XCTAssertEqual(loggedOutBuilder.loggedOutViewControllerCallCount, 0) + XCTAssertEqual(loggedInBuilder.loggedInViewControllerCallCount, 1) + } +} + +class PlayersStoreMock: PlayersStore { + func add(listener: PlayersStoreListener) {} + + func remove(listener: PlayersStoreListener) {} + + var names: (String, String)? +} + +class LoggedOutBuilderMock: LoggedOutBuilder { + let viewController = UIViewController() + + var loggedOutViewControllerCallCount = 0 + var loggedOutViewController: UIViewController { + loggedOutViewControllerCallCount += 1 + return viewController + } +} + +class LoggedInBuilderMock: LoggedInBuilder { + let viewController = UIViewController() + + var loggedInViewControllerCallCount = 0 + var loggedInViewController: UIViewController { + loggedInViewControllerCallCount += 1 + return viewController + } +} diff --git a/Sample/MVC-plain/TicTacToe/TicTacToe.xcodeproj/project.pbxproj b/Sample/MVC-plain/TicTacToe/TicTacToe.xcodeproj/project.pbxproj new file mode 100644 index 00000000..53c38330 --- /dev/null +++ b/Sample/MVC-plain/TicTacToe/TicTacToe.xcodeproj/project.pbxproj @@ -0,0 +1,695 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 4139095720A0F2BC00A26FF5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4139095620A0F2BC00A26FF5 /* AppDelegate.swift */; }; + 4139095E20A0F2BD00A26FF5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4139095D20A0F2BD00A26FF5 /* Assets.xcassets */; }; + 4139096120A0F2BD00A26FF5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4139095F20A0F2BD00A26FF5 /* LaunchScreen.storyboard */; }; + 4139097520A1060300A26FF5 /* RootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4139097420A1060300A26FF5 /* RootViewController.swift */; }; + 4139097920A1111900A26FF5 /* RootComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4139097820A1111900A26FF5 /* RootComponent.swift */; }; + 4139097B20A113AE00A26FF5 /* PlayersStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4139097A20A113AD00A26FF5 /* PlayersStore.swift */; }; + 4139097D20A1174700A26FF5 /* NeedleGenerated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4139097C20A1174700A26FF5 /* NeedleGenerated.swift */; }; + 4139098020A21F0500A26FF5 /* LoggedOutComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4139097F20A21F0500A26FF5 /* LoggedOutComponent.swift */; }; + 4139098220A21F8000A26FF5 /* LoggedOutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4139098120A21F8000A26FF5 /* LoggedOutViewController.swift */; }; + 4139098420A2211F00A26FF5 /* SnapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4139098320A2211F00A26FF5 /* SnapKit.framework */; }; + 4139098E20A22B3900A26FF5 /* RootViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4139098D20A22B3900A26FF5 /* RootViewControllerTests.swift */; }; + 4139099720A2481700A26FF5 /* LoggedInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4139099620A2481700A26FF5 /* LoggedInViewController.swift */; }; + 4139099920A2483300A26FF5 /* LoggedInComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4139099820A2483300A26FF5 /* LoggedInComponent.swift */; }; + 4139099B20A2489100A26FF5 /* ScoreStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4139099A20A2489100A26FF5 /* ScoreStore.swift */; }; + 413909A420A4E58A00A26FF5 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 413909A320A4E58A00A26FF5 /* GameViewController.swift */; }; + 413909A620A4EA7300A26FF5 /* GameComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 413909A520A4EA7300A26FF5 /* GameComponent.swift */; }; + F7DA7AE020A275DA0037FF17 /* ScoreSheetComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7DA7ADF20A275DA0037FF17 /* ScoreSheetComponent.swift */; }; + F7DA7AE220A275EF0037FF17 /* ScoreSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7DA7AE120A275EF0037FF17 /* ScoreSheetViewController.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 4139099020A22B3900A26FF5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4139094B20A0F2BC00A26FF5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4139095220A0F2BC00A26FF5; + remoteInfo = TicTacToe; + }; + 4140797B21F808230081B8A9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4140797421F808230081B8A9 /* NeedleFoundation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = "NeedleFoundation::NeedleFoundation::Product"; + remoteInfo = NeedleFoundation; + }; + 4140797D21F808230081B8A9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4140797421F808230081B8A9 /* NeedleFoundation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = "NeedleFoundation::NeedleFoundationTests::Product"; + remoteInfo = NeedleFoundationTests; + }; + 4140798B21F808B30081B8A9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4140797421F808230081B8A9 /* NeedleFoundation.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = "NeedleFoundation::NeedleFoundation"; + remoteInfo = NeedleFoundation; + }; + BC1FD60E229320730077EEF3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4140797421F808230081B8A9 /* NeedleFoundation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = "NeedleFoundation::NeedleFoundationTest::Product"; + remoteInfo = NeedleFoundationTest; + }; + BC1FD610229320730077EEF3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4140797421F808230081B8A9 /* NeedleFoundation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = "NeedleFoundation::NeedleFoundationTestTests::Product"; + remoteInfo = NeedleFoundationTestTests; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 4139095320A0F2BC00A26FF5 /* TicTacToe.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TicTacToe.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4139095620A0F2BC00A26FF5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 4139095D20A0F2BD00A26FF5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 4139096020A0F2BD00A26FF5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 4139096220A0F2BD00A26FF5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4139097420A1060300A26FF5 /* RootViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootViewController.swift; sourceTree = ""; }; + 4139097820A1111900A26FF5 /* RootComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootComponent.swift; sourceTree = ""; }; + 4139097A20A113AD00A26FF5 /* PlayersStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayersStore.swift; sourceTree = ""; }; + 4139097C20A1174700A26FF5 /* NeedleGenerated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeedleGenerated.swift; sourceTree = ""; }; + 4139097F20A21F0500A26FF5 /* LoggedOutComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedOutComponent.swift; sourceTree = ""; }; + 4139098120A21F8000A26FF5 /* LoggedOutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedOutViewController.swift; sourceTree = ""; }; + 4139098320A2211F00A26FF5 /* SnapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SnapKit.framework; path = ../Carthage/Build/iOS/SnapKit.framework; sourceTree = ""; }; + 4139098B20A22B3900A26FF5 /* TicTacToeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TicTacToeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 4139098D20A22B3900A26FF5 /* RootViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootViewControllerTests.swift; sourceTree = ""; }; + 4139098F20A22B3900A26FF5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4139099620A2481700A26FF5 /* LoggedInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedInViewController.swift; sourceTree = ""; }; + 4139099820A2483300A26FF5 /* LoggedInComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedInComponent.swift; sourceTree = ""; }; + 4139099A20A2489100A26FF5 /* ScoreStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScoreStore.swift; sourceTree = ""; }; + 413909A320A4E58A00A26FF5 /* GameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = ""; }; + 413909A520A4EA7300A26FF5 /* GameComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameComponent.swift; sourceTree = ""; }; + 4140797421F808230081B8A9 /* NeedleFoundation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = NeedleFoundation.xcodeproj; path = ../../../NeedleFoundation.xcodeproj; sourceTree = ""; }; + F7DA7ADF20A275DA0037FF17 /* ScoreSheetComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScoreSheetComponent.swift; sourceTree = ""; }; + F7DA7AE120A275EF0037FF17 /* ScoreSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScoreSheetViewController.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4139095020A0F2BC00A26FF5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4139098420A2211F00A26FF5 /* SnapKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4139098820A22B3900A26FF5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4139094A20A0F2BC00A26FF5 = { + isa = PBXGroup; + children = ( + 4140797421F808230081B8A9 /* NeedleFoundation.xcodeproj */, + 4139095520A0F2BC00A26FF5 /* Sources */, + 4139098C20A22B3900A26FF5 /* Tests */, + 4139095420A0F2BC00A26FF5 /* Products */, + 4139096820A0F39B00A26FF5 /* Frameworks */, + ); + sourceTree = ""; + }; + 4139095420A0F2BC00A26FF5 /* Products */ = { + isa = PBXGroup; + children = ( + 4139095320A0F2BC00A26FF5 /* TicTacToe.app */, + 4139098B20A22B3900A26FF5 /* TicTacToeTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 4139095520A0F2BC00A26FF5 /* Sources */ = { + isa = PBXGroup; + children = ( + 413909A220A4E57C00A26FF5 /* Game */, + 4139099520A247D800A26FF5 /* LoggedIn */, + 4139097E20A21EE500A26FF5 /* LoggedOut */, + 4139097320A105EE00A26FF5 /* Root */, + F7DA7ADE20A275B00037FF17 /* ScoreSheet */, + 4139095620A0F2BC00A26FF5 /* AppDelegate.swift */, + 4139097C20A1174700A26FF5 /* NeedleGenerated.swift */, + 4139095D20A0F2BD00A26FF5 /* Assets.xcassets */, + 4139095F20A0F2BD00A26FF5 /* LaunchScreen.storyboard */, + 4139096220A0F2BD00A26FF5 /* Info.plist */, + ); + path = Sources; + sourceTree = ""; + }; + 4139096820A0F39B00A26FF5 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4139098320A2211F00A26FF5 /* SnapKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 4139097320A105EE00A26FF5 /* Root */ = { + isa = PBXGroup; + children = ( + 4139097420A1060300A26FF5 /* RootViewController.swift */, + 4139097820A1111900A26FF5 /* RootComponent.swift */, + 4139097A20A113AD00A26FF5 /* PlayersStore.swift */, + ); + path = Root; + sourceTree = ""; + }; + 4139097E20A21EE500A26FF5 /* LoggedOut */ = { + isa = PBXGroup; + children = ( + 4139097F20A21F0500A26FF5 /* LoggedOutComponent.swift */, + 4139098120A21F8000A26FF5 /* LoggedOutViewController.swift */, + ); + path = LoggedOut; + sourceTree = ""; + }; + 4139098C20A22B3900A26FF5 /* Tests */ = { + isa = PBXGroup; + children = ( + 4139098D20A22B3900A26FF5 /* RootViewControllerTests.swift */, + 4139098F20A22B3900A26FF5 /* Info.plist */, + ); + path = Tests; + sourceTree = ""; + }; + 4139099520A247D800A26FF5 /* LoggedIn */ = { + isa = PBXGroup; + children = ( + 4139099620A2481700A26FF5 /* LoggedInViewController.swift */, + 4139099820A2483300A26FF5 /* LoggedInComponent.swift */, + 4139099A20A2489100A26FF5 /* ScoreStore.swift */, + ); + path = LoggedIn; + sourceTree = ""; + }; + 413909A220A4E57C00A26FF5 /* Game */ = { + isa = PBXGroup; + children = ( + 413909A320A4E58A00A26FF5 /* GameViewController.swift */, + 413909A520A4EA7300A26FF5 /* GameComponent.swift */, + ); + path = Game; + sourceTree = ""; + }; + 4140797521F808230081B8A9 /* Products */ = { + isa = PBXGroup; + children = ( + 4140797C21F808230081B8A9 /* NeedleFoundation.framework */, + BC1FD60F229320730077EEF3 /* NeedleFoundationTest.framework */, + BC1FD611229320730077EEF3 /* NeedleFoundationTestTests.xctest */, + 4140797E21F808230081B8A9 /* NeedleFoundationTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + F7DA7ADE20A275B00037FF17 /* ScoreSheet */ = { + isa = PBXGroup; + children = ( + F7DA7ADF20A275DA0037FF17 /* ScoreSheetComponent.swift */, + F7DA7AE120A275EF0037FF17 /* ScoreSheetViewController.swift */, + ); + path = ScoreSheet; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4139095220A0F2BC00A26FF5 /* TicTacToe */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4139096520A0F2BD00A26FF5 /* Build configuration list for PBXNativeTarget "TicTacToe" */; + buildPhases = ( + 41E465B6216D5A3600C9509F /* ShellScript */, + 4139094F20A0F2BC00A26FF5 /* Sources */, + 4139095020A0F2BC00A26FF5 /* Frameworks */, + 4139095120A0F2BC00A26FF5 /* Resources */, + 4139097020A0F4AB00A26FF5 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 4140798C21F808B30081B8A9 /* PBXTargetDependency */, + ); + name = TicTacToe; + productName = TicTacToe; + productReference = 4139095320A0F2BC00A26FF5 /* TicTacToe.app */; + productType = "com.apple.product-type.application"; + }; + 4139098A20A22B3900A26FF5 /* TicTacToeTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4139099220A22B3900A26FF5 /* Build configuration list for PBXNativeTarget "TicTacToeTests" */; + buildPhases = ( + 4139098720A22B3900A26FF5 /* Sources */, + 4139098820A22B3900A26FF5 /* Frameworks */, + 4139098920A22B3900A26FF5 /* Resources */, + 413909A120A376D800A26FF5 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 4139099120A22B3900A26FF5 /* PBXTargetDependency */, + ); + name = TicTacToeTests; + productName = TicTacToeTests; + productReference = 4139098B20A22B3900A26FF5 /* TicTacToeTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4139094B20A0F2BC00A26FF5 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0930; + LastUpgradeCheck = 0930; + TargetAttributes = { + 4139095220A0F2BC00A26FF5 = { + CreatedOnToolsVersion = 9.3; + }; + 4139098A20A22B3900A26FF5 = { + CreatedOnToolsVersion = 9.3; + TestTargetID = 4139095220A0F2BC00A26FF5; + }; + }; + }; + buildConfigurationList = 4139094E20A0F2BC00A26FF5 /* Build configuration list for PBXProject "TicTacToe" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 4139094A20A0F2BC00A26FF5; + productRefGroup = 4139095420A0F2BC00A26FF5 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 4140797521F808230081B8A9 /* Products */; + ProjectRef = 4140797421F808230081B8A9 /* NeedleFoundation.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 4139095220A0F2BC00A26FF5 /* TicTacToe */, + 4139098A20A22B3900A26FF5 /* TicTacToeTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 4140797C21F808230081B8A9 /* NeedleFoundation.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = NeedleFoundation.framework; + remoteRef = 4140797B21F808230081B8A9 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4140797E21F808230081B8A9 /* NeedleFoundationTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = file; + path = NeedleFoundationTests.xctest; + remoteRef = 4140797D21F808230081B8A9 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + BC1FD60F229320730077EEF3 /* NeedleFoundationTest.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = NeedleFoundationTest.framework; + remoteRef = BC1FD60E229320730077EEF3 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + BC1FD611229320730077EEF3 /* NeedleFoundationTestTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = file; + path = NeedleFoundationTestTests.xctest; + remoteRef = BC1FD610229320730077EEF3 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 4139095120A0F2BC00A26FF5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4139096120A0F2BD00A26FF5 /* LaunchScreen.storyboard in Resources */, + 4139095E20A0F2BD00A26FF5 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4139098920A22B3900A26FF5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 4139097020A0F4AB00A26FF5 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/../Carthage/Build/iOS/SnapKit.framework", + ); + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/SnapKit.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/local/bin/carthage copy-frameworks\n"; + }; + 413909A120A376D800A26FF5 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/../Carthage/Build/iOS/SnapKit.framework", + ); + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/SnapKit.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/local/bin/carthage copy-frameworks\n"; + }; + 41E465B6216D5A3600C9509F /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export SOURCEKIT_LOGGING=0 && ../../../Generator/bin/needle generate Sources/NeedleGenerated.swift Sources/ --header-doc ../../copyright_header.txt\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4139094F20A0F2BC00A26FF5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4139099B20A2489100A26FF5 /* ScoreStore.swift in Sources */, + 4139098020A21F0500A26FF5 /* LoggedOutComponent.swift in Sources */, + 4139097520A1060300A26FF5 /* RootViewController.swift in Sources */, + 4139097920A1111900A26FF5 /* RootComponent.swift in Sources */, + 413909A420A4E58A00A26FF5 /* GameViewController.swift in Sources */, + 4139095720A0F2BC00A26FF5 /* AppDelegate.swift in Sources */, + F7DA7AE220A275EF0037FF17 /* ScoreSheetViewController.swift in Sources */, + F7DA7AE020A275DA0037FF17 /* ScoreSheetComponent.swift in Sources */, + 4139097B20A113AE00A26FF5 /* PlayersStore.swift in Sources */, + 4139098220A21F8000A26FF5 /* LoggedOutViewController.swift in Sources */, + 4139099920A2483300A26FF5 /* LoggedInComponent.swift in Sources */, + 413909A620A4EA7300A26FF5 /* GameComponent.swift in Sources */, + 4139097D20A1174700A26FF5 /* NeedleGenerated.swift in Sources */, + 4139099720A2481700A26FF5 /* LoggedInViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4139098720A22B3900A26FF5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4139098E20A22B3900A26FF5 /* RootViewControllerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 4139099120A22B3900A26FF5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4139095220A0F2BC00A26FF5 /* TicTacToe */; + targetProxy = 4139099020A22B3900A26FF5 /* PBXContainerItemProxy */; + }; + 4140798C21F808B30081B8A9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = NeedleFoundation; + targetProxy = 4140798B21F808B30081B8A9 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 4139095F20A0F2BD00A26FF5 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 4139096020A0F2BD00A26FF5 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 4139096320A0F2BD00A26FF5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../Carthage/Build/iOS"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 4139096420A0F2BD00A26FF5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../Carthage/Build/iOS"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 4139096620A0F2BD00A26FF5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = Sources/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.uber.needle.tictactoe.TicTacToe; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 4139096720A0F2BD00A26FF5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = Sources/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.uber.needle.tictactoe.TicTacToe; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 4139099320A22B3900A26FF5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.uber.needle.tictactoe.TicTacToeTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TicTacToe.app/TicTacToe"; + }; + name = Debug; + }; + 4139099420A22B3900A26FF5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.uber.needle.tictactoe.TicTacToeTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TicTacToe.app/TicTacToe"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4139094E20A0F2BC00A26FF5 /* Build configuration list for PBXProject "TicTacToe" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4139096320A0F2BD00A26FF5 /* Debug */, + 4139096420A0F2BD00A26FF5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4139096520A0F2BD00A26FF5 /* Build configuration list for PBXNativeTarget "TicTacToe" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4139096620A0F2BD00A26FF5 /* Debug */, + 4139096720A0F2BD00A26FF5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4139099220A22B3900A26FF5 /* Build configuration list for PBXNativeTarget "TicTacToeTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4139099320A22B3900A26FF5 /* Debug */, + 4139099420A22B3900A26FF5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4139094B20A0F2BC00A26FF5 /* Project object */; +} diff --git a/Sample/MVC/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Sample/MVC-plain/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Sample/MVC/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Sample/MVC-plain/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/Sample/MVC/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Sample/MVC-plain/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Sample/MVC/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Sample/MVC-plain/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Sample/MVC/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Sample/MVC-plain/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from Sample/MVC/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to Sample/MVC-plain/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/Sample/MVC-rx/.gitignore b/Sample/MVC-rx/.gitignore new file mode 100644 index 00000000..f3917ea9 --- /dev/null +++ b/Sample/MVC-rx/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +/.build +/Packages +/Carthage diff --git a/Sample/MVC/Cartfile b/Sample/MVC-rx/Cartfile similarity index 100% rename from Sample/MVC/Cartfile rename to Sample/MVC-rx/Cartfile diff --git a/Sample/MVC/Cartfile.resolved b/Sample/MVC-rx/Cartfile.resolved similarity index 100% rename from Sample/MVC/Cartfile.resolved rename to Sample/MVC-rx/Cartfile.resolved diff --git a/Sample/MVC-rx/TicTacToe/Sources/AppDelegate.swift b/Sample/MVC-rx/TicTacToe/Sources/AppDelegate.swift new file mode 100644 index 00000000..7fceecff --- /dev/null +++ b/Sample/MVC-rx/TicTacToe/Sources/AppDelegate.swift @@ -0,0 +1,37 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import NeedleFoundation +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + registerProviderFactories() + + let window = UIWindow(frame: UIScreen.main.bounds) + self.window = window + + let rootComponent = RootComponent() + window.rootViewController = rootComponent.rootViewController + + window.makeKeyAndVisible() + return true + } +} diff --git a/Sample/MVC-rx/TicTacToe/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json b/Sample/MVC-rx/TicTacToe/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/Sample/MVC-rx/TicTacToe/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/MVC-rx/TicTacToe/Sources/Assets.xcassets/Contents.json b/Sample/MVC-rx/TicTacToe/Sources/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/Sample/MVC-rx/TicTacToe/Sources/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/MVC-rx/TicTacToe/Sources/Base.lproj/LaunchScreen.storyboard b/Sample/MVC-rx/TicTacToe/Sources/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..f83f6fd5 --- /dev/null +++ b/Sample/MVC-rx/TicTacToe/Sources/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sample/MVC/TicTacToe/Sources/Game/GameComponent.swift b/Sample/MVC-rx/TicTacToe/Sources/Game/GameComponent.swift similarity index 100% rename from Sample/MVC/TicTacToe/Sources/Game/GameComponent.swift rename to Sample/MVC-rx/TicTacToe/Sources/Game/GameComponent.swift diff --git a/Sample/MVC/TicTacToe/Sources/Game/GameViewController.swift b/Sample/MVC-rx/TicTacToe/Sources/Game/GameViewController.swift similarity index 100% rename from Sample/MVC/TicTacToe/Sources/Game/GameViewController.swift rename to Sample/MVC-rx/TicTacToe/Sources/Game/GameViewController.swift diff --git a/Sample/MVC-rx/TicTacToe/Sources/Info.plist b/Sample/MVC-rx/TicTacToe/Sources/Info.plist new file mode 100644 index 00000000..4222ac2d --- /dev/null +++ b/Sample/MVC-rx/TicTacToe/Sources/Info.plist @@ -0,0 +1,43 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Sample/MVC/TicTacToe/Sources/LoggedIn/LoggedInComponent.swift b/Sample/MVC-rx/TicTacToe/Sources/LoggedIn/LoggedInComponent.swift similarity index 100% rename from Sample/MVC/TicTacToe/Sources/LoggedIn/LoggedInComponent.swift rename to Sample/MVC-rx/TicTacToe/Sources/LoggedIn/LoggedInComponent.swift diff --git a/Sample/MVC/TicTacToe/Sources/LoggedIn/LoggedInViewController.swift b/Sample/MVC-rx/TicTacToe/Sources/LoggedIn/LoggedInViewController.swift similarity index 100% rename from Sample/MVC/TicTacToe/Sources/LoggedIn/LoggedInViewController.swift rename to Sample/MVC-rx/TicTacToe/Sources/LoggedIn/LoggedInViewController.swift diff --git a/Sample/MVC/TicTacToe/Sources/LoggedIn/ScoreStream.swift b/Sample/MVC-rx/TicTacToe/Sources/LoggedIn/ScoreStream.swift similarity index 100% rename from Sample/MVC/TicTacToe/Sources/LoggedIn/ScoreStream.swift rename to Sample/MVC-rx/TicTacToe/Sources/LoggedIn/ScoreStream.swift diff --git a/Sample/MVC/TicTacToe/Sources/LoggedOut/LoggedOutComponent.swift b/Sample/MVC-rx/TicTacToe/Sources/LoggedOut/LoggedOutComponent.swift similarity index 100% rename from Sample/MVC/TicTacToe/Sources/LoggedOut/LoggedOutComponent.swift rename to Sample/MVC-rx/TicTacToe/Sources/LoggedOut/LoggedOutComponent.swift diff --git a/Sample/MVC/TicTacToe/Sources/LoggedOut/LoggedOutViewController.swift b/Sample/MVC-rx/TicTacToe/Sources/LoggedOut/LoggedOutViewController.swift similarity index 100% rename from Sample/MVC/TicTacToe/Sources/LoggedOut/LoggedOutViewController.swift rename to Sample/MVC-rx/TicTacToe/Sources/LoggedOut/LoggedOutViewController.swift diff --git a/Sample/MVC/TicTacToe/Sources/NeedleGenerated.swift b/Sample/MVC-rx/TicTacToe/Sources/NeedleGenerated.swift similarity index 98% rename from Sample/MVC/TicTacToe/Sources/NeedleGenerated.swift rename to Sample/MVC-rx/TicTacToe/Sources/NeedleGenerated.swift index febf2c76..30a22b95 100644 --- a/Sample/MVC/TicTacToe/Sources/NeedleGenerated.swift +++ b/Sample/MVC-rx/TicTacToe/Sources/NeedleGenerated.swift @@ -18,6 +18,8 @@ import NeedleFoundation import RxSwift import UIKit +let needleDependenciesHash : String? = nil + // MARK: - Registration public func registerProviderFactories() { diff --git a/Sample/MVC/TicTacToe/Sources/Root/PlayersStream.swift b/Sample/MVC-rx/TicTacToe/Sources/Root/PlayersStream.swift similarity index 100% rename from Sample/MVC/TicTacToe/Sources/Root/PlayersStream.swift rename to Sample/MVC-rx/TicTacToe/Sources/Root/PlayersStream.swift diff --git a/Sample/MVC/TicTacToe/Sources/Root/RootComponent.swift b/Sample/MVC-rx/TicTacToe/Sources/Root/RootComponent.swift similarity index 100% rename from Sample/MVC/TicTacToe/Sources/Root/RootComponent.swift rename to Sample/MVC-rx/TicTacToe/Sources/Root/RootComponent.swift diff --git a/Sample/MVC/TicTacToe/Sources/Root/RootViewController.swift b/Sample/MVC-rx/TicTacToe/Sources/Root/RootViewController.swift similarity index 100% rename from Sample/MVC/TicTacToe/Sources/Root/RootViewController.swift rename to Sample/MVC-rx/TicTacToe/Sources/Root/RootViewController.swift diff --git a/Sample/MVC/TicTacToe/Sources/ScoreSheet/ScoreSheetComponent.swift b/Sample/MVC-rx/TicTacToe/Sources/ScoreSheet/ScoreSheetComponent.swift similarity index 100% rename from Sample/MVC/TicTacToe/Sources/ScoreSheet/ScoreSheetComponent.swift rename to Sample/MVC-rx/TicTacToe/Sources/ScoreSheet/ScoreSheetComponent.swift diff --git a/Sample/MVC/TicTacToe/Sources/ScoreSheet/ScoreSheetViewController.swift b/Sample/MVC-rx/TicTacToe/Sources/ScoreSheet/ScoreSheetViewController.swift similarity index 100% rename from Sample/MVC/TicTacToe/Sources/ScoreSheet/ScoreSheetViewController.swift rename to Sample/MVC-rx/TicTacToe/Sources/ScoreSheet/ScoreSheetViewController.swift diff --git a/Sample/MVC-rx/TicTacToe/Tests/Info.plist b/Sample/MVC-rx/TicTacToe/Tests/Info.plist new file mode 100644 index 00000000..6c40a6cd --- /dev/null +++ b/Sample/MVC-rx/TicTacToe/Tests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Sample/MVC/TicTacToe/Tests/RootViewControllerTests.swift b/Sample/MVC-rx/TicTacToe/Tests/RootViewControllerTests.swift similarity index 100% rename from Sample/MVC/TicTacToe/Tests/RootViewControllerTests.swift rename to Sample/MVC-rx/TicTacToe/Tests/RootViewControllerTests.swift diff --git a/Sample/MVC/TicTacToe/TicTacToe.xcodeproj/project.pbxproj b/Sample/MVC-rx/TicTacToe/TicTacToe.xcodeproj/project.pbxproj similarity index 100% rename from Sample/MVC/TicTacToe/TicTacToe.xcodeproj/project.pbxproj rename to Sample/MVC-rx/TicTacToe/TicTacToe.xcodeproj/project.pbxproj diff --git a/Sample/MVC-rx/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Sample/MVC-rx/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/Sample/MVC-rx/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Sample/MVC-rx/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Sample/MVC-rx/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Sample/MVC-rx/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Sample/MVC-rx/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Sample/MVC-rx/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..0c67376e --- /dev/null +++ b/Sample/MVC-rx/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/Sample/README.md b/Sample/README.md index 5b73d09b..b8561e82 100644 --- a/Sample/README.md +++ b/Sample/README.md @@ -1,6 +1,6 @@ # Sample App Using Needle -The folder "MVC" contains the simple MVC architecture based TicTacToe app. The folder "Pluginized" contains the same TicTacToe app but built with a pluginized DI structure where the dependencies are divided into separate core and non-core trees. +The folder "MVC-rx" contains the simple MVC architecture based TicTacToe app using the reactive approach (RxSwift) while "MVC-plain" contains its non-reactive implementation. The folder "Pluginized" contains the same TicTacToe app but built with a pluginized DI structure where the dependencies are divided into separate core and non-core trees. ## Build & Run TicTacToe