Programmatically/Manually create a MKPlacemark/CLPlacemark - ios

Problem
I have a set of placemark information (country, city, etc) and a Lat/Lon pair. I would like to use this to create an MKPlacemark object.
Discussion
It appears that this class can only be created by
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate addressDictionary:(NSDictionary *)addressDictionary
whose docs state states
You can create placemark objects manually for entities for which you already have address information, such as contacts in the Address Book. Creating a placemark object explicitly avoids the need to query the reverse geocoder object for the same information.
Perfect! I have already reverse-geocoded and wish to avoid such a query. What can I add to the dictionary?
For a list of strings that you can use for the keys of this dictionary, see the “Address Property” constants in ABPerson Reference. All of the keys in should be at the top level of the dictionary.
Which shows relevant keys
const ABPropertyID kABPersonAddressProperty;
const CFStringRef kABPersonAddressStreetKey;
const CFStringRef kABPersonAddressCityKey;
const CFStringRef kABPersonAddressStateKey;
const CFStringRef kABPersonAddressZIPKey;
const CFStringRef kABPersonAddressCountryKey;
const CFStringRef kABPersonAddressCountryCodeKey;
This falls quite short of the base traits for an MKPlacemark:
Accessing the Location Data
location property
Accessing the Placemark Attributes
name property
addressDictionary property
ISOcountryCode property
country property
postalCode property
administrativeArea property
subAdministrativeArea property
locality property
subLocality property
thoroughfare property
subThoroughfare property
region property
Accessing Geographic Information
inlandWater property
ocean property
Accessing Landmark Information
areasOfInterest property
Fortunately, the actual header file for MKPlacemark's superclass says something about the address dictionary:
// address dictionary properties
#property (nonatomic, readonly) NSString *name; // eg. Apple Inc.
#property (nonatomic, readonly) NSString *thoroughfare; // street address, eg. 1 Infinite Loop
#property (nonatomic, readonly) NSString *subThoroughfare; // eg. 1
#property (nonatomic, readonly) NSString *locality; // city, eg. Cupertino
#property (nonatomic, readonly) NSString *subLocality; // neighborhood, common name, eg. Mission District
#property (nonatomic, readonly) NSString *administrativeArea; // state, eg. CA
#property (nonatomic, readonly) NSString *subAdministrativeArea; // county, eg. Santa Clara
#property (nonatomic, readonly) NSString *postalCode; // zip code, eg. 95014
#property (nonatomic, readonly) NSString *ISOcountryCode; // eg. US
#property (nonatomic, readonly) NSString *country; // eg. United States
#property (nonatomic, readonly) NSString *inlandWater; // eg. Lake Tahoe
#property (nonatomic, readonly) NSString *ocean; // eg. Pacific Ocean
#property (nonatomic, readonly) NSArray *areasOfInterest; // eg. Golden Gate Park
So, I create a dictionary and then pass it like so:
return [[[MKPlacemark alloc] initWithCoordinate:aLocation.coordinate addressDictionary:addressDictionary] autorelease];
Unfortunately, after all that, introspection shows that the information did not stick:
NSLog(#"placemark %# from %#", placemark, addressDictionary);
NSLog(#"has %#", placemark.thoroughfare);
Prints
2012-01-31 20:14:22.545 [15450:1403] placemark <+___,-___> +/- 0.00m from {
administrativeArea = __;
postalCode = _____;
subAdministrativeArea = ___;
subThoroughfare = __;
thoroughfare = "_____";
}
2012-01-31 20:14:22.545[15450:1403] has (null)
Conclusion
So, I'm about at the end here. Has anyone figured out how to create your own MKPlacemark? Thanks.

You can subclass MKPlacemark:
In MyPlacemark.h
#interface MyPlacemark : MKPlacemark
extern NSString * const kCustomPlacemarkAddressThoroughfareKey;
extern NSString * const kCustomPlacemarkAddressSubThoroughfareKey;
extern NSString * const kCustomPlacemarkAddressLocalityKey;
extern NSString * const kCustomPlacemarkAddressSubLocalityKey;
extern NSString * const kCustomPlacemarkAddressAdministrativeAreaKey;
extern NSString * const kCustomPlacemarkAddressSubAdministrativeAreaKey;
extern NSString * const kCustomPlacemarkAddressPostalCodeKey;
extern NSString * const kCustomPlacemarkAddressCountryKey;
extern NSString * const kCustomPlacemarkAddressCountryCodeKey;
#end
In MyPlacemark.m:
#import "MyPlacemark.h"
#implementation MyPlacemark
NSString * const kCustomPlacemarkAddressThoroughfareKey = #"thoroughfare";
NSString * const kCustomPlacemarkAddressSubThoroughfareKey = #"subThoroughfare";
NSString * const kCustomPlacemarkAddressLocalityKey = #"locality";
NSString * const kCustomPlacemarkAddressSubLocalityKey = #"subLocality";
NSString * const kCustomPlacemarkAddressAdministrativeAreaKey = #"administrativeArea";
NSString * const kCustomPlacemarkAddressSubAdministrativeAreaKey = #"subAdministrativeArea";
NSString * const kCustomPlacemarkAddressPostalCodeKey = #"postalCode";
NSString * const kCustomPlacemarkAddressCountryKey = #"country";
NSString * const kCustomPlacemarkAddressCountryCodeKey = #"countryCode";
- (NSString *)thoroughfare
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressThoroughfareKey];
}
- (NSString *)subThoroughfare
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressSubThoroughfareKey];
}
- (NSString *)locality
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressLocalityKey];
}
- (NSString *)subLocality
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressSubLocalityKey];
}
- (NSString *)administrativeArea
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressAdministrativeAreaKey];
}
- (NSString *)subAdministrativeArea
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressSubAdministrativeAreaKey];
}
- (NSString *)postalCode
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressPostalCodeKey];
}
- (NSString *)country
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressCountryKey];
}
- (NSString *)countryCode
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressCountryCodeKey];
}
#end
It looks ugly, but it's the only way so far that I've found to work.

Related

Adding Custom Objects to NSMutableArray

I mainly program in Java and can't understand why this isn't working. I'm trying to create a temporary object "Judge" in my for loop. I then want to add that object to an NSMutableArray so in the end I have an array filled with different Judge objects. After the for loop I run through all the objects in the Array and they're all the last Judge Object.
The NSLog shows that "JudgeTemp" object is being assigned the right values while in the for loop. My guess is that it's not creating a new object called JudgeTemp every time but referencing the old already created JudgeTemp.
NSMutableArray *Judges = [NSMutableArray arrayWithCapacity:30];
for (int i=0; i<[courtinfoarray count]; i++) {
Judge1= [[courtinfoarray objectAtIndex:i] componentsSeparatedByString:#"|"];
Judge *JudgeTemp=[[Judge alloc]init];
[JudgeTemp setName:[Judge1 objectAtIndex:0] picture:[Judge1 objectAtIndex:1] courtroom:[Judge1 objectAtIndex:2] phone:[Judge1 objectAtIndex:3] undergrad:[Judge1 objectAtIndex:4] lawschool:[Judge1 objectAtIndex:5] opdasa:[Judge1 objectAtIndex:6] career:[Judge1 objectAtIndex:7] judgecode:[Judge1 objectAtIndex:8]];
NSLog(#"%#",[JudgeTemp getName]);
[Judges addObject:JudgeTemp];
NSLog(#"%#",[[Judges objectAtIndex:i]getName]);
}
Judges Class
#implementation Judge
NSString *name;
NSString *picture;
NSString *courtroom;
NSString *phone;
NSString *undergrad;
NSString *lawschool;
NSString *opdasa;
NSString *career;
NSString *judgecommentcode;
-(void) setName:(NSString *)n picture:(NSString *) p courtroom:(NSString *)c phone:(NSString *)ph undergrad: (NSString *) u lawschool: (NSString *)l opdasa: (NSString *) o career: (NSString *)ca judgecode: (NSString *)jcode{
name = n;
picture = p;
courtroom = c;
phone = ph;
undergrad = u;
lawschool = l;
opdasa = o;
career = ca;
judgecommentcode = jcode;
}
-(NSString*) getName{
return name;
}
The problem is with your Judge class. When you define variables directly in your #implementation they have global scope and are not instance variables. What you need to do is put those variable declarations in your #interface instead:
#interface Judge : NSObject {
NSString *name;
NSString *picture;
NSString *courtroom;
NSString *phone;
NSString *undergrad;
NSString *lawschool;
NSString *opdasa;
NSString *career;
NSString *judgecommentcode;
}
// ...
#end
Edit: Apparently you can declare them in your #implementation, you just have to wrap them in { }. See: Instance variables declared in ObjC implementation file

detect the class of a property with name in objective-c [duplicate]

This question already has answers here:
property type or class using reflection
(2 answers)
Closed 9 years ago.
I have an NSObject in objective-c at runtime and i want to know the class of a property in this object , i have the name of this property as NSString , how can I do that.
EDIT :
IntrospectionUtility class :
#implementation IntrospectionUtility
// this function returns an array of names of properties
+ (NSMutableArray*) getProperties:(Class)class
{
NSMutableArray *properties = [[NSMutableArray alloc] init];
unsigned int outCount, i;
objc_property_t *objc_properties = class_copyPropertyList(class, &outCount);
for(i = 0; i < outCount; i++) {
objc_property_t property = objc_properties[i];
const char *propName = property_getName(property);
if(propName) {
NSString *propertyName = [NSString stringWithCString:propName encoding:[NSString defaultCStringEncoding]];
[properties addObject:propertyName];
}
}
free(objc_properties);
return properties;
}
#end
class test :
#interface JustAnExample : NSObject
#property (nonatomic, strong) NSString *a;
#property (nonatomic, strong) NSString *b;
#property (nonatomic, strong) NSString *c;
#property (nonatomic, strong) NSString *d;
#end
#implementation JustAnExample
- (void) justAnExampleTest
{
NSMutableArray *attributes = [IntrospectionUtility getProperties:self.class];
for (NSString *attribute in attributes) {
//i want to know the type of each attributte
}
}
#end
i have the name of this property as NSString
You can use the function class_getProperty(Class cls, const char *name) to find the property for a given class. Then use property_getAttributes(objc_property_t property) to get the property's attributes, including the encoded type string. Read the Declared Properties section of the Objective-C Runtime Programming Guide for more info.

Using Foundation_EXPORT correctly

I have a file called Event.h:
#interface Event : NSObject
FOUNDATION_EXPORT NSString * const KP_STATUS_NEW
FOUNDATION_EXPORT NSString * const KP_STATUS_APPROVED
FOUNDATION_EXPORT NSString * const KP_STATUS_DELETED
#property (nonatomic, strong) NSString * name;
#property (nonatomic, strong) NSString * description;
#property (nonatomic, strong) NSString * status
I would like programmers who use my SDK to have access to the STATUS strings especially when setting a status for an Event object. Should I be using FOUNDATION_EXPORT like the above?
So that a programmer can just do
Event * myEvent = [[Event alloc] init];
myEvent.status = STATUS_NEW;
?
Is that the way to do it in objective-c?
By the way KP is the common prefix for the project. Should I be prefixing the status with KP or something else? What's the standard?
You can just use extern, rather than FOUNDATION_EXPORT (which I believe is what it is defined as anyway).
Using a common prefix is a good idea, given the lack of namespaces in Objective-C and this goes double for a class called Event which is a very common name.
So something like this, looks OK to me:
#import "KPEvent.h"
KPEvent * myEvent = [[KPEvent alloc] init];
myEvent.status = KP_STATUS_NEW;
or better still:
myEvent.status = KP_EVENT_STATUS_NEW;
if statuses only relate to the event class.
What you don't explain, is why you cannot use an enum, which is more elegant:
typedef enum {
KP_EVENT_STATUS_NEW,
KP_EVENT_STATUS_APPROVED,
KP_EVENT_STATUS_DELETED
} KpEventStatus;
and you can forget about that extern nonsense.

Generate MD5 hash from Objective-C object [duplicate]

This question already has answers here:
MD5 algorithm in Objective-C
(5 answers)
Closed 9 years ago.
I'd like to generate an MD5 hash for an NSObject:
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * type;
#property (nonatomic, retain) NSString * unit;
#property (nonatomic, retain) NSArray * fields;
What is the best way to do so? I've seen examples for hashing from a dictionary or an array, but not from an entire NSObject.
To generate a MD5 hash for an NSObject or a subclass of NSObject, you need to convert it into something that's easily hashable but still represents the state of the instance. A JSON string is one such option. The code looks like this:
Model.h
#import <Foundation/Foundation.h>
#interface Model : NSObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * type;
#property (nonatomic, retain) NSString * unit;
#property (nonatomic, retain) NSArray * fields;
- (NSString *)md5Hash;
#end
Model.m
#import <CommonCrypto/CommonDigest.h>
#import "Model.h"
#implementation Model
- (NSString *)md5Hash
{
// Serialize this Model instance as a JSON string
NSDictionary *map = #{ #"name": self.name, #"type": self.type,
#"unit": self.unit, #"fields": self.fields };
NSError *error = NULL;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:map
options:NSJSONWritingPrettyPrinted
error:&error];
if (error != nil) {
NSLog(#"Serialization Error: %#", error);
return nil;
}
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// Now create the MD5 hashs
const char *ptr = [jsonString UTF8String];
unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH];
CC_MD5(ptr, strlen(ptr), md5Buffer);
NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
[output appendFormat:#"%02x",md5Buffer[i]];
return output;
}
#end
Then you can easily retrieve the MD5 hash just by calling the md5Hash method
Model *obj = [Model new];
obj.name = #"...";
obj.type = #"...";
obj.unit = #"...";
obj.fields = #[ ... ];
NSString *hashValue = [obj md5Hash];
You can convert the object into a dictionary if you already have code for creating the hash:
NSDictionary *dict = [myObject dictionaryWithValuesForKeys:#[#"name", #"type", #"unit", #"fields"]];
Or you could implement <NSCoding> on your class, archive it and hash the resulting data.

How to localize text based on criterion other than language

I have an application which will be marketed in different European countries. We've gone through the process of localizing the application so that its strings are maintained in the language-specific .lproj files in the Settings.bundle. This all works fine. The problem is that there are some strings which don't key off language, but off the country where the app is run. For example, there are strings which differ between the Austrian version of the app and the German version of the app, even though both these countries speak German. When it's run for the first time, the app asks the user which country it's running in.
Is there a way in which I can maintain these country-specific strings in a resource file, and have the resource file used at run time be decided by a user setting, in this case the country where the app is running, rather than the device language?
Thanks,
Peter Hornby
Define two bundles on a singleton, fallback and preferred...
#import <Foundation/Foundation.h>
#interface Localization : NSObject
#property (nonatomic, retain) NSString* fallbackCountry;
#property (nonatomic, retain) NSString* preferredCountry;
#property (nonatomic, retain) NSDictionary* fallbackCountryBundle;
#property (nonatomic, retain) NSDictionary* preferredCountryBundle;
+(Localization *)sharedInstance;
- (NSString*) countryStringForKey:(NSString*)key;
#end
#import "Localization.h"
#implementation Localization
#synthesize fallbackCountryBundle, preferredCountryBundle;
#synthesize fallbackCountry, preferredCountry;
+(Localization *)sharedInstance
{
static dispatch_once_t pred;
static Localization *shared = nil;
dispatch_once(&pred, ^{
shared = [[Localization alloc] init];
[shared setFallbackCountry:#"country-ES"];
NSLocale *locale = [NSLocale currentLocale];
NSString *countryCode = [locale objectForKey:NSLocaleCountryCode];
[shared setPreferredCountry:[NSString stringWithFormat:#"country-%#",countryCode]];
});
return shared;
}
-(void) setFallbackCountry:(NSString*)country
{
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:country ofType:#"strings"];
self.fallbackCountryBundle = [NSDictionary dictionaryWithContentsOfFile:bundlePath];
trace(#"Fallback: %# %#",[bundlePath lastPathComponent], self.fallbackCountryBundle);
}
-(void) setPreferredCountry:(NSString*)country
{
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:country ofType:#"strings"];
self.preferredCountryBundle = [NSDictionary dictionaryWithContentsOfFile:bundlePath];
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:bundlePath isDirectory:nil];
if (!exists) warn(#"%#.strings %#", country, exists ? #"FOUND" : #"NOT FOUND");
trace(#"Preferred: %# %#",[bundlePath lastPathComponent], self.preferredCountryBundle);
}
- (NSString*) countryStringForKey:(NSString*)key
{
NSString* result = nil;
if (preferredCountryBundle!=nil) result = [preferredCountryBundle objectForKey:key];
if (result == nil) result = [fallbackCountryBundle objectForKey:key];
if (result == nil) result = key;
return result;
}
#end
Then call it from a macro function
#define countryString(key) [[Localization sharedInstance]countryStringForKey:key];
Write a default file for ES, and one file per supported language. eg:
/*
country-ES.strings
*/
"hello" = "hello";
And just get the value for the key:
countryString(#"hello");

Resources