How to initialise NSDate in a parameterised constructor in Objective C? - 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_];

Related

How to convert this NSTimeInterval line from Swift to Objective-C

How to convert this into Objective-C ?
let date = NSDate(timeIntervalSinceReferenceDate: interval)
I tried this:
NSDate *date = [NSDate timeIntervalSinceReferenceDate: (interval)];
It generates an error of course.
Tells me: No known class method for selector timeIntervalSinceReferenceDate:
Also here is some context:
NSTimeInterval interval = [[NSDate alloc]init].timeIntervalSinceReferenceDate + [NSDate weekInSeconds].doubleValue;
NSDate *date = [NSDate timeIntervalSinceReferenceDate: (interval)];
return [self isSameWeekAsDate:(date)];
timeIntervalSinceReferenceDate: is not a method implemented on the NSDate class. When bridging from Objective-C to Swift, Swift does some renaming with initializers.
In order to initialize an NSDate object in Objective-C with a reference date, you must call either the class method dateWith... or the instance method initWith...:
NSDate * const date = [NSDate dateWithTimeIntervalSinceReferenceDate: timeInterval];
or...
NSDate * const date = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate: timeInterval];
I've added the const here to make this more closely match the Swift code with the let declaration.

How to display the time whenever value of the current minute changes (with Cocoa)?

Ok this one really has me stumped. The challenge seems simple but has a few restrictions that are really throwing me off.
Gist of the program:
(1) When the minute changes the application should output the current time with the format HH:mm:ss.
(2) When the second changes the application should output a character for every second and a different character for every tenth second (i.e. ".........|.........|"). Here it is important to note that I cannot just count seconds; rather, the program must know the value of the second of the current time and output the corresponding character only if it is divisible by 10.
Restrictions:
(1) The program can have 2 classes -- one for the minute display and one for the second display.
(2) Each class can contain only one void method that takes a timer as a parameter.
(3) The main function will create an object of each class and 2 NSTimers. The first timer must have its interval set at 0.5 and must target the minutes object. The second timer must have its interval set at 0.1 and must target the seconds object.
I would love to just set the first time to fire every minute and the second timer to fire ever second, but I am not allowed to tamper with that. This is as far as I got before I was stumped:
main.m:
MinuteDisplay *minutes = [[MinuteDisplay alloc] init];
__unused NSTimer *minutesTimer =
[NSTimer scheduledTimerWithTimeInterval:0.5
target:minutes
selector:#selector(minuteChange:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] run];
MinuteDisplay.m:
- (void)minuteChange:(NSTimer *)timer
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss"];
NSDateFormatter *testFormatter = [[NSDateFormatter alloc] init];
[testFormatter setDateFormat:#"ss"];
NSDate *now = [NSDate date];
NSString *nowString = [dateFormatter stringFromDate:now];
NSString *testString = [testFormatter stringFromDate:now];
if ([testString isEqual:#"00"]) {
NSLog(#"%#", nowString];
}
}
Example output:
01:09:00
//.5 second interval
01:09:00
I haven't even bothered with the second half of this challenge yet because I got stumped here. This technically works, but I know I'm doing it wrong, since the output isn't dependent upon the program knowing the value of the minute has changed. Also, because the timer is set to fire every .5 seconds, I get two lines of output every minute instead of just one (which does not satisfy the specs).
Can anyone suggest where to go from here? This one really has my mind boggled.
If you can not modify the timer interval, the other alternative is to use an instance variable in your MinuteDisplay class, for example, displayString. And modify the minuteChange method as follows:
- (void)minuteChange:(NSTimer *)timer
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss"];
NSDateFormatter *testFormatter = [[NSDateFormatter alloc] init];
[testFormatter setDateFormat:#"ss"];
NSDate *now = [NSDate date];
NSString *nowString = [dateFormatter stringFromDate:now];
NSString *testString = [testFormatter stringFromDate:now];
if ([testString isEqual:#"00"]) {
if(![nowString isEqualToString:dispString]) {
dispString = nowString;
NSLog(#"*************** %#", dispString);
}
}
}
This will make sure that it is logged only once a minute
Working minuteChange method:
- (void)minuteChange:(NSTimer *)timer
{
NSDate *now = [NSDate date];
if (!self.lastTime) {
self.lastTime = [NSDate date];
}
if (now > self.lastTime) {
if ([[self.seconds stringFromDate:now] isEqualToString:#"00"]) {
printf("\n%s", [[self.output stringFromDate:now] UTF8String]);
self.lastTime = now;
}
}
self.output = [[NSDateFormatter alloc] init];
[self.output setDateFormat:#"HH:mm:ss"];
self.seconds = [[NSDateFormatter alloc] init];
[self.seconds setDateFormat:#"ss"];
}
Working secondChange method:
- (void)secondChange:(NSTimer *)timer
{
long secondsSince1970 = time(NULL);
if (!self.lastSecond) {
self.lastSecond = secondsSince1970;
}
if (secondsSince1970 > self.lastSecond) {
if (secondsSince1970 % 10 == 0) {
printf("|");
self.lastSecond = secondsSince1970;
} else {
printf(".");
self.lastSecond = secondsSince1970;
}
}
}
Changes to minuteChange:
The most important change was the addition of the lastTime ivar to the class. Now it's simple...if lastTime is null, assign it the value of the current date. If current time is greater than last time, test if it's a new minute and then output the time if it is. ONLY update lastTime if a new minute was output (this ensures that the time is only displayed once per minute).
secondChange:
Took a different approach with this one and now I'm wishing I had gone with straight C the first time around. This one is so simple! Check if the current second is greater than the last second. If so, check if it's divisible by 10. If so, print "|". If not, print ".". Super easy and actually more accurate than setting the timer to fire every second. Sheesh.

NSLocale currency symbol, show before or after amount value

I am using StoreKit to implement an in app purchase store in my application.
I have a custom design and it means that the value of the price should be white and large, and the currency symbol smaller, darker and aligned to the top of the price value.
I can get the currency symbol without any problems by using the NSLocale in SKproduct's priceLocale property, and the value of the price in the price property.
My problem is knowing when I should put the currency symbol before the price and when to put it after the price.
Examples:
$5,99
0,79€
I could easily use the NSNumberFormatter to get this worked out "out of the box", but since my layout defines a different style for the value and currency symbol, I've found myself in a position where a more manual workaround is required.
Any thoughts ?
You have everything you need in your SKProduct instance. Just use NSNumberFormatter in conjunction and that's it.
NSNumberFormatter *priceFormatter = [[NSNumberFormatter alloc] init];
[priceFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
for (SKProduct *product in response.products) {
[priceFormatter setLocale:product.priceLocale];
NSLog(#"Price for %# is: %#",product.localizedTitle,[priceFormatter stringFromNumber:product.price]);
}
Swift 3+
let priceFormatter = NumberFormatter()
priceFormatter.numberStyle = .currency
for product in response.products {
priceFormatter.locale = product.priceLocale
let localizedPrice = priceFormatter.string(from: product.price)
print("Price for \(product.localizedTitle) is: \(localizedPrice)")
}
The locale object doesn't seem to provide this information directly, but of course the number formatter must know it. You're not supposed to ask (new-style) number formatters for their format directly, although that'll probably work, and you can then look for the currency symbol, ¤, in the format string.
Possibly better would be to create a CFNumberFormatter, which does explicitly allow you to view its format, and then inspect that string:
// NSLocale and CFLocale are toll-free bridged, so if you have an existing
// NSNumberFormatter, you can get its locale and use that instead.
CFLocaleRef usLocale = CFLocaleCreate(NULL, CFSTR("en_US"));
CFNumberFormatterRef usFormatter = CFNumberFormatterCreate(NULL, usLocale, kCFNumberFormatterCurrencyStyle);
CFLocaleRef frLocale = CFLocaleCreate(NULL, CFSTR("fr_FR"));
CFNumberFormatterRef frFormatter = CFNumberFormatterCreate(NULL, frLocale, kCFNumberFormatterCurrencyStyle);
NSString * usString = (__bridge NSString *)CFNumberFormatterGetFormat(usFormatter);
NSString * frString = (__bridge NSString *)CFNumberFormatterGetFormat(frFormatter);
NSUInteger loc = ([usString rangeOfString:#"¤"]).location;
NSLog(#"Currency marker at beginning for US? %#", (loc == 0) ? #"YES" : #"NO");
loc = ([frString rangeOfString:#"¤"]).location;
NSLog(#"Currency marker at end for FR? %#", (loc == [frString length] - 1) ? #"YES" : #"NO");
I use this solution (Swift):
let currencyFormat = CFNumberFormatterGetFormat(CFNumberFormatterCreate(nil, locale, .CurrencyStyle)) as NSString
let positiveNumberFormat = currencyFormat.componentsSeparatedByString(";")[0] as NSString
let currencySymbolLocation = positiveNumberFormat.rangeOfString("¤").location
return (currencySymbolLocation == 0) ? .Before : .After
The accepted answer should be fixed since the CFNumberFormatterGetFormat sometimes (for some locales) returns double value: ¤##,#00.0;-¤##,#00.0 which includes a negative number format. Make sure to parse that string.
My solution for this was to set the decimal style and set the minimum number of significant digits.
static NSNumberFormatter *NumberFormatter;
if (!NumberFormatter) {
NumberFormatter = [[NSNumberFormatter alloc] init];
[NumberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[NumberFormatter setUsesSignificantDigits:YES];
[NumberFormatter setMinimumSignificantDigits:2];
}
NSString *formattedNumberString = [NumberFormatter stringFromNumber:#(valueInEuro)];
NSString *stringInEuro = [NSString stringWithFormat:#"€ %#", formattedNumberString];
I have created an extension of SKProduct, putting things where they belong imho.
extension SKProduct
{
var localizedPrice: String {
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = .CurrencyStyle
numberFormatter.locale = self.priceLocale
numberFormatter.formatterBehavior = .Behavior10_4
return numberFormatter.stringFromNumber(self.price)!
}
}
That way of formatting is, by the way, also exactly what Apple suggests in the In-App Purchase Programming Guide, section Retrieving Product Information.
Swift 3
An extension function on Locale:
extension Locale {
func IsCurrenySymbolAtStart() -> Bool {
let currencyFormatter = NumberFormatter()
currencyFormatter.numberStyle = .currency
currencyFormatter.locale = self
let positiveFormat = currencyFormatter.positiveFormat as NSString
let currencySymbolLocation = positiveFormat.range(of: "¤").location
return (currencySymbolLocation == 0)
}
}
Usage:
let displayCurrencySymbolAtStart = NSLocale.current.IsCurrenySymbolAtStart()

Getting an NSDate via NSDateFormatter from a string of unknown format

I know there are many NSDateFormatter questions on here, so if I duplicate, I'm sorry. I just couldn't find anything that was quite what Im asking.
From all the questions here on SO, I have come to the conclusion that -[NSDateFormatter dateFromString:] will always return NULL if your formatter object doesn't have the correct date format. How do you get a date from a string if you don't know the format? I'm trying to get a date from a UITextField.
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[NSLocale currentLocale]];
[formatter setLenient:YES];
NSDate *tempDate = [formatter dateFromString:self.birthdayTxtfld.text];
self.currentCustomer.birthday = ([self.birthdayTxtfld.text isEqualToString:#""]) ? NULL : tempDate;
[formatter release];
tempDate is always NULL.
I think your taking the wrong approach. I would on the other hand restrict and format the UITextField so the user has to enter the date in a specific format. Or just use a date picker. There are just way too may different inputs the user could give you.
Or you can read through this: NSDate
Another option is to create a list of accepted date formats:
#define DATEFORMATS #[#"MM/dd/yyyy", #"MM/dd/yy",...
Then Have a method that you pass the date string to and check if you can format it:
+ (NSDateFormatter*)getDateFormat:(NSString*)dateString {
NSArray *dateFormats = DATEFORMATS;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
NSDate *date = nil;
for (NSString *dateFormat in dateFormats) {
[formatter setDateFormat:dateFormat];
date = [formatter dateFromString:dateString];
if (date) {
return formatter;
}
}
return nil;
}
If you get nil its not a date or its in a format you don't support. Otherwise you will have the correct format you need. You can switch this around to return the date instead of the format. I have it this way because I needed the format not the date for a project.
While I'm in agreement with #Jaybit that you probably need to ditch the text box and use a better input, the answer to this specific question lies in some crafty string parsing. Whenever you are doing string parsing, RegEx is your friend. Web developers end up having to do this crap all the time. This example is in JavaScript, but the RegEx ought to be portable enough that it works in ObjC:
http://www.codingoptimist.com/2009/07/using-javascript-and-regex-to-parse.html
You can do this with RegExKit or NSRegularExpression

Editing value of an Object and duplicating it

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.