Displaying Data in ios Charts - swift

I am creating an ios Chart that is based off of documents in Firestore. Here is my code:
import UIKit
import Charts
import Firebase
import FirebaseFirestoreSwift
import FirebaseAuth
class YearChartViewController: UIViewController, ChartViewDelegate {
enum valetTimeCategory: String{
typealias RawValue = String
case Year = "Year"
case Month = "Month"
case DaysOfWeeks = "Day1"
case CalendarDays = "Day2"
case Hour = "Hour"
}
var selectedCategory = valetTimeCategory.Year.rawValue
private var yearListener: ListenerRegistration!
#IBOutlet weak var segmentedController: UISegmentedControl!
var timeRetrieval = [timeData]()
var timeRetrievalYear = [yearTimeData]()
var barChart = BarChartView()
var pieChart = PieChartView()
lazy var companyUser = ""
#IBOutlet weak var localeIdLabel: UILabel!
private var timeDataCollection: CollectionReference!
var documentName1 = ""
override func viewDidLoad() {
super.viewDidLoad()
barChart.delegate = self
pieChart.delegate = self
localeIdLabel.text = companyUser
timeDataCollection = Firestore.firestore().collection("users").document(companyUser ).collection("time_data")
}
#IBAction func valetTimeCategory3(_ sender: Any) {
switch segmentedController.selectedSegmentIndex {
case 0:
selectedCategory = valetTimeCategory.Year.rawValue
case 1:
selectedCategory = valetTimeCategory.Month.rawValue
case 2:
selectedCategory = valetTimeCategory.DaysOfWeeks.rawValue
case 3: selectedCategory = valetTimeCategory.CalendarDays.rawValue
case 4: selectedCategory = valetTimeCategory.Hour.rawValue
default:
valetTimeCategory.Year.rawValue
}
self.yearListener.remove()
setListener()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setListener()
}
func setListener() {
barChart.frame = CGRect(x: 0, y: 0, width: 250, height: 250)
barChart.center = view.center
view.addSubview(barChart)
var entries = [BarChartDataEntry]()
yearListener = timeDataCollection.whereField(selectedCategory, isNotEqualTo: "Yaguay").order(by: selectedCategory).addSnapshotListener{ (querySnapshot, error) in
if let err = error {
debugPrint("Error fetching docs: \(err)")
} else {
self.timeRetrieval.removeAll()
guard let snap = querySnapshot else { return }
for document in snap.documents {
let FirebaseData = document.data()
let tiempoEs1 = FirebaseData["Year"] as? String ?? ""
let tiempoEs2 = FirebaseData["Month"] as? String ?? ""
let tiempoEs3 = FirebaseData["Day1"] as? String ?? ""
let tiempoEs4 = FirebaseData["Hour"] as? String ?? ""
let tiempoEs5 = FirebaseData["Day2"] as? String ?? ""
let documentIdThird = document.documentID
let newTimeRetrieval = timeData(timeContinuum1: tiempoEs1, timeContinuum2: tiempoEs2, timeContinuum3: tiempoEs3, timeContinuum4: tiempoEs4, timeContinuum5: tiempoEs5, documentId3: documentIdThird);
let newYearRetrieval = yearTimeData(timeContinuumYear: tiempoEs1)
self.timeRetrieval.append(newTimeRetrieval)
self.timeRetrievalYear.append(newYearRetrieval);
if self.selectedCategory == valetTimeCategory.Year.rawValue {
let q = Int(tiempoEs1)
let b = q?.nonzeroBitCount
entries.append(BarChartDataEntry(x: Double(q!), y: Double(self.timeRetrieval.count)));
let set = BarChartDataSet(entries: entries
)
set.colors = ChartColorTemplates.joyful()
let data = BarChartData(dataSet: set)
self.barChart.data = data
let format = NumberFormatter()
format.numberStyle = .decimal
let formatter = DefaultValueFormatter(formatter: format)
data.setValueFormatter(formatter)
self.barChart.legendRenderer.computeLegend(data: BarChartData(dataSet: set))
self.barChart.chartDescription?.text = "Years"
self.yearListener.remove()
}
if self.selectedCategory == valetTimeCategory.Month.rawValue {
let d = tiempoEs2.hashValue.nonzeroBitCount
entries.append(BarChartDataEntry(x: Double(d), y: Double(self.timeRetrieval.count)));
let set = BarChartDataSet(entries: entries, label: "\(self.selectedCategory)"
)
print(self.selectedCategory)
set.colors = ChartColorTemplates.joyful()
let data = BarChartData(dataSet: set)
self.barChart.data = data
self.yearListener.remove()
}
if self.selectedCategory == valetTimeCategory.DaysOfWeeks.rawValue {
let d = tiempoEs3.hashValue.nonzeroBitCount
entries.append(BarChartDataEntry(x: Double(d), y: Double(self.timeRetrieval.count)));
let set = BarChartDataSet(entries: entries, label: "\(self.selectedCategory)"
)
print(self.selectedCategory)
set.colors = ChartColorTemplates.joyful()
let data = BarChartData(dataSet: set)
self.barChart.data = data
self.yearListener.remove()
}
if self.selectedCategory == valetTimeCategory.CalendarDays.rawValue {
let q = Int(tiempoEs5)
let b = q?.nonzeroBitCount
entries.append(BarChartDataEntry(x: Double(b!), y: Double(self.timeRetrieval.count)));
let set = BarChartDataSet(entries: entries, label: "\(self.selectedCategory)"
)
print(self.selectedCategory)
set.colors = ChartColorTemplates.joyful()
let data = BarChartData(dataSet: set)
self.barChart.data = data
self.yearListener.remove()
}
if self.selectedCategory == valetTimeCategory.Hour.rawValue {
let q = tiempoEs4.hashValue.nonzeroBitCount
entries.append(BarChartDataEntry(x: Double(q), y: Double(self.timeRetrieval.count)));
let set = BarChartDataSet(entries: entries, label: "\(self.selectedCategory)"
)
print(self.selectedCategory)
set.colors = ChartColorTemplates.joyful()
let data = BarChartData(dataSet: set)
self.barChart.data = data
self.yearListener.remove()
}
}
}
}
}
#IBAction func monthButton1Tapped(_ sender: Any) {
self.companyUser = localeIdLabel.text!
performSegue(withIdentifier: "Month", sender: self)
}
#IBAction func logout(_ sender: Any) {
let auth = Auth.auth()
do {
try auth.signOut()
transitionToHome()
} catch let signOutError as Error {
debugPrint(signOutError)
}
}
func transitionToHome() {
let homeViewController = storyboard?.instantiateViewController(identifier: Constants.Storyboard.homeViewController)
view.window?.rootViewController = homeViewController
view.window?.makeKeyAndVisible()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! MonthChartViewController
vc.documentName = self.companyUser
}
}
Hopefully the random, nonsensical names aren't too distracting, but my problem is my Bar Chart isn't exactly displaying the right data. When I run the code, it displays:
Chart Image
There are 22 items (documents) in my collection. So it explicitly displays 22 objects, and if you count the numbers on each bar, the count on each bar is the correct number in each category (5 for 2020 and 17 for 2021). I don't know how to get my bar chart to explicitly display "17" and "5" for each category, please help if you know how to solve this.

timeRetrieval is appending additional entries each loop without clearing the previous entries. This is why the second bar has exactly the sum total.
You have a line self.timeRetrieval.removeAll() that is above the start of your for loop. Moving this line inside the loop should resolve this behavior.

Related

LineCharts doesn't show data from Realm

I want to show my data to LineCharts.
I have checked the Realm Studio, there is data saved in the Realm. It just doesn't show anything one the View.
Maybe something going wrong with my code.
import UIKit
import Charts
import RealmSwift
class GraphViewController: UIViewController {
#IBOutlet weak var linechart: LineChartView!
weak var axisFormatDelegate: IAxisValueFormatter?
override func viewDidLoad() {
super.viewDidLoad()
axisFormatDelegate = self
// Do any additional setup after loading the view.
//setLineGraph()
updateChartWithData()
}
func updateChartWithData() {
var dataEntries: [ChartDataEntry] = []
let scoreCounts = getScoreFromDatabase()
//let visitorCounts = getScoreFromDatabase()
for i in 0..<scoreCounts.count {
let timeIntervalForDate: TimeInterval = scoreCounts[i].exactdate.timeIntervalSince1970
let dataEntry = ChartDataEntry(x: Double(timeIntervalForDate), y: Double(scoreCounts[i].datesc))
dataEntries.append(dataEntry)
}
let chartDataSet = LineChartDataSet(entries: dataEntries, label: "Score count" )
linechart.data = LineChartData(dataSet: chartDataSet)
linechart.chartDescription?.text = "Your Score"
let xaxis = linechart.xAxis
xaxis.valueFormatter = axisFormatDelegate
}
func getScoreFromDatabase() -> Results<DateScore> {
let realm = try! Realm(configuration: Realm.Configuration(inMemoryIdentifier: "MyInMemoryRealm"))
return realm.objects(DateScore.self)
}
}
extension GraphViewController: IAxisValueFormatter {
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYY/MM/DD"
return dateFormatter.string(from: Date(timeIntervalSince1970: value))
}
}
Here is my Realm class
import Foundation
import RealmSwift
class DateScore: Object {
#objc dynamic var datesc : Int16 = 0
#objc dynamic var exactdate : Date = Date()
}
And I got error message below is this related?
[Unknown process name] CGAffineTransformInvert: singular matrix.
I am a beginner. Can anyone help me?

Variable is not updating - func takes default variable

In my ThirdScreenViewController I change the variable number with the IBAction pressed.
import Foundation
import UIKit
class ThirdScreenViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
var weatherManager = WeatherManager()
var team = "leer"
static var number = 1
#IBAction func bayernMunchen(_ sender: UIButton) {
team = "bayernMunchen"
}
#IBAction func borussiaDortmund(_ sender: UIButton) {
team = "borussiaDortmund"
}
#IBAction func schalke(_ sender: UIButton) {
team = "schalke"
}
#IBAction func pressed(_ sender: UIButton) {
switch team {
case "bayernMunchen":
ThirdScreenViewController.number = 46
case "borussiaDortmund":
ThirdScreenViewController.number = 41
case "schalke":
ThirdScreenViewController.number = 45
default: print(8)
}
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "WeatherViewController") as! WeatherViewController
self.present(nextViewController, animated:true, completion:nil)
}
}
In an other swift (not a View Controller) file I have a function which takes number and does something with it.
import Foundation
import UIKit
var TeamOne = ""
var TeamTwo = ""
var ScoreOne = ""
var ScoreTwo = ""
var TeamThree = ""
var TeamFour = ""
var ScoreThree = ""
var ScoreFour = ""
var cityName = ThirdScreenViewController.number
struct WeatherManager {
let weatherURL = "https://livescore-api.com/api-client/teams/matches.json?number=10&team_id=19&key=d33FTnnd6qwvEmjz&secret=BbO3REPYFXvb7fpkit0cQnpXNWssiL1U&number=3&team_id=\(cityName)"
func fetchWeather () {
let urlString = "\(weatherURL)"
perfromRequest(urlString: urlString)
}
func perfromRequest(urlString: String)
{
//1.Url erstellen
if let url = URL(string: urlString) {
//2. URLSession starten
let session = URLSession(configuration: .default)
//3. Give session a task
let task = session.dataTask(with: url) { (gettingInfo, response, error) in
if error != nil{
print(error!)
return
}
if let safeFile = gettingInfo {
self.parseJSON(weatherFile: safeFile)
}
}
//4. Start the task
task.resume()
}
}
//Das Ergebnis von oben wird hier ausgegeben
func parseJSON(weatherFile: Data) {
let decoder = JSONDecoder()
do{
let decodedFile = try decoder.decode(WeatherFile.self, from: weatherFile)
TeamOne = decodedFile.data[0].home_name
ScoreOne = decodedFile.data[0].score
TeamTwo = decodedFile.data[0].away_name
ScoreTwo = decodedFile.data[0].score
TeamThree = decodedFile.data[1].home_name
ScoreThree = decodedFile.data[1].score
TeamFour = decodedFile.data[1].away_name
ScoreFour = decodedFile.data[1].score
} catch {
print(error)
}
}
}
In a third swift file I use this func weatherManager.fetchWeather() to call what happens in my second swift file.
But here is the problem. It takes the variable number with the default value 1 and not with the value 41/46/45. What am I doing wrong?
Basically global variables outside of any class and static variables to share data is bad practice.
Apart from that to get the team ID dynamically delete the line
var cityName = ThirdScreenViewController.number
In the struct replace
let weatherURL = "https://livescore-api.com/api-client/teams/matches.json?number=10&team_id=19&key=d33FTnnd6qwvEmjz&secret=BbO3REPYFXvb7fpkit0cQnpXNWssiL1U&number=3&team_id=\(cityName)"
with
let weatherURL = "https://livescore-api.com/api-client/teams/matches.json?number=10&team_id=19&key=d33FTnnd6qwvEmjz&secret=BbO3REPYFXvb7fpkit0cQnpXNWssiL1U&number=3&team_id="
and
let urlString = "\(weatherURL)"
with
let urlString = weatherURL + String(ThirdScreenViewController.number)
Note: Consider to rename the weather related stuff to the team related stuff

Swift Firebase doesn't store Bool

I am having two ViewControllers, one called ComposingOverviewViewController and another one called CheapestViewController.
If the User presses a Button, he/ she ;) will be shown the CheapestViewController. If the user presses "Choose", cheapestBoolbecomes true. The selection made should be stored in Firebase, with the bool set as "true", so it can be checked afterwards, if the selection should be shown or not. Even when I check it while saving the data to firebase, Xcode tells me that the value is actually "true". But in Firebase it gets stored as "false".
I have to say, that to actually show something in CheapestViewController, the User has first to press the Button check Values. Since I am aware, that as soon as the View switches back again from CheapestViewControllerto ComposingOverviewViewControllerthe generated Selection gets lost. So as soon as the User presses "Save" in the ComposingOverviewController the selection gets generated again.
Maybe it has to do something with this.
I deleted as much unnecessary code as possible.
Hope that's okay for everyone!
But I actually don't have any clue.
Maybe someone can help. :)
class ComposingOVerviewViewController: UIViewController {
#IBOutlet weak var cheapestImageView: UIImageView!
var individualimageViewBool = false
var cheapestimageviewBool = false
var efficientImageViewBool = false
var bestImageViewBool = false
var overAllBestImageViewBool = false
var DollarJPerfor:Double?
var projectCreated:String?
let uid = User.current.uid
var indivCompValue = [WeighScore]()
var projCompValue = [ProjCompValue]()
var projectBudget:Int = 0
var projectDifficulty:Double = 0
var projectCreativity:Double = 0
var finalCheapest: [WeighScore] = []
var cheapestBool:Bool?
var freelancerArray = [WeighScore]()
var jobAmountNeeded = [JobNeeded]()
override func viewDidLoad() {
super.viewDidLoad()
UserService.projectjobamount(for: User.current, projectCreated: projectCreated ?? "no Value") { (jobNeeded) in
self.jobAmountNeeded = jobNeeded
}
UserService.individualComposingValues(for: User.current) { (indivCompValue) in
self.freelancerArray = indivCompValue
}
projectSpecification(for: User.current, projectCreated: projectCreated ?? "no Value")
imageHighlights()
}
// MARK: - Highlighten the Images when Team selected
func imageHighlights() {
if individualimageViewBool == true {
individualImageView.image = UIImage(named: "Individual-1")
} else {
individualImageView.image = UIImage(named: "Individual") }
if cheapestimageviewBool == true {
cheapestImageView.image = UIImage(named: "cheapesthigh")
cheapestBool = true
}
else { cheapestImageView.image = UIImage(named: "Cheapest") }
if efficientImageViewBool == true {
efficientImageView.image = UIImage(named: "Efficient-1")
}
else { efficientImageView.image = UIImage(named: "Efficient") }
if bestImageViewBool == true {
bestImageView.image = UIImage(named: "Besthighlight") }
else {bestImageView.image = UIImage(named: "Best") }
if overAllBestImageViewBool == true {
overAllBestImageView.image = UIImage(named: "absolutbesthigh") }
else {overAllBestImageView.image = UIImage(named: "absolutbest") }
}
func getCheapest(member: [WeighScore], neededValue: [JobNeeded]) {
var tempArray1: [WeighScore] = []
for jobAmount in jobAmountNeeded {
let neededValue = jobAmount.jobAmount
tempArray1 = freelancerArray.filter { $0.memberJob == jobAmount.jobName}
let amountInArray = tempArray1.count
if neededValue == 0 { return
} else if neededValue == amountInArray{
tempArray1.sort(by: {$0.normalPrice! < $1.normalPrice!})
for value in tempArray1 {
print(value.memberName as Any)
print(tempArray1.count)
finalBest.append(value) }
} else {
tempArray1.sort(by: {$0.normalPrice! < $1.normalPrice!})
let deletedValue = tempArray1.count - neededValue
print("deleted Value: ", deletedValue)
tempArray1.remove(0..<deletedValue)
for value in tempArray1 {
print(value.memberName as Any)
print(tempArray1.count)
finalCheapest.append(value)
}
}
}
}
// MARK: - Button Function to check Values only
#IBAction func checkValues(_ sender: Any) {
getBest(member: freelancerArray, neededValue: jobAmountNeeded, budgetLevel: projectBudget)
getAbsolutBest(member: freelancerArray, neededValue: jobAmountNeeded)
getMostEfficient(member: freelancerArray, neededValue: jobAmountNeeded)
getCheapest(member: freelancerArray, neededValue: jobAmountNeeded)
}
// MARK: - Save the Team/ Teams
#IBAction func saveAction(_ sender: Any) {
print(cheapestBool)
getCheapest(member: freelancerArray, neededValue: jobAmountNeeded)
for value in finalCheapest {
let finalCheapest = "finalCheapest"
PostService.proposedTeams(projectCreated: projectCreated ?? "No Value", teamType: finalCheapest, memberJob: value.memberJob ?? "no value", memberName: value.memberName ?? "no Value", memberID: value.memberID ?? "no Value", ajp: value.ajp ?? 999, crea: value.crea ?? 999, ijr: value.ijr ?? 999, qpa: value.qpa ?? 999, coj: value.coj ?? 999, los: value.los ?? 999, iracc: value.iracc ?? 999, dph: value.dph ?? 999, normalPrice: value.normalPrice ?? 999, sumFreelanceRating: value.sumFreelanceRating ?? 999, priceJPerfScore: value.priceJPerfScore ?? 999, teamSelected: cheapestBool ?? false)
}
}
// MARK: - Pass data to specific ViewController of Team Alternatives
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "teamToCheapest" {
let destVC = segue.destination as! CheapestViewController
print(sentcheapestTeam.count)
destVC.finalCheapest = finalCheapest
}
}
// MARK: - Go to specific Teams
#IBAction func cheapest(_ sender: Any) {
performSegue(withIdentifier: "teamToCheapest", sender: self)
}
}
// MARK: - Extensions
extension Array {
mutating func remove(_ range: Range<Int>) -> Array {
let values = Array(self[range])
self.removeSubrange(range)
return values
}
}
class CheapestViewController: UIViewController {
// MARK: - Properties
#IBOutlet weak var tableView: UITableView!
// var VC = ComposingOVerviewViewController()
var finalCheapest = [WeighScore]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "cheapestToOverview" {
let destVC = segue.destination as! ComposingOVerviewViewController
destVC.cheapestBool = true
}
}
#IBAction func chooseCheapest(_ sender: Any) {
print(finalCheapest.count)
for value in finalCheapest {
print(value.memberJob)
}
performSegue(withIdentifier: "cheapestToOverview", sender: self)
}
}
class WeighScore {
var key: String?
let memberJob: String?
let memberName: String?
let memberID:String?
let ajp: Double?
let crea:Double?
let ijr:Double?
let qpa:Double?
let coj:Double?
let los:Double?
let iracc:Double?
let dph:Double?
let normalPrice:Double?
var sumFreelanceRating:Double?
let priceJPerfScore:Double?
init(ajp: Double, crea:Double, ijr:Double, qpa:Double, coj:Double, los:Double, iracc:Double, dph:Double, sumFreelanceRating:Double, normalPrice:Double, memberJob: String, memberName: String, priceJPerfScore:Double, memberID:String) {
self.ajp = ajp
self.crea = crea
self.ijr = ijr
self.qpa = qpa
self.coj = coj
self.los = los
self.iracc = iracc
self.dph = dph
self.sumFreelanceRating = sumFreelanceRating
self.normalPrice = normalPrice
self.memberName = memberName
self.memberJob = memberJob
self.priceJPerfScore = priceJPerfScore
self.memberID = memberID
}
var dictValue: [String: Any] {
return ["memberName" : memberName,
"memberJob" : memberJob,
"ajp" : ajp,
"crea" : crea,
"ijr" : ijr,
"qpa" : qpa,
"coj" : coj,
"los" : los,
"iracc" : iracc,
"dph" : dph,
"normalPrice": normalPrice,
"sumFreelanceRating" : sumFreelanceRating,
"priceJPerfScore": priceJPerfScore,
"memberID": memberID,
]
}
init?(snapshot: DataSnapshot) {
guard let dict = snapshot.value as? [String: Any],
let memberJob = dict["memberJob"] as? String,
let memberName = dict["memberName"] as? String,
let ajp = dict["ajp"] as? Double,
let crea = dict["crea"] as? Double,
let ijr = dict["ijr"] as? Double,
let qpa = dict["qpa"] as? Double,
let coj = dict["coj"] as? Double,
let los = dict["los"] as? Double,
let iracc = dict["iracc"] as? Double,
let dph = dict["dph"] as? Double,
let normalPrice = dict["normalPrice"] as? Double,
let sumFreelanceRating = dict["sumFreelanceRating"] as? Double,
let priceJPerfScore = dict["priceJPerfScore"] as? Double,
let memberID = dict["memberID"] as? String
else {return nil}
self.memberJob = memberJob
self.memberName = memberName
self.ajp = ajp
self.crea = crea
self.ijr = ijr
self.qpa = qpa
self.coj = coj
self.los = los
self.iracc = iracc
self.dph = dph
self.normalPrice = normalPrice
self.sumFreelanceRating = sumFreelanceRating
self.priceJPerfScore = priceJPerfScore
self.memberID = memberID
}
}
class TeamSetting {
var key: String?
let memberJob: String?
let memberName: String?
let memberID:String?
let ajp: Double?
let crea:Double?
let ijr:Double?
let qpa:Double?
let coj:Double?
let los:Double?
let iracc:Double?
let dph:Double?
let normalPrice:Double?
var sumFreelanceRating:Double?
let priceJPerfScore:Double?
var teamSelected: Bool?
init(ajp: Double, crea:Double, ijr:Double, qpa:Double, coj:Double, los:Double, iracc:Double, dph:Double, sumFreelanceRating:Double, normalPrice:Double, memberJob: String, memberName: String, priceJPerfScore:Double, memberID:String, teamSelected: Bool) {
self.ajp = ajp
self.crea = crea
self.ijr = ijr
self.qpa = qpa
self.coj = coj
self.los = los
self.iracc = iracc
self.dph = dph
self.sumFreelanceRating = sumFreelanceRating
self.normalPrice = normalPrice
self.memberName = memberName
self.memberJob = memberJob
self.priceJPerfScore = priceJPerfScore
self.memberID = memberID
self.teamSelected = teamSelected
}
var dictValue: [String: Any] {
return ["memberName" : memberName,
"memberJob" : memberJob,
"ajp" : ajp,
"crea" : crea,
"ijr" : ijr,
"qpa" : qpa,
"coj" : coj,
"los" : los,
"iracc" : iracc,
"dph" : dph,
"normalPrice": normalPrice,
"sumFreelanceRating" : sumFreelanceRating,
"priceJPerfScore": priceJPerfScore,
"memberID": memberID,
"teamSelected": teamSelected]
}
init?(snapshot: DataSnapshot) {
guard let dict = snapshot.value as? [String: Any],
let memberJob = dict["memberJob"] as? String,
let memberName = dict["memberName"] as? String,
let ajp = dict["ajp"] as? Double,
let crea = dict["crea"] as? Double,
let ijr = dict["ijr"] as? Double,
let qpa = dict["qpa"] as? Double,
let coj = dict["coj"] as? Double,
let los = dict["los"] as? Double,
let iracc = dict["iracc"] as? Double,
let dph = dict["dph"] as? Double,
let normalPrice = dict["normalPrice"] as? Double,
let sumFreelanceRating = dict["sumFreelanceRating"] as? Double,
let priceJPerfScore = dict["priceJPerfScore"] as? Double,
let memberID = dict["memberID"] as? String,
let teamSelected = dict["teamSelected"] as? Bool
else {return nil}
self.memberJob = memberJob
self.memberName = memberName
self.ajp = ajp
self.crea = crea
self.ijr = ijr
self.qpa = qpa
self.coj = coj
self.los = los
self.iracc = iracc
self.dph = dph
self.normalPrice = normalPrice
self.sumFreelanceRating = sumFreelanceRating
self.priceJPerfScore = priceJPerfScore
self.memberID = memberID
self.teamSelected = teamSelected
}
}
There's no firebase code in the question so answering this question
Swift Firebase doesn't store Bool?
is not possible.
There's a lot of code in the question but it appears this is a Master->Detail setup (?) with the master viewController being ComposingOVerviewViewController and the detail being CheapestViewController. If so one issue appears to be that the detail viewController is spawning a new master?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "cheapestToOverview" {
let destVC = segue.destination as! ComposingOVerviewViewController
destVC.cheapestBool = true
}
}

app reverts all input variables to defaults after some time

I wrote my fist app, very simple, that creates a user defined number of badges at random times during a user defined window of time. It works fine but after some time (not sure how long, 2-4 hours), all of the user input information reverts to the defaults of the program. The issue is it is supposed to run each day but it is annoying to have to set it each morning. I am not sure if this is a coding issue or if the app 'reboots' when it is not doing anything in the background. Note that this occurs on my iPhone 8 but not on the simulator (or I am not patient enough for it to occur on the simulator).
I have put several print and label to try to identify when it occurs; I am sure I am putting them in the correct places. I apologize for including so much code - I tried to weed some of the mistakes out but I do not know where the problem is.
import UserNotifications
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var EarlyTimePicker: UITextField!
#IBOutlet weak var LateTimePicker: UITextField!
#IBOutlet weak var NumQuestions: UITextField!
#IBOutlet weak var myLabel_Questions: UILabel!// Attached to the label box
#IBOutlet weak var myLabel_StartEndTime: UILabel!
#IBOutlet weak var myLabel_TestResetTime: UILabel!
#IBOutlet weak var myLabel_CurrentEarlyTime: UILabel!
private var earlyTimePicker: UIDatePicker?
private var lateTimePicker: UIDatePicker?
override func viewDidLoad() {
super.viewDidLoad()
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in //ask for permission in order to show messages on the lock screen
if granted {
print("Yay!")
} else {
print("D'oh")
}
}
earlyTimePicker = UIDatePicker()
earlyTimePicker?.datePickerMode = .time //change to .time
earlyTimePicker?.addTarget(self, action: #selector(ViewController.earlyTimeChanged(earlyTimePicker:)),for: .valueChanged)
lateTimePicker = UIDatePicker()
lateTimePicker?.datePickerMode = .time //change to .time
lateTimePicker?.addTarget(self, action: #selector(ViewController.lateTimeChanged(lateTimePicker:)),for: .valueChanged)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.viewTapped(gestureRecognizer:)))
view.addGestureRecognizer(tapGesture)
EarlyTimePicker.inputView = earlyTimePicker
LateTimePicker.inputView = lateTimePicker
}
#objc func viewTapped(gestureRecognizer: UITapGestureRecognizer){
view.endEditing(true)
}
var earlyTime=480
var earlyTimehour=0
var earlyTimeminute=0
#objc func earlyTimeChanged(earlyTimePicker: UIDatePicker){
let earlyTimeFormatter = DateFormatter()
earlyTimeFormatter.dateFormat = "h:mm a"
earlyTimeFormatter.amSymbol = "AM"
earlyTimeFormatter.pmSymbol = "PM"
EarlyTimePicker.text = earlyTimeFormatter.string(from: earlyTimePicker.date)
view.endEditing(true)
let earlyTimedate = earlyTimePicker.date
let earlyTimecomponents = Calendar.current.dateComponents([.hour, .minute], from: earlyTimedate)
earlyTimehour = earlyTimecomponents.hour!
earlyTimeminute = earlyTimecomponents.minute!
earlyTime = earlyTimecomponents.hour! * 60 + earlyTimecomponents.minute!
print("earlyTimehour: \(earlyTimecomponents.hour!)")
print("earlyTimeminute: \(earlyTimecomponents)")
print("earlyTime: \(earlyTime)")
print("Current Time: \(Date())")
}
var lateTime=1200
var lateTimehour=0
var lateTimeminute=0
#objc func lateTimeChanged(lateTimePicker: UIDatePicker){
let lateTimeFormatter = DateFormatter()
lateTimeFormatter.dateFormat = "h:mm a"
lateTimeFormatter.amSymbol = "AM"
lateTimeFormatter.pmSymbol = "PM"
LateTimePicker.text = lateTimeFormatter.string(from: lateTimePicker.date)
view.endEditing(true)
let lateTimedate = lateTimePicker.date
let lateTimecomponents = Calendar.current.dateComponents([.hour, .minute], from: lateTimedate)
lateTimehour = lateTimecomponents.hour!
lateTimeminute = lateTimecomponents.minute!
lateTime = lateTimecomponents.hour! * 60 + lateTimecomponents.minute!
let testMinute = lateTime % 60
let testHour = lateTime / 60
print("lateTimehour: \(lateTimecomponents.hour!)")
print("lateTimeminute: \(lateTimecomponents)")
print("lateTime: \(lateTime)")
print("testHour: \(testHour)")
print("testMinute: \(testMinute)")
myLabel_TestResetTime.text = "Time Set \(Date())"
myLabel_CurrentEarlyTime.text = "Current Early Time: \(earlyTime) / OnOff: \(OnOff)"
}
let PickedString = ["One","Two","Three","Four", "Five","Six","Seven","Eight"]
// #IBAction func TestCallFunction(_ sender: UIButton) {
// scheduleLocal()
// }
//NEED TO REPEAT THIS FUNCTION AT EARLY TIME - 10
//need to stop repeating with a cancel button (while bool true, do it, while false, stop. Default is false)
var RunDaily: Timer?
var OnOff = false
var QuestionNum = 1
#IBAction func Launch(_ sender: UIButton) {
OnOff = true
let center = UNUserNotificationCenter.current()
center.removeAllPendingNotificationRequests()
guard let QuestionNumA = Int(NumQuestions.text!) else { //This is how to get the UserInterface VALUE as a number
print("not a number!: \(String(describing: NumQuestions.text))")
return
}
print("Number of Questions: \(QuestionNumA)")
// var QuestionNum = 1
if QuestionNumA > 10 {QuestionNum=10} else {QuestionNum=QuestionNumA}
print("QuestionNumA:\(QuestionNumA) vs QuestionNum: \(QuestionNum)")
printStuff()
showMessage()
}
#IBAction func Stop(_ sender: UIButton) {
OnOff = false
printStuff()
}
func printStuff() {
if OnOff == true {
print("Bool is On : \(OnOff)")
RunDaily = Timer.scheduledTimer(timeInterval: 86400, target: self, selector: #selector(showMessage), userInfo: nil, repeats: true)//86400
}
if OnOff == false {
print("Bool is Off : \(OnOff)")
RunDaily?.invalidate()
let center = UNUserNotificationCenter.current()
center.removeAllPendingNotificationRequests()
}
}
func SaveDefaultData(){ // THis is the structure to SAVE input data for when the app relaunches (causes error when run.
let defaults = UserDefaults.standard
defaults.set("Date()", forKey:"key1")
//defaults.set(earlyTimePicker, forKey:"earlyTimePickerSet") cannot set earlyTimePicker. causes crash
// defaults.set(lateTimePicker, forKey:"lateTimePickerSet")
defaults.set(earlyTime, forKey:"earlyTimeSet")
defaults.set(lateTime, forKey:"lateTimeSet")
defaults.set(QuestionNum, forKey:"QuestionNumSet")
}
func SetDefaultData(){// THis is the structure to Set input for when the app relaunches
let defaults = UserDefaults.standard
if let savedValue = defaults.string(forKey: "key1"){
print("Here you will get saved value \(savedValue)")
} else {
print("No value in Userdefault,Either you can save value here or perform other operation")
defaults.set("Here you can save value", forKey: "key1")
}
if let earlyTimeValue = defaults.string(forKey: "earlyTimeSet"){
print("Here you will get saved value \(earlyTimeValue)")
earlyTime = UserDefaults.standard.value(forKey: "earlyTimeSet") as? Int ?? 485
} else {
print("No value in Userdefault,Either you can save value here or perform other operation")
defaults.set("Here you can save value", forKey: "earlyTimeSet")
earlyTime = 500
}
if let lateTimeValue = defaults.string(forKey: "lateTimeSet"){
print("Here you will get saved value \(lateTimeValue)")
lateTime = UserDefaults.standard.value(forKey: "lateTimeSet") as? Int ?? 1265
} else {
print("No value in Userdefault,Either you can save value here or perform other operation")
defaults.set("Here you can save value", forKey: "lateTimeSet")
lateTime = 1230
}
if let QuestionNumValue = defaults.string(forKey: "QuestionNumSet"){
print("Here you will get saved value \(QuestionNumValue)")
QuestionNum = UserDefaults.standard.value(forKey: "QuestionNumSet") as? Int ?? 4
} else {
print("No value in Userdefault,Either you can save value here or perform other operation")
defaults.set("Here you can save value", forKey: "QuestionNumSet")
QuestionNum = 2
}
}
#objc func showMessage() {
let center = UNUserNotificationCenter.current()
if lateTime <= earlyTime {
lateTime = earlyTime+1
if earlyTimehour <= 12 {
LateTimePicker.text = "\(earlyTimehour):\(earlyTimeminute) AM"
}
if earlyTimehour > 12 {
let EarlyTimeAfternoon = earlyTimehour - 12
LateTimePicker.text = "\(EarlyTimeAfternoon):\(earlyTimeminute) PM"
}
}
// center.removeAllPendingNotificationRequests()
// THIS IS WHERE ALL THE USER INPUT GETS INTO THE PROGRAM //
// guard let QuestionNumA = Int(NumQuestions.text!) else { //This is how to get the UserInterface VALUE as a number
// print("not a number!: \(String(describing: NumQuestions.text))")
// return
// }
//print("Number of Questions: \(QuestionNumA)")
// THIS IS WHERE ALL THE USER INPUT GETS INTO THE PROGRAM //
var RandHourArray:[Int] = [0]
var RandMinArray:[Int] = [0]
var RandQuestionArray:[Int] = [0]
var Counter = 1
// var QuestionNum = 1
//if QuestionNumA > 2 {QuestionNum=10} else {QuestionNum=QuestionNumA}
// print("QuestionNumA:\(QuestionNumA) vs QuestionNum: \(QuestionNum)")
for _ in 0 ... QuestionNum-1{
// Pick random times for badges
//let RandHour = Int.random(in: earlyTimehour ... lateTimehour)
let RandTimeMinFromMidnight = Int.random(in: self.earlyTime ... self.lateTime)
let ConvertRandTimeHours = RandTimeMinFromMidnight / 60
let ConvertRandTimeMinutes = RandTimeMinFromMidnight % 60
RandHourArray.append(ConvertRandTimeHours)
//let RandMin = Int.random(in: earlyTimeminute ... lateTimeminute)
RandMinArray.append(ConvertRandTimeMinutes)
let RandQuestion = Int.random(in: 0 ... self.PickedString.count-1)
RandQuestionArray.append(RandQuestion)
//print("RandTimeMinFromMidnight: \(RandTimeMinFromMidnight)")
// print("RandHourArray: \(RandHourArray)")
// print("ConvertRandTimeHours: \(ConvertRandTimeHours)")
// print("RandMinArray: \(RandMinArray)")
// print("ConvertRandTimeMinutes: \(ConvertRandTimeMinutes)")
}
myLabel_Questions.text = "# of questions: \(QuestionNum)"//\(QuestionNumA)"
myLabel_StartEndTime.text = "Start Time \(earlyTime) / End Time \(lateTime)"
let content_A = UNMutableNotificationContent()
content_A.title = "Prompt"
content_A.body = self.PickedString[RandQuestionArray[Counter]] //
content_A.categoryIdentifier = "alarm"
content_A.userInfo = ["customData": "fizzbuzz"]
content_A.sound = UNNotificationSound.default
var dateComponents_A = DateComponents()
dateComponents_A.hour = RandHourArray[Counter]
dateComponents_A.minute = RandMinArray[Counter]
let trigger_A = UNCalendarNotificationTrigger(dateMatching: dateComponents_A, repeats: false)
let request_A = UNNotificationRequest(identifier: UUID().uuidString, content: content_A, trigger: trigger_A)
center.add(request_A)
print("Request A time: \(RandHourArray[Counter]) : \(RandMinArray[Counter])")
print("Question String picked A: \(self.PickedString[RandQuestionArray[Counter]])")
Counter=2
if Counter<=QuestionNum {
let content_B = UNMutableNotificationContent()
content_B.title = "Prompt"
content_B.body = self.PickedString[RandQuestionArray[Counter]]
content_B.categoryIdentifier = "alarm"
content_B.userInfo = ["customData": "fizzbuzz"]
content_B.sound = UNNotificationSound.default
var dateComponents_B = DateComponents()
dateComponents_B.hour = RandHourArray[Counter]
dateComponents_B.minute = RandMinArray[Counter]
let trigger_B = UNCalendarNotificationTrigger(dateMatching: dateComponents_B, repeats: false)
let request_B = UNNotificationRequest(identifier: UUID().uuidString, content: content_B, trigger: trigger_B)
center.add(request_B)
print("Request B time: \(RandHourArray[Counter]) : \(RandMinArray[Counter])")
print("Question String picked B: \(self.PickedString[RandQuestionArray[Counter]])")
}
}
}
You should store your data inside UserDefaults, Keychain and Core Data or other stuff. if you dont store your data every time you close the application all the data will deallocate from the memory because they were stored in the heap.
Unsaved data:
let myLabel: UILabel = UILabel()
myLabel.text = "Some text"
Should save like:
UserDefaults.standard.setValue(myLabel.text, forKey: "it.is.custom")
And load like:
myLabel.text = UserDefaults.standard.value(forKey: "it.is.custom") as? String
refrence to study: https://fluffy.es/persist-data/

Cell of UITableView is not displayed at all

No cell of UITableView is displayed.
Probably the cause seems to be part of the setting of delegate and dataSource of UITableView, but I do not know the specific point.
So paste all the code.
If you do not understand the intention of the question, please do not hesitate to tell me.
I will tell you sincerity sincerity.
import UIKit
import Firebase
import FirebaseStorage
import FirebaseFirestore
import SDWebImage
struct CellData {
var date: Date
var time: String
var title: String
var name: String
var image: URL
}
struct TableSection<SectionItem: Comparable&Hashable, RowItem>: Comparable {
var sectionItem: SectionItem
var rowItems: [RowItem]
static func < (lhs: TableSection, rhs: TableSection) -> Bool {
return lhs.sectionItem > rhs.sectionItem
}
static func == (lhs: TableSection, rhs: TableSection) -> Bool {
return lhs.sectionItem == rhs.sectionItem
}
static func group(rowItems : [RowItem], by criteria : (RowItem) -> SectionItem ) -> [TableSection<SectionItem, RowItem>] {
let groups = Dictionary(grouping: rowItems, by: criteria)
return groups.map(TableSection.init(sectionItem:rowItems:)).sorted()
}
}
fileprivate func parseDate(_ str: String) -> Date {
let dateFormat = DateFormatter()
dateFormat.dateFormat = "yyyy年MM月dd日"
return dateFormat.date(from: str)!
}
fileprivate func firstDayOfMonth(date: Date) -> Date {
let calendar = Calendar.current
let components = calendar.dateComponents([.year, .month, .day], from: date)
return calendar.date(from: components)!
}
class timelineViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
var arr = [CellData]()
let db = Firestore.firestore()
var sections = [TableSection<Date, CellData>]()
var teamIDFromFirebase: String = ""
var fireAuthUID = (Auth.auth().currentUser?.uid ?? "no data")
var dataImageFromFirestore = [Any]()
var dataTitleFromFireStore = [Any]()
var dataTimeFromFirestore = [Any]()
var dataNameFromFireStore = [Any]()
var dataDateFromFiewstore = [Any]()
var timelineDocumentIdArr = [Any]()
var draftDocumentIdArr = [Any]()
var submitDocumentIdArr = [Any]()
var selectedNum = 0
#IBOutlet weak var circleButton: UIButton!
#IBOutlet weak var userTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
circleButton = Circle()
if arr != nil {
self.arr = []
self.dataNameFromFireStore = [Any]()
self.dataTimeFromFirestore = [Any]()
self.dataTitleFromFireStore = [Any]()
self.dataImageFromFirestore = [Any]()
self.submitDocumentIdArr = [Any]()
self.selectedNum = 1
userTable.delegate = self
userTable.dataSource = self
userTable.register(UINib(nibName: "userTableViewCell", bundle: nil), forCellReuseIdentifier: "cellName")
self.db.collection("users").document(self.fireAuthUID).addSnapshotListener { (snapshot3, error) in
guard let document3 = snapshot3 else {
print("erorr2 \(String(describing: error))")
return
}
guard let data = document3.data() else { return }
self.teamIDFromFirebase = data["teamID"] as? String ?? ""
self.db.collection("diary").document(self.teamIDFromFirebase).collection("diaries").whereField("submit", isEqualTo: true).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
return self.arr = [CellData(date: parseDate(""), time: "", title: "", name: "", image:URL(string: "")!)]
} else {
var i = 0
for document in querySnapshot!.documents {
self.timelineDocumentIdArr.append(document.documentID)
guard let documentData: [String: Any] = document.data() else { return }
self.dataTitleFromFireStore.append((documentData["今日のタイトル"] as? String)!)
self.dataTimeFromFirestore.append((documentData["time"] as? String)!)
self.dataNameFromFireStore.append((documentData["userName"] as? String)!)
self.dataImageFromFirestore.append((documentData["image"] as? String)!)
self.dataDateFromFiewstore.append((documentData["date"] as? String)!)
self.arr.append(CellData(date: parseDate(self.dataDateFromFiewstore[i] as! String), time: self.dataTimeFromFirestore[i] as? String ?? "", title: self.dataTitleFromFireStore[i] as? String ?? "", name: self.dataNameFromFireStore[i] as? String ?? "", image: URL(string: self.dataImageFromFirestore[i] as! String)!))
i += 1
}
self.userTable.reloadData()
}
}
}
}
self.sections = TableSection.group(rowItems: self.arr, by: { (headline) in
firstDayOfMonth(date: headline.date)
})
}
func numberOfSections(in tableView: UITableView) -> Int {
return self.sections.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let section = self.sections[section]
let date = section.sectionItem
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy年MM月dd日"
return dateFormatter.string(from: date)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let section = self.sections[section]
return section.rowItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = userTable.dequeueReusableCell(withIdentifier: "cellName", for: indexPath) as! userTableViewCell
let section = self.sections[indexPath.section]
let cellDetail = section.rowItems[indexPath.row]
cell.userTitle.text = cellDetail.title
cell.userName.text = cellDetail.name
cell.userTime.text = cellDetail.time
cell.userImage.sd_setImage(with: cellDetail.image)
return cell
}
}
You should assign the sections property inside the closure. This is corrected the viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
circleButton = Circle()
if arr != nil {
self.arr = []
self.dataNameFromFireStore = [Any]()
self.dataTimeFromFirestore = [Any]()
self.dataTitleFromFireStore = [Any]()
self.dataImageFromFirestore = [Any]()
self.submitDocumentIdArr = [Any]()
self.selectedNum = 1
userTable.delegate = self
userTable.dataSource = self
userTable.register(UINib(nibName: "userTableViewCell", bundle: nil), forCellReuseIdentifier: "cellName")
self.db.collection("users").document(self.fireAuthUID).addSnapshotListener { (snapshot3, error) in
guard let document3 = snapshot3 else {
print("erorr2 \(String(describing: error))")
return
}
guard let data = document3.data() else { return }
self.teamIDFromFirebase = data["teamID"] as? String ?? ""
self.db.collection("diary").document(self.teamIDFromFirebase).collection("diaries").whereField("submit", isEqualTo: true).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
return self.arr = [CellData(date: parseDate(""), time: "", title: "", name: "", image:URL(string: "")!)]
} else {
var i = 0
for document in querySnapshot!.documents {
self.timelineDocumentIdArr.append(document.documentID)
guard let documentData: [String: Any] = document.data() else { return }
self.dataTitleFromFireStore.append((documentData["今日のタイトル"] as? String)!)
self.dataTimeFromFirestore.append((documentData["time"] as? String)!)
self.dataNameFromFireStore.append((documentData["userName"] as? String)!)
self.dataImageFromFirestore.append((documentData["image"] as? String)!)
self.dataDateFromFiewstore.append((documentData["date"] as? String)!)
self.arr.append(CellData(date: parseDate(self.dataDateFromFiewstore[i] as! String), time: self.dataTimeFromFirestore[i] as? String ?? "", title: self.dataTitleFromFireStore[i] as? String ?? "", name: self.dataNameFromFireStore[i] as? String ?? "", image: URL(string: self.dataImageFromFirestore[i] as! String)!))
i += 1
}
self.sections = TableSection.group(rowItems: self.arr, by: { (headline) in
firstDayOfMonth(date: headline.date)
})
self.userTable.reloadData()
}
}
}
}
}