├── MonoBank ├── CashRegister.mp3 ├── Assets.xcassets │ ├── Contents.json │ ├── Tokens │ │ ├── Contents.json │ │ ├── car.imageset │ │ │ ├── car.png │ │ │ └── Contents.json │ │ ├── cat.imageset │ │ │ ├── cat.png │ │ │ └── Contents.json │ │ ├── dog.imageset │ │ │ ├── dog.png │ │ │ └── Contents.json │ │ ├── hat.imageset │ │ │ ├── hat.png │ │ │ └── Contents.json │ │ ├── iron.imageset │ │ │ ├── iron.png │ │ │ └── Contents.json │ │ ├── ship.imageset │ │ │ ├── ship.png │ │ │ └── Contents.json │ │ ├── shoe.imageset │ │ │ ├── shoe.png │ │ │ └── Contents.json │ │ ├── horse.imageset │ │ │ ├── horse.png │ │ │ └── Contents.json │ │ ├── money.imageset │ │ │ ├── money.png │ │ │ └── Contents.json │ │ ├── cannon.imageset │ │ │ ├── cannon.png │ │ │ └── Contents.json │ │ ├── thimble.imageset │ │ │ ├── thimble.png │ │ │ └── Contents.json │ │ ├── car_filled.imageset │ │ │ ├── car_filled.png │ │ │ └── Contents.json │ │ ├── cat_filled.imageset │ │ │ ├── cat_filled.png │ │ │ └── Contents.json │ │ ├── dog_filled.imageset │ │ │ ├── dog_filled.png │ │ │ └── Contents.json │ │ ├── hat_filled.imageset │ │ │ ├── hat_filled.png │ │ │ └── Contents.json │ │ ├── iron_filled.imageset │ │ │ ├── iron_filled.png │ │ │ └── Contents.json │ │ ├── ship_filled.imageset │ │ │ ├── ship_filled.png │ │ │ └── Contents.json │ │ ├── shoe_filled.imageset │ │ │ ├── shoe_filled.png │ │ │ └── Contents.json │ │ ├── wheelbarrow.imageset │ │ │ ├── wheelbarrow.png │ │ │ └── Contents.json │ │ ├── cannon_filled.imageset │ │ │ ├── cannon_filled.png │ │ │ └── Contents.json │ │ ├── horse_filled.imageset │ │ │ ├── horse_filled.png │ │ │ └── Contents.json │ │ ├── money_filled.imageset │ │ │ ├── money_filled.png │ │ │ └── Contents.json │ │ ├── thimble_filled.imageset │ │ │ ├── thimble_filled.png │ │ │ └── Contents.json │ │ └── wheelbarrow_filled.imageset │ │ │ ├── wheelbarrow_filled.png │ │ │ └── Contents.json │ ├── icon.imageset │ │ ├── 1024.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── 120.png │ │ ├── 152.png │ │ ├── 167.png │ │ ├── 180.png │ │ ├── 20.png │ │ ├── 29.png │ │ ├── 40-1.png │ │ ├── 40-2.png │ │ ├── 40.png │ │ ├── 58-1.png │ │ ├── 58.png │ │ ├── 60.png │ │ ├── 76.png │ │ ├── 80-1.png │ │ ├── 80.png │ │ ├── 87.png │ │ ├── 120-1.png │ │ └── Contents.json │ └── Settings.imageset │ │ ├── Settings-44.png │ │ ├── Settings-66.png │ │ └── Contents.json ├── TokenCollectionViewCell.swift ├── CircleView.swift ├── PlayerCollectionViewCell.swift ├── Token.swift ├── AppDelegate.swift ├── Player.swift ├── Info.plist ├── BankManager.swift ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── SettingsViewController.swift ├── AddPlayerViewController.swift └── MainViewController.swift ├── BoardBank.xcodeproj ├── project.xcworkspace │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── project.pbxproj ├── BoardBankUITests ├── Info.plist ├── UITestWithRecorder.swift ├── screens │ ├── AddPlayerScreen.swift │ ├── SettingsScreen.swift │ └── HomeScreen.swift └── AddPlayerTests.swift ├── fastlane ├── README.md └── Fastfile ├── README.md ├── LICENSE ├── docs ├── LICENSE └── README.md └── .gitignore /MonoBank/CashRegister.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/CashRegister.mp3 -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/icon.imageset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/icon.imageset/1024.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/120.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/152.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/167.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/180.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/20.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/29.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/40-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/40-1.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/40-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/40-2.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/40.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/58-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/58-1.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/58.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/60.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/76.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/80-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/80-1.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/80.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/87.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/car.imageset/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/car.imageset/car.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/cat.imageset/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/cat.imageset/cat.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/dog.imageset/dog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/dog.imageset/dog.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/hat.imageset/hat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/hat.imageset/hat.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/120-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/AppIcon.appiconset/120-1.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/iron.imageset/iron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/iron.imageset/iron.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/ship.imageset/ship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/ship.imageset/ship.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/shoe.imageset/shoe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/shoe.imageset/shoe.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/horse.imageset/horse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/horse.imageset/horse.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/money.imageset/money.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/money.imageset/money.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Settings.imageset/Settings-44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Settings.imageset/Settings-44.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Settings.imageset/Settings-66.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Settings.imageset/Settings-66.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/cannon.imageset/cannon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/cannon.imageset/cannon.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/thimble.imageset/thimble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/thimble.imageset/thimble.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/car_filled.imageset/car_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/car_filled.imageset/car_filled.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/cat_filled.imageset/cat_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/cat_filled.imageset/cat_filled.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/dog_filled.imageset/dog_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/dog_filled.imageset/dog_filled.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/hat_filled.imageset/hat_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/hat_filled.imageset/hat_filled.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/iron_filled.imageset/iron_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/iron_filled.imageset/iron_filled.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/ship_filled.imageset/ship_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/ship_filled.imageset/ship_filled.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/shoe_filled.imageset/shoe_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/shoe_filled.imageset/shoe_filled.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/wheelbarrow.imageset/wheelbarrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/wheelbarrow.imageset/wheelbarrow.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/cannon_filled.imageset/cannon_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/cannon_filled.imageset/cannon_filled.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/horse_filled.imageset/horse_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/horse_filled.imageset/horse_filled.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/money_filled.imageset/money_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/money_filled.imageset/money_filled.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/thimble_filled.imageset/thimble_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/thimble_filled.imageset/thimble_filled.png -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/wheelbarrow_filled.imageset/wheelbarrow_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akifev99/BoardBank/HEAD/MonoBank/Assets.xcassets/Tokens/wheelbarrow_filled.imageset/wheelbarrow_filled.png -------------------------------------------------------------------------------- /BoardBank.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MonoBank/TokenCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TokenCollectionViewCell.swift 3 | // MonoBank 4 | // 5 | // Created by Richard Neitzke on 04/01/2017. 6 | // Copyright © 2017 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class TokenCollectionViewCell: UICollectionViewCell { 12 | @IBOutlet var tokenView: UIImageView! 13 | } 14 | -------------------------------------------------------------------------------- /MonoBank/CircleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CircleView.swift 3 | // MonoBank 4 | // 5 | // Created by Richard Neitzke on 03/01/2017. 6 | // Copyright © 2017 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CircleView: UIView { 12 | 13 | override func layoutSubviews() { 14 | super.layoutSubviews() 15 | self.layer.cornerRadius = self.layer.bounds.width/2 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "1024.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/car.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "car.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/cat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cat.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/dog.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "dog.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/hat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hat.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/horse.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "horse.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/iron.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "iron.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/money.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "money.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/ship.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ship.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/shoe.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "shoe.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/PlayerCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlayerCollectionViewCell.swift 3 | // MonoBank 4 | // 5 | // Created by Richard Neitzke on 03/01/2017. 6 | // Copyright © 2017 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PlayerCollectionViewCell: UICollectionViewCell { 12 | @IBOutlet var tokenView: UIImageView! 13 | @IBOutlet var nameLabel: UILabel! 14 | @IBOutlet var balanceLabel: UILabel! 15 | } 16 | -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/cannon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cannon.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/thimble.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "thimble.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/car_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "car_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/cat_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cat_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/dog_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "dog_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/hat_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hat_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/horse_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "horse_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/iron_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "iron_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/money_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "money_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/ship_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ship_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/shoe_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "shoe_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/wheelbarrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "wheelbarrow.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/cannon_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cannon_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/thimble_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "thimble_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Tokens/wheelbarrow_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "wheelbarrow_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/Settings.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Settings-44.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "Settings-66.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /MonoBank/Token.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Token.swift 3 | // MonoBank 4 | // 5 | // Created by Richard Neitzke on 03/01/2017. 6 | // Copyright © 2017 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Token with its asset name 12 | 13 | enum Token: String { 14 | case cannon = "cannon" 15 | case car = "car" 16 | case cat = "cat" 17 | case dog = "dog" 18 | case hat = "hat" 19 | case horse = "horse" 20 | case iron = "iron" 21 | case moneyBag = "money" 22 | case ship = "ship" 23 | case shoe = "shoe" 24 | case thimble = "thimble" 25 | case wheelbarrow = "wheelbarrow" 26 | } 27 | -------------------------------------------------------------------------------- /BoardBankUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /MonoBank/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // MonoBank 4 | // 5 | // Created by Richard Neitzke on 31/12/2016. 6 | // Copyright © 2016 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 17 | 18 | return true 19 | } 20 | 21 | func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { 22 | if UIDevice.current.userInterfaceIdiom == .pad { 23 | return .all 24 | } else { 25 | return .portrait 26 | } 27 | } 28 | 29 | } 30 | 31 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | 5 | Make sure you have the latest version of the Xcode command line tools installed: 6 | 7 | ``` 8 | xcode-select --install 9 | ``` 10 | 11 | Install _fastlane_ using 12 | ``` 13 | [sudo] gem install fastlane -NV 14 | ``` 15 | or alternatively using `brew cask install fastlane` 16 | 17 | # Available Actions 18 | ## iOS 19 | ### ios simulator_test 20 | ``` 21 | fastlane ios simulator_test 22 | ``` 23 | 24 | ### ios device_test 25 | ``` 26 | fastlane ios device_test 27 | ``` 28 | 29 | 30 | ---- 31 | 32 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. 33 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). 34 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## XCUITest Framework Tutorial 2 | 3 | --- 4 | 5 | This repository provides complete example of setting up XCUITest Framework using Screen Objects Pattern for iOS Automation Testing. Original Tutorial is available in my blog: [Getting Started with Xcode UI Testing](https://medium.com/@akifev/mobile-automation-testing-getting-started-with-xcode-ui-testing-5051cf61b558), [Running iOS tests in parallel](https://medium.com/@akifev/mobile-automation-testing-running-ios-tests-in-parallel-d68bcb574fc4). 6 | 7 | --- 8 | 9 | ## Overview 10 | 11 | ### Screen Objects 12 | 13 | * AddPlayerScreen.swift 14 | * HomeScreen.swift 15 | * SettingsScreen.swift 16 | 17 | ### Test Case class 18 | 19 | * AddPlayerTests.swift 20 | 21 | ### How to tap Button 22 | 23 | ``` settingsButton.tap() ``` 24 | 25 | 26 | ### Verify that Element is exist on the screen 27 | 28 | ```XCTAssertTrue(homeScreen.isPlayerDisplayed(name: testPlayerName1, balance: defaultPlayerBalance, currency: defaultCurrency))``` 29 | 30 | 31 | ### How to type text 32 | 33 | ```currencyTextField.clearAndEnterText(text: currency) ``` 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Richard Neitzke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Richard Neitzke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MonoBank/Player.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Player.swift 3 | // MonoBank 4 | // 5 | // Created by Richard Neitzke on 03/01/2017. 6 | // Copyright © 2017 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a player of the current game 12 | 13 | class Player: NSObject, NSCoding { 14 | 15 | var name: String 16 | var balance: Int 17 | var token: Token 18 | 19 | init(name: String, balance: Int, token: Token) { 20 | self.name = name 21 | self.balance = balance 22 | self.token = token 23 | } 24 | 25 | // Methods to conform to NSCoding 26 | 27 | required convenience init?(coder aDecoder: NSCoder) { 28 | guard let name = aDecoder.decodeObject(forKey: "name") as? String, 29 | let tokenRawValue = aDecoder.decodeObject(forKey: "token") as? String 30 | else { return nil } 31 | 32 | self.init(name: name, balance: aDecoder.decodeInteger(forKey: "balance"), token: Token(rawValue: tokenRawValue)!) 33 | } 34 | 35 | func encode(with aCoder: NSCoder) { 36 | aCoder.encode(name, forKey: "name") 37 | aCoder.encode(balance, forKey: "balance") 38 | aCoder.encode(token.rawValue, forKey: "token") 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |

5 | 6 |

BoardBank

7 |

Money Manager for Board Games

8 | 9 |
10 | 11 | 12 | ### Screenshots 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | ### Description 32 | 33 | BoardBank lets you focus on the fun sides of MONOPOLY® by handling all the money transactions for you. This app keeps track of the accounts of up to 6 players so you can enjoy the game without having to worry about counting money anymore. 34 | 35 | * Add players by tapping the "+" button 36 | * Transfer money by simply dragging between the players or the bank 37 | * Quickly add $200 when a player passes "GO" by tapping on him 38 | * Customize the currency, the quick add amount and the default balance in the settings 39 | 40 | 41 | ### Used Resources 42 | 43 | Some icons are licensed under CC BY-ND 3.0 and provided by [icons8](http://icons8.com). 44 | -------------------------------------------------------------------------------- /MonoBank/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | BoardBank 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.75 21 | CFBundleVersion 22 | 642 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | UIInterfaceOrientationPortraitUpsideDown 39 | 40 | UISupportedInterfaceOrientations~ipad 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationPortraitUpsideDown 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ##### 2 | # OS X temporary files that should never be committed 3 | .DS_Store 4 | *.swp 5 | *.lock 6 | profile 7 | 8 | ##### 9 | # DotEnv files 10 | .env 11 | 12 | #### 13 | # Xcode temporary files that should never be committed 14 | *~.nib 15 | 16 | #### 17 | # Objective-C/Swift specific 18 | *.hmap 19 | *.ipa 20 | 21 | #### 22 | # Xcode build files 23 | DerivedData/ 24 | build/ 25 | Builds/ 26 | 27 | ##### 28 | # Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) 29 | *.pbxuser 30 | *.mode1v3 31 | *.mode2v3 32 | *.perspectivev3 33 | !default.pbxuser 34 | !default.mode1v3 35 | !default.mode2v3 36 | !default.perspectivev3 37 | 38 | #### 39 | # Xcode 4 40 | xcuserdata 41 | !xcschemes 42 | # Xcode 4 43 | *.moved-aside 44 | 45 | #### 46 | # XCode 4 workspaces - more detailed 47 | !xcshareddata 48 | !default.xcworkspace 49 | *.xcworkspacedata 50 | 51 | 52 | #### 53 | # Xcode 5 54 | *.xccheckout 55 | *.xcuserstate 56 | 57 | #### 58 | # Xcode 7 59 | *.xcscmblueprint 60 | 61 | #### 62 | # AppCode 63 | .idea/ 64 | 65 | #### 66 | # Other Xcode files 67 | profile 68 | *.hmap 69 | *.ipa 70 | 71 | #### 72 | # CocoaPods 73 | Pods/ 74 | !Podfile 75 | !Podfile.lock 76 | 77 | #### 78 | # Carthage 79 | Carthage/Build.rbenv-vars 80 | !Cartfile 81 | !Cartfile.private 82 | !Cartfile.resolved 83 | 84 | #### 85 | # Fastlane 86 | # Temporary profiling data 87 | /fastlane/report.xml 88 | # Deliver temporary error output 89 | /fastlane/Error*.png 90 | # Deliver temporary preview output 91 | /fastlane/Preview.html 92 | # Snapshot generated screenshots 93 | /fastlane/screenshots/*/*-portrait.png 94 | /fastlane/screenshots/*/*-landscape.png 95 | /fastlane/screenshots/screenshots.html 96 | # Frameit generated screenshots 97 | /fastlane/screenshots/*/*-portrait_framed.png 98 | /fastlane/screenshots/*/*-landscape_framed.png 99 | 100 | #### 101 | # rbenv 102 | .rbenv-vars 103 | -------------------------------------------------------------------------------- /BoardBankUITests/UITestWithRecorder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITestWithRecorder.swift 3 | // BoardBankUITests 4 | // 5 | // Created by Maksim Akifev on 5/5/18. 6 | // Copyright © 2018 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class UITestWithRecorder: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | 33 | let app = XCUIApplication() 34 | app.navigationBars["BoardBank"].buttons["Add"].tap() 35 | 36 | let tablesQuery = app.tables 37 | tablesQuery/*@START_MENU_TOKEN@*/.textFields["Player"]/*[[".cells.textFields[\"Player\"]",".textFields[\"Player\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap() 38 | tablesQuery/*@START_MENU_TOKEN@*/.staticTexts["Add Player"]/*[[".cells.staticTexts[\"Add Player\"]",".staticTexts[\"Add Player\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap() 39 | 40 | // Element expected to be displayed 41 | let player = app.collectionViews.staticTexts["Test"] 42 | 43 | // Assert that element is displayed 44 | XCTAssertTrue(player.exists) 45 | 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # This file contains the fastlane.tools configuration 2 | # You can find the documentation at https://docs.fastlane.tools 3 | # 4 | # For a list of all available actions, check out 5 | # 6 | # https://docs.fastlane.tools/actions 7 | # 8 | 9 | # Uncomment the line if you want fastlane to automatically update itself 10 | # update_fastlane 11 | 12 | default_platform(:ios) 13 | 14 | platform :ios do 15 | 16 | #################################### Constants #################################### 17 | 18 | TEST_SIMULATORS = ['iPhone 8','iPhone SE','iPhone X'] 19 | TEST_DEVICES = [ 20 | 'platform=iOS,id=9a31ef5bf7216fa79ec31af3f29901c209175937', 21 | 'platform=iOS,id=8a31ef5bf2216fa49ec31af3f29901c209075937' 22 | ] 23 | 24 | ################################## Helper Methods ################################## 25 | 26 | desc "Generate test reports" 27 | def generate_report 28 | puts "Generating Test Report ..." 29 | sh 'xchtmlreport -r test_output/BoardBank.test_result' 30 | puts "Test Report Succesfully generated" 31 | end 32 | 33 | ################################### Private Lanes ################################### 34 | 35 | desc "Execute tests on simulators" 36 | private_lane :parrallel_simulator_test do 37 | scan( 38 | scheme: 'BoardBank', # Project scheme name 39 | clean: true, # clean project folder before test execution 40 | devices: TEST_SIMULATORS, 41 | result_bundle: "TestResults" 42 | ) 43 | generate_report 44 | end 45 | 46 | desc "Execute tests on phisical devices" 47 | private_lane :parrallel_device_test do 48 | scan( 49 | scheme: 'BoardBank', # Project scheme name 50 | clean: true, # Clean project folder before test execution 51 | destination: TEST_DEVICES, # Devices for testing 52 | result_bundle: "TestResults" # To generate advanced test reports 53 | ) 54 | generate_report 55 | end 56 | 57 | ################################### Public Lanes ################################### 58 | 59 | desc "Execute tests on simulators with error handling" 60 | lane :simulator_test do 61 | begin 62 | parrallel_simulator_test 63 | rescue 64 | generate_report 65 | end 66 | end 67 | 68 | desc "Execute tests on phisical devices with error handling" 69 | lane :device_test do 70 | begin 71 | parrallel_device_test 72 | rescue 73 | generate_report 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /MonoBank/BankManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BankManager.swift 3 | // MonoBank 4 | // 5 | // Created by Richard Neitzke on 03/01/2017. 6 | // Copyright © 2017 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Manages data 12 | 13 | class BankManager { 14 | 15 | static let shared = BankManager() 16 | 17 | /// Formats balances with the current currency symbol 18 | let numberFormatter = NumberFormatter() 19 | 20 | /// Currency symbol used by the app 21 | var currencySymbol = "$" { 22 | didSet { 23 | numberFormatter.currencySymbol = currencySymbol 24 | } 25 | } 26 | 27 | /// Default balance when adding a player 28 | var defaultBalance = 1500 29 | 30 | /// Amount which the user can quickly add in the player menu 31 | var quickAddAmount = 200 32 | 33 | /// All players of the current game 34 | var players = [Player]() 35 | 36 | var soundsEnabled = true 37 | 38 | init() { 39 | // Fetch previously set values from UserDefaults 40 | if let currencySymbol = UserDefaults.standard.string(forKey: "currencySymbol") { 41 | self.currencySymbol = currencySymbol 42 | } 43 | if let defaultBalance = UserDefaults.standard.value(forKey: "defaultBalance") as? Int { 44 | self.defaultBalance = defaultBalance 45 | } 46 | if let quickAddAmount = UserDefaults.standard.value(forKey: "quickAddAmount") as? Int { 47 | self.quickAddAmount = quickAddAmount 48 | } 49 | if let playersData = UserDefaults.standard.object(forKey: "players") as? Data { 50 | players = NSKeyedUnarchiver.unarchiveObject(with: playersData) as! [Player] 51 | } 52 | if let soundsEnabled = UserDefaults.standard.object(forKey: "soundsEnabled") as? Bool { 53 | self.soundsEnabled = soundsEnabled 54 | } 55 | 56 | // Configure numberFormatter 57 | numberFormatter.numberStyle = .currency 58 | numberFormatter.locale = Locale(identifier: "es_CL") 59 | numberFormatter.currencySymbol = currencySymbol 60 | } 61 | 62 | /// Saves the current state of the BankManager 63 | func save() { 64 | UserDefaults.standard.set(currencySymbol, forKey: "currencySymbol") 65 | UserDefaults.standard.set(defaultBalance, forKey: "defaultBalance") 66 | UserDefaults.standard.set(quickAddAmount, forKey: "quickAddAmount") 67 | let playersData = NSKeyedArchiver.archivedData(withRootObject: players) 68 | UserDefaults.standard.set(playersData, forKey: "players") 69 | UserDefaults.standard.set(soundsEnabled, forKey: "soundsEnabled") 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /BoardBankUITests/screens/AddPlayerScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddPlayerScreen.swift 3 | // BoardBankUITests 4 | // 5 | // Created by Maksim Akifev on 4/23/18. 6 | // Copyright © 2018 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | extension XCUIElement { 12 | 13 | func clearAndEnterText(text: String) { 14 | /** 15 | Removes any current text in the field before typing in the new value 16 | - Parameter text: the text to enter into the field 17 | */ 18 | guard let stringValue = self.value as? String else { 19 | XCTFail("Tried to clear and enter text into a non string value") 20 | return 21 | } 22 | self.tap() 23 | let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: stringValue.count) 24 | self.typeText(deleteString) 25 | self.typeText(text) 26 | } 27 | } 28 | 29 | class AddPlayerScreen: XCTest { 30 | 31 | // MARK: AddPlayerScreen Locators 32 | 33 | let playerTextFieldLocator = XCUIApplication().textFields["Player"] 34 | let balanceTextFieldLocator = XCUIApplication().textFields["1500"] 35 | let addPlayerButtonLocator = XCUIApplication().staticTexts["Add Player"] 36 | 37 | // MARK: AddPlayerScreen Methods 38 | 39 | func setPlayerNameWith(_ name: String) { 40 | /** 41 | Set Player Name Text Field with new value 42 | - Parameter name: the text to enter into the field 43 | */ 44 | playerTextFieldLocator.tap() 45 | playerTextFieldLocator.typeText(name) 46 | } 47 | 48 | func setPlayerBalanceWith(_ balance: String) { 49 | /** 50 | Set Player Balance Text Field with new value 51 | - Parameter balance: the text to enter into the field 52 | */ 53 | balanceTextFieldLocator.tap() 54 | balanceTextFieldLocator.clearAndEnterText(text: balance) 55 | } 56 | 57 | func setPlayerImageWith(_ image: String) { 58 | /** 59 | Set Player Image with new value 60 | - Parameter image: image identifier for new value 61 | */ 62 | let imageSelectorLocator = XCUIApplication().tables/*@START_MENU_TOKEN@*/.collectionViews/*[[".cells.collectionViews",".collectionViews"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.cells.otherElements.containing(.image, identifier:image).element 63 | imageSelectorLocator.tap() 64 | } 65 | 66 | func addPlayer() { 67 | /** 68 | Press Add button 69 | */ 70 | addPlayerButtonLocator.tap() 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /BoardBankUITests/screens/SettingsScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsScreen.swift 3 | // BoardBankUITests 4 | // 5 | // Created by Maksim Akifev on 4/23/18. 6 | // Copyright © 2018 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class SettingsScreen: XCTest { 12 | 13 | // MARK: Setings Screen Locators 14 | 15 | static let cells = XCUIApplication().tables.cells 16 | 17 | let newGameButton = XCUIApplication().tables.staticTexts["New Game"] 18 | let areYouSureAlert = XCUIApplication().alerts["Are you sure?"].buttons["New Game"] 19 | let saveSettingsButton = XCUIApplication().buttons["Save Settings"] 20 | let cancelButton = XCUIApplication().navigationBars["Settings"].buttons["Cancel"] 21 | let currencyTextField = cells.containing(.staticText, identifier:"CURRENCY").children(matching: .textField).element 22 | let defaultBalanceTextField = cells.containing(.staticText, identifier:"DEFAULT BALANCE").children(matching: .textField).element 23 | let quickAmountTextField = cells.containing(.staticText, identifier:"QUICK ADD AMOUNT").children(matching: .textField).element 24 | let enableSoundSwitch = XCUIApplication().buttons["enableSoundSwitch"] 25 | 26 | // MARK: Settings Screen Methods 27 | 28 | func newGame() { 29 | /** 30 | Press New Game button 31 | */ 32 | newGameButton.tap() 33 | areYouSureAlert.tap() 34 | } 35 | 36 | func setCurrencyWith(currency: String) { 37 | /** 38 | Set CURRENCY Text Field with new value 39 | - Parameter currency: the text to enter into the field 40 | */ 41 | currencyTextField.tap() 42 | currencyTextField.clearAndEnterText(text: currency) 43 | } 44 | 45 | func setDefaultBalanceWith(balance: String) { 46 | /** 47 | Set DEFAULT BALANCE Text Field with new value 48 | - Parameter balance: the text to enter into the field 49 | */ 50 | defaultBalanceTextField.tap() 51 | defaultBalanceTextField.clearAndEnterText(text: balance) 52 | } 53 | 54 | func setQuickAddAmountWith(amount: String) { 55 | /** 56 | Set QUICK ADD AMOUNT Text Field with new value 57 | - Parameter amount: the text to enter into the field 58 | */ 59 | quickAmountTextField.tap() 60 | quickAmountTextField.clearAndEnterText(text:amount) 61 | } 62 | 63 | func saveSettings() { 64 | /** 65 | Press Save Setings button 66 | */ 67 | saveSettingsButton.tap() 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /MonoBank/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "40.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "60.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "58.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "87.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "80.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "120.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "120-1.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "180.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "20.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "40-1.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "29.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "58-1.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "40-2.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "80-1.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "76.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "152.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "167.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "idiom" : "ios-marketing", 107 | "size" : "1024x1024", 108 | "scale" : "1x" 109 | } 110 | ], 111 | "info" : { 112 | "version" : 1, 113 | "author" : "xcode" 114 | } 115 | } -------------------------------------------------------------------------------- /BoardBankUITests/screens/HomeScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeScreen.swift 3 | // BoardBankUITests 4 | // 5 | // Created by Maksim Akifev on 4/23/18. 6 | // Copyright © 2018 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class HomeScreen: XCTest { 12 | 13 | // MARK: Screen Locators 14 | 15 | static let app = XCUIApplication() 16 | static let boardbankNavigationBar = app.navigationBars["BoardBank"] 17 | 18 | let collectionViews = app.collectionViews.cells.otherElements 19 | let deleteButton = app.buttons["Delete"] 20 | let add200Button = app.buttons["Add $200"] 21 | let renameButton = app.buttons["Rename"] 22 | let renamePlayerAlertOkButton = app.alerts["Rename Player"].buttons["OK"] 23 | let renamePlayerAlertTextField = app.alerts["Rename Player"].textFields[""] 24 | let settingsButton = boardbankNavigationBar.buttons["Settings"] 25 | let addPlayerButton = boardbankNavigationBar.buttons["Add"] 26 | 27 | // MARK: Screen Methods 28 | 29 | func isPlayerDisplayed(name: String, balance: String, currency: String) -> Bool { 30 | /** 31 | Check if player is dispaleyd on the screen 32 | - Parameter name: the text identifier for player name 33 | - Parameter balance: the text identifier for player balance 34 | - Parameter currency: the text identifier for player currency 35 | */ 36 | let player = collectionViews.containing(.staticText, identifier:name).staticTexts["\(currency)\(balance)"] 37 | return (player.exists) 38 | 39 | } 40 | 41 | func deletePlayerWith(name: String, balance: String, currency: String) { 42 | /** 43 | Delete the player 44 | - Parameter name: the text identifier for player name 45 | - Parameter balance: the text identifier for player balance 46 | - Parameter currency: the text identifier for player currency 47 | */ 48 | let player = collectionViews.containing(.staticText, identifier:name).staticTexts["\(currency)\(balance)"] 49 | player.tap() 50 | deleteButton.tap() 51 | } 52 | 53 | func addPlayerBalanceWith(name: String, balance: String, currency: String) { 54 | /** 55 | Add Player balance with default quick add amount 56 | - Parameter name: the text identifier for player name 57 | - Parameter balance: the text identifier for player balance 58 | - Parameter currency: the text identifier for player currency 59 | */ 60 | let player = collectionViews.containing(.staticText, identifier:name).staticTexts["\(currency)\(balance)"] 61 | player.tap() 62 | add200Button.tap() 63 | } 64 | 65 | func renamePlayerNameWith(name: String, balance: String, currency: String, newName: String) { 66 | /** 67 | Rename Player name 68 | - Parameter name: the text identifier for player name 69 | - Parameter balance: the text identifier for player balance 70 | - Parameter currency: the text identifier for player currency 71 | - Parameter newName: the text to enter into the field 72 | */ 73 | let player = collectionViews.containing(.staticText, identifier:name).staticTexts["\(currency)\(balance)"] 74 | player.tap() 75 | renameButton.tap() 76 | renamePlayerAlertTextField.clearAndEnterText(text: newName) 77 | renamePlayerAlertOkButton.tap() 78 | } 79 | 80 | func addPlayer() { 81 | /** 82 | Press Add button 83 | */ 84 | addPlayerButton.tap() 85 | } 86 | 87 | func settings() { 88 | /** 89 | Press Settings button 90 | */ 91 | settingsButton.tap() 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /MonoBank/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /MonoBank/SettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.swift 3 | // MonoBank 4 | // 5 | // Created by Richard Neitzke on 04/01/2017. 6 | // Copyright © 2017 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SafariServices 11 | 12 | class SettingsViewController: UITableViewController, UITextFieldDelegate { 13 | 14 | @IBOutlet var currencyTextField: UITextField! 15 | @IBOutlet var defaultBalanceTextField: UITextField! 16 | @IBOutlet var quickAddTextField: UITextField! 17 | @IBOutlet var soundsEnabledSwitch: UISwitch! 18 | 19 | @IBOutlet var defaultBalanceCurrencyLabel: UILabel! 20 | @IBOutlet var quickAddCurrencyLabel: UILabel! 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | soundsEnabledSwitch.isOn = BankManager.shared.soundsEnabled 25 | // Printing Debug Description for the element 26 | // print(soundsEnabledSwitch.debugDescription) 27 | refreshText() 28 | } 29 | 30 | func refreshText() { 31 | currencyTextField.text = BankManager.shared.currencySymbol 32 | currencyTextField.placeholder = BankManager.shared.currencySymbol 33 | defaultBalanceCurrencyLabel.text = BankManager.shared.currencySymbol 34 | quickAddCurrencyLabel.text = BankManager.shared.currencySymbol 35 | 36 | defaultBalanceTextField.text = String(BankManager.shared.defaultBalance) 37 | defaultBalanceTextField.placeholder = String(BankManager.shared.defaultBalance) 38 | quickAddTextField.text = String(BankManager.shared.quickAddAmount) 39 | quickAddTextField.placeholder = String(BankManager.shared.quickAddAmount) 40 | } 41 | 42 | @IBAction func currencyTextFieldChanged(_ sender: UITextField) { 43 | let currencySymbol = sender.text! 44 | defaultBalanceCurrencyLabel.text = currencySymbol 45 | quickAddCurrencyLabel.text = currencySymbol 46 | } 47 | 48 | @IBAction func currencyTextFieldAction(_ sender: Any) { 49 | defaultBalanceTextField.becomeFirstResponder() 50 | } 51 | 52 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 53 | if indexPath.section == 1 { 54 | if indexPath.row == 0 { 55 | // Save Settings 56 | if !currencyTextField.text!.isEmpty { 57 | BankManager.shared.currencySymbol = currencyTextField.text! 58 | } 59 | if let defaultBalance = Int(defaultBalanceTextField.text!) { 60 | BankManager.shared.defaultBalance = defaultBalance 61 | } 62 | if let quickAddAmount = Int(quickAddTextField.text!) { 63 | BankManager.shared.quickAddAmount = quickAddAmount 64 | } 65 | if BankManager.shared.soundsEnabled { 66 | soundsEnabledSwitch.setOn(true, animated: true) 67 | } 68 | BankManager.shared.save() 69 | refreshText() 70 | let mainViewController = navigationController!.viewControllers.first as! MainViewController 71 | mainViewController.playerCollectionView.reloadData() 72 | navigationController?.popViewController(animated: true) 73 | } else if indexPath.row == 1 { 74 | // New Game 75 | let warningAlertController = UIAlertController(title: "Are you sure?", message: "By starting a new game you will lose all of your current game data.", preferredStyle: .alert) 76 | let resetAction = UIAlertAction(title: "New Game", style: .destructive, handler: { action in 77 | BankManager.shared.players = [Player]() 78 | BankManager.shared.save() 79 | let mainViewController = self.navigationController?.viewControllers.first as! MainViewController 80 | mainViewController.playerCollectionView.reloadData() 81 | mainViewController.playerNumberChanged() 82 | self.navigationController?.popViewController(animated: true) 83 | }) 84 | warningAlertController.addAction(resetAction) 85 | let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) 86 | warningAlertController.addAction(cancelAction) 87 | present(warningAlertController, animated: true) 88 | } 89 | } 90 | 91 | if indexPath.section == 2 { 92 | if indexPath.row == 1 { 93 | // Contact 94 | let mailURL = URL(string: "mailto:richardneitzke.rn+MonoBank@gmail.com")! 95 | UIApplication.shared.openURL(mailURL) 96 | } else if indexPath.row == 2 { 97 | // Icons8 98 | let safariVC = SFSafariViewController(url: URL(string: "https://icons8.com")!) 99 | present(safariVC, animated: true, completion: nil) 100 | } 101 | } 102 | 103 | tableView.deselectRow(at: indexPath, animated: true) 104 | } 105 | 106 | 107 | 108 | } 109 | -------------------------------------------------------------------------------- /MonoBank/AddPlayerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddPlayerViewController.swift 3 | // MonoBank 4 | // 5 | // Created by Richard Neitzke on 04/01/2017. 6 | // Copyright © 2017 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AddPlayerViewController: UITableViewController, UITextFieldDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { 12 | 13 | @IBOutlet var nameTextField: UITextField! 14 | @IBOutlet var balanceTextField: UITextField! 15 | @IBOutlet var currencySymbolLabel: UILabel! 16 | @IBOutlet var tokenCollectionView: UICollectionView! 17 | 18 | let tokens = ["cannon", "car", "cat", "dog", "hat", "horse", "iron", "money", "ship", "shoe", "thimble", "wheelbarrow"] 19 | var selectedToken = 0 20 | 21 | override func viewDidLoad() { 22 | currencySymbolLabel.text = BankManager.shared.currencySymbol 23 | balanceTextField.text = String(BankManager.shared.defaultBalance) 24 | balanceTextField.placeholder = String(BankManager.shared.defaultBalance) 25 | } 26 | 27 | override func viewDidAppear(_ animated: Bool) { 28 | nameTextField.becomeFirstResponder() 29 | } 30 | 31 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 32 | if textField.restorationIdentifier == "nameTextField" { 33 | balanceTextField.becomeFirstResponder() 34 | } 35 | return false 36 | } 37 | 38 | // TableView 39 | 40 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 41 | if indexPath.section == 2 && indexPath.row == 0 { 42 | let strippedBalance = balanceTextField.text!.components(separatedBy: CharacterSet.decimalDigits.inverted).joined() 43 | let name = nameTextField.text!.isEmpty ? "Player" : nameTextField.text! 44 | let balance = Int(strippedBalance) == nil ? BankManager.shared.defaultBalance : Int(strippedBalance)! 45 | let player = Player(name: name, balance: balance, token: Token(rawValue: tokens[selectedToken])!) 46 | BankManager.shared.players.append(player) 47 | BankManager.shared.save() 48 | let mainViewController = navigationController?.viewControllers.first as! MainViewController 49 | mainViewController.playerCollectionView.reloadData() 50 | mainViewController.playerNumberChanged() 51 | navigationController?.popViewController(animated: true) 52 | } 53 | 54 | tableView.deselectRow(at: indexPath, animated: true) 55 | } 56 | 57 | override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 58 | if indexPath.section == 1 && indexPath.row == 0 { 59 | // iPhone has two rows, iPad has one row 60 | let rowsHeight = UIDevice.current.userInterfaceIdiom == .phone ? tokenCellSize.height*2: tokenCellSize.height 61 | return rowsHeight + 30 62 | } else { 63 | return 44 64 | } 65 | } 66 | 67 | override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 68 | if section == 0 { 69 | return 25 70 | } else { 71 | return 5 72 | } 73 | } 74 | 75 | // Token CollectionView 76 | 77 | func numberOfSections(in collectionView: UICollectionView) -> Int { 78 | return 1 79 | } 80 | 81 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 82 | return 12 83 | } 84 | 85 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 86 | let tokenCell = collectionView.dequeueReusableCell(withReuseIdentifier: "tokenCell", for: indexPath) as! TokenCollectionViewCell 87 | let isSelected = indexPath.item == selectedToken 88 | let tokenName = isSelected ? tokens[indexPath.item] + "_filled" : tokens[indexPath.item] 89 | tokenCell.tokenView.image = UIImage(named: tokenName)!.withRenderingMode(.alwaysTemplate) 90 | let affineTransform = isSelected ? CGAffineTransform(scaleX: 1.1, y: 1.1) : CGAffineTransform.identity 91 | UIView.animate(withDuration: 0.1, animations: { 92 | tokenCell.transform = affineTransform 93 | }) 94 | return tokenCell 95 | } 96 | 97 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 98 | selectedToken = indexPath.item 99 | collectionView.reloadData() 100 | } 101 | 102 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 103 | return tokenCellSize 104 | } 105 | 106 | var tokenCellSize: CGSize { 107 | // iPhone has two rows, iPad has one row 108 | let amountOfCellsInRow: CGFloat = UIDevice.current.userInterfaceIdiom == .phone ? 6 : 12 109 | let horizontalSpace = 20 + (10 * amountOfCellsInRow - 1) 110 | let width = (tableView.bounds.width-horizontalSpace)/amountOfCellsInRow 111 | return CGSize(width: width, height: width) 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /BoardBankUITests/AddPlayerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddPlayerTests.swift 3 | // BoardBankUITests 4 | // 5 | // Created by Maksim Akifev on 4/23/18. 6 | // Copyright © 2018 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class AddPlayerTests: XCTestCase { 12 | 13 | // MARK: Test Data 14 | 15 | let testPlayerName1 = "testPlayerName1" 16 | let testPlayerName2 = "testPlayerName2" 17 | let testCurrency = "@" 18 | let testPlayerBalance1 = "99.999" 19 | let testPlayerBalance2 = "77.777" 20 | let defaultPlayerBalance = "1.500" 21 | let defaultPlayerBalancePlusDefaultAddBalance = "1.700" 22 | let defaultCurrency = "$" 23 | 24 | // MARK: Screen Objects initializers 25 | 26 | let settingsScreen = SettingsScreen() 27 | let addPlayerScreen = AddPlayerScreen() 28 | let homeScreen = HomeScreen() 29 | 30 | // MARK: Test support methods 31 | 32 | override func setUp() { 33 | super.setUp() 34 | 35 | // Put setup code here. This method is called before the invocation of each test method in the class. 36 | 37 | // In UI tests it is usually best to stop immediately when a failure occurs. 38 | continueAfterFailure = false 39 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 40 | XCUIApplication().launch() 41 | 42 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 43 | 44 | // Starting testing from clear state 45 | homeScreen.settings() 46 | settingsScreen.newGame() 47 | } 48 | 49 | override func tearDown() { 50 | // Put teardown code here. This method is called after the invocation of each test method in the class. 51 | super.tearDown() 52 | } 53 | 54 | // MARK: Tests 55 | 56 | func testAddPlayerWithDefaultBalance() { 57 | /** 58 | Add new Player with default balance and assert that added player is displayed on the Home Screen 59 | */ 60 | homeScreen.addPlayer() 61 | addPlayerScreen.setPlayerNameWith(testPlayerName1) 62 | addPlayerScreen.addPlayer() 63 | 64 | XCTAssertTrue(homeScreen.isPlayerDisplayed(name: testPlayerName1, balance: defaultPlayerBalance, currency: defaultCurrency)) 65 | } 66 | 67 | func testAddPlayerWithDefaultBalanceThenDelete() { 68 | /** 69 | Add new Player with default balance and assert that added player is displayed on the Home Screen 70 | Then delete the added player and assert that deleted player is not displayed on the Home Screen 71 | */ 72 | self.testAddPlayerWithDefaultBalance() 73 | homeScreen.deletePlayerWith(name: testPlayerName1, balance: defaultPlayerBalance, currency: defaultCurrency) 74 | 75 | XCTAssertFalse(homeScreen.isPlayerDisplayed(name: testPlayerName1, balance: defaultPlayerBalance, currency: defaultCurrency)) 76 | } 77 | 78 | func testAddTwoPlayerWithCustomBalance() { 79 | /** 80 | Add two new Players with custom balance and assert that added players are displayed on the Home Screen 81 | */ 82 | homeScreen.addPlayer() 83 | addPlayerScreen.setPlayerNameWith(testPlayerName1) 84 | addPlayerScreen.setPlayerBalanceWith(testPlayerBalance1) 85 | addPlayerScreen.addPlayer() 86 | 87 | homeScreen.addPlayer() 88 | addPlayerScreen.setPlayerNameWith(testPlayerName2) 89 | addPlayerScreen.setPlayerBalanceWith(testPlayerBalance2) 90 | addPlayerScreen.addPlayer() 91 | 92 | XCTAssertTrue(homeScreen.isPlayerDisplayed(name: testPlayerName1, balance: testPlayerBalance1, currency: defaultCurrency)) 93 | XCTAssertTrue(homeScreen.isPlayerDisplayed(name: testPlayerName2, balance: testPlayerBalance2, currency: defaultCurrency)) 94 | } 95 | 96 | func testAddPlayerWithCustomBalanceThenRename() { 97 | /** 98 | Add new Player with custom balance and rename then assert that modified player is displayed on the Home Screen 99 | */ 100 | homeScreen.addPlayer() 101 | addPlayerScreen.setPlayerNameWith(testPlayerName1) 102 | addPlayerScreen.setPlayerBalanceWith(testPlayerBalance1) 103 | addPlayerScreen.addPlayer() 104 | homeScreen.renamePlayerNameWith(name: testPlayerName1, balance: testPlayerBalance1, currency: defaultCurrency, newName: testPlayerName2) 105 | 106 | XCTAssertTrue(homeScreen.isPlayerDisplayed(name: testPlayerName2, balance: testPlayerBalance1, currency: defaultCurrency)) 107 | } 108 | 109 | func testAddPlayerWithDefaultBalanceThenAddQuickAddAmount() { 110 | /** 111 | Add new Player with default balance and add quick add amountthen assert that that modified player is displayed on the Home Screen 112 | */ 113 | homeScreen.addPlayer() 114 | addPlayerScreen.setPlayerNameWith(testPlayerName1) 115 | addPlayerScreen.setPlayerBalanceWith(defaultPlayerBalance) 116 | addPlayerScreen.addPlayer() 117 | homeScreen.addPlayerBalanceWith(name: testPlayerName1, balance: defaultPlayerBalance, currency: defaultCurrency) 118 | 119 | XCTAssertTrue(homeScreen.isPlayerDisplayed(name: testPlayerName1, balance: defaultPlayerBalancePlusDefaultAddBalance, currency: defaultCurrency)) 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /MonoBank/MainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.swift 3 | // MonoBank 4 | // 5 | // Created by Richard Neitzke on 03/01/2017. 6 | // Copyright © 2017 Richard Neitzke. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AVFoundation 11 | 12 | class MainViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { 13 | 14 | @IBOutlet var playerCollectionView: UICollectionView! 15 | @IBOutlet var addPlayerButton: UIBarButtonItem! 16 | @IBOutlet var infoLabel: UILabel! 17 | 18 | /// Maximum amount of players 19 | let maxPlayers = 6 20 | /// Number of player cells per row 21 | var numberOfPlayersPerRow: CGFloat = 2 22 | 23 | // Variables for Transactions 24 | var fromPoint: CGPoint? 25 | var fromPlayer: Int? 26 | var toPlayer: Int? 27 | 28 | // Current path and layer of the transaction line 29 | var linePath = UIBezierPath() 30 | var lineLayer = CAShapeLayer() 31 | 32 | // Cell that is currently being moved 33 | var movingCell: UICollectionViewCell? 34 | 35 | // AudiPlayer for playing the cash register sound 36 | var audioPlayer: AVAudioPlayer! 37 | 38 | override func viewDidLoad() { 39 | // Configure gestureRecognizer 40 | let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(panGestureRecognizer:))) 41 | let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressGesture)) 42 | self.view.addGestureRecognizer(panGestureRecognizer) 43 | self.view.addGestureRecognizer(longPressGestureRecognizer) 44 | 45 | // Configure lineLayer 46 | lineLayer.lineWidth = 5 47 | lineLayer.strokeColor = UIColor.gray.cgColor 48 | view.layer.addSublayer(lineLayer) 49 | 50 | // Initialize numberOfPlayersPerRow 51 | numberOfPlayersPerRow = UIApplication.shared.statusBarOrientation.isPortrait ? 2 : 3 52 | 53 | // Initialize audioPlayer 54 | if let soundPath = Bundle.main.path(forResource: "CashRegister", ofType: "mp3") { 55 | let soundURL = URL(fileURLWithPath: soundPath) 56 | try! audioPlayer = AVAudioPlayer(contentsOf: soundURL) 57 | } else { 58 | audioPlayer = AVAudioPlayer() 59 | } 60 | 61 | playerNumberChanged() 62 | } 63 | 64 | override func viewWillAppear(_ animated: Bool) { 65 | // Refresh numberOfPlayersPerRow 66 | numberOfPlayersPerRow = UIApplication.shared.statusBarOrientation.isPortrait ? 2 : 3 67 | playerCollectionView.reloadData() 68 | } 69 | 70 | /// Disable/enable addPlayerButton 71 | func playerNumberChanged() { 72 | if BankManager.shared.players.count < maxPlayers { 73 | addPlayerButton.isEnabled = true 74 | } else { 75 | addPlayerButton.isEnabled = false 76 | } 77 | if BankManager.shared.players.count > 0 { 78 | infoLabel.isHidden = true 79 | } else { 80 | infoLabel.isHidden = false 81 | } 82 | } 83 | 84 | // Methods that handle transactions 85 | 86 | func handlePanGesture(panGestureRecognizer: UIPanGestureRecognizer) { 87 | switch panGestureRecognizer.state { 88 | case .began: 89 | // New line, reset fromPlayer and fromPoint 90 | fromPlayer = nil 91 | fromPoint = nil 92 | 93 | if let fromPlayer = playerForPoint(gestureRecognizer: panGestureRecognizer) { 94 | self.fromPlayer = fromPlayer 95 | fromPoint = panGestureRecognizer.location(in: view) 96 | animateCellPop(forPlayer: fromPlayer, active: true) 97 | 98 | } 99 | case .changed: 100 | // Draw line between fromPoint and current location 101 | guard let fromPoint = fromPoint else { return } 102 | linePath.removeAllPoints() 103 | linePath.move(to: fromPoint) 104 | linePath.addLine(to: panGestureRecognizer.location(in: view)) 105 | lineLayer.opacity = 1 106 | lineLayer.path = linePath.cgPath 107 | 108 | // Animate transaction pop of toPlayer 109 | guard let potentialToPlayer = playerForPoint(gestureRecognizer: panGestureRecognizer) else { 110 | animateCellPop(forPlayer: toPlayer, active: false) 111 | toPlayer = nil 112 | return 113 | } 114 | 115 | guard potentialToPlayer != fromPlayer else { 116 | animateCellPop(forPlayer: toPlayer, active: false) 117 | toPlayer = nil 118 | return 119 | } 120 | 121 | if potentialToPlayer != toPlayer { 122 | animateCellPop(forPlayer: toPlayer, active: false) 123 | animateCellPop(forPlayer: potentialToPlayer, active: true) 124 | toPlayer = potentialToPlayer 125 | } 126 | 127 | 128 | case .ended: 129 | // Animate fade away of transaction line 130 | UIView.animate(withDuration: 1, animations: { self.lineLayer.opacity = 0 }) 131 | 132 | // Animate transaction pop 133 | animateCellPop(forPlayer: fromPlayer, active: false) 134 | animateCellPop(forPlayer: toPlayer, active: false) 135 | 136 | toPlayer = nil 137 | 138 | guard let toPlayer = playerForPoint(gestureRecognizer: panGestureRecognizer), 139 | let fromPlayer = fromPlayer, fromPlayer != toPlayer 140 | else { return } 141 | 142 | let fromName = fromPlayer == -1 ? "Bank" : BankManager.shared.players[fromPlayer].name 143 | let toName = toPlayer == -1 ? "Bank" : BankManager.shared.players[toPlayer].name 144 | 145 | let transactionAlertController = UIAlertController(title: "Transfer Money", message: "from \(fromName) to \(toName)", preferredStyle: .alert) 146 | transactionAlertController.addTextField(configurationHandler: { 147 | $0.keyboardType = .numberPad 148 | $0.text = BankManager.shared.currencySymbol + " " 149 | }) 150 | let okAction = UIAlertAction(title: "OK", style: .default, handler: { action in 151 | // Transfer money from fromPlayer to toPlayer 152 | let strippedInput = transactionAlertController.textFields!.first!.text!.components(separatedBy: CharacterSet.decimalDigits.inverted).joined() 153 | if let amount = Int(strippedInput) { 154 | if fromPlayer == -1 { 155 | BankManager.shared.players[toPlayer].balance += amount 156 | } else if toPlayer == -1 { 157 | BankManager.shared.players[fromPlayer].balance -= amount 158 | } else { 159 | BankManager.shared.players[fromPlayer].balance -= amount 160 | BankManager.shared.players[toPlayer].balance += amount 161 | } 162 | } 163 | BankManager.shared.save() 164 | self.playerCollectionView.reloadData() 165 | if BankManager.shared.soundsEnabled { self.audioPlayer.play() } 166 | }) 167 | transactionAlertController.addAction(okAction) 168 | let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) 169 | transactionAlertController.addAction(cancelAction) 170 | present(transactionAlertController, animated: true) 171 | default: 172 | print("Gesture Recognizer State not handled") 173 | } 174 | } 175 | 176 | /// Returns the number of the playerCell at a given point, -1 for bank 177 | func playerForPoint(gestureRecognizer: UIGestureRecognizer) -> Int? { 178 | let item = playerCollectionView.indexPathForItem(at: gestureRecognizer.location(in: playerCollectionView))?.item 179 | guard let selectedItem = item else { return nil } 180 | return selectedItem - 1 181 | } 182 | 183 | /// Animates transaction pop for a player 184 | func animateCellPop(forPlayer player: Int?, active: Bool) { 185 | guard let player = player else { return } 186 | let cell = playerCollectionView.cellForItem(at: IndexPath(item: player+1, section: 0)) 187 | let affineTransfrom = active ? CGAffineTransform(scaleX: 1.1, y: 1.1) : CGAffineTransform.identity 188 | UIView.animate(withDuration: 0.1, animations: { 189 | cell?.transform = affineTransfrom 190 | }) 191 | } 192 | 193 | // Methods that handle moving cells 194 | 195 | func handleLongPressGesture(gestureRecognizer: UILongPressGestureRecognizer) { 196 | let movingIndexPath = playerCollectionView.indexPathForItem(at: gestureRecognizer.location(in: playerCollectionView)) 197 | switch gestureRecognizer.state { 198 | case .began: 199 | guard let indexPath = movingIndexPath else { break } 200 | guard indexPath.item != 0 else { break } 201 | movingCell = playerCollectionView.cellForItem(at: indexPath) 202 | UIView.animate(withDuration: 0.1, delay: 0.0, options: [.allowUserInteraction, .beginFromCurrentState], animations: { 203 | self.movingCell?.alpha = 0.7 204 | self.movingCell?.transform = CGAffineTransform(scaleX: 1.3, y: 1.3) 205 | }, completion: nil) 206 | playerCollectionView.beginInteractiveMovementForItem(at: indexPath) 207 | case .changed: 208 | playerCollectionView.updateInteractiveMovementTargetPosition(gestureRecognizer.location(in: playerCollectionView)) 209 | movingCell?.alpha = 0.7 210 | movingCell?.transform = CGAffineTransform(scaleX: 1.3, y: 1.3) 211 | case .ended: 212 | playerCollectionView.endInteractiveMovement() 213 | animatePuttingDownCell(cell: movingCell) 214 | default: 215 | playerCollectionView.cancelInteractiveMovement() 216 | animatePuttingDownCell(cell: movingCell) 217 | } 218 | } 219 | 220 | // Disable movement of the bank cell 221 | func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool { 222 | return indexPath.item == 0 ? false : true 223 | } 224 | 225 | func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { 226 | guard destinationIndexPath.item != 0 else { return } 227 | BankManager.shared.players.insert(BankManager.shared.players.remove(at: sourceIndexPath.item-1), at: destinationIndexPath.item-1) 228 | } 229 | 230 | // Disable movement to the bank cell 231 | func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveFromItemAt originalIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath { 232 | if proposedIndexPath.item == 0 { 233 | return IndexPath(item: 1, section: 0) 234 | } else { 235 | return proposedIndexPath 236 | } 237 | } 238 | 239 | // By littlebitesofcocoa.com/104-interactive-collection-view-re-ordering 240 | func animatePuttingDownCell(cell: UICollectionViewCell?) { 241 | UIView.animate(withDuration: 0.1, delay: 0.0, options: [.allowUserInteraction, .beginFromCurrentState], animations: { 242 | cell?.alpha = 1.0 243 | cell?.transform = CGAffineTransform.identity 244 | }, completion: nil) 245 | } 246 | 247 | 248 | 249 | // PlayerCollectionView 250 | 251 | func numberOfSections(in collectionView: UICollectionView) -> Int { 252 | return 1 253 | } 254 | 255 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 256 | return BankManager.shared.players.count + 1 // Bank + Players 257 | } 258 | 259 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 260 | if indexPath.item == 0 { 261 | // Bank Cell 262 | let bankCell = collectionView.dequeueReusableCell(withReuseIdentifier: "bankCell", for: indexPath) 263 | return bankCell 264 | } else { 265 | // Player Cell 266 | let playerCell = collectionView.dequeueReusableCell(withReuseIdentifier: "playerCell", for: indexPath) as! PlayerCollectionViewCell 267 | let player = BankManager.shared.players[indexPath.item-1] 268 | playerCell.nameLabel.text = player.name 269 | playerCell.balanceLabel.text = BankManager.shared.numberFormatter.string(from: player.balance as NSNumber)! 270 | if player.balance > 0 { 271 | playerCell.balanceLabel.textColor = UIColor.black 272 | } else { 273 | playerCell.balanceLabel.textColor = UIColor.red 274 | } 275 | playerCell.tokenView.image = UIImage(named: player.token.rawValue)?.withRenderingMode(.alwaysTemplate) 276 | return playerCell 277 | } 278 | } 279 | 280 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 281 | if indexPath.item == 0 { 282 | return bankCellSize 283 | } else { 284 | return playerCellSize 285 | } 286 | } 287 | 288 | var bankCellSize: CGSize { 289 | let flowLayout = playerCollectionView.collectionViewLayout as! UICollectionViewFlowLayout 290 | let horizintalInsets = flowLayout.sectionInset.left+flowLayout.sectionInset.right 291 | return CGSize(width: playerCollectionView.bounds.width-horizintalInsets, height: playerCollectionView.bounds.height/8) 292 | } 293 | 294 | var playerCellSize: CGSize { 295 | let flowLayout = playerCollectionView.collectionViewLayout as! UICollectionViewFlowLayout 296 | let horizintalInsets = flowLayout.sectionInset.left+flowLayout.sectionInset.right 297 | let verticalInsets = flowLayout.sectionInset.top + flowLayout.sectionInset.bottom 298 | 299 | let horizontalSpace = horizintalInsets + (flowLayout.minimumInteritemSpacing * (numberOfPlayersPerRow - 1)) 300 | let width = (playerCollectionView.bounds.width-horizontalSpace)/numberOfPlayersPerRow 301 | 302 | let verticalSpace = verticalInsets + (flowLayout.minimumLineSpacing * ceil(CGFloat(maxPlayers)/numberOfPlayersPerRow)) 303 | let height = (playerCollectionView.bounds.height-verticalSpace-(playerCollectionView.bounds.height/8))/ceil(CGFloat(maxPlayers)/numberOfPlayersPerRow) 304 | 305 | return CGSize(width: width, height: height) 306 | } 307 | 308 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 309 | if indexPath.item != 0 { 310 | let cell = collectionView.cellForItem(at: indexPath)! 311 | let player = BankManager.shared.players[indexPath.item-1] 312 | let playerAlertController = UIAlertController(title: "\(player.name): \(BankManager.shared.numberFormatter.string(from: player.balance as NSNumber)!)", message: "What do you want to do with this player?", preferredStyle: .actionSheet) 313 | // 314 | playerAlertController.popoverPresentationController?.sourceView = cell.contentView 315 | playerAlertController.popoverPresentationController?.sourceRect = cell.contentView.frame 316 | let quickAddAction = UIAlertAction(title: "Add \(BankManager.shared.numberFormatter.string(from: BankManager.shared.quickAddAmount as NSNumber)!)", style: .default, handler: { action in 317 | player.balance += BankManager.shared.quickAddAmount 318 | BankManager.shared.save() 319 | collectionView.reloadData() 320 | }) 321 | playerAlertController.addAction(quickAddAction) 322 | let renameAction = UIAlertAction(title: "Rename", style: .default, handler: { action in 323 | let renameAlertController = UIAlertController(title: "Rename Player", message: "Enter a new name for \(player.name).", preferredStyle: .alert) 324 | renameAlertController.addTextField(configurationHandler: { $0.autocapitalizationType = .words }) 325 | let okAction = UIAlertAction(title: "OK", style: .default, handler: { action in 326 | if !renameAlertController.textFields!.first!.text!.isEmpty { 327 | player.name = renameAlertController.textFields!.first!.text! 328 | BankManager.shared.save() 329 | collectionView.reloadData() 330 | } 331 | }) 332 | renameAlertController.addAction(okAction) 333 | let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) 334 | renameAlertController.addAction(cancelAction) 335 | self.present(renameAlertController, animated: true) 336 | }) 337 | playerAlertController.addAction(renameAction) 338 | let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: { action in 339 | BankManager.shared.players.remove(at: indexPath.item-1) 340 | BankManager.shared.save() 341 | collectionView.reloadData() 342 | self.playerNumberChanged() 343 | }) 344 | playerAlertController.addAction(deleteAction) 345 | let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) 346 | playerAlertController.addAction(cancelAction) 347 | present(playerAlertController, animated: true) 348 | } 349 | } 350 | 351 | // Methods required to support landscape 352 | 353 | override func willRotate(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) { 354 | numberOfPlayersPerRow = toInterfaceOrientation.isPortrait ? 2 : 3 355 | } 356 | 357 | override func viewWillLayoutSubviews() { 358 | playerCollectionView.reloadData() 359 | } 360 | 361 | 362 | } 363 | -------------------------------------------------------------------------------- /BoardBank.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 792B193C1E1C0D20003F991D /* Player.swift in Sources */ = {isa = PBXBuildFile; fileRef = 792B193B1E1C0D1F003F991D /* Player.swift */; }; 11 | 792B19401E1C17C8003F991D /* BankManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 792B193F1E1C17C8003F991D /* BankManager.swift */; }; 12 | 792B19421E1C19AB003F991D /* Token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 792B19411E1C19AB003F991D /* Token.swift */; }; 13 | 79B28EB01E1C068300CFA38D /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79B28EAF1E1C068300CFA38D /* MainViewController.swift */; }; 14 | 79B78BFA1E17E00900181778 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79B78BF91E17E00900181778 /* AppDelegate.swift */; }; 15 | 79B78BFF1E17E00900181778 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 79B78BFD1E17E00900181778 /* Main.storyboard */; }; 16 | 79B78C011E17E00900181778 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 79B78C001E17E00900181778 /* Assets.xcassets */; }; 17 | 79B78C041E17E00900181778 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 79B78C021E17E00900181778 /* LaunchScreen.storyboard */; }; 18 | 79C7D31D1E3CA24E00D4884B /* CashRegister.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 79C7D31C1E3CA24E00D4884B /* CashRegister.mp3 */; }; 19 | 79E3FFC51E1D612500EFD89F /* AddPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79E3FFC41E1D612500EFD89F /* AddPlayerViewController.swift */; }; 20 | 79E3FFC71E1D692300EFD89F /* TokenCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79E3FFC61E1D692300EFD89F /* TokenCollectionViewCell.swift */; }; 21 | 79E3FFC91E1D886A00EFD89F /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79E3FFC81E1D886A00EFD89F /* SettingsViewController.swift */; }; 22 | 79ED560B1E1C5113006247D2 /* PlayerCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79ED560A1E1C5113006247D2 /* PlayerCollectionViewCell.swift */; }; 23 | 79ED560D1E1C53EC006247D2 /* CircleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79ED560C1E1C53EC006247D2 /* CircleView.swift */; }; 24 | B51FE543208EC50D002B4F36 /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51FE542208EC50D002B4F36 /* HomeScreen.swift */; }; 25 | B51FE545208EC553002B4F36 /* AddPlayerScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51FE544208EC553002B4F36 /* AddPlayerScreen.swift */; }; 26 | B51FE547208EC56F002B4F36 /* SettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51FE546208EC56F002B4F36 /* SettingsScreen.swift */; }; 27 | B51FE549208EC5B4002B4F36 /* AddPlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51FE548208EC5B4002B4F36 /* AddPlayerTests.swift */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | B51FE53D208EC4E3002B4F36 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 79B78BEE1E17E00900181778 /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = 79B78BF51E17E00900181778; 36 | remoteInfo = BoardBank; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 792B193B1E1C0D1F003F991D /* Player.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Player.swift; sourceTree = ""; }; 42 | 792B193F1E1C17C8003F991D /* BankManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BankManager.swift; sourceTree = ""; }; 43 | 792B19411E1C19AB003F991D /* Token.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Token.swift; sourceTree = ""; }; 44 | 79B28EAF1E1C068300CFA38D /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 45 | 79B78BF61E17E00900181778 /* BoardBank.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BoardBank.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 79B78BF91E17E00900181778 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 47 | 79B78BFE1E17E00900181778 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 48 | 79B78C001E17E00900181778 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 49 | 79B78C031E17E00900181778 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 50 | 79B78C051E17E00900181778 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 79C7D31C1E3CA24E00D4884B /* CashRegister.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = CashRegister.mp3; sourceTree = ""; }; 52 | 79E3FFC41E1D612500EFD89F /* AddPlayerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddPlayerViewController.swift; sourceTree = ""; }; 53 | 79E3FFC61E1D692300EFD89F /* TokenCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenCollectionViewCell.swift; sourceTree = ""; }; 54 | 79E3FFC81E1D886A00EFD89F /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; 55 | 79ED560A1E1C5113006247D2 /* PlayerCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerCollectionViewCell.swift; sourceTree = ""; }; 56 | 79ED560C1E1C53EC006247D2 /* CircleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleView.swift; sourceTree = ""; }; 57 | B51FE538208EC4E3002B4F36 /* BoardBankUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BoardBankUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | B51FE542208EC50D002B4F36 /* HomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = ""; }; 59 | B51FE544208EC553002B4F36 /* AddPlayerScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddPlayerScreen.swift; sourceTree = ""; }; 60 | B51FE546208EC56F002B4F36 /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = ""; }; 61 | B51FE548208EC5B4002B4F36 /* AddPlayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddPlayerTests.swift; sourceTree = ""; }; 62 | /* End PBXFileReference section */ 63 | 64 | /* Begin PBXFrameworksBuildPhase section */ 65 | 79B78BF31E17E00900181778 /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | B51FE535208EC4E3002B4F36 /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | /* End PBXFrameworksBuildPhase section */ 80 | 81 | /* Begin PBXGroup section */ 82 | 79B78BED1E17E00900181778 = { 83 | isa = PBXGroup; 84 | children = ( 85 | 79B78BF81E17E00900181778 /* BoardBank */, 86 | B51FE539208EC4E3002B4F36 /* BoardBankUITests */, 87 | 79B78BF71E17E00900181778 /* Products */, 88 | ); 89 | sourceTree = ""; 90 | }; 91 | 79B78BF71E17E00900181778 /* Products */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 79B78BF61E17E00900181778 /* BoardBank.app */, 95 | B51FE538208EC4E3002B4F36 /* BoardBankUITests.xctest */, 96 | ); 97 | name = Products; 98 | sourceTree = ""; 99 | }; 100 | 79B78BF81E17E00900181778 /* BoardBank */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 79B78BF91E17E00900181778 /* AppDelegate.swift */, 104 | 79B28EAF1E1C068300CFA38D /* MainViewController.swift */, 105 | 79E3FFC41E1D612500EFD89F /* AddPlayerViewController.swift */, 106 | 79E3FFC81E1D886A00EFD89F /* SettingsViewController.swift */, 107 | 792B193B1E1C0D1F003F991D /* Player.swift */, 108 | 79E3FFC61E1D692300EFD89F /* TokenCollectionViewCell.swift */, 109 | 79ED560A1E1C5113006247D2 /* PlayerCollectionViewCell.swift */, 110 | 792B19411E1C19AB003F991D /* Token.swift */, 111 | 792B193F1E1C17C8003F991D /* BankManager.swift */, 112 | 79B78BFD1E17E00900181778 /* Main.storyboard */, 113 | 79ED560C1E1C53EC006247D2 /* CircleView.swift */, 114 | 79B78C001E17E00900181778 /* Assets.xcassets */, 115 | 79C7D31C1E3CA24E00D4884B /* CashRegister.mp3 */, 116 | 79B78C021E17E00900181778 /* LaunchScreen.storyboard */, 117 | 79B78C051E17E00900181778 /* Info.plist */, 118 | ); 119 | name = BoardBank; 120 | path = MonoBank; 121 | sourceTree = ""; 122 | }; 123 | B51FE539208EC4E3002B4F36 /* BoardBankUITests */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | B51FE54A208EC647002B4F36 /* screens */, 127 | B51FE548208EC5B4002B4F36 /* AddPlayerTests.swift */, 128 | ); 129 | path = BoardBankUITests; 130 | sourceTree = ""; 131 | }; 132 | B51FE54A208EC647002B4F36 /* screens */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | B51FE546208EC56F002B4F36 /* SettingsScreen.swift */, 136 | B51FE544208EC553002B4F36 /* AddPlayerScreen.swift */, 137 | B51FE542208EC50D002B4F36 /* HomeScreen.swift */, 138 | ); 139 | path = screens; 140 | sourceTree = ""; 141 | }; 142 | /* End PBXGroup section */ 143 | 144 | /* Begin PBXNativeTarget section */ 145 | 79B78BF51E17E00900181778 /* BoardBank */ = { 146 | isa = PBXNativeTarget; 147 | buildConfigurationList = 79B78C081E17E00900181778 /* Build configuration list for PBXNativeTarget "BoardBank" */; 148 | buildPhases = ( 149 | 79B78BF21E17E00900181778 /* Sources */, 150 | 79B78BF31E17E00900181778 /* Frameworks */, 151 | 79B78BF41E17E00900181778 /* Resources */, 152 | 79879F2C1E1AFEE200066C69 /* ShellScript */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = BoardBank; 159 | productName = MonoBank; 160 | productReference = 79B78BF61E17E00900181778 /* BoardBank.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | B51FE537208EC4E3002B4F36 /* BoardBankUITests */ = { 164 | isa = PBXNativeTarget; 165 | buildConfigurationList = B51FE541208EC4E3002B4F36 /* Build configuration list for PBXNativeTarget "BoardBankUITests" */; 166 | buildPhases = ( 167 | B51FE534208EC4E3002B4F36 /* Sources */, 168 | B51FE535208EC4E3002B4F36 /* Frameworks */, 169 | B51FE536208EC4E3002B4F36 /* Resources */, 170 | ); 171 | buildRules = ( 172 | ); 173 | dependencies = ( 174 | B51FE53E208EC4E3002B4F36 /* PBXTargetDependency */, 175 | ); 176 | name = BoardBankUITests; 177 | productName = BoardBankUITests; 178 | productReference = B51FE538208EC4E3002B4F36 /* BoardBankUITests.xctest */; 179 | productType = "com.apple.product-type.bundle.ui-testing"; 180 | }; 181 | /* End PBXNativeTarget section */ 182 | 183 | /* Begin PBXProject section */ 184 | 79B78BEE1E17E00900181778 /* Project object */ = { 185 | isa = PBXProject; 186 | attributes = { 187 | LastSwiftUpdateCheck = 0920; 188 | LastUpgradeCheck = 0820; 189 | ORGANIZATIONNAME = "Richard Neitzke"; 190 | TargetAttributes = { 191 | 79B78BF51E17E00900181778 = { 192 | CreatedOnToolsVersion = 8.1; 193 | DevelopmentTeam = 9325T7U3SR; 194 | ProvisioningStyle = Automatic; 195 | }; 196 | B51FE537208EC4E3002B4F36 = { 197 | CreatedOnToolsVersion = 9.2; 198 | DevelopmentTeam = 9325T7U3SR; 199 | ProvisioningStyle = Automatic; 200 | TestTargetID = 79B78BF51E17E00900181778; 201 | }; 202 | }; 203 | }; 204 | buildConfigurationList = 79B78BF11E17E00900181778 /* Build configuration list for PBXProject "BoardBank" */; 205 | compatibilityVersion = "Xcode 3.2"; 206 | developmentRegion = English; 207 | hasScannedForEncodings = 0; 208 | knownRegions = ( 209 | Base, 210 | ); 211 | mainGroup = 79B78BED1E17E00900181778; 212 | productRefGroup = 79B78BF71E17E00900181778 /* Products */; 213 | projectDirPath = ""; 214 | projectRoot = ""; 215 | targets = ( 216 | 79B78BF51E17E00900181778 /* BoardBank */, 217 | B51FE537208EC4E3002B4F36 /* BoardBankUITests */, 218 | ); 219 | }; 220 | /* End PBXProject section */ 221 | 222 | /* Begin PBXResourcesBuildPhase section */ 223 | 79B78BF41E17E00900181778 /* Resources */ = { 224 | isa = PBXResourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | 79B78C041E17E00900181778 /* LaunchScreen.storyboard in Resources */, 228 | 79B78C011E17E00900181778 /* Assets.xcassets in Resources */, 229 | 79C7D31D1E3CA24E00D4884B /* CashRegister.mp3 in Resources */, 230 | 79B78BFF1E17E00900181778 /* Main.storyboard in Resources */, 231 | ); 232 | runOnlyForDeploymentPostprocessing = 0; 233 | }; 234 | B51FE536208EC4E3002B4F36 /* Resources */ = { 235 | isa = PBXResourcesBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | }; 241 | /* End PBXResourcesBuildPhase section */ 242 | 243 | /* Begin PBXShellScriptBuildPhase section */ 244 | 79879F2C1E1AFEE200066C69 /* ShellScript */ = { 245 | isa = PBXShellScriptBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | ); 249 | inputPaths = ( 250 | ); 251 | outputPaths = ( 252 | ); 253 | runOnlyForDeploymentPostprocessing = 0; 254 | shellPath = /bin/sh; 255 | shellScript = "buildNumber=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\")\nbuildNumber=$(($buildNumber + 1))\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\""; 256 | }; 257 | /* End PBXShellScriptBuildPhase section */ 258 | 259 | /* Begin PBXSourcesBuildPhase section */ 260 | 79B78BF21E17E00900181778 /* Sources */ = { 261 | isa = PBXSourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | 79B78BFA1E17E00900181778 /* AppDelegate.swift in Sources */, 265 | 792B19421E1C19AB003F991D /* Token.swift in Sources */, 266 | 792B193C1E1C0D20003F991D /* Player.swift in Sources */, 267 | 79E3FFC91E1D886A00EFD89F /* SettingsViewController.swift in Sources */, 268 | 79B28EB01E1C068300CFA38D /* MainViewController.swift in Sources */, 269 | 792B19401E1C17C8003F991D /* BankManager.swift in Sources */, 270 | 79E3FFC71E1D692300EFD89F /* TokenCollectionViewCell.swift in Sources */, 271 | 79E3FFC51E1D612500EFD89F /* AddPlayerViewController.swift in Sources */, 272 | 79ED560D1E1C53EC006247D2 /* CircleView.swift in Sources */, 273 | 79ED560B1E1C5113006247D2 /* PlayerCollectionViewCell.swift in Sources */, 274 | ); 275 | runOnlyForDeploymentPostprocessing = 0; 276 | }; 277 | B51FE534208EC4E3002B4F36 /* Sources */ = { 278 | isa = PBXSourcesBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | B51FE549208EC5B4002B4F36 /* AddPlayerTests.swift in Sources */, 282 | B51FE547208EC56F002B4F36 /* SettingsScreen.swift in Sources */, 283 | B51FE543208EC50D002B4F36 /* HomeScreen.swift in Sources */, 284 | B51FE545208EC553002B4F36 /* AddPlayerScreen.swift in Sources */, 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | /* End PBXSourcesBuildPhase section */ 289 | 290 | /* Begin PBXTargetDependency section */ 291 | B51FE53E208EC4E3002B4F36 /* PBXTargetDependency */ = { 292 | isa = PBXTargetDependency; 293 | target = 79B78BF51E17E00900181778 /* BoardBank */; 294 | targetProxy = B51FE53D208EC4E3002B4F36 /* PBXContainerItemProxy */; 295 | }; 296 | /* End PBXTargetDependency section */ 297 | 298 | /* Begin PBXVariantGroup section */ 299 | 79B78BFD1E17E00900181778 /* Main.storyboard */ = { 300 | isa = PBXVariantGroup; 301 | children = ( 302 | 79B78BFE1E17E00900181778 /* Base */, 303 | ); 304 | name = Main.storyboard; 305 | sourceTree = ""; 306 | }; 307 | 79B78C021E17E00900181778 /* LaunchScreen.storyboard */ = { 308 | isa = PBXVariantGroup; 309 | children = ( 310 | 79B78C031E17E00900181778 /* Base */, 311 | ); 312 | name = LaunchScreen.storyboard; 313 | sourceTree = ""; 314 | }; 315 | /* End PBXVariantGroup section */ 316 | 317 | /* Begin XCBuildConfiguration section */ 318 | 79B78C061E17E00900181778 /* Debug */ = { 319 | isa = XCBuildConfiguration; 320 | buildSettings = { 321 | ALWAYS_SEARCH_USER_PATHS = NO; 322 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 323 | CLANG_ANALYZER_NONNULL = YES; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 325 | CLANG_CXX_LIBRARY = "libc++"; 326 | CLANG_ENABLE_MODULES = YES; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_WARN_BOOL_CONVERSION = YES; 329 | CLANG_WARN_CONSTANT_CONVERSION = YES; 330 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 331 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 332 | CLANG_WARN_EMPTY_BODY = YES; 333 | CLANG_WARN_ENUM_CONVERSION = YES; 334 | CLANG_WARN_INFINITE_RECURSION = YES; 335 | CLANG_WARN_INT_CONVERSION = YES; 336 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 337 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 338 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 339 | CLANG_WARN_UNREACHABLE_CODE = YES; 340 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 341 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 342 | COPY_PHASE_STRIP = NO; 343 | DEBUG_INFORMATION_FORMAT = dwarf; 344 | ENABLE_STRICT_OBJC_MSGSEND = YES; 345 | ENABLE_TESTABILITY = YES; 346 | GCC_C_LANGUAGE_STANDARD = gnu99; 347 | GCC_DYNAMIC_NO_PIC = NO; 348 | GCC_NO_COMMON_BLOCKS = YES; 349 | GCC_OPTIMIZATION_LEVEL = 0; 350 | GCC_PREPROCESSOR_DEFINITIONS = ( 351 | "DEBUG=1", 352 | "$(inherited)", 353 | ); 354 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 355 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 356 | GCC_WARN_UNDECLARED_SELECTOR = YES; 357 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 358 | GCC_WARN_UNUSED_FUNCTION = YES; 359 | GCC_WARN_UNUSED_VARIABLE = YES; 360 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 361 | MTL_ENABLE_DEBUG_INFO = YES; 362 | ONLY_ACTIVE_ARCH = YES; 363 | PRODUCT_NAME = BoardBank; 364 | SDKROOT = iphoneos; 365 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 366 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 367 | TARGETED_DEVICE_FAMILY = "1,2"; 368 | }; 369 | name = Debug; 370 | }; 371 | 79B78C071E17E00900181778 /* Release */ = { 372 | isa = XCBuildConfiguration; 373 | buildSettings = { 374 | ALWAYS_SEARCH_USER_PATHS = NO; 375 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 376 | CLANG_ANALYZER_NONNULL = YES; 377 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 378 | CLANG_CXX_LIBRARY = "libc++"; 379 | CLANG_ENABLE_MODULES = YES; 380 | CLANG_ENABLE_OBJC_ARC = YES; 381 | CLANG_WARN_BOOL_CONVERSION = YES; 382 | CLANG_WARN_CONSTANT_CONVERSION = YES; 383 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 384 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 385 | CLANG_WARN_EMPTY_BODY = YES; 386 | CLANG_WARN_ENUM_CONVERSION = YES; 387 | CLANG_WARN_INFINITE_RECURSION = YES; 388 | CLANG_WARN_INT_CONVERSION = YES; 389 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 390 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 391 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 392 | CLANG_WARN_UNREACHABLE_CODE = YES; 393 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 394 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 395 | COPY_PHASE_STRIP = NO; 396 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 397 | ENABLE_NS_ASSERTIONS = NO; 398 | ENABLE_STRICT_OBJC_MSGSEND = YES; 399 | GCC_C_LANGUAGE_STANDARD = gnu99; 400 | GCC_NO_COMMON_BLOCKS = YES; 401 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 402 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 403 | GCC_WARN_UNDECLARED_SELECTOR = YES; 404 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 405 | GCC_WARN_UNUSED_FUNCTION = YES; 406 | GCC_WARN_UNUSED_VARIABLE = YES; 407 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 408 | MTL_ENABLE_DEBUG_INFO = NO; 409 | PRODUCT_NAME = BoardBank; 410 | SDKROOT = iphoneos; 411 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 412 | TARGETED_DEVICE_FAMILY = "1,2"; 413 | VALIDATE_PRODUCT = YES; 414 | }; 415 | name = Release; 416 | }; 417 | 79B78C091E17E00900181778 /* Debug */ = { 418 | isa = XCBuildConfiguration; 419 | buildSettings = { 420 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 421 | DEVELOPMENT_TEAM = 9325T7U3SR; 422 | INFOPLIST_FILE = MonoBank/Info.plist; 423 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 424 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 425 | PRODUCT_BUNDLE_IDENTIFIER = com.akifev.MonoBank; 426 | PRODUCT_NAME = "$(TARGET_NAME)"; 427 | SWIFT_VERSION = 3.0; 428 | TARGETED_DEVICE_FAMILY = "1,2"; 429 | }; 430 | name = Debug; 431 | }; 432 | 79B78C0A1E17E00900181778 /* Release */ = { 433 | isa = XCBuildConfiguration; 434 | buildSettings = { 435 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 436 | DEVELOPMENT_TEAM = 9325T7U3SR; 437 | INFOPLIST_FILE = MonoBank/Info.plist; 438 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 439 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 440 | PRODUCT_BUNDLE_IDENTIFIER = com.akifev.MonoBank; 441 | PRODUCT_NAME = "$(TARGET_NAME)"; 442 | SWIFT_VERSION = 3.0; 443 | TARGETED_DEVICE_FAMILY = "1,2"; 444 | }; 445 | name = Release; 446 | }; 447 | B51FE53F208EC4E3002B4F36 /* Debug */ = { 448 | isa = XCBuildConfiguration; 449 | buildSettings = { 450 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 451 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 452 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 453 | CLANG_WARN_COMMA = YES; 454 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 455 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 456 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 457 | CLANG_WARN_STRICT_PROTOTYPES = YES; 458 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 459 | CODE_SIGN_IDENTITY = "iPhone Developer"; 460 | CODE_SIGN_STYLE = Automatic; 461 | DEVELOPMENT_TEAM = 9325T7U3SR; 462 | GCC_C_LANGUAGE_STANDARD = gnu11; 463 | INFOPLIST_FILE = BoardBankUITests/Info.plist; 464 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 466 | PRODUCT_BUNDLE_IDENTIFIER = com.akifev.BoardBankUITests; 467 | PRODUCT_NAME = "$(TARGET_NAME)"; 468 | SWIFT_VERSION = 4.0; 469 | TARGETED_DEVICE_FAMILY = "1,2"; 470 | TEST_TARGET_NAME = BoardBank; 471 | }; 472 | name = Debug; 473 | }; 474 | B51FE540208EC4E3002B4F36 /* Release */ = { 475 | isa = XCBuildConfiguration; 476 | buildSettings = { 477 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 478 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 479 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 480 | CLANG_WARN_COMMA = YES; 481 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 482 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 483 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 484 | CLANG_WARN_STRICT_PROTOTYPES = YES; 485 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 486 | CODE_SIGN_IDENTITY = "iPhone Developer"; 487 | CODE_SIGN_STYLE = Automatic; 488 | DEVELOPMENT_TEAM = 9325T7U3SR; 489 | GCC_C_LANGUAGE_STANDARD = gnu11; 490 | INFOPLIST_FILE = BoardBankUITests/Info.plist; 491 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 492 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 493 | PRODUCT_BUNDLE_IDENTIFIER = com.akifev.BoardBankUITests; 494 | PRODUCT_NAME = "$(TARGET_NAME)"; 495 | SWIFT_VERSION = 4.0; 496 | TARGETED_DEVICE_FAMILY = "1,2"; 497 | TEST_TARGET_NAME = BoardBank; 498 | }; 499 | name = Release; 500 | }; 501 | /* End XCBuildConfiguration section */ 502 | 503 | /* Begin XCConfigurationList section */ 504 | 79B78BF11E17E00900181778 /* Build configuration list for PBXProject "BoardBank" */ = { 505 | isa = XCConfigurationList; 506 | buildConfigurations = ( 507 | 79B78C061E17E00900181778 /* Debug */, 508 | 79B78C071E17E00900181778 /* Release */, 509 | ); 510 | defaultConfigurationIsVisible = 0; 511 | defaultConfigurationName = Release; 512 | }; 513 | 79B78C081E17E00900181778 /* Build configuration list for PBXNativeTarget "BoardBank" */ = { 514 | isa = XCConfigurationList; 515 | buildConfigurations = ( 516 | 79B78C091E17E00900181778 /* Debug */, 517 | 79B78C0A1E17E00900181778 /* Release */, 518 | ); 519 | defaultConfigurationIsVisible = 0; 520 | defaultConfigurationName = Release; 521 | }; 522 | B51FE541208EC4E3002B4F36 /* Build configuration list for PBXNativeTarget "BoardBankUITests" */ = { 523 | isa = XCConfigurationList; 524 | buildConfigurations = ( 525 | B51FE53F208EC4E3002B4F36 /* Debug */, 526 | B51FE540208EC4E3002B4F36 /* Release */, 527 | ); 528 | defaultConfigurationIsVisible = 0; 529 | defaultConfigurationName = Release; 530 | }; 531 | /* End XCConfigurationList section */ 532 | }; 533 | rootObject = 79B78BEE1E17E00900181778 /* Project object */; 534 | } 535 | -------------------------------------------------------------------------------- /MonoBank/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 300 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 332 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 364 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 465 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | --------------------------------------------------------------------------------