I used the delegate option using UserDefaults to store and load arrays into a table view and it works good but I am trying to use the singleton database option now.
I have everything in my app working right except when I add a player to myRoster or draftOrder it doesn't add the player.
My data:
import Foundation
import UIKit
class PlayerData: Codable {
var num: Int = 0
var name: String = ""
var team: String = ""
var position: String = ""
var strikeThrough: Bool = false
var color: Bool = false
var accessory: Bool = false
var rosterPosition: Int = -1
var draftPosition: Int = -1
init(num: Int, name: String, team: String, position: String) {
self.num = num
self.name = name
self.team = team
self.position = position
}
}
var objectsArray = [PlayerData]()
var myRoster: [PlayerData] = []
var draftOrder: [PlayerData] = []
class Database {
static let shared = Database()
fileprivate var allPlayers: [PlayerData]
fileprivate func playerDataURL() -> URL {
let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in:.userDomainMask).last!
return documentDirectoryURL.appendingPathComponent("playerData.json")
}
fileprivate func playersReadFromDisk() -> [PlayerData]? {
let filePlayerURL = playerDataURL()
guard FileManager.default.fileExists(atPath: filePlayerURL.path) else {
return nil
}
do {
let fileContents = try Data(contentsOf: filePlayerURL)
let list = try JSONDecoder().decode([PlayerData].self, from: fileContents)
return list
} catch let error as NSError {
NSLog("Error reading file: \(error.localizedDescription)")
}
return nil
}
fileprivate func playersWriteToDisk() {
do {
let data = try JSONEncoder().encode(allPlayers)
try data.write(to: playerDataURL())
} catch let error as NSError {
NSLog("Error reading file: \(error.localizedDescription)")
}
}
func changeMade() {
playersWriteToDisk()
}
func playerList(position: String) -> [PlayerData] {
return allPlayers.filter({ $0.position == position })
}
func rosterList() -> [PlayerData] {
let rosteredPlayers = allPlayers.filter { ($0.rosterPosition >= 0)}
return rosteredPlayers.sorted(by: { $0.rosterPosition < $1.rosterPosition })
}
func draftList() -> [PlayerData] {
let draftedPlayers = allPlayers.filter({ $0.draftPosition >= 0 })
return draftedPlayers.sorted(by: { $0.draftPosition < $1.draftPosition })
}
func addToRosterList(player: PlayerData) {
let maxRosteredIndex = allPlayers.map({ $0.rosterPosition }).max()!
player.rosterPosition = maxRosteredIndex + 1
}
func addToDraftList(player: PlayerData) {
let maxDraftedIndex = allPlayers.map({ $0.draftPosition }).max()!
player.draftPosition = maxDraftedIndex + 1
}
func removeFromRosterList(player: PlayerData) {
let currentRosterPosition = player.rosterPosition
player.rosterPosition = -1 // No longer on the rosterList. Gets all players on roster at a higher index.
let higherNumberedRosterPlayers = allPlayers.filter({ $0.rosterPosition > currentRosterPosition}) // Their rosterPositions have now gone down by one.
for otherPlayer in higherNumberedRosterPlayers {
otherPlayer.rosterPosition -= 1 // Decrease position by 1.
}
}
init() {
allPlayers = [PlayerData]()
if let playerList = playersReadFromDisk() {
allPlayers = playerList
} else {
allPlayers = [
// Quarterbacks
PlayerData(num: 1, name: "Patrick Mahomes", team: "KC", position: "QB"),
PlayerData(num: 2, name: "Deshaun Watson", team: "HOU", position: "QB"),
PlayerData(num: 3, name: "Aaron Rodgers", team: "GB", position: "QB"),
PlayerData(num: 4, name: "Matt Ryan", team: "ATL", position: "QB"),
PlayerData(num: 5, name: "Baker Mayfield", team: "CLE", position: "QB"),
PlayerData(num: 6, name: "Carson Wentz", team: "PHI", position: "QB"),
PlayerData(num: 7, name: "Jared Goff", team: "LAR", position: "QB"),
PlayerData(num: 8, name: "Cam Newton", team: "CAR", position: "QB"),
PlayerData(num: 9, name: "Andrew Luck", team: "IND", position: "QB"),
PlayerData(num: 10, name: "Drew Brees", team: "NO", position: "QB"),
PlayerData(num: 11, name: "Ben Roethlisberger", team: "PIT", position: "QB"),
PlayerData(num: 12, name: "Dak Prescott", team: "DAL", position: "QB"),
PlayerData(num: 13, name: "Russell Wilson", team: "SEA", position: "QB"),
PlayerData(num: 14, name: "Kyler Murray", team: "ARI", position: "QB"),
PlayerData(num: 15, name: "Tom Brady", team: "NE", position: "QB"),
PlayerData(num: 16, name: "Lamar Jackson", team: "BAL", position: "QB"),
PlayerData(num: 17, name: "Mitchell Trubisky", team: "CHI", position: "QB"),
PlayerData(num: 18, name: "Jameis Winston", team: "TB", position: "QB"),
PlayerData(num: 19, name: "Philip Rivers", team: "LAC", position: "QB"),
PlayerData(num: 20, name: "Kirk Cousins", team: "MIN", position: "QB")
]
}
}
}
myRoster view controller
import UIKit
class MyTeam_2019_2020: UIViewController, UITableViewDelegate, UITableViewDataSource {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
myRoster = Database.shared.rosterList()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myRoster.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let player = myRoster[indexPath.row]
let str = "\(player.name), \(player.team) - \(player.position)"
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "\(indexPath.row+1). \([str])"
cell.textLabel?.adjustsFontSizeToFitWidth = true
cell.textLabel?.font = UIFont.systemFont(ofSize: 22)
return cell
}
func save() {
let defaults = UserDefaults.standard
defaults.set(myRoster, forKey: "saveMyRoster")
defaults.set(draftOrder, forKey: "saveDraftOrder")
}
@IBOutlet weak var myTeam_20192020: UILabel!
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
Top_30_Quartebacks view controller
import UIKit
@available(iOS 11.0, *)
class Top_30_Quarterbacks: UIViewController, UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objectsArray.count
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
objectsArray = Database.shared.playerList(position: "QB")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let num = objectsArray[indexPath.row].num
let name = objectsArray[indexPath.row].name
let team = objectsArray[indexPath.row].team
cell.textLabel?.text = "\(num). \(name), \(team)"
cell.textLabel?.adjustsFontSizeToFitWidth = true
cell.textLabel?.font = UIFont.systemFont(ofSize: 22)
if objectsArray[indexPath.row].strikeThrough == false && objectsArray[indexPath.row].accessory == false && objectsArray[indexPath.row].color == false {
cell.textLabel?.text = "\(num). \(name), \(team)"
cell.textLabel?.attributedText = noStrikeThroughText("\(num). \(name), \(team)")
cell.accessoryType = UITableViewCell.AccessoryType.none
cell.backgroundColor = .none
}
else if objectsArray[indexPath.row].strikeThrough == true && objectsArray[indexPath.row].accessory == true && objectsArray[indexPath.row].color == true {
cell.textLabel?.text = "\(num). \(name), \(team)"
cell.textLabel?.attributedText = strikeThroughText("\(num). \(name), \(team)")
cell.accessoryType = UITableViewCell.AccessoryType.checkmark
cell.backgroundColor = .systemGray3
} else {
cell.textLabel?.text = "\(num). \(name), \(team)"
cell.textLabel?.attributedText = strikeThroughText("\(num). \(name), \(team)")
cell.accessoryType = UITableViewCell.AccessoryType.none
cell.backgroundColor = .systemGray2
}
return cell
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let cell = self.top30QuarterbacksTable.cellForRow(at: indexPath)
let str: String = (cell?.textLabel!.text)!
let player = objectsArray[indexPath.row].name
if cell?.textLabel?.attributedText == strikeThroughText(str) {
strikeThroughTextBool = true
} else {
strikeThroughTextBool = false
}
if strikeThroughTextBool == false {
let add = UIContextualAction(style: .normal, title: "Add") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
let alert = UIAlertController(title: "Add Player", message: "Are you sure you want to add '\(player)' to your roster?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
actionPerformed(false)
}))
alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
self.addPlayer(index: indexPath)
let okayAlert = UIAlertController(title: "Player Added!", message: "You added '\(player)' to your roster?", preferredStyle: .alert)
okayAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (alertAction) in
actionPerformed(true)
}))
self.present(okayAlert, animated: true)
}))
self.present(alert, animated: true)
}
add.backgroundColor = .systemGreen
let taken = UIContextualAction(style: .normal, title: "Taken") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
let alert = UIAlertController(title: "Player Taken", message: "Are you sure you want to mark '\(player)' as taken and not available?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
actionPerformed(false)
}))
alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
self.markAsTaken(index: indexPath)
let okayAlert = UIAlertController(title: "Taken Player", message: "You marked '\(player)' as taken and not available!", preferredStyle: .alert)
okayAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (alertAction) in
actionPerformed(true)
}))
self.present(okayAlert, animated: true)
}))
self.present(alert, animated: true)
}
taken.backgroundColor = .systemRed
let config = UISwipeActionsConfiguration(actions: [taken, add])
config.performsFirstActionWithFullSwipe = false
return config
} else {
let undo = UIContextualAction(style: .normal, title: "Undo") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
let alert = UIAlertController(title: "Undo", message: "Are you sure you want to undo the action for '\(player)'?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
actionPerformed(false)
}))
alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
self.removePlayer(index: indexPath)
let okayAlert = UIAlertController(title: "Action Undone", message: "The previous action for '\(player)' has been undone!", preferredStyle: .alert)
okayAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (alertAction) in
actionPerformed(true)
}))
self.present(okayAlert, animated: true)
}))
self.present(alert, animated: true)
}
undo.backgroundColor = .systemBlue
let config = UISwipeActionsConfiguration(actions: [undo])
config.performsFirstActionWithFullSwipe = false
return config
}
}
func addPlayer(index: IndexPath) {
let player = objectsArray[index.row]
player.strikeThrough = true
player.accessory = true
player.color = true
Database.shared.addToRosterList(player: player)
Database.shared.addToDraftList(player: player)
top30QuarterbacksTable.reloadData()
Database.shared.changeMade()
}
func markAsTaken(index: IndexPath) {
let player = objectsArray[index.row]
player.strikeThrough = true
player.color = true
player.accessory = false
top30QuarterbacksTable.reloadData()
Database.shared.changeMade()
}
func removePlayer(index: IndexPath) {
let player = objectsArray[index.row]
player.strikeThrough = false
player.color = false
player.accessory = false
top30QuarterbacksTable.reloadData()
Database.shared.changeMade()
}
@IBOutlet weak var top30QuarterbacksTable: UITableView!
@IBOutlet var view1: UIView!
@IBOutlet weak var view2: UIView!
@IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
hi,
the problem now is that there are too many themes in your code, some from your initial efforts, some from Claude31, and several from me, that just don't knit too well together.
rather than giving you a long reply here in this forum (i really do hate code boxes and i prefer that posts not get too long), i've mocked up an app that contains all the elements you need, it manages everything that needs to be persisted in a Database singleton (essentailly a global variable), it unifies and simplifies some of the code that's been patched together these last few weeks, and it does exactly what you want (as best i can tell).
you can find the project at:
bitbucket.org/delawaremathguy/japio
hope that helps,
DMG