Building Objective-C Command Line App? - objective-c

I'm kinda new to Objective-C and I have a basic app I'm tinkering with in Xcode that simply reads all calendar events (using EventKit) and then reformats the data and dumps it as a json encoded string.
I can get the code to run and return the data correctly on my iMac (in Xcode), but my Macbook Air returns no results using the same calendars (synced through iCloud). It also runs and returns results on a friends machine (in Xcode). If I build the app and run it from the command line, I get no results, even on the machines that it does work properly in Xcode.
Is there something extra that I'm missing here? This is the code I currently have. Can someone give me some kind of pointer as to what to try next to get this to work? I just can't figure out why it works on one machine and not the other, and why it works in Xcode and not from the command line.
Thoughts?
#import <Foundation/Foundation.h>
#import <EventKit/EKEventStore.h>
#import <EventKit/EKEvent.h>
#import <EventKit/EKCalendarItem.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
EKEventStore *store = [[EKEventStore alloc] init];
NSCalendar *currCalendar = [NSCalendar currentCalendar];
NSDateComponents *oneDayAgoComponents = [[NSDateComponents alloc] init];
oneDayAgoComponents.day = -1;
NSDate *oneDayAgo = [currCalendar dateByAddingComponents:oneDayAgoComponents
toDate:[NSDate date]
options:0];
NSDateComponents *oneYearFromNowComponents = [[NSDateComponents alloc] init];
oneYearFromNowComponents.year = 1;
NSDate *oneYearFromNow = [currCalendar dateByAddingComponents:oneYearFromNowComponents
toDate:[NSDate date]
options:0];
NSPredicate *predicate = [store predicateForEventsWithStartDate:oneDayAgo
endDate:oneYearFromNow
calendars:nil];
NSArray *events = [store eventsMatchingPredicate:predicate];
int i;
int eventCount = (int)[events count];
NSMutableArray *formattedEvents = [[NSMutableArray alloc] init];
for (i=0; i<eventCount; i++) {
NSDictionary *dict = #{
#"title" : [[events objectAtIndex:i] title],
#"starts" : [[[events objectAtIndex:i] startDate] description],
#"ends" : [[[events objectAtIndex:i] endDate] description],
};
[formattedEvents addObject:dict];
}
NSArray *eventArray = [NSArray arrayWithArray:formattedEvents];
NSData *jsondata = [NSJSONSerialization dataWithJSONObject:eventArray options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsondata encoding:NSUTF8StringEncoding];
printf("%s", [jsonString UTF8String]);
return 0;
}
}

Ended up being a sandbox issue. I had to go check the EKAuthorizationStatus of EKEntityTypeEvent's to determine if the app had access to the events and if not, request it.
Seems odd that it requested access initially from within Xcode with no issues on my iMac but never did on my Macbook but, oh well. Fixed.

Related

NSMutableArrays and memory in loops

Apologies first of all for the rather vague title but I'm not entirely sure how to categorise the question.
I'm using Xcode 8.1 on High Sierra and I've been banging my head against what really ought to be a simple piece of code. I think I'm failing to grasp something key about Objective-C memory allocation.
So, first something I seem to have sorted.
I tried the following code:
fullDetails = [[NSArray alloc] init];
for( lineContent in fileByLine)
{
fullDetails = [lineContent componentsSeparatedByString:#"\t"];
}
When I run this I get 27 entries in fileByLine and fullDetails correctly gets populated, line by line. However, nothing goes into fullDetails, despite the fact that it has been initialised. I worried that it is somehow nil until an initial value is inserted, so I also tried populating by index, with the index starting at zero. Same effect.
However, the following code works fine:
for( lineContent in fileByLine)
{
fullDetails = [[NSArray alloc] initWithArray:[lineContent componentsSeparatedByString:#"\t"]];
}
(I did say it ought to be really simple.)
So, I understand how to get the result I want but I really don't see why the first gobbet doesn't work. I guess it's something about the scope of the variable inside and outside the for loop - but why?
I now come to the real problem (for me). In the expanded gobbet:
NSString *newEntry = [[NSString alloc] init];
NSMutableArray *workingArray = [[NSMutableArray alloc] init];
for( lineContent in fileByLine)
{
fullDetails = [[NSArray alloc] initWithArray:[lineContent componentsSeparatedByString:#"\t"]];
newEntry = [fullDetails objectAtIndex:1];
[workingArray addObject:newEntry];
}
everything works fine (including newEntry) but I am unable to add the iterated newEntry items to the workingArray: I don't get an error; just 0 entries.
I've tried various mods to the addObject line. In particular, I worried that it was simply overwriting a single NSString store (although, even then, I should have had one item). So I tried:
NSString *newEntry = [[NSString alloc] init];
NSMutableArray *workingArray = [[NSMutableArray alloc] init];
for( lineContent in fileByLine)
{
fullDetails = [[NSArray alloc] initWithArray:[lineContent componentsSeparatedByString:#"\t"]];
newEntry = [fullDetails objectAtIndex:1];
[workingArray addObject:[[NSString alloc] initWithString:newEntry]];
}
and
NSMutableArray *workingArray = [[NSMutableArray alloc] init];
for( lineContent in fileByLine)
{
fullDetails = [[NSArray alloc] initWithArray:[lineContent componentsSeparatedByString:#"\t"]];
NSString *newEntry = [[NSString alloc] initWithString:[fullDetails objectAtIndex:1]];
[workingArray addObject:[[NSString alloc] initWithString:newEntry]];
}
and
NSMutableArray *workingArray = [[NSMutableArray alloc] init];
for( lineContent in fileByLine)
{
fullDetails = [[NSArray alloc] initWithArray:[lineContent componentsSeparatedByString:#"\t"]];
[workingArray addObject:[[NSString alloc] initWithString:[fullDetails objectAtIndex:1]]];
}
So, please tell me what I'm missing. I'm sure it's something pretty obvious but I can't see it.
I upgraded Xcode (to version 9.4.1) and it now works fine. So, it appears to have been a bug in the old version (although I'm loathe to cry, "bug!": it's too easy a solution). Anyway, one answer that ought to have been obvious: make sure I'm using the latest version.

Segmentation Error In Objective C

I am trying to write a program for NSDate in ubuntu (as I don't have mac so I am using Ubuntu to run my objective c programs).
While compiling the program I am getting an error "Segmentation fault (core dumped)".
Below is my code
#import <Foundation/Foundation.h>
#import <objc/objc.h>
#import <objc/Object.h>
int main()
{
NSAutoreleasePool * pool=[[NSAutoreleasePool alloc] init];
NSDate *now = [NSDate date];
NSLog(#"now %#", now);
NSTimeInterval secondsInAWeek = 7 * 24 * 60 * 60;
NSLog(#"secondsInAWeek %#", secondsInAWeek);
NSDate *lastWeek = [NSDate dateWithTimeInterval: secondsInAWeek
sinceDate:now];
NSLog(#"last week %#", lastWeek);
NSDate *nextWeek = [NSDate dateWithTimeInterval: secondsInAWeek
sinceDate:now];
NSLog(#"next week %#", nextWeek);
[pool drain];
return 0;
}
Please help me to find the error. I am able to get the output for NSDate *now, but after that i am getting the segmentation error.
Ask me if you need any more info regarding the code.
I guess I am able to get a solution to my answer. I was trying different things and this solution worked but still I don't know how it did.
#import <Foundation/Foundation.h>
#import <objc/objc.h>
#import <objc/Object.h>
int main()
{
NSAutoreleasePool * pool=[[NSAutoreleasePool alloc] init];
NSDate *now = [NSDate date];
NSLog(#"now %#", now);
NSTimeInterval secondsInAWeek = 7 * 24 * 60 * 60;
NSLog(#"secondsInAWeek %f", secondsInAWeek);
NSDate* lastWeek = [[[NSDate alloc] initWithTimeInterval:-secondsInAWeek sinceDate:now] autorelease];
NSLog(#"last week %#", lastWeek);
NSDate *nextWeek = [[[NSDate alloc] initWithTimeInterval:secondsInAWeek sinceDate:now] autorelease];
NSLog(#"next week %#", nextWeek);
[pool drain];
return 0;
}
I have changed
NSDate *lastWeek = [NSDate dateWithTimeInterval: secondsInAWeek
sinceDate:now];
with
NSDate *lastWeek = [[[NSDate alloc]
initWithTimeInterval:secondsInAWeek sinceDate:now] autorelease];
Its working fine and giving the desired output.
I still don't know why and how it worked. :)

NSMutableDictionary losing object

I'm trying to store arrays of objects in an Mutable Dictionary, but it seems like the dictionary is losing some of my arrays (or maybe the arrays are losing the data?).
Anyways, here's where I'm at:
- (NSDictionary *)getTicketsByDay:(NSArray *)tickets {
// take an array of tickets and return a dictionary with dates (given by
// NSDateFormatterShortStyle) as keys and arrays of tickets as the values
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterShortStyle];
// get NSDate object without time (only month, day, year)
unsigned int flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSMutableDictionary *datesDict = [[NSMutableDictionary alloc] init];
for (Ticket *ticket in tickets) {
NSDateComponents *ticketDateNoTimeComponents = [calendar components:flags fromDate:[ticket createdAt]];
NSDate *ticketDateNoTime = [calendar dateFromComponents:ticketDateNoTimeComponents];
NSString *dateString = [formatter stringFromDate:ticketDateNoTime];
NSMutableArray *ticketArray = [datesDict objectForKey:dateString];
NSLog(#"%lu", [ticketArray count]);
if (ticketArray == nil) {
NSLog(#"it's here: %#", dateString);
ticketArray = [[NSMutableArray alloc] init];
}
[ticketArray addObject:ticket];
NSLog(#"%lu", [ticketArray count]);
[datesDict setObject:ticketArray forKey:dateString];
}
return datesDict;
}
But then on the console, at random places (although the same places every time), I get something like
41
41
42
0
it's here: 6/29/12
1
even though the key for the previous objects was also "6/29/12". I've also had it print all the keys in the dictionary and there is only 1.
So somewhere I'm losing my data. What's going on?
I should also mention that I'm on 10.7.4 and using ARC.
The code looks fine to me (if you include suggestions from #ConradShultz)
Note that you don't need to create the ticketDateNoTime since you're using a date format, it will always generate the short format string even if the date contains a time...
So your code could be simplified to:
- (NSDictionary *)getTicketsByDay:(NSArray *)tickets {
// take an array of tickets and return a dictionary with dates (given by
// NSDateFormatterShortStyle) as keys and arrays of tickets as the values
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterShortStyle];
NSMutableDictionary *datesDict = [[NSMutableDictionary alloc] init];
for (Ticket *ticket in tickets) {
NSString *dateString = [formatter stringFromDate:[ticket createdAt]];
NSMutableArray *ticketArray = [datesDict objectForKey:dateString];
NSLog(#"%lu", [ticketArray count]);
if (ticketArray == nil) {
NSLog(#"it's here: %#", dateString);
ticketArray = [[NSMutableArray alloc] init];
[datesDict setObject:ticketArray forKey:dateString];
}
[ticketArray addObject:ticket];
NSLog(#"%lu", [ticketArray count]);
}
return datesDict;
}
From the looks of it you'll merely leak memory, and your way of replacing the dictionary entry with itself seems unusual (but I think it should work), but what makes you think you are loosing objects? You are printing the size of your array, which is different for different date strings, so maybe you just got a new date string which made it create a new array for that date?
And about the memory leaking/the unusual code: a more traditional way would be
NSMutableArray *ticketArray = [datesDict objectForKey:dateString];
if (ticketArray == nil) {
ticketArray = [[NSMutableArray alloc] init];
[datesDict setObject:ticketArray forKey:dateString];
[ticketArray release];
}
[ticketArray addObject:ticket];

EXC_BAD_ACESS error

I get that error EXC_BAD_ACESS at the following line:
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionTitle",i];
Here is the for loop where the above code line is located:
for (i=0; i < count; ++i)
{
//Save the occasionS details to NSUserDefaults
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionTitle",i];
NSString *dateVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionDate",i];
NSString *imageVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionImage",i];
[[NSUserDefaults standardUserDefaults] setValue:[[[self displayedObjects] objectAtIndex:i]
title] forKey:titleVarName];
[[NSUserDefaults standardUserDefaults] setValue:[[[self displayedObjects] objectAtIndex:i]
date] forKey:dateVarName];
[[NSUserDefaults standardUserDefaults] setValue:[[[self displayedObjects] objectAtIndex:i]
imagePath] forKey:imageVarName];
//release
[titleVarName release];
[dateVarName release];
[imageVarName release];
[self dismissModalViewControllerAnimated:YES];
}
Isn't ok to alloc objects and release them inside a for loop?
You need to use %d or %i specifier instead of %# to specify an integer. If %# is used with int then it will try to access the object at the address specified by the int. For example, if the value of i is one then it is trying to access the object at address one which will cause a bad access.
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%d",#"occasionTitle",i];
And also you don't need alloc and release here, though that is not the reason of bad access. You can use a convenience constructor.
NSString *titleVarName = [NSString stringWithFormat:#"occasionTitle%d", i];
// release not required
Do the same for dateVarName and imageVarName too.
Assuming i is an int, that line should be
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%i",#"occasionTitle",i];
%# is used for Cocoa objects, not primitives like an int, float or bool;
Use the %# format specifier only for NSObject objects.
As i is an integer in your code, you have to use %d or %i for integers.
Moreover, there is no need to include the string using %#, you can use the static string directly in your format string:
NSString *titleVarName = [[NSString alloc] initWithFormat:#"occasionTitle%i",i];

Random word From Core-data (NSString)

I have a core-data model with a Entity called Goodie with Attribute called thingsYouWant of type String.
i want to pick a random word from "thingsYouWant" when u push a button, and put that in a string with format.
but i keep getting a NSString may not respond to objectAtIndex error ;-(
Update:
here is my working code:
-( void ) viewDidLoad {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription
entityForName:#"Goodie" inManagedObjectContext:
self.managedObjectContext ]];
NSError *error = nil;
NSArray *array = [self.managedObjectContext
executeFetchRequest:request error:&error];
if (array == nil)
{
// Deal with error...
}
if(array.count > 0){
int r = random()% [array count];
goodie = [array objectAtIndex:r];
} else { // no one to fetch - generate one
goodie = [NSEntityDescription
insertNewObjectForEntityForName:#"Goodie"
inManagedObjectContext:self.managedObjectContext ];
}
- (void) winText {
NSArray *components = [[goodie thingsYouWant]
componentsSeparatedByString:#" "];
NSInteger randomIndex = (random() % [components count]);
NSString *newWord = [components objectAtIndex:randomIndex];
winLabel.text =[NSString stringWithFormat: #"Congratulations,
the carrot you have earned is -->>> %#", newWord];
}
Thank you :-D
Skov
First, array.count is an improper use of dot syntax. count is not a property of NSArray but a method call.
Second, which line is giving you the error? Are you getting it at the [array objectAtIndex:0] or at [goodie.thingsYouWant objectAtIndex:R]? If it is the latter then you need to see what the property thingsYouWant is defined as. I suspect it is a string property.
Update
If you want to grab a word out of a string then you need to split the string up into an array. Using the method -componentsSeparatedByString:. From there you can then grab one of them at random.
An example of this would be:
NSArray *components = [[goodie thingsYouWant] componentsSeparatedByString:#" "];
NSInteger randomIndex = (random() % [components count]);
NSString *newWord = [components objectAtIndex:randomIndex];
Referring to this statement of yours
Goodie with Attribute called
thingsYouWant of type String
I am with Marcus S. Zarra
Please have a look at Non-Standard Persistent Attributes to store your array in core data.