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!
Related
I have a task where I have to read the file once a day.
1)Label with text and the date blank.
2)When the user first goes to view. I need to show the text, save it and the date when it was followed
3)Leave it all on the screen all day.
4)When the next day comes. Compare the saved date with the current date.
5)If the current date is greater than the stored date. then read the file again, show the text and the date when this is done and save
(While there is this. The text changes every time you enter the application.
A saved date = 0,)
// compare oderDate and currentDate
func showTextToDay() {
let newDatePredict = Date()
let olderDate = UserDefaults.standard.object(forKey: dateUser) as! Date // THIS PROBLEM
let order = Calendar.current.compare(olderDate, to: newDatePredict, toGranularity: .day)
switch order {
case .orderedDescending:
print("DESCENDING")
case .orderedAscending:
readFilePrediction() //read again file bcs new day
print("ASCENDING")
case .orderedSame:
print("SAME")
}
}
func readFilePrediction() {
//some code
}
// save olderDate Func
func saveDatePredictFunc() {
let oldDatePredict = Date()
UserDefaults.standard.set(oldDatePredict, forKey: dateUser)
let dateLabel = UserDefaults.standard.object(forKey: dateUser) as! Date
let df = DateFormatter()
df.dateFormat = "dd/MM/yyyy"
toDateOutlet.text = df.string(from: dateLabel)
defaults.set(toDateOutlet.text, forKey: dateUser)
}
You have to check also if a date a available at all.
The guard statement represents a NOR expression. readFilePrediction will be executed if the date does NOT exist OR the date comparison is ascending.
func showTextToDay()
{
guard let olderDate = UserDefaults.standard.object(forKey: dateUser) as? Date,
Calendar.current.compare(olderDate, to: Date(), toGranularity: .day) != .orderedAscending else {
readFilePrediction()
saveDatePredictFunc()
return
}
}
And reading the data right after saving it to UserDefaults (dateLabel) is very bad practice. You got the value. And delete also the line to save the string representation of the date. The line breaks the ability to read the value as Date.
func saveDatePredictFunc()
{
let oldDatePredict = Date()
UserDefaults.standard.set(oldDatePredict, forKey: dateUser)
let df = DateFormatter()
df.dateFormat = "dd/MM/yyyy"
toDateOutlet.text = df.string(from: oldDatePredict)
}
I can get it to work fine using just Strings but if I try to use Doubles or NSDates then I get an error:
"Cannot assign value of type "NSDate?" to type "String?"
"Cannot assign value of type "Double?" to type "String?"
#IBAction func save(_ sender: Any) {
if item != nil {
item?.startdate = startDate.text
item?.pickup = pickup.text
item?.miles = miles.text
item?.company = company.text
item?.destination = destination.text
item?.enddate = endDate.text
} else {
let entitydescription = NSEntityDescription.entity(forEntityName: "Entity", in: pc)
let item = Entity(entity: entitydescription!, insertInto: pc)
item.startdate = startDate.text
item.pickup = pickup.text
item.miles = miles.text
item.company = company.text
item.destination = destination.text
item.enddate = endDate.text
}
do {
try pc.save()
} catch {
print(error)
return
}
navigationController!.popViewController(animated: true)
}
Here is what type each field is:
#NSManaged public var startdate: NSDate?
#NSManaged public var pickup: String?
#NSManaged public var miles: Double
#NSManaged public var company: String?
#NSManaged public var destination: String?
#NSManaged public var enddate: NSDate?
You need to convert miles.text, startdate.text and enddate.text to Double, NSDate and NSDate, respectively.
For startdate.text / enddate.text:
I'm not sure how what limit you have set for these values when the item is saved, but you should use a default value just incase the conversion fails. For this example, assume the dates are formatted "5/15/17" which takes the format M/d/y
let defaultDate = NSDate() //current date
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "M/d/y"
if let startDateText = startdate.text, let startDate = dateFormatter.string(from: startDateText) as? NSDate {
item?.startdate = startDate
} else {
item?.startdate = defaultDate
}
if let endDateText = startdate.text, let endDate = dateFormatter.string(from: endDateText) as? NSDate {
item?.startdate = endDate
} else {
item?.startdate = defaultDate
}
For miles.text:
Same idea, use a default value incase the conversion failed based on what the text will be for item.miles
if let milesText = miles.text {
item?.miles = Double(miles.text) ?? 0.0 //default is 0.0
} else {
item?.miles = 0.0
}
or an easy one-liner—
item?.miles = Double(miles.text ?? "0.0")
if there's a specific default value you have in mind, just declare it before you assign the item's property,
let defaultMilesStr = "0.432"
item?.miles = Double(miles.text ?? defaultMiles)
Also, just a tip, it's good practice to not leave optionals wrapped when assigning values. So even though there was check to make sure item != nil, it's overall better to safely unwrap item with a "guard" or "if-let". Since you are creating a new item if one doesn't exist, id go with if-let in this case:
if let item = item {
// assign values to item's prop's
// item.startdate = .....
} else if let entityDescription = NSEntityDescription.entity(forEntityName: "Entity", in: pc), let item = Entity(entity: entityDescription, insertInto: pc) {
// assign values to item's prop's
// item.startdate = .....
}
//further execution
Convert your date string to NSdate then you are able to save that
Try this code i have provide my date string you mat change with your format
let dateString = "Thu, 22 Oct 2015 07:45:17 +0000"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEE, dd MMM yyyy hh:mm:ss +zzzz"
dateFormatter.locale = Locale.init(identifier: "en_GB")
let dateObj = dateFormatter.date(from: dateString)
dateFormatter.dateFormat = "MM-dd-yyyy"
Try this :
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = /* date_format_you_want_in_string from
* http://userguide.icu-project.org/formatparse/datetime
*/
let date = dateFormatter.date(from: /* your_date_string */)
and set it as :
item?.date = date
For miles you can use :
if let dMiles = Double(miles.text!) {
item?.miles.text = dMiles
} else {
print("Not a valid Double: \(textField.text!)")
}
Hope it Helps!!
How can I compare the values from data Base to my current date ? A date is saved in the data base when some action is performed, I need to compare that date with my current date and do some action. How can I do that ?
The date is saved my date base in this format:
var tasksin2 : Task?
dateFormatter.dateFormat = "dd/MM/yyyy hh:mm"
let mydate="\(dateFormatter.string(from: datePicker.date))"
tasksin2?.time=mydate
(UIApplication.shared.delegate as! AppDelegate).saveContext()
Here ya go:
extension String {
func toDate() -> Date? {
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy hh:mm"
return formatter.date(from: self)
}
}
class MyViewController: UIViewController {
var dateString1: String!
var dateString2: String!
override func viewDidLoad() {
super.viewDidLoad()
// set dateStrings
printDateTypes()
}
func printDateTypes() {
guard let date1 = dateString1.toDate() else {
print("dateString1: \(dateString1) | Failed to cast to \"dd/MM/yyyy hh:mm\"")
return
}
guard let date2 = dateString2.toDate() else {
print("dateString2: \(dateString2) | Failed to cast to \"dd/MM/yyyy hh:mm\"")
return
}
let isDescending = date1.compare(date2) == ComparisonResult.orderedDescending
print("orderedDescending: \(isDescending)")
let isAscending = date1.compare(date2) == ComparisonResult.orderedAscending
print("orderedAscending: \(isAscending)")
let isSame = date1.compare(date2) == ComparisonResult.orderedSame
print("orderedSame: \(isSame)")
}
}
I did a lot of searching through Stackoverflow but I havent found answer for my problem.
I am developing an app and I get JSON data for some events. What I get is the start time of the event and the duration of the event. All data in recived as String.
In one screen of the app I would like to show only the event that are currently going on.
for example:
Class Event {
var startTime: String?
var duration: String?
}
let event1 = Event()
event1.starTime = "12-12-2016, 10:50 AM"
event1.duration = "50min"
let event2 = Event()
event2.starTime = "12-12-2016, 09:50 AM"
event2.duration = "40min"
let event3 = Event()
event3.starTime = "12-12-2016, 10:10 AM"
event3.duration = "90min"
let allEvents = [event1, event2, event3]
and let say the the current date and time is 12-12-2016, 11:00AM. How can I filter/find events in allEvents that are still going on if we compare them to the current date?
Thank you in advance.
EDIT: My solution
I created method for converting dateString and durationString to startDate: Date and endDate: Date
static func convertDateStringAndDurationStringToStartAndEndDate(date: String, duration: String) -> (start: Date, end: Date)? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
guard let startDate = dateFormatter.date(from: date) else { return nil }
guard let duration = Int(duration) else { return nil }
// recived interval is in minutes, time interval must be calculated in seconds
let timeInterval = TimeInterval(Int(duration) * 60 )
let endDate = Date(timeInterval: timeInterval, since: startDate)
return (startDate, endDate)
}
For filtering I have created separated method. In my case I am using Realm database, but you will get the point.
static func filterResultsForNowPlaying(results: Results<Show>?) -> Results<Show>? {
let currentDate = NSDate()
let datePredicate = NSPredicate(format: "startDate <= %# AND %# <= endDate", currentDate, currentDate)
let filteredShows = results?.filter(datePredicate)
return filteredShows
}
You will need to convert them into dates, using DateFormatter, and then use a .filter over the array and have it match on if the current date is in range.
If you have the ability to change the Event class, you can greatly simplify your code if you replace your Event class with the DateInterval class, which does the same thing:
let minutes = 60;
let formatter = DateFormatter()
formatter.dateFormat = "MM-dd-yyyy"
let event1 = DateInterval(
start: formatter.date(from: "12-12-2016")!,
duration: TimeInterval(20 * minutes)
)
let now = Date()
if (event1.contains(now)) {
print("Event 1 is still active")
}
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")
}