Trouble with Core Data dates - iphone

I've got a Core Data model set up, with two entities in a one-to-many relationship (Items, and for each item, there can be multiple ResetDates). I'm pretty confident the model is set up correctly.
I can add new Items, and when doing so, add a new ResetDate (using the current date, with [NSDate date]). I can retrieve and display Items. What I'm having trouble with is retrieving and displaying the ResetDates.
Updated: It works now, thanks very much to the answerers below. Here's the code in question:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"resetDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:&sortDescriptor count:1];
NSMutableArray *sortedResets = [[NSMutableArray alloc] initWithArray:[item.resets allObjects]];
[sortedResets sortUsingDescriptors:sortDescriptors];
NSDate *oldDate = [[sortedResets lastObject] resetDate];
if ( !oldDate ) {
oldDate = [NSDate date];
}
NSInteger numberOfDays = [self timeIntervalWithStartDate:oldDate withEndDate:currentDate]; // This function works fine, when given two NSDate objects
daysSinceLabel.text = [NSString stringWithFormat:#"%d days", numberOfDays];

First, NSArray -objectAtIndex: is not returning nil if you pass it an index that is out of the bounds, it will raise an NSRangeException, when you're not sure about the index, and need to use -objectAtIndex:, you have to call the -count method before to check.
More importantly, an NSArray can't contain a nil value, as nil is not an object.
Then, no, it's not an NSDate object, when you ask item for it's resets relationship (item.resets), you get an NSSet that contain Reset managed objects in return, not NSDate objects, what you want is the resetDate attribute of the returned Reset managed objects, maybe something like this :
// NSArray -lastObject method return nil if the array is empty
// Sending messages to nil is Ok there, so we can call resetDate directly
NSDate *oldDate = [[sortedResets lastObject] resetDate];
if ( !oldDate ) {
oldDate = [NSDate date];
}
Hope that help, and that my English is understandable...

Maybe replacing :
NSDate *oldDate = sortedResets[0];
with :
NSDate *oldDate = [sortedResets objectAtIndex:0];
will help. sortedResets is an NSArray object, not a C array ;)

Related

How to sort an NSArray full of EKCalendars by the title

I've got a NSArray with a bunch of EKCalendar Objects in it. I need to sort them alphabetically. I'm new to selectors but I think I need something like...
NSArray *array = [otherArray sortedArrayUsingSelector:#selector('localizedCaseInsensitiveCompare:title')];
Cheers
You cannot do it that way. Instead do the following:
NSArray *sortedArray = [array sortedArrayUsingComparator: ^(id obj1, id obj2) {
EKCalendar *cal1 = (EKCalendar *)obj1;
EKCalendar *cal2 = (EKCalendar *)obj2;
return [cal1.title localizedCaseInsensitiveCompare:cal2.title];
}];
Edit - an explanation:
-sortedArrayUsingComparator takes what is called a 'block' (an inline function) that must return an NSComparisonResult. All the hard work is done for you, as your block is run for as many pairs of objects as is needed to establish the correct order. Then all this does is cast each object type to an EKCalendar and then compare the two titles. You can adapt this to work for any type of object.
This should do the trick:
NSMutableArray *sortDescriptors = [NSMutableArray array];
NSSortDescriptor *sortByTitleAsc = [[[NSSortDescriptor alloc] initWithKey:#"title" ascending:YES] autorelease];
[sortDescriptors addObject:sortByTitleAsc];
NSArray *arraySortedByTitle = [otherArray sortedArrayUsingDescriptors:sortDescriptors];
No, you don't want to use a selector, you want to use a key path, which requires a sort descriptor. You can't append an arbitrary property name to a selector name. The selector must exactly match the method name. Otherwise you just get nothing (nil/NULL/0) for the selector.
id sortedArray = [array sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"title" ascending:YES]]];
For the sake of completeness and and timeliness, here’s the Swift 4 version:
let store = EKEventStore()
let calendars = store.calendars(for: .event)
let calendarsSorted = calendars.sorted { $0.title.localizedCaseInsensitiveCompare($1.title) == ComparisonResult.orderedAscending }
By the way, please don’t forget to request access to the store by calling store.requestAccess(to:completion:) before accessing the store’s data.

How to sort an array of dates in iphone? [duplicate]

This question already has answers here:
Sort NSArray of date strings or objects
(9 answers)
Closed 3 years ago.
I have an array of dates that are adding one by one to the table view , but i want to sort them in ascending order irrespective of the sequence added them,can any one help me
You can use NSMutableArray's method sortUsingComparator, and write your comparison block using NSDate's compare method.
If you are using NSDate object this should be easy:
NSDate *date1 = ...;
NSDate *date2 = ...;
NSDate *date3 = ...;
NSArray *dates = [NSArray arrayWithObjects:date1,date2,date3,nil];
NSArray *sortedArray = [dates [anArray sortedArrayUsingSelector:#selector(compare:)];
NSArray* sortedDateList = [dateList sortedArrayUsingComparator:^(NSDate* dateOne, NSDate* dateTwo) {
return [dateOne compare:dateTwo];
}];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"beginDate" ascending:TRUE];
[myMutableArray sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
Use this sort descriptor for sorting array of dates

NSNumber for MPMediaItemPropertyPersistentID to NSString and back again

I'm looping through all the songs from an iPhone's music library using the following code:
NSArray * songs = [[NSArray alloc] initWithArray:[[MPMediaQuery songsQuery] collections]];
for (MPMediaItemCollection * item in songs){
NSString * persistentID = [[[item representativeItem] valueForProperty:MPMediaItemPropertyPersistentID] stringValue];
// Do something with it.
}
[songs release];
Pretty basic stuff.
I'm getting the PersistentID as an NSString because I need to write it to an XML file (for transmission over a network to another device). Hence the reason I can't just leave it as an NSNumber.
The other device will then ask for the iPhone to play a track by transmitting the PersistentID back again.
At this point, the iPhone has an NSString of the PersistentID of the track it should play.
It would be combersome to loop through every song again and compare PersistentIDs until I find the track I want, so I'm trying to use the MPMediaPropertyPredicate to have the iPhone search for me.
I'm using the following code for the search:
MPMediaPropertyPredicate * predicate = [MPMediaPropertyPredicate predicateWithValue:persistentID forProperty:MPMediaItemPropertyPersistentID];
MPMediaQuery * songsQuery = [[MPMediaQuery alloc] init];
[songsQuery addFilterPredicate:predicate];
if ([[songsQuery items] count]){
MPMediaItem * item = [[songsQuery items] objectAtIndex:0];
// Play item.
}
[songsQuery release];
Where persistentID is the NSString from earlier.
Weirdly, this works for some songs, not for others. i.e, sometimes the items array is not empty, even though I'm passing an NSString, not an NSNumber.
I'm wondering if there's a way to convert my NSString back to the NSNumber it came from, and how I can do that.
UPDATE: I've tried the NSNumberFormatter, I've also tried something like:
[NSNumber numberWithFloat:[persID floatValue]];
I've tried all the standard ways of doing it without prevail.
This is working pretty well, no problems so far:
unsigned long long ullvalue = strtoull([persistentID UTF8String], NULL, 0);
NSNumber * numberID = [[NSNumber alloc] initWithUnsignedLongLong:ullvalue];
MPMediaPropertyPredicate * predicate = [MPMediaPropertyPredicate predicateWithValue:numberID forProperty:MPMediaItemPropertyPersistentID];
[numberID release];
// And so on.
Hope this helps anyone else who ran into this problem.
If I understand you correctly, you're trying to convert an NSString to an NSNumber. To do this, you can use a NSNumberFormatter. Take a look at the numberFromString: method.
NSNumberFormatter Class Reference
I just had to do the same thing. The Query Predicate for MPMediaItemPropertyPersistentID has to be passed in as NSNumber. I found this answer on converting NSString to NSNumber from another StackOverflow post:
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber * persistentIDasNumber = [f numberFromString:persistantID];
[f release];
MPMediaPropertyPredicate * predicate = [MPMediaPropertyPredicate predicateWithValue:persistentIDasNumber forProperty:MPMediaItemPropertyPersistentID];
This worked for me.
I ran into something very similar and worked out the following:
NSNumber *musicIdentifier = [[[NSNumberFormatter alloc] init] numberFromString: persistentID];
MPMediaQuery *query = [[MPMediaQuery alloc] initWithFilterPredicates:[NSSet setWithObject:[MPMediaPropertyPredicate predicateWithValue:musicIdentifier forProperty:MPMediaItemPropertyPersistentID]]];
MPMediaItem *item = query.items.firstObject;

How to get an NSDate from an integer passed in as and id?

This is driving me nuts so I hope someone can help an Objective-C noob figure this out. Here's the deal:
I'm developing an iPhone app using Titanium Appcelerator and I'm trying to create an add-on module in XCode that will allow me to send an event to the iPhone calendar. What I'd like to do is to calculate the date and time in terms of seconds since Jan 1, 2001 (GMT) and send it directly to calendar without having to mess with the string-to-date stuff that seems always to return the wrong time. To this point, I've not been able to get the integer into the event date fields, both of which are NSDate types.
Titanium takes arguments from Javascript and compiles it into object code, so I can call my "cal" object like this:
var startDate = 316367923;
var endDate = 316367923;
var results = cal.newEvent(startTime,endTime)
. . . and this is how the "cal" object receives that call:
-(BOOL)newEvent:(id)args {
id startDate = [args objectAtIndex:0];
id endDate = [args objectAtIndex:1];
...
What I'm hoping to do get these integers into the event object:
EKEventStore *eventDB = [[EKEventStore alloc] init];
EKEvent *theEvent = [EKEvent eventWithEventStore:eventDB];
...
theEvent.startDate = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate: (int) startDate];
theEvent.endDate = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate: (int) endDate];
This compiles with no errors but causes my app to bomb nonetheless, so I'm figuring I've got something missing in my understanding. Any idea of where I'm going wrong?
Thanks,
Mark
Quite probably 'args' is an NSArray, hence startDate and endDate are objects, not literals. You probably want to do something like:
theEvent.startDate = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:[startDate intValue]];
If e.g. startDate is an NSNumber. Otherwise, check out the Titanium docs to find out the type of numbers passed in.
This may not be the cause of your crash, but the initWithTimeIntervalSinceReferenceDate NSDate method is expecting an NSTimeInterval (defined as typedef double NSTimeInterval) not an integer.
i.e.: Its method signature is:
- (id)initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)seconds
I'd bet startDate and endDate are being converted to NSNumber objects instead of ints. Therefore, try the following lines:
theEvent.startDate = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate: (int) [startDate intValue]];
theEvent.endDate = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate: (int) [endDate intValue]];
I doubt that your crash has anything to do with it but, you might be leaking. The lines:
theEvent.startDate = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate: (int) startDate];
theEvent.endDate = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate: (int) endDate];
assign an object with retain count 1, if the setter for startDate and EndDate takes ownership of them (I don't know if java will do that or not so I might be wrong) you must store them in a local variable before assigning them to theEvent, so that you can call release afterwards.
so it would look like this:
NSDate *sDate = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:[startDate intValue]];
theEvent.startDate = sDate;
[sDate release];

DatePicker stopping CoreData working as expected

I have an app which saves text and the date from a UIDatePicker and then shows that note if you got back into that date in the UIDatePicker.
It works great! Only I have found that setting the UIDatePicker date to today stops CoreData working.
It's only when I run this setDate line does it stop core data from working. The app runs fine without crashing, it just doesn't save any data. If I comment that line out, it works a charm. But I need to have the UIDatePicker on today when the app loads.
I use this when the application starts:
NSDate *now = [[NSDate alloc] init];
[datePicker setDate:now];
This to fetch the note:
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSEntityDescription *testEntity = [NSEntityDescription entityForName:#"DatedText" inManagedObjectContext:self.managedObjectContext];
[fetch setEntity:testEntity];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"dateSaved == %#", datePicker.date];
[fetch setPredicate:pred];
NSError *fetchError = nil;
NSArray *fetchedObjs = [self.managedObjectContext executeFetchRequest:fetch error:&fetchError];
if (fetchError != nil) {
NSLog(#"fetchError = %#, details = %#",fetchError,fetchError.userInfo);
}
noteTextView.text = [[fetchedObjs objectAtIndex:0] valueForKey:#"savedText"];
And this to save the note:
NSManagedObject *newDatedText;
newDatedText = [NSEntityDescription insertNewObjectForEntityForName:#"DatedText" inManagedObjectContext:self.managedObjectContext];
[newDatedText setValue:noteTextView.text forKey:#"savedText"];
[newDatedText setValue:datePicker.date forKey:#"dateSaved"];
NSError *saveError = nil;
[self.managedObjectContext save:&saveError];
if (saveError != nil) {
NSLog(#"[%# saveContext] Error saving context: Error = %#, details = %#",[self class], saveError,saveError.userInfo);
}
Remember NSDate saves not only DD/MM/YYYY but also HH:MM:SS.
At a guess I think when you pick a DD/MM/YYYY from the picker, it saves with a default time of 0:00:00 but in the case above when you set the picker date to now you are actually manipulating the HH:MM:SS to something else (even though you don't see it manually).
To illustrate what I'm trying to say, when you fetch is with a predicate of (dateSaved == picker.date) it is looking for a date in the format DD/MM/YYYY 00:00:00 and for arguments sake you may have saved it on DD/MM/YYYY 09:00:01.
You will need to do some formatting of your NSDate attribute if you want this to work.
Datepicker is by default set to todays date only. You don't need to do it manually.