Editing value of an Object and duplicating it - objective-c

I have an events object which is given below
NSString *name;
NSString *date;
NSInteger id;
I am storing the events object in a NSMutabelArray. I want to add to the date and store in a different array. For that I am using the code below
NSString *curDate = event.Date;
NSDate *date = [dateFormat dateFromString:curDate];
for(int i=0;i<5;i++)
{
Events *newEvent = event;
NSDate *newDate = [date dateByAddingTimeInterval:60*60*24*1];
newEvent.date = [dateFormat stringFromDate:newDate];
[deleg.events addObject:newEvent];
date = newDate;
}
So after final iteration of loop all the objects in deleg.events is having the last calculated date. How can I resolve it.
Thanks

You're not making a new event. Your line
Events *newEvent = event;
is just creating a new variable that references the exact same event object, which means you now have added the exact same object to the array 5 times.
I don't know how your Events class works. If it conforms to NSCopying, then you can use
Events *newEvent = [[event copy] autorelease];
If not, you'll have to create a brand new Events object (using [[Events alloc] init] or whatever is appropriate for the class) and populate it with the appropriate data.

Related

How to initialise NSDate in a parameterised constructor in Objective C?

This is the code written in Objective C where I am initialising values into a parameterised constructor. While all the fields are taking entries properly, I am having trouble entering in the NSDate field. Given below is the Constructor declaration, implementation and finally the method call.
//Init declaration
-(instancetype)initWithParam1:(NSNumber*)customerId_ withParam2: (NSString*)firstName_ withParam3:(NSString*)lastName_ withParam4:(NSDate*)dateOfBirth_ withParam5:(NSString*)address_ withParam6:(NSNumber*)mobileNumber_;
//Init definition
-(instancetype)initWithParam1:(NSNumber *)customerId_ withParam2:(NSString *)firstName_ withParam3:(NSString *)lastName_ withParam4:(NSDate *)dateOfBirth_ withParam5:(NSString *)address_ withParam6:(NSNumber *)mobileNumber_
{
self = [super init];
if(self)
{
self.customerId = customerId_;
self.firstName = firstName_;
self.lastName = lastName_;
self.dateOfBirth = dateOfBirth_;
self.address = address_;
self.mobileNumber = mobileNumber_;
}
return self;
//Init call
Customer *c1 = [[Customer alloc]initWithParam1:#1001 withParam2:#"Aman" withParam3:#"Zaidi" withParam4:#"22-05-1993" withParam5:#"Bangalore" withParam6:#9567812345];
Any suggestions on how to enter date in that field. Sorry for the naivety but I am totally new to Objective C and I am getting used to the syntaxes.
The date is a string.
You need to change the parameter to NSString
... withParam4:(NSString *)dateOfBirth_ ...
and in the init method convert the string to NSDate with NSDateFormatter
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = #"dd-MM-yyyy";
self.dateOfBirth = [formatter dateFromString:dateOfBirth_];

NSArray filter using NSPredicate, comparing dates

I have a Person entity where the attribute date_of_birth is declared as NSString. If I have an array of 'Person' instances and I need to filter them down to only those whose date_of_birth is less that 25/11/2005 I am using a predicate whose format when NSLogged is:
FUNCTION(date_of_birth, "yyyy_MM_dd_dateFormat") <[cd] CAST(154575908.000000, "NSDate")
where yyyy_MM_dd_dateFormat() is a category method on NSString that returns the string instance as a date.
I am not getting the expected results. Am I doing something wrong, and what is the bit where it says CAST(154575908.000000, "NSDate" and is that valid?
UPDATE: changing the date_of_birth attribute type to NSDate is not an option at the moment due to the size, maturity and complexity of the project.
Dates are best represented by NSDate, which implements inequality via earlierDate: and laterDate: methods. These answer the earlier/later date between the receiver and the parameter.
Your conversion method probably looks something like this ...
// return an NSDate for a string given in dd/MM/yyyy
- (NSDate *)dateFromString:(NSString *)string {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"dd/MM/yyyy"];
return [formatter dateFromString:string];
}
The array can be filtered with a block-based NSPredicate that uses NSDate comparison...
NSDate *november25 = [self dateFromString:#"25/11/2005"];
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(Person *person, NSDictionary *bind){
// this is the important part, lets get things in NSDate form so we can use them.
// of course it would be quicker to alter the data type, but we can covert on the fly
NSDate *dob = [self dateFromString:person.date_of_birth];
return date_of_birth == [november25 earlierDate:dob];
}];
// assumes allPeople is an NSArray of Person objects to be filtered
// and assumes Person has an NSString date_of_birth property
NSArray *oldPeople = [allPeople filteredArrayUsingPredicate:predicate];
Here, person date is type of NSDate.
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"date >= %#",person.date];
list=[list filteredArrayUsingPredicate:predicate2];
But in case you saved with NSString type , than you need to cast comparison date into string than use like instead of >=

OO Design & Core Data: Where to put aggregate calculation method?

I have a NSManagedObject company with the properties (NSString*) name and (NSDate*) lastAvailableInterim.
I would like to calculate aggregate values for an array of companies (NSArray *companies), for instance the last reporting date which equals the end of a quarter based on the following code sample:
- (NSDate*)latestAvailableInterimFor:(NSArray*)companies
{ // returns the maximum interim report date for all companies in the selection, date has to have end month = quarter end
NSCalendar *gregorian = [[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];
__block NSDate *maxLatestAvailableInterim;
[self.companies enumerateObjectsUsingBlock:^(IBCompany *company, NSUInteger idx, BOOL *stop) {
NSDateComponents *monthComponents = [gregorian components:NSMonthCalendarUnit fromDate:company.latestAvailableInterim];
NSAssert((company.latestAvailableInterim),#"LatestAvailableInterim must be <> NIL!");
NSInteger month = [monthComponents month];
if ( month % 3 == 0 ) maxLatestAvailableInterim = MAX(maxLatestAvailableInterim, company.latestAvailableInterim);
}];
return maxLatestAvailableInterim;
}
From a object oriented perspective and based on the MVC scheme, where would I put this code?
In the view controller, which handles the result value or "near" the (NSManagedObject*) company subclass or would it make sense to add a category to NSArray?
Thank you!
Not a category to NSArray. You can add a class method to your IBCompany class that receives an NSManagedObjectContext and uses it to perform a fetch to find the right date you are looking for. Try to do the finding of the right date via an NSFetchRequest instead of doing in-memory check.
You can also do it in the view controller that handles the resulting information.

How do I rewrite the UIDatePicker component?

I've noticed that the UIDatePicker doesn't work with NSHebrewCalendar in iOS 5.0 or 5.1. I've decided to try and write my own. I'm confused as to how to populate the data and how to maintain the labels for the dates in a sane and memory efficient manner.
How many rows are there actually in each component? When do the rows get "reloaded" with new labels?
I'm going to give this a shot, and I'll post as I find out, but please post if you know anything.
First off, thanks for filing the bug about UIDatePicker and the Hebrew calendar. :)
EDIT Now that iOS 6 has been released, you'll find that UIDatePicker now works correctly with the Hebrew calendar, making the code below unnecessary. However, I'll leave it for posterity.
As you've discovered, creating a functioning date picker is a difficult problem, because there are bajillions of weird edge cases to cover. The Hebrew calendar is particularly weird in this regard, having an intercalary month (Adar I), whereas most of western civilization is used to a calendar that only adds an extra day about once every 4 years.
That being said, creating a minimal Hebrew date picker isn't too complex, assuming you're willing to forego some of the niceties that UIDatePicker offers. So let's make it simple:
#interface HPDatePicker : UIPickerView
#property (nonatomic, retain) NSDate *date;
- (void)setDate:(NSDate *)date animated:(BOOL)animated;
#end
We're simply going to subclass UIPickerView and add support for a date property. We're going to ignore minimumDate, maximumDate, locale, calendar, timeZone, and all the other fun properties that UIDatePicker provides. This will make our job much simpler.
The implementation is going to start off with a class extension:
#interface HPDatePicker () <UIPickerViewDelegate, UIPickerViewDataSource>
#end
Simply to hide that the HPDatePicker is its own delegate and datasource.
Next we'll define a couple of handy constants:
#define LARGE_NUMBER_OF_ROWS 10000
#define MONTH_COMPONENT 0
#define DAY_COMPONENT 1
#define YEAR_COMPONENT 2
You can see here that we're going to hard-code the order of the calendar units. In other words, this date picker will always display things as Month-Day-Year, regardless of any customizations or locale settings that the user may have. So if you're using this in a locale where the default format would want "Day-Month-Year", then too bad. For this simple example, this will suffice.
Now we start the implementation:
#implementation HPDatePicker {
NSCalendar *hebrewCalendar;
NSDateFormatter *formatter;
NSRange maxDayRange;
NSRange maxMonthRange;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self setDelegate:self];
[self setDataSource:self];
[self setShowsSelectionIndicator:YES];
hebrewCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSHebrewCalendar];
formatter = [[NSDateFormatter alloc] init];
[formatter setCalendar:hebrewCalendar];
maxDayRange = [hebrewCalendar maximumRangeOfUnit:NSDayCalendarUnit];
maxMonthRange = [hebrewCalendar maximumRangeOfUnit:NSMonthCalendarUnit];
[self setDate:[NSDate date]];
}
return self;
}
- (void)dealloc {
[hebrewCalendar release];
[formatter release];
[super dealloc];
}
We're overriding the designated initializer to do some setup for us. We set the delegate and datasource to be ourself, show the selection indicator, and create a hebrew calendar object. We also create an NSDateFormatter and tell it that it should format NSDates according to the hebrew calendar. We also pull out a couple of NSRange objects and cache them as ivars so we don't have to constantly be looking things up. Finally, we initialize it with the current date.
Here are the implementations of the exposed methods:
- (void)setDate:(NSDate *)date {
[self setDate:date animated:NO];
}
-setDate: just forwards on to the other method
- (NSDate *)date {
NSDateComponents *c = [self selectedDateComponents];
return [hebrewCalendar dateFromComponents:c];
}
Retrieve an NSDateComponents representing whatever is selected at the moment, turn it into an NSDate, and return that.
- (void)setDate:(NSDate *)date animated:(BOOL)animated {
NSInteger units = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents *components = [hebrewCalendar components:units fromDate:date];
{
NSInteger yearRow = [components year] - 1;
[self selectRow:yearRow inComponent:YEAR_COMPONENT animated:animated];
}
{
NSInteger middle = floor([self pickerView:self numberOfRowsInComponent:MONTH_COMPONENT] / 2);
NSInteger startOfPhase = middle - (middle % maxMonthRange.length) - maxMonthRange.location;
NSInteger monthRow = startOfPhase + [components month];
[self selectRow:monthRow inComponent:MONTH_COMPONENT animated:animated];
}
{
NSInteger middle = floor([self pickerView:self numberOfRowsInComponent:DAY_COMPONENT] / 2);
NSInteger startOfPhase = middle - (middle % maxDayRange.length) - maxDayRange.location;
NSInteger dayRow = startOfPhase + [components day];
[self selectRow:dayRow inComponent:DAY_COMPONENT animated:animated];
}
}
And this is where fun stuff starts happening.
First, we'll take the date we were given and ask the hebrew calendar to break it up into a date components object. If I give it an NSDate that corresponds to the gregorian date of 4 Apr 2012, then the hebrew calendar is going to give me an NSDateComponents object that corresponds to 12 Nisan 5772, which is the same day as 4 Apr 2012.
Based on this information, I figure out which row to select in each unit, and select it. The year case is simple. I simply subtract one (rows are zero-based, but years are 1-based).
For months, I pick the middle of the rows column, figure out where that sequence starts, and add the month number to it. The same with the days.
The implementations of the base <UIPickerViewDataSource> methods are fairly trivial. We're displaying 3 components, and each one has 10,000 rows.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 3;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return LARGE_NUMBER_OF_ROWS;
}
Getting what's selected at the current moment is fairly simple. I get the selected row in each component and either add 1 (in the case of NSYearCalendarUnit), or do a little mod operation to account for the repeating nature of the other calendar units.
- (NSDateComponents *)selectedDateComponents {
NSDateComponents *c = [[NSDateComponents alloc] init];
[c setYear:[self selectedRowInComponent:YEAR_COMPONENT] + 1];
NSInteger monthRow = [self selectedRowInComponent:MONTH_COMPONENT];
[c setMonth:(monthRow % maxMonthRange.length) + maxMonthRange.location];
NSInteger dayRow = [self selectedRowInComponent:DAY_COMPONENT];
[c setDay:(dayRow % maxDayRange.length) + maxDayRange.location];
return [c autorelease];
}
Finally, I need some strings to show in the UI:
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
NSString *format = nil;
NSDateComponents *c = [[NSDateComponents alloc] init];
if (component == YEAR_COMPONENT) {
format = #"y";
[c setYear:row+1];
[c setMonth:1];
[c setDay:1];
} else if (component == MONTH_COMPONENT) {
format = #"MMMM";
[c setYear:5774];
[c setMonth:(row % maxMonthRange.length) + maxMonthRange.location];
[c setDay:1];
} else if (component == DAY_COMPONENT) {
format = #"d";
[c setYear:5774];
[c setMonth:1];
[c setDay:(row % maxDayRange.length) + maxDayRange.location];
}
NSDate *d = [hebrewCalendar dateFromComponents:c];
[c release];
[formatter setDateFormat:format];
NSString *title = [formatter stringFromDate:d];
return title;
}
#end
This is where things are a little bit more complicated. Unfortunately for us, NSDateFormatter can only format things when given an actual NSDate. I can't just say "here's a 6" and hope to get back "Adar I". Thus, I have to construct an artificial date that has the value I want in the unit I care about.
In the case of years, that's pretty simple. Just create a date components for that year on Tishri 1, and I'm good.
For months, I have to make sure that the year is a leap year. By doing so, I can guarantee that the month names will always be "Adar I" and "Adar II", regardless of whether the current year happens to be a leap year or not.
For days, I picked an arbitrary year, because every Tishri has 30 days (and no month in the Hebrew calendar has more than 30 days).
Once we've built the appropriate date components object, we can quickly turn it into an NSDate with our hebrewCalendar ivar, set the format string on the date formatter to only be producing strings for the unit we care about, and generate a string.
Assuming you've done all this correctly, you'll end up with this:
Some notes:
I've left out the implementation of -pickerView:didSelectRow:inComponent:. It's up to you to figure out how you want to notify that the selected date changed.
This doesn't handle graying out invalid dates. For example, you might want to consider graying out "Adar I" if the currently selected year isn't a leap year. This would require using -pickerView:viewForRow:inComponent:reusingView: instead of the titleForRow: method.
UIDatePicker will highlight the current date in blue. Again, you'd have to return a custom view instead of a string to do that.
Your date picker will have a blackish bezel, because it is a UIPickerView. Only UIDatePickers get the blueish one.
The components of the picker view will span its entire width. If you want things to fit more naturally, you'll have to override -pickerView:widthForComponent: to return a sane value for the appropriate component. This could involve hard coding a value or generating all the strings, sizing them each, and picking the largest one (plus some padding).
As noted previously, this always displays things in Month-Day-Year order. Making this dynamic to the current locale would be a little bit trickier. You'd have to get a #"d MMMM y" string localized to the current locale (hint: look at the class methods on NSDateFormatter for that), and then parse it to figure out what the order is.
I assume you're using a UIPickerView?
UIPickerView works like a UITableView in that you specify another class to be the dataSource/delegate, and it gets it's data by calling methods on that class (which should conform to the UIPickerViewDelegate/DataSource protocol).
So what you should do is create a new class that is a subclass of UIPickerView, then in the initWithFrame method, set it to be its own datasource/delegate and then implement the methods.
If you've not used a UITableView before, you should familiarise yourself with the datasource/delegate pattern because it's a bit tricky to get your head around, but once you understand it it's very easy to use.
If it helps, I've written a custom UIPicker for selecting countries. This works in pretty much the same way you'll want to do your class, except that I'm using an array of country names instead of dates:
https://github.com/nicklockwood/CountryPicker
With regard to the memory question, the UIPickerView only loads the labels that are visible onscreen and recycles them as they scroll off the bottom and back onto the top, calling your delegate again each time it needs to display a new row. This is very memory efficient as there will only be a few labels on screen at a time.

Adding CalTasks from an NSArray

I have an NSArray of Strings and what I want to do is create a new CalTask (for the calendar store) for every String, I want the Name of the task to be the string that is being added, the Priority and Due Date to be set in the code.
For example I have an array with the Strings To-Do 1, TD 2, TD 3.
So I want to create 3 CalTasks, the first one with the Name To-Do 1 and the second with the name TD 2 etc. See what I'm talking about. But I want all the Priorities and Due Dates to be the Same.
What you can do is loop over the NSArray and create and add a new CalTask with a predefined priority and due date:
// Set up the array
NSArray *array = [NSArray arrayWithObjects:#"TD1", #"TD2", #"TD3", nil];
// Get the calendar
CalCalendarStore *store = [CalCalendarStore defaultCalendarStore];
CalCalendar *calendar = [[store calendars] objectAtIndex:0];
// Note: you can change which calendar you're adding to by changing the index or by
// using CalCalendarStore's -calendarWithUID: method
// Define priority and due date
NSDate *dueDate = [NSDate date]; // By default due now - change as needed
CalPriority priority = CalPriorityMedium; // By default medium - change as needed
// Loop, adding tasks
for(NSString *title in array) {
// Create task
CalTask *task = [CalTask task];
task.dueDate = dueDate;
task.priority = priority;
task.title = title;
task.calendar = calendar;
// Save task
NSError *error = nil;
if(![store saveTask:task error:&error]) {
// Diagnostic error handling
NSAlert *anAlert = [NSAlert alertWithError:error];
[anAlert runModal];
}
}
Have a look at the Calendar Store Programming Guide.