├── 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 |
--------------------------------------------------------------------------------