Basically every time I close and reopen app it crashes when I open History tab. Im pretty sure its because of how i use NSUserDefaults. Can anyone guide me through?
What I noticed is that sometimes it opens History tab after reopening but if i add a new entry to the Nsuserdefaults it will crash, after I reopen it it will work fine and will show the previous entry or it will remove everything and keep the new entry only.
//
// SecondViewController.swift
// Angelina
//
// Created by Artiom Sobol on 1/3/16.
// Copyright © 2016 Artiom Sobol. All rights reserved.
//
import UIKit
class History: UIViewController, UITableViewDataSource, UITableViewDelegate
{
// test variable
var test: MyHistory!
// array to store unarchived history
var newHistory = [MyHistory]()
//outlet for tableview
let defaults = NSUserDefaults.standardUserDefaults()
@IBOutlet var tableView: UITableView!
override func viewDidLoad()
{
//change the background
self.view.backgroundColor = UIColor(patternImage: UIImage(named: "newBackground.jpg")!)
super.viewDidLoad()
if let savedPeople = defaults.objectForKey("MyHistory") as? NSData {
newHistory = NSKeyedUnarchiver.unarchiveObjectWithData(savedPeople) as! [MyHistory]
}
tableView.delegate = self
tableView.dataSource = self
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
tableView.reloadData()
}
func tableView(tableView: UITableView,numberOfRowsInSection section: Int) -> Int
{
return self.newHistory.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("historyCell", forIndexPath: indexPath) as! historyCell
let person = newHistory[indexPath.item]
// let defaults2 = NSUserDefaults.standardUserDefaults()
print("This is count", newHistory.count)
// if let savedPeople = defaults.objectForKey("MyHistory") as? NSData {
// newHistory = //NSKeyedUnarchiver.unarchiveObjectWithData(savedPeople) as! [MyHistory]
// }
// cell.durationLabel.text = String(person.durationNumber)
let (hour,minutes,seconds) = secondsToHoursMinutesSeconds(person.durationNumber)
if(seconds < 10 && minutes < 10)
{
cell.durationLabel.text = "0\(hour):0\(minutes):0\(seconds)"
}
else if(seconds > 9 && minutes < 10)
{
cell.durationLabel.text = "0\(hour):0\(minutes):\(seconds)"
}
else if(seconds > 9 && minutes > 9)
{
cell.durationLabel.text = "0\(hour):\(minutes):\(seconds)"
}
else if(seconds < 10 && minutes > 9)
{
cell.durationLabel.text = "0\(hour):\(minutes):0\(seconds)"
}
cell.kicksLabel.text = String(person.kicksNumber)
return cell
}
func secondsToHoursMinutesSeconds (seconds : Int) -> (Int, Int, Int)
{
return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)
}
}
I think i just realized that my array gets restarted after the app is turned off. thats why the table crashes cus it goes out of bounds..
this is my other file where the saving of the array happens.
import UIKit
class Kicks: UIViewController
{
var count = 0 as Int
var countKicks = 0 as Int
var kickReached = false as Bool
var pressedOnce = true as Bool
var timer = NSTimer()
var test: MyHistory!
var dateString = " "
@IBOutlet var timerLabel: UITextField!
@IBOutlet var kicksLabel: UITextField!
@IBOutlet var dateDisplay: UITextField!
@IBOutlet var kickbutton: UIButton!
var myHistoryArray: [MyHistory] = []
var currentMyHistory: MyHistory!
override func viewDidLoad()
{
super.viewDidLoad()
self.printTimestamp()
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = UIColor(patternImage: UIImage(named: "background13.png")!)
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func kickButton()
{
//currentMyHistory.kicksNumber = 5
kickbutton.setImage(UIImage(named: "gold-button-heart-icon.png"), forState: UIControlState.Normal)
if(pressedOnce == true)
{
pressedOnce = false
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("counter"), userInfo: nil, repeats: true)
}
else if(kickReached == true)
{
// let date = NSDate()
// let calendar = NSCalendar.currentCalendar()
// let timer_total = calendar.components([ .Hour, .Minute, .Second], fromDate: date)
}
else if(pressedOnce == false)
{
countKicks++
kicksLabel.text = "\(countKicks)"
if(countKicks == 10)
{
self.printTimestamp()
kickReached = true
timer.invalidate()
kickbutton.setImage(UIImage(named: "pink-button-heart-icon.png"), forState: UIControlState.Normal)
congratsAlert()
currentMyHistory = MyHistory(kicksNumber: countKicks, durationNumber: count)
myHistoryArray.append(currentMyHistory)
test = myHistoryArray[0]
print("countof array ", myHistoryArray.count)
printTimestamp() // Prints "Sep 9, 2014, 4:30 AM"
//save data
let savedData = NSKeyedArchiver.archivedDataWithRootObject(myHistoryArray)
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(savedData, forKey: "MyHistory")
clear()
}
}
}
func printTimestamp() {
let timestamp = NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .MediumStyle, timeStyle: .ShortStyle)
print(timestamp)
dateDisplay.text = "\(timestamp)"
}
// save countKicks, count, and stamp i
func congratsAlert()
{
let alert = UIAlertController(title: "Congratulation", message: "Yay!!! Angelina kicked 10 times in less than 2 hours.",preferredStyle: .Alert)
let okAction = UIAlertAction(title: "Ok",style: .Default,handler:{(action:UIAlertAction) -> Void in})
alert.addAction(okAction)
presentViewController(alert,animated: true,completion: nil)
}
func clear()
{
count = 0
countKicks = 0
kickReached = false
pressedOnce = true
timerLabel.text = "00:00:0\(count)"
kicksLabel.text = "\(countKicks)"
}
func counter()
{
++count
let (hour,minutes,seconds) = secondsToHoursMinutesSeconds(count)
if(seconds < 10 && minutes < 10)
{
timerLabel.text = "0\(hour):0\(minutes):0\(seconds)"
}
else if(seconds > 9 && minutes < 10)
{
timerLabel.text = "0\(hour):0\(minutes):\(seconds)"
}
else if(seconds > 9 && minutes > 9)
{
timerLabel.text = "0\(hour):\(minutes):\(seconds)"
}
else if(seconds < 10 && minutes > 9)
{
timerLabel.text = "0\(hour):\(minutes):0\(seconds)"
}
}
func secondsToHoursMinutesSeconds (seconds : Int) -> (Int, Int, Int)
{
return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)
}
}
It says clearly:
'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
Double check your array objects like newHistory
has all the elements when you app starts
It seems
func tableView(tableView: UITableView,numberOfRowsInSection section: Int) -> Int
{
return self.newHistory.count
}
self.newHistory.count is not 0 so it will trigger cellForRowAtIndexPath
, try add two break points inside numberOfRowsInSection
and cellForRowAtIndexPath
, so you can know what's goes wrong. Usually, if no data, numberOfRowsInSection
will return 0 and cellForRowAtIndexPath
will not be triggered.
You can also an exception point In Xcode BreakPoint navigator, so you can break at any exceptions. This is simple debug skills you need to master.