NSDateFormatter Incompatible Type Error - objective-c

I am receiving the following error in Xcode:
warning: incompatible Objective-C types 'struct NSDate *', expected 'struct NSString *' when passing argument 1 of 'setUpdate:' from distinct Objective-C type
The error happens when I am trying to save the formatted string to myObj.update
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
//myObj is an object with instance variable 'update' as a NSString string
myObj.update = [formatter dateFromString:#"2011-03-17T18:15:05Z"];
[formatter release];
I know I am doing something wrong that is minor but can't pinpoint. Thanks for the help! :)

dateFromString: returns an instance of NSDate, not a string. You need to make your update property an NSDate. The compiler is basically telling you that the method is returning a date, but you're trying to assign that to a string property, and that's just not gonna be healthy!

Related

Why do I have to define the class of the pointer?

I'm studying Objc with the book from BigNerdRanch 'Objective-C Programming from Aaron Hillegass' and there's this thing that keeps puzzling me.
I understand that the complier needs to know what kind of variable I'm talking about so i have to declare the var type before assigning a value.
int myNum = 10;
Fine. But when it comes to ObjC classes, what's the reason for declaring the type of pointer if I have to declare it right after the =, when I alloc and init it?
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
Obviously the *dateFormatter object is an istance of NSDateFormatter, I wrote it in the allocation. Why do i have to declare it at the beginning too?
'Cause if I try to do something like
NSString *timeZone = [NSTimeZone systemTimeZone];
Xcode clearly warns me with 'Incompatible pointer types initializing 'NSString *' with an expression of type 'NSTimeZone *'.
I feel like I'm missing something. Sorry if it's a dumb question, trying to learn.
Here the real question is rather "Why do I have to define the correct class of the pointer?"...
The answer is: you may want to use the variable in some other context as well. If you message [NSTimeZone systemTimeZone] directly, then the compiler may be able to deduce the type, but what if you message the variable? If you go with the weaker-typed
id tz = [NSTimeZone systemTimeZone];
then there's much less opportunity for the compiler to check for errors if you use tz where an NSTimeZone * is expected than it could if you declared it as NSTimeZone *tz.
As an even clearer example, suppose you have a method:
- (NSTimeZone *) userSpecifiedTimeZone {
id timeZone = [NSTimeZone timeZoneWithAbbreviation:self.timeZoneName];
if (timeZone == nil)
timeZone = [NSTimeZone timeZoneWithName:self.timeZoneName];
if (timeZone == nil)
timeZone = self.timeZoneName;
return timeZone;
}
See the bug?
Xcode won't catch it, since it's perfectly valid to assign any object to a variable of type id, and to return an id from a method whose return type is any object type, and to subsequently assign that id to another id variable, or try to send messages to it, in the calling code.
You'll find this bug—if you don't catch it early with your own human eyes—only at run time, and only when the user enters a bogus time zone name, and you then try to use that time zone name (wrongly returned as this method's result) as an NSTimeZone object.
Compare to the statically-typed version:
- (NSTimeZone *) userSpecifiedTimeZone {
NSTimeZone *timeZone = [NSTimeZone timeZoneWithAbbreviation:self.timeZoneName];
if (timeZone == nil)
timeZone = [NSTimeZone timeZoneWithName:self.timeZoneName];
if (timeZone == nil)
timeZone = self.timeZoneName; //Clang calls shenanigans here
return timeZone;
}
Clang rightly objects that assigning an NSString * to a variable typed as NSTimeZone * is suspicious.
You don't have to define the class of the pointer, but the potential otherwise for bugs like the one shown above is why we do it.
But when it comes to ObjC classes, what's the reason for declaring the
type of pointer if I have to declare it right after the =, when I
alloc and init it?
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
You're not declaring the type of pointer twice. There's a lot going on in this statement. The first occurrence of 'NSDateFormatter' is telling the compiler that dataformatter is a pointer to this type of object, whereas the second occurrence of 'NSDateFormatter' is calling the 'alloc' method in the NSDateFormatter class. Same word, two completely different meanings.
The first thing that happens is [NSDateFormatter alloc] which is calling the (class) method 'alloc' in the 'NSDateFormatter' class. This returns an (empty) instance of an NSDateFormatter object in which the method 'init' is called. A pointer to the resultant object is then stored in your 'dateFormatter' variable, and we tell the compiler that this is a pointer to an NSDateFormatter object.
Think of it like this:
NSDateFormatter *dateFormatter; Create a pointer to an NSDateFormatter object.
newDate = [NSDateFormatter alloc]; Create an empty NSDateFormatter object by calling the class method alloc in NSDateFormatter
[newDate init]; Initialise it by calling the onject's 'init' method
dateformatter = *newDate; Assign a pointer to it to my variable.

Setting today's date to NSDate

I have a NSDate property as below and I wish to set it with today's date.
#property (nonatomic, copy, readwrite) NSDate *todayDate;
I tried the following but I am getting an error:
NSDate *date = [[NSDate alloc] init];
self.todayDate = [date isToday];
I guess, isToday just checks whether the date is today's date and doesn't set the date.
I am getting following 2 errors:
Implicit conversion of 'BOOL' (aka 'signed char') to 'NSDate *' is disallowed with ARC
Incompatible integer to pointer conversion assigning to 'NSDate *' from 'BOOL' (aka 'signed char');
How do I set NSDate to today's date in Objective C?
NSDate has you covered:
NSDate *today = [NSDate date];
This is a common pattern in Apple's frameworks: semantic "factory" class methods. This call replaces your calls to alloc and init, and is a preferred way to work with common objects. It's also a nice pattern to emulate in your own classes :-)
On of the core concepts which should shed light on the "why" is that of immutability. Many objects (NSString, NSNumber, etc) are considered (at least in practice) to be immutable. This means they get their value exactly once: during init. This leads the the "other" answer also being an instantiation-time technique: a custom initializer.
NSDate *today = [[NSDate alloc] initWithTimeIntervalSinceNow: 0];
This will set your property to todays date. The error is because, as you suspect, you are assigning a boolean to a pointer.
self.todayDate = [NSDate date];

Very strange NSPredicate behavior when logging

I'm trying to create a predicate to sort an array of objects that have a date property. However, when I try to NSLog the predicate, an exception is raised and I get a strange error.
Here's the code:
NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy"];
NSDate * minDate = [dateFormatter dateFromString:#"2011"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"date >= %# ", minDate];
NSLog(predicate);
Here's the error:
2012-02-04 12:45:58.400 MyApp[42746:17303] -[NSComparisonPredicate length]: unrecognized selector sent to instance 0x9359000
So, why is this happening and how can I fix it?
try: NSLog(#"%#", predicate)
NSLog expects a NSString, that works as an format string. The number of format specifiers in the format string will specify, how many arguments need to follow.
from the doc:
NSLog
Logs an error message to the Apple System Log facility.
void NSLog (
NSString *format,
...
);
Discussion
Simply calls NSLogv, passing it a variable number of
arguments.

NSDateFormatter and setDateFormat: and SSS

Using NSDateFormatter, here is my input :
00:20:11,026 (NSString)
And here is the output i want :
"00:20:11,026"; (NSDate)
but what I obtained is :
"1970-01-01 00:20:11 +0100" (NSDate)
// SSS is missing and there is too much information...
Here is what i coded :
...
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"hh:mm:ss,SSS"];
[dateFormatter dateFromString:#"00:20:11,026"];
...
Well, first of all, your NSDateFormatter is just fine.
Judging by the output, it looks like you're just NSLogging an actual NSDate object, and expecting the format to be representative of the NSDateFormatter that was used to create it. All NSDate objects will be formatted the same if you NSLog them, regardless of whether you got them from an NSDateFormatter or not. The "1970-01-01 00:20:11 +0100" string that you are worried about is actually supplied by NSDate's implementation of the -description method.
So my guess is that, while the fractional seconds (SSS) are missing from your log statement, they actually are present and accounted for in the NSDate object. Furthermore, the "too much information" is also just an artifact of the default description provided by NSDate. I'm fairly certain your actual NSDate contains all of the information you want it to.

NSDate's initWithString: is returning nil

I'm using Stig Brautaset's JSON framework serialize some objects, including NSDates (which are not directly supported).
I decided to use NSDate's description as the JSONFragment representation of a date (I don't care about the minor loss of precision incurred in doing so).
To extend Stig Brautaset's JSON framework to include NSDates, I defined a category:
#interface NSDate (NSDate_JSON) <JSONInitializer>
-(NSString *) JSONFragment;
#end
To recreate an NSDate (and other classes) from JSON, I defined a protocol with the following initializer:
#protocol JSONInitializer <NSObject>
-(id) initWithJSONRepresentation: (NSString *) aJSONRepresentation;
#end
I'm having issues with this initializer. In NSDate's case, it just calls initWithString:, and that's were I get into trouble: it always returns nil. This is the implementation:
#import "NSDate+JSON.h"
#implementation NSDate (NSDate_JSON)
-(NSString *) JSONFragment{
NSString *strRepr = [self description];
return [strRepr JSONFragment];
}
-(id) initWithJSONRepresentation:(NSString *)aJSONRepresentation{
return [self initWithString: aJSONRepresentation]; //returns nil!
}
#end
I'm not sure what's going on. Besides, the compiler warns me that the initWithString: method in initWithJSONRepresentation: could not be found.
Anybody knows what might be going on?
The full source code for a test case is available here.
You should always use an NSDateFormatter when attempting to convert a string into a date or vice versa. -initWithString: does exist on the Mac, but not on iOS. It requires the string to be in an extremely precise format. Using a date formatter is by far the superior solution.
And as a side note, your code would break if Apple ever decided to change the format of -[NSDate description].
Your test program is:
NSDate *d1 = [[[NSDate alloc] init] autorelease];
NSString *repr = [d1 JSONFragment];
NSDate *dd = [[[NSDate alloc] initWithString:[d1 description]] autorelease ];
NSDate *d2 = [[[NSDate alloc] initWithJSONRepresentation:repr] autorelease];
Your -JSONFragment category method is:
-(NSString *) JSONFragment{
NSString *strRepr = [self description];
return [strRepr JSONFragment];
}
What’s happening in this method is that you obtain a string representation of that date using -description, and then a JSON representation of that string using -JSONFragment.
In SBJSON, -JSONFragment returns the representation of a given object as JSON data. The JSON specification requires that strings are quoted. In your program:
NSString *repr = [d1 JSONFragment];
repr contains a string like #"\"2011-04-29 10:20:30 -0600\"". Because of the quotes, that string is not a valid string to be used with -[NSDate initWithString:].
If you change:
NSString *repr = [d1 JSONFragment];
to:
NSString *repr = [[d1 JSONFragment] JSONFragmentValue];
so that SBJSON parses that fragment and returns a string without the quotes, it should work.
The reason initWithString: can't be found is that unless you're importing Foundation and didn't show us here, your code can't see NSDate.h, so it doesn't know that initWithString: exists.
Dave's quite right about relying on description and using an NSDateFormatter instead. It doesn't seem likely that description will change, but there's no guarantee that it will continue to be valid input for initWithString: which has a strict input requirement:
A string that specifies a date and time value in the international string representation format — YYYY-MM-DD HH:MM:SS ±HHMM, where ±HHMM is a time zone offset in hours and minutes from GMT (for example, “2001-03-24 10:45:32 +0600”).
You must specify all fields of the format string, including the time zone offset, which must have a plus or minus sign prefix.
If your string differs in any way (including, as has become apparent, having quotes in it), you'll get nil.