How to save variable to NSUserdefaults without fatal error - ios

I am trying to save some variable to NSUserDefaults when tapping on a UICollectionView cell, but I get the following error.
fatal error: unexpectedly found nil while unwrapping an Optional value
Here is the code I am using when a cell is tapped.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
// handle tap events
let text = Globals.sinceLabelArray[indexPath.item]
userDefaults.setObject(text, forKey: "sinceText")
let image = String(Globals.imagesArray[indexPath.item])
userDefaults.setObject(image, forKey: "khoury")
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let daydate = Globals.datesArray[indexPath.item] as String
userDefaults.setObject(daydate, forKey: "day")
performSegueWithIdentifier("menutohome", sender: nil)
}
Here is the view it impacts, and how.
let day = userDefault.objectForKey("day") as? NSDate
let date1 = day
let date2 = NSDate()
let diffDateComponents = NSCalendar.currentCalendar().components([NSCalendarUnit.Day, NSCalendarUnit.Hour, NSCalendarUnit.Minute, NSCalendarUnit.Second], fromDate: date1!, toDate: date2, options: NSCalendarOptions.init(rawValue: 0))
Thanks

I think you're setting dayDate as String
let daydate = Globals.datesArray[indexPath.item] as String // <- String
userDefaults.setObject(daydate, forKey: "day")
While when you called it back as NSDate
let day = userDefault.objectForKey("day") as? NSDate // <- NSDate
UPDATE:
You can save the dateObject as String first into NSUserDefaults. Once you want to use it, then use dateFormatter to make it as NSDate.
Example:
When you call it back from NSUserDefaults
let day = userDefault.objectForKey("day") as? String
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" // this format must equal to what it returns from the server
let dayDate: NSDate = dateFormatter.dateFromString(day)
print(dayDate) // your NSDate

If you are not sure if a variable is nil or not try and use conditional statement before using it for example:
Instead of:
let text = Globals.sinceLabelArray[indexPath.item]
userDefaults.setObject(text, forKey: "sinceText")
use:
if let text = Globals.sinceLabelArray[indexPath.item] {
userDefaults.setObject(text, forKey: "sinceText")
} else {
print("error has happened here")
}

Related

How to remove Optional(" ") and format date in Swift

I have the following code:
let dateToday = NSDate()
I “pass” it to the Score View Controller via a function:
func saveScore(){
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let gameResult = NSEntityDescription.insertNewObjectForEntityForName("SaveGame", inManagedObjectContext: appDelegate.managedObjectContext) as! SaveGame
gameResult.datePlayed = dateToday
// other data goes here
self.performSegueWithIdentifier(“scoreSegue", sender: self)
}
In the Score View Controller, I display it as:
cell.textLabel?.text = "Date: \(game.datePlayed)
However, I only get this line with the Optional(" ") line.
pls check my screenshot
How do I remove the Optional (" ") and how do I format the date to:
MM/dd/yyyy hh:mm (AM/PM)?
To remove the Optionnal("") indication you can use conditionnal binding like this :
if let date = game.datePlayed {
cell.textLabel?.text = "Date: \(date)"
} else {
//Here display something if no date is available
}
You could also force unwrap your variable with game.datePlayed! but I would recommend against it
To format your date to something readable, use NSDateFormatter like that:
let formatter = NSDateFormatter()
formatter.dateStyle = NSDateFormatterStyle.LongStyle
formatter.timeStyle = .MediumStyle
let dateString = formatter.stringFromDate(date)
You can change the dateStyle and timeStyle to suit your needs (choice between: .ShortStyle, .MediumStyle, .LongStyle and .FullStyle)
Additionaly you could use a custom date formatter like the following:
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd 'at' HH:mm"
let dateString = formatter.stringFromDate(date)

How to verify today date with last CoreData entry

I have a CoreData attributes that 2 (value, date). When I click on a UIButton, it added an entry corresponding to the value of the UIButton.
I will wish to limit the addition of entry to a daily. Basically, I will wish to check the current date and the date of the last entry. If it's value are identical, this is not added.
My Function
func data(sender: UIButton) {
// Date Format
let date = NSDate()
let formatter = NSDateFormatter()
formatter.dateFormat = "YYYY/MM/dd"
let dateFormat = formatter.stringFromDate(date)
// Load Entity
let AppDel : AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let theContext : NSManagedObjectContext = AppDel.managedObjectContext
let theEnt = NSEntityDescription.entityForName("Mood", inManagedObjectContext: theContext)
// Create Item
let newItem = Mood(entity: theEnt!, insertIntoManagedObjectContext: theContext)
newItem.mood = String(sender.tag)
newItem.date = dateFormat
// Save Item
do {
try theContext.save()
} catch _ {
}
}
Thank you in advance for your response.
If you fetch the Mood objects, sorted by date in descending order, the first item returned will be the last entry. You can set the fetchLimit to 1 to avoid loading more objects than are necessary. You can then test to see whether the date attribute matches, and handle accordingly:
func data(sender: UIButton) {
// Date Format
let date = NSDate()
let formatter = NSDateFormatter()
formatter.dateFormat = "YYYY/MM/dd"
let dateFormat = formatter.stringFromDate(date)
// Get context and entity details
let AppDel : AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let theContext : NSManagedObjectContext = AppDel.managedObjectContext
let theEnt = NSEntityDescription.entityForName("Mood", inManagedObjectContext: theContext)
// Fetch the latest entry
let fetch = NSFetchRequest()
fetch.entity = theEnt!
let sort = NSSortDescriptor(key: "date", ascending:false)
fetch.sortDescriptors = [sort]
fetch.fetchLimit = 1
let results = try! theContext.executeFetchRequest(fetch) as! [Mood]
// NB should do proper try/catch error checking
// Check for existing entry
if (results.count > 0) {
// Check whether date matches
if (results[0].date != dateFormat) {
let newItem = Mood(entity: theEnt!, insertIntoManagedObjectContext: theContext)
newItem.mood = String(sender.tag)
newItem.date = dateFormat
}
} else { // No entries yet, I assume you want to add one...
let newItem = Mood(entity: theEnt!, insertIntoManagedObjectContext: theContext)
newItem.mood = String(sender.tag)
newItem.date = dateFormat
}
// Save Item
do {
try theContext.save()
} catch _ {
}
}
Note that (given your code newItem.date = dateFormat) I am assuming the date attribute is a string which you set using the same format ("YYYY/MM/dd"). This strips out the time information and so avoids the need for the date comparisons, but also has the advantage that a string sort is equivalent to a date sort (perhaps you chose that format for that reason). If date is in fact a Date attribute, the sort will still work but you will need to use a date comparison.
I use an NSDate extension for this.
extension NSDate {
class func areDatesSameDay(dateOne:NSDate,dateTwo:NSDate) -> Bool {
let calender = NSCalendar.currentCalendar()
let flags: NSCalendarUnit = [.Day, .Month, .Year]
let compOne: NSDateComponents = calender.components(flags, fromDate: dateOne)
let compTwo: NSDateComponents = calender.components(flags, fromDate: dateTwo);
return (compOne.day == compTwo.day && compOne.month == compTwo.month && compOne.year == compTwo.year);
}
}
Usage is like this.
if NSDate.areDatesSameDay(dateOne, dateTwo: dateTwo) {
// Dates are same day
} else {
// Dates are not the same day
}
#Tom Harrington Has just pointed out that you can use the NSCalendar methods to do this more simply
let calender = NSCalendar.currentCalendar()
if calender.isDate(dateOne, inSameDayAsDate: dateTwo) {
// Dates are same day
}
So we can make my lovely extension even simpler...
extension NSDate {
func isSameDayAs(date:NSDate) -> Bool {
let calender = NSCalendar.currentCalendar()
return calender.isDate(self, inSameDayAsDate: date)
}
}
Then use it like this.
if dateOne.isSameDayAs(dateTwo) {
// Dates are same day
} else {
// Dates are not the same day
}
And thats Numberwang!

timeIntervalSinceDate returns the wrong value

I have two functions.
The first is writing NSDate() to NSUserDefaults as a string.
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss.SSSSxxx"
let dateString = dateFormatter.stringFromDate(NSDate())
NSUserDefaults.standardUserDefaults().setObject(dateString, forKey: "lastDate")
NSUserDefaults.standardUserDefaults().synchronize()
The second one is reading this value, converting it to NSDate and comparing with the current time and date.
let dateString = try NSUserDefaults.standardUserDefaults().stringForKey("lastDate")
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss.SSSSxxx"
if dateString != nil {
let date = try dateFormatter.dateFromString(dateString!)
if date != nil {
let interval = NSDate().timeIntervalSinceDate(date!)
let interval2 = Int(interval)
print(interval2)
}
} else {
//some code here
}
The thing is, it returns not 5 seconds (as it should, for example) but something like 43295 or 44592. I logged dates and strings and they seemed fine. Where did I break things?

NSDate to display only Date in TableViewCell

I am able to get NSDate to appear as date only and to show it in textfields to add and edit to core data but in the tableviewcell when using the subtitle style it comes out with both the date and time which I don't need thee time.
Any help is appreciated.
the following code is my NSDateFormatter
import Foundation
extension NSDate{
var stringValue: String{
return self.toString()
}
func toString() -> String {
let formatter = NSDateFormatter()
formatter.dateFormat = "dd-MMM-YYYY"
let str = formatter.stringFromDate(self)
return str
}
}
extension String{
var dateValue: NSDate?{
return self.toDate()
}
func toDate() -> NSDate? {
let formatter = NSDateFormatter()
formatter.dateFormat = "dd-MMM-YYYY"
if let date = formatter.dateFromString(self) {
return date
}else{
// if format failed, Put some code here
return nil // an example
}
}
}
the following code is the subtitle style. date is called from cordite
cell.detailTextLabel!.text = "(ddate)"
OK, Found out what I was doing wrong. I added the following line above the cell.detail.textLabel and it works
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MMM-dd-yyy"
let dateForText = ddate as? NSDate
cell.detailTextLabel!.text = dateFormatter.stringFromDate(dateForText!)

How do i get a number data and date data from parse and put it to UILabel?

Currently I want to retrieve createdAt and price from parse database and the datatypes for both are Number and NSDate. After I retrieved it I want to pass to UILabel which is date and price respectively. The problem right now is that whenever I tried to run the simulator, both of the UILabel data don't show up while string data type does show up.
My current code
if let createdAt = object?["createdAt"] as? String {
cell.date.text = createdAt
}
if let priceTitle = object?["price"] as? String {
cell.price.text = priceTitle
}
For Date, use NSDateFormatter.stringFromDate method so your code should be:
if let createdAt = object?["createdAt"] as? NSDate {
let dateFormatter = NSDateFormatter()
var theDateFormat = NSDateFormatterStyle.ShortStyle
dateFormatter.dateStyle = theDateFormat
cell.date.text = dateFormatter.stringFromDate(createdAt)
}
createdAt is a special value in Parse. It is PFObject's property and you access it with dot notation. So this is how you should proceed:
if let object = object as? PFObject {
let date = object.createdAt
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "HH:mm MMMM dd, yyyy"
let dateStr = dateFormatter.stringFromDate(date!)
cell.date.text = dateStr
}

Resources