I have a JSON request which gives prayer times. How can I use Objective-C to work out which is next? The JSON looks like this:
{
address = "<null>";
city = "<null>";
country = UK;
"country_code" = GB;
daylight = 1;
for = daily;
items = (
{
asr = "5:22 pm";
"date_for" = "2013-7-1";
dhuhr = "1:01 pm";
fajr = "2:15 am";
isha = "11:47 pm";
maghrib = "9:24 pm";
shurooq = "4:39 am";
}
);
latitude = "50.9994081";
link = "http://muslimsalat.com/UK";
longitude = "0.5039011";
"map_image" = "http://maps.google.com/maps/api/staticmap?center=50.9994081,0.5039011&sensor=false&zoom=13&size=300x300";
"postal_code" = "<null>";
"prayer_method_name" = "Muslim World League";
"qibla_direction" = "119.26";
query = "51.000000,0.500000";
state = "<null>";
timezone = 0;
title = UK;
"today_weather" = {
pressure = 1020;
temperature = 14;
};
}
My code so far is:
-(CLLocationCoordinate2D) getLocation{
CLLocationManager *locationManager = [[[CLLocationManager alloc] init] autorelease];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
[locationManager startUpdatingLocation];
CLLocation *location = [locationManager location];
CLLocationCoordinate2D coordinate = [location coordinate];
return coordinate;
}
//class to convert JSON to NSData
- (IBAction)getDataFromJson:(id)sender {
//get the coords:
CLLocationCoordinate2D coordinate = [self getLocation];
NSString *latitude = [NSString stringWithFormat:#"%f", coordinate.latitude];
NSString *longitude = [NSString stringWithFormat:#"%f", coordinate.longitude];
NSLog(#"*dLatitude : %#", latitude);
NSLog(#"*dLongitude : %#",longitude);
//load in the times from the json
NSString *myURLString = [NSString stringWithFormat:#"http://muslimsalat.com/%#,%#/daily/5.json", latitude, longitude];
NSURL *url = [NSURL URLWithString:myURLString];
NSData *jsonData = [NSData dataWithContentsOfURL:url];
if(jsonData != nil)
{
NSError *error = nil;
id result = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
NSArray *jsonArray = (NSArray *)result; //convert to an array
if (error == nil)
NSLog(#"%#", result);
NSLog(#"%#", jsonArray);
for (id element in jsonArray) {
NSLog(#"Element asr: %#", [element objectForKey:#"asr"]);
}
}
}
How can I get the current time and determine which prayer comes next?
Thank you!
You need to get the 'items' dictionary using [result objectForKey:#"items"]. Then convert the string values to NSDate's using an NSDateFormatter. Then, iterate through the new dictionary and find the time with the smallest time interval to now using [currentDate timeIntervalSinceNow]. This is your next prayer.
Get a list of the dates, then use an NSPredicate to filter that list to dates >= [NSDate date], then sort it ascending. Then the first item in the filtered, sorted array will be the next date.
Read Apple's Date and Time Programming Guide. You will find methods to convert you string times ("2:15am" etc.) into NSDate objects, methods to get the current date and time (e.g. [NSDate new] return the current date and time), and methods for comparing date/times so you can find the next one.
Related
I am building a personal budget app that tracks my purchases. I have an NSDictionary of NSArray that looks like this:
{
46 = (
{
DATE = "11/14/2019";
ID = 21;
PLACE = Kroger;
PRICE = "23.32";
},
{
DATE = "11/14/2019";
ID = 22;
PLACE = Walmart;
PRICE = "12.54";
}
);
47 = (
{
DATE = "11/18/2019";
ID = 23;
PLACE = Cinco;
PRICE = "65.32";
}
);
}
The keys in the NSDictionary are determined by strftime using %W. Basically I have a set limit I can spend per week (i.e. 190) and I want in each titleForHeaderInSection: to have a total value that is determined by each field's PRICE in each key.
I currently have the following on the app:
The following calculate the NSDictionary listed above along with the total value:
Note: arrFoodInfo is a NSDictionary of just the entries without the week numbers
for (NSDictionary *dict in self.arrFoodInfo) {
NSString *date = [dict objectForKey:#"DATE"];
[totalPrices addObject:[dict objectForKey:#"PRICE"]];
for (NSString *price in totalPrices) {
if ([totalPrices containsObject:price]) {
_sumPrices += [price floatValue]; // Calculates total value
[totalPrices removeObject:price];
}
}
NSDateFormatter *SDF = [[NSDateFormatter alloc]
init];
[SDF setDateStyle:NSDateFormatterMediumStyle];
[SDF setDateFormat:#"MM/dd/yyyy"];
NSDate *convertedDate = [SDF dateFromString:date];
NSDateComponents *dateComponents = [calendar components:NSCalendarUnitYear | NSCalendarUnitWeekOfYear fromDate:convertedDate];
NSInteger week = dateComponents.weekOfYear;
NSInteger index = week;
NSNumber *key = #(index);
NSMutableArray *weekArray = weeksDictionary[key];
if (!weekArray) {
weekArray = [NSMutableArray array];
weeksDictionary[key] = weekArray;
}
[weekArray addObject:dict];
}
At the moment, I am getting the total of each PRICE but the total value should be different per section. What is the best option to determine this?
This is my first time using JSON. In my app I am fetching some data from "Open Weather API". I get back some array, with way to many entries, and I need to search ones that are on specific day and return temp.day and weather.icon from them. So far I managed to fetch data propely. As far I know i should use some FOR loop with IF inside it, but I am pulling my hairs out how to do it in Objective C.
There is my fetch method:
- (void)fetchedData:(NSData *)responseData {
//parse out the json data
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData //1
options:kNilOptions
error:&error];
NSArray* weatherList = [json objectForKey:#"list"];
NSLog(#"everything %#", weatherList ); //3
}
And part array from NSLog:
{
clouds = 92;
deg = 265;
dt = 1456221600;
humidity = 100;
pressure = "1007.96";
rain = "0.24";
speed = "5.12";
temp = {
day = "4.11";
eve = "4.62";
max = "4.87";
min = "2.78";
morn = "4.11";
night = "2.78";
};
weather = (
{
description = "light rain";
icon = 10d;
id = 500;
main = Rain;
}
);
},
{
clouds = 88;
deg = 268;
dt = 1456308000;
humidity = 98;
pressure = "1012.04";
rain = "1.31";
speed = "6.35";
temp = {
day = "3.57";
eve = "3.74";
max = "3.74";
min = "2.38";
morn = "2.53";
night = "2.38";
};
weather = (
{
description = "light rain";
icon = 10d;
id = 500;
main = Rain;
}
);
},
{
clouds = 20;
deg = 243;
dt = 1456394400;
humidity = 100;
pressure = "1015.42";
snow = "0.25";
speed = "5.08";
temp = {
day = "2.95";
eve = "4.09";
max = "4.54";
min = "-0.65";
morn = "1.74";
night = "-0.65";
};
weather = (
{
description = "light snow";
icon = 13d;
id = 600;
main = Snow;
}
);
},
{
clouds = 80;
deg = 273;
dt = 1456480800;
humidity = 100;
pressure = "1016.63";
snow = "0.04";
speed = "2.79";
temp = {
day = "0.2";
eve = "2.79";
max = "2.79";
min = "-3.33";
morn = "-2.7";
night = "-2.42";
};
weather = (
{
description = "light snow";
icon = 13d;
id = 600;
main = Snow;
}
);
},
{
clouds = 87;
deg = 132;
dt = 1456567200;
humidity = 0;
pressure = "1007.07";
rain = "0.22";
snow = "0.03";
speed = 5;
temp = {
day = "3.96";
eve = "1.48";
max = "3.96";
min = "-3.12";
morn = "-3.12";
night = "-1.59";
};
weather = (
{
description = "clear sky";
icon = 01d;
id = 800;
main = Clear;
}
);
}
What are good practices? Do I fetch proper enough big part of JSON?
Should I use dome NSDictionary too? Any ideas are welcome!
Thanks
1) As per documentation given the time is in GMT format so you need to convert the time in IST to compare
here is code to convert the time . I dont know your time zone I converted it in Indian Standard Time
for ( NSDictionary *obj in weatherList) {
NSDateFormatter *inDateFormatter = [[NSDateFormatter alloc] init];
inDateFormatter.dateFormat = #"dd-MM-yyyy";
inDateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:#"IST"];
NSDate *inDate = [inDateFormatter dateFromString:[obj objectForKey:#"dt"]];
if ([self isItToday:inDate]) {
NSDictionary * tempDict = [obj objectForKey:#"temp"];
NSDictionary * weatherDict = [[obj objectForKey:#"weather"]objectForKey:0];
NSString * dayTemp = [tempDict objectForKey:#"day"];//only storing for time being you can use where you needed
NSString * icon = [weatherDict objectForKey:#"icon"];//only storing for time being you can use where you needed
}
}
2) Now you need to compare it with todays date
+ (BOOL)isItToday:(NSDate *)date {
if (date != nil) {
NSString *givenDateString = [self stringFromDate:date inDateFormat:#"dd-MM-yyyy"];
NSString *toDayString = [self stringFromDate:[NSDate date] inDateFormat:#"dd-MM-yyyy"];
NSDate *givenDate = [self dateFromString:givenDateString inDateFormat:#"dd-MM-yyyy"];
NSDate *toDay = [self dateFromString:toDayString inDateFormat:#"dd-MM-yyyy"];
if ([givenDate isEqualToDate:toDay]) {
return YES;
} else {
return NO;
}
} else {
return NO;
}
}
+ (NSString *)stringFromDate:(NSDate *)date inDateFormat:(NSString *)inDateformat
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:kLocaleIdentifierEN_US_POSIX];
[dateFormatter setDateFormat:inDateformat];//#"dd/MM/yyyy"
return [dateFormatter stringFromDate:date];
}
+ (NSDate *)dateFromString:(NSString *)stringDate inDateFormat:(NSString *)inDateformat
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:kLocaleIdentifierEN_US_POSIX];
[dateFormatter setDateFormat:inDateformat];//#"dd/MM/yyyy"
return [dateFormatter dateFromString:stringDate];
}
also add this line
#define kLocaleIdentifierEN_US_POSIX [NSLocale localeWithLocaleIdentifier:#"en_US_POSIX"]
Use following code,
double comparingdt = 1456308000;
__block NSString *tempday, *weathericon;
[weatherList enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSDictionary *weather, NSUInteger idx, BOOL * _Nonnull stop) {
*stop = ([weather[#"dt"] doubleValue] == comparingdt);
if (*stop)
{
tempday = weather[#"temp"][#"day"];
weathericon = [weather[#"weather"] firstObject][#"icon"];
}
}];
I need to get particular values from below JSON response, The values are I have mentioned below
Reg no: (Need to show callout)
Lat : (Need to use drop pin on map)
Long : (Need to use drop pin on map)
Name : (Need to show callout)
Age : (Need to show callout)
NOTE : The school of array values getting from server so It will Increase based on A , B and C categories. Its not static!
{
A = {
school = (
{
reg_no = 1;
latitude = "22.345";
longitude = "-12.4567";
student = (
{
name = "akila";
age = "23";
}
);
city = "<null>";
state = TN;
},
{
reg_no = 2;
latitude = "22.345";
longitude = "-12.4567";
student = (
{
name = "sam";
age = "23";
}
);
city = "<null>";
state = TN;
}, {
reg_no = 3;
latitude = "22.345";
longitude = "-12.4567";
student = (
{
name = "lansi";
age = "23";
}
);
city = "<null>";
state = TN;
}
);
Schoolname = "Good School";
categories = school;
};
}
My Code (Below code not working) :
if (data) {
NSError *error;
NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves | NSJSONReadingMutableContainers error:&error];
NSDictionary *response = JSON[#"response"];
for (NSDictionary *entry in response[#"A"][#"school"]) {
NSString *regNo = entry[#"reg_no"];
NSString *name = entry[#"student"][#"name"];
NSString *age = entry[#"student"][#"age"];
double latitude = [entry[#"latitude"] doubleValue];
double longitude = [entry[#"longitude"] doubleValue];
MKPointAnnotation *myAnnotation = [[MKPointAnnotation alloc] init];
myAnnotation.coordinate = CLLocationCoordinate2DMake(latitude, longitude);
myAnnotation.title = name;
myAnnotation.subtitle = [NSString stringWithFormat:#"Reg: %#, Age: %#", regNo, age];
[mapView addAnnotation:myAnnotation];
}
Try NSJSONSerialization
NSError *e = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: &e];
if (!jsonArray) {
NSLog(#"Error parsing JSON: %#", e);
} else {
for(NSDictionary *item in jsonArray) {
NSLog(#"Item: %#", item);
}
}
You can use NSJSONSerialization to parse the JSON, then you can access the values you need in a loop using Objective-C's subscripting syntax. Once you have all these, you can add the entries as MKAnnotations on an MKMapView to get what you want. Note that MKAnnotation is a protocol, so you'll need to create a class that implements it.
MKMapView *mapView = [MKMapView new];
NSDictionary *parsedJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
for (NSDictionary *entry in parsedJSON[#"A"][#"school") {
NSString *regNo = entry[#"reg_no"];
NSString *name = entry[#"student"][#"name"];
NSString *age = entry[#"student"][#"age"];
double latitude = [entry[#"latitude"] doubleValue];
double longitude = [entry[#"longitude"] doubleValue];
id<MKAnnotation> annotation = [/*class implementing MKAnnotation*/ new];
annotation.coordinate = CLLocationCoordinate2DMake(latitude, longitude);
annotation.title = name;
annotation.subtitle = [NSString stringWithFormat:#"Reg: %#, Age: %#", regNo, age];
[mapView addAnnotation:annotation];
}
u should iterate entry[#"student"] to get the data of every student
entry[#"student"][#"name"] will get no data even if entry[#"student"] only have one
Your JSON file that you have provided for lacks of 'response' key in it. However, you are searching as NSDictionary *response = JSON[#"response"];. That could be the problem. Other than this, you can try NSDictionary's valueForKey: method to search deeper in your JSON.
In my project, I try to compare a known location to an inputted location by using block sortedArrayUsingComparator:^(id a, id b). I have a dictionary array called locationArray containing a lat long and a station number that corresponds to that lat long point. I try to compare each locationArray station to the inputted station. I do this by taking the absolute value of the difference between the two which gives me a distance. Then I try to sort locationArray based on the distance from the inputted station from closest to furthest away.
//locationArray
#define kStation #"station"
#define kLatitude #"latitude"
#define kLongitude #"longitude"
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"499CSV" ofType:#"csv"];
NSString *csvString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSArray *locations = [csvString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSMutableArray *CSVArray = [NSMutableArray array];
NSCharacterSet *whiteSPNewLine = [NSCharacterSet whitespaceAndNewlineCharacterSet];
for (NSString * location in locations)
{
NSArray *components = [location componentsSeparatedByString:#","];
double latitude = [[components[0] stringByTrimmingCharactersInSet:whiteSPNewLine] doubleValue];
double longitude = [[components[1] stringByTrimmingCharactersInSet:whiteSPNewLine] doubleValue];
NSString *station = [components[2] stringByTrimmingCharactersInSet:whiteSPNewLine];
NSDictionary *dict = #{kLatitude: #(latitude),
kLongitude: #(longitude),
kStation: station};
[CSVArray addObject:dict];
}
NSLog(#"The contents of CSVArray = %#",[CSVArray description]);
{
latitude = "41.674364";
longitude = "-81.23700700000001";
station = 40150;
},
{
latitude = "41.67517";
longitude = "-81.235038";
station = 40763;
},
{
latitude = "41.673106";
longitude = "-81.24017499999999";
station = 39175;
}, ...
My block code that directly follows locationArray.
NSArray *orderedPlaces = [CSVArray sortedArrayUsingComparator:^(id a, id b) {
NSDictionary *dictA;
NSDictionary *dictB;
NSString *locA;
NSString *locB;
int distanceA;
int distanceB;
dictA = (NSDictionary *)a;
dictB = (NSDictionary *)b;
NSLog(#"dictA = %#", dictA);
NSLog(#"dictB = %#", dictB);
locA = [dictA objectForKey:kStation];
locB = [dictB objectForKey:kStation];
NSLog(#"locA = %#", locA);
NSLog(#"locB = %#", locB);
distanceA = abs(stationNumber-[locA intValue]);
distanceB = abs(stationNumber-[locB intValue]);
NSLog(#"distanceA = %d", distanceA);
NSLog(#"distanceB = %d", distanceB);
if (distanceA < distanceB) {
return NSOrderedAscending;
} else if (distanceA > distanceB) {
return NSOrderedDescending;
} else {
return NSOrderedSame;
}
}];
NSLog(#"The contents of array = %#",[orderedPlaces description]);
The block runs but it's not sorting locationsArray as intended. orderedPlaces returns an unsorted locationsArray. By running NSLOG on the block components, I see that it successfully recognizes locationsArray and creates the distance object. I must be missing something because I use the same code in a different part of my project where I compare locationsArray to the lat long of the user's location and it works well. Please help me identify the issue keeping it from working as intended.
*If you need any more information or clarification, just ask.
I don't understand your sort block. You're calculating distance A as abs(stationNumber-[locA intValue]).
What is stationNumber? Is that some fixed integer index? How can the difference between a fixed station number and the station numbers in your array have anything to do with distances?
It seems to me that your distance values should be
(target.lat - this_station.lat)^2 + (target.long - this_station.long)^2
That will give you the square of the pythagorean distance between your target point and one of the stations you are comparing. Then in your comparison you select the item who's distance squared is less.
You can skip the square root, because you're just comparing them. That will make your distance calculations much faster.
Try this:
NSArray *orderedPlaces = [CSVArray sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
^(id a, id b) means ^void (id a, id b) and your return value is ignored.
I have some JSON data which is pulled from a URL. The code I have written works fine to download the JSON and parse it, but I cannot seem to access it how I need too, especially where the data is contained as a sub-element of another one.
Here is the JSON format:
{
address = "<null>";
city = "<null>";
country = UK;
"country_code" = GB;
daylight = 1;
for = daily;
items = (
{
asr = "5:22 pm";
"date_for" = "2013-7-1";
dhuhr = "1:01 pm";
fajr = "2:15 am";
isha = "11:47 pm";
maghrib = "9:24 pm";
shurooq = "4:39 am";
}
);
latitude = "50.9994081";
link = "http://muslimsalat.com/UK";
longitude = "0.5039011";
"map_image" = "http://maps.google.com/maps/api/staticmap?center=50.9994081,0.5039011&sensor=false&zoom=13&size=300x300";
"postal_code" = "<null>";
"prayer_method_name" = "Muslim World League";
"qibla_direction" = "119.26";
query = "51.000000,0.500000";
state = "<null>";
timezone = 0;
title = UK;
"today_weather" = {
pressure = 1020;
temperature = 14;
};
}
(These are Islamic prayer times.)
My Objective-C so far is this:
-(CLLocationCoordinate2D) getLocation{
CLLocationManager *locationManager = [[[CLLocationManager alloc] init] autorelease];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
[locationManager startUpdatingLocation];
CLLocation *location = [locationManager location];
CLLocationCoordinate2D coordinate = [location coordinate];
return coordinate;
}
//class to convert JSON to NSData
- (IBAction)getDataFromJson:(id)sender {
//get the coords:
CLLocationCoordinate2D coordinate = [self getLocation];
NSString *latitude = [NSString stringWithFormat:#"%f", coordinate.latitude];
NSString *longitude = [NSString stringWithFormat:#"%f", coordinate.longitude];
NSLog(#"*dLatitude : %#", latitude);
NSLog(#"*dLongitude : %#",longitude);
//load in the times from the json
NSString *myURLString = [NSString stringWithFormat:#"http://muslimsalat.com/%#,%#/daily/5.json", latitude, longitude];
NSURL *url = [NSURL URLWithString:myURLString];
NSData *jsonData = [NSData dataWithContentsOfURL:url];
if(jsonData != nil)
{
NSError *error = nil;
id result = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
NSArray *jsonArray = (NSArray *)result; //convert to an array
if (error == nil)
NSLog(#"%#", result);
NSLog(#"%#", jsonArray);
for (id element in jsonArray) {
NSLog(#"Element: %#", [element description]);
}
}
}
When running this code, the only output I get is a list of element names (address, city, country, so on). items is given, but not its child elements. I understand that this is what I am asking the code for with:
for (id element in jsonArray) {
NSLog(#"Element: %#", [element description]);
}
but I do not know how to move onto the next step.
The only data values which I require are in fact the times themselves (so, items>asr, items>dhuhr, etc).
How can I get these values themselves and then save them as values I can work with?
Thank you!
(...); - is Array
{...}; - is Dictionary
so your "element" is Dictionary
use objectForKey:
example:
for (id element in jsonArray) {
NSLog(#"Element asr: %#", [element objectForKey:#"asr"]); // or element[#"asr"]
}
NSArray *jsonArray = (NSArray *)result; //convert to an array
This doesn't 'convert', it's just you promising the compiler that result is really an NSArray. And in this case it's a lie.
Your code is currently just printing a list of the keys in the dictionary that is returned in the JSON. Try this to get to the list of items (it's an array so you need to deal with there possibly being multiple entries):
NSDictionary *result = [NSJSONSerialization ...
for (NSDictionary *itemDict in result[#"items"]) {
NSLog(#"item: %#", itemDict);
}
Then you can extract the times.
You can extract info by following:
NSError* error = nil;
NSDictionary *userInfo; //your main data
if([NSJSONSerialization class])
userInfo = [NSJSONSerialization JSONObjectWithData:[request responseData] options:kNilOptions error:&error];
//to extract items
NSDictionary *items = [[[userInfo objectForKey:#"items"] JSONValue] objectAtIndex:0];