-(NSString*)urlEscapeString:(NSString *)unencodedString
{
CFStringRef originalStringRef = (__bridge_retained CFStringRef)unencodedString;
NSString *s = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,originalStringRef, NULL, (CFStringRef)#"!*'\"();:#&=$,/?%#[]%", kCFStringEncodingUTF8);
CFRelease(originalStringRef);
return s;
}
Related
I want to convert a custom object into a propertyList. Then later I want to retrieve it via NSOutlineView. I use a NSKeyedArchiver/unArchiver to convert my object to and from a NSData.
In my custom object (OutlineItem):
-(id)pasteboardPropertyListForType:(NSPasteboardType)type
{
if ([[[info draggingPasteboard] types] containsObject: kPrivateDragUTI])
{
NSData *itemData = [NSKeyedArchiver archivedDataWithRootObject:self];
NSString* itemString = [[NSString alloc]initWithData:itemData encoding:NSASCIIStringEncoding];
return itemString;
}
In NSOutlineView:
- (id<NSPasteboardWriting>)outlineView:(NSOutlineView *)outlineView pasteboardWriterForItem:(id)item
{
OutlineItem* anItem = item;
return anItem;
}
Later:
- (BOOL)outlineView:(NSOutlineView *)destinationOutlineView
acceptDrop:(id <NSDraggingInfo>)info item:(id)destinationParentItem
childIndex:(NSInteger)destinationIndex
{
NSPasteboard *pboard = [info draggingPasteboard];
OutlineItem* pbItem = nil;
if ([[pboard types] containsObject: kPrivateDragUTI])
{
NSString* stringForData = [[info draggingPasteboard] stringForType:kPrivateDragUTI];
NSData *data = [stringForData dataUsingEncoding:NSASCIIStringEncoding];
StringForData looks OK, but data is nil at this point.
Why is data nil?
I'm downloading an NSManagedObject. Assigning an NSString to valueForKey#"username" of that object. Next, I assign that string to a cell.textLabel.text in UITableView and I'm getting an exception.
The following is the code thats throwing an exception:
-(NSString *)fetchUserName
{
if ([fetchedUserNameObject objectAtIndex:0]!= NULL)
{
recipientUser = (User*)(fetchedUserNameObject);
NSString *userName = (NSString*)[recipientUser valueForKey:#"username"];
return userName;
}
}
.....
NSString *recipientUserName = [self fetchUserName]
......
- (IBAction)reviewButtonPressed:(UIBarButtonItem *)sender
{
NSLog(#"Recipient UserName from Review Button %#", recipientUserName);
PDReviewVC *modalVC = [self.storyboard instantiateViewControllerWithIdentifier:#"pdReview"];
UINavigationController *navBar=[[UINavigationController alloc]initWithRootViewController:modalVC];
modalVC.recipientUserName= self.recipientUserName;
[self presentViewController:navBar animated:YES completion:NULL];
}
Log
2013-08-30 11:51:49.393 Time[1188:c07] Recipient UserName from Review Button (
iphone3gs
)
Now in PDReviewVC:
.h
#property (nonatomic, retain) NSString * recipientUserName;
.m
#synthesize recipientUserName;
...
//cellForRowAtIndexPath
if (indexPath.section == 0)
{
cell.textLabel.text = recipientUserName;
}
I get the following error on cell.textLabel.text = recipientUserName;:
2013-08-30 11:51:54.679 Time[1188:c07] -[__NSArrayI isEqualToString:]: unrecognized selector sent to instance 0xb889390
2013-08-30 11:53:53.303 Time[1188:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI isEqualToString:]: unrecognized selector sent to instance 0xb889390'
*** First throw call stack:
UPDATE
NSLog for the following code:
if ([fetchedUserNameObject objectAtIndex:0]!= NULL)
{
recipientUser = (User*)(fetchedUserNameObject);
NSLog(#"User %#", recipientUser);
NSString *userName = (NSString*)[recipientUser valueForKey:#"username"];
NSLog (#"userName from fetchUserName: %#", userName);
NSLog(#"%#", userName);
return userName;
}
else return NULL;
NSLog:
2013-08-30 12:36:29.495 Time[1341:c07] User (
"<User: 0xa55cd60> (entity: User; id: 0xa5a0ae0 <x-coredata://3B273CFB-1CAA-4FA0-95DC-BA9420219380-1341-000007F42366B21A/User/piphone3gs> ; data: <fault>)"
)
2013-08-30 12:36:29.496 Time[1341:c07] userName from fetchUserName: (
iphone3gs
)
2013-08-30 12:36:29.496 Time[1341:c07] (
iphone3gs
)
The answer to your question is right in your error message. :-)
It's telling you that the object you think is of type NSString is really NSArray. Thus, NSArray does not respond to any method called isEqualToString.
In your fetchUserName method you are casting an object as NSString and returning it, but apparently you are getting an NSArray here. When you set this object to your label's text property, something goes on behind the scenes to ask if the current string property is equal to the one you're trying to set. Then, error.
Try placing this line before you return in the fetchUserName method:
NSLog(#"%#",username);
return username;
Then modify your question with the console results of this NSLog() and we can help you figure out what is inside the array.
Ok, before you return the username object, do this:
NSString *username;
id object = [recipient valueForKey:#"username"];
if ([object isKindOfClass:[NSString class]]) {
username = (NSString *)object;
return username;
} else if ([object isKindOfClass:[NSArray class]]) {
NSArray *returnedArray = (NSArray *)object;
if (returnedArray.count > 0) {
id arrayMember = [returnedArray objectAtIndex:0];
if ([arrayMember isKindOfClass:[NSString class]]) {
username = (NSString *)arrayMember;
return username;
}
}
}
return nil;
You are casting [recipientUser valueForKey:#"username"] as an NSString to get your code to work, when it is in fact returning an NSArray. Remove the cast (NSString*) and get the code working so you are in fact pulling a string rather than an array from recipientUser.
To debug, I'd suggest changing your logging to:
if ([fetchedUserNameObject objectAtIndex:0]!= NULL)
{
recipientUser = (User*)(fetchedUserNameObject);
NSArray *userNames = [recipientUser valueForKey:#"username"];
for (int i=0; i < [userNames count]; i++) {
NSLog(#"Username %i is %#", i, [userNames objectAtIndex:i]);
}
}
In this app, I am using an sqlite database that was created by another app. I can query the database using the Firefox SQLite Manager and see that what I am searching for does exist in the database. I have reduced my query to something very simple, but still get nothing returned in my NSFetchedResultsController.
Here is my code:
- (NSFetchedResultsController*) frc {
if (!frc_) {
#autoreleasepool {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription * entity = [NSEntityDescription entityForName:#"INDEX" inManagedObjectContext:[ManagedObjectContext moc]];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:15];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"lemma = 'dog'"];
[NSFetchedResultsController deleteCacheWithName:nil];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"lemma" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:[ManagedObjectContext moc]
sectionNameKeyPath:#"lemma"
cacheName:nil];
aFetchedResultsController.delegate = (id<NSFetchedResultsControllerDelegate>)self;
NSError *error = nil;
if (![aFetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved Error %#, %#", error, [error userInfo]);
abort();
}
self.frc = aFetchedResultsController;
}
}
return frc_;
}
There is an entity called "INDEX" in the data model. To confirm that the entity has the lemma property, I show the content of its lemma property, getting this:
po [[entity propertiesByName] objectForKey:#"lemma"]
(id) $3 = 0x1104f740 (<NSAttributeDescription: 0x1104f740>), name lemma, isOptional 1, isTransient 0, entity INDEX, renamingIdentifier lemma, validation predicates (
), warnings (
), versionHashModifier (null)
userInfo {
}, attributeType 700 , attributeValueClassName NSString, defaultValue (null)
Examining the contents of the aFetchedResultsController immediately after the fetch (where it is assigned to self.frc) gives this:
po aFetchedResultsController
(NSFetchedResultsController *) $4 = 0x1105c5c0 <NSFetchedResultsController: 0x1105c5c0>
po [[aFetchedResultsController fetchedObjects] count]
(id) $1 = 0x00000000 <nil>
I suppose the problem here is something very basic, but I don't know what I am overlooking.
I found the answer at the Ray Wenderlich site. The problem is that the sqlite file must be moved from the application bundle to the application documents directory.
This is done by copying it form the bundle into the documents directory if it is not already there.
Here is the code for creating the persistent store coordinator:
- (NSPersistentStoreCoordinator *)pstore {
if (pstore_ != nil) {
return pstore_;
}
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"words.sqlite"];
NSURL *storeUrl = [NSURL fileURLWithPath: storePath];
// THIS IS THE KEY PIECE TO IMPORTING AN EXISTING SQLITE FILE:
// Put down default db if it doesn't already exist
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle]
pathForResource:#"words" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
pstore_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.mom];
if(![pstore_ addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:storeUrl options:options error:&error]) {
// Error for store creation should be handled in here
}
return pstore_;
}
I got issues when I analyse my code.
Espacially on this methods.
I pucked up this lines on the web. It works well but I got a Portential leak message
Potential leak on the object. (on the return).
// Encode a string to embed in an URL.
+ (NSString *)encodeToPercentEscapeString:(NSString*)string {
return (NSString *)
CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef) string,
NULL,
(CFStringRef) #"!*'();:#&=+$,/?%#[]",
kCFStringEncodingUTF8);
}
// Decode a percent escape encoded string.
+ (NSString *)decodeFromPercentEscapeString:(NSString *)string {
return (NSString *)
CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL,
(CFStringRef) string,
CFSTR(""),
kCFStringEncodingUTF8);
}
Thanks.
I just found this post:
iPhone memory leaking?
I have to release the CFString with CFRelease();
+ (NSString *)encodeToPercentEscapeString:(NSString*)string {
CFStringRef str = CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef) string,
NULL,
(CFStringRef) #"!*'();:#&=+$,/?%#[]",
kCFStringEncodingUTF8);
NSString *s = [NSString stringWithString:(NSString *)str];
CFRelease(str);
return s;
}
Im working on an iOS app where I need to store and retrieve from an SQLite DB, a representation of a NSString that has subscripts. I can create a NSString at compile time with a constant:
#"Br\u2082_CCl\u2084"
\u2082 is a 2 subscript, \u2084 is a 4 subscript. What im storing in the SQLite db is:
"Br\u2082_CCl\u2084"
But what I can not figure out how to do, is reconvert that back into an NSString. The data comes back from the db as a char * "Br\\u2082_CCl\\u2084" Stripping out the extra slash has made no difference in my feeble experiments. I need a way get that back into an NSString - Thanks!
You need one of the NSString stringWithCString class methods or the corresponding initWithCString instance methods.
I solved the problem like this - error checking removed for clarity -
the unicode string comes into the parameter stringEncoded from the db like:
"MgBr\u2082_CH\u2082Cl\u2082"
+(NSString *)decodeUnicodeBytes:(char *)stringEncoded {
unsigned int unicodeValue;
char *p, buff[5];
NSMutableString *theString;
NSString *hexString;
NSScanner *pScanner;
theString = [[[NSMutableString alloc] init] autorelease];
p = stringEncoded;
buff[4] = 0x00;
while (*p != 0x00) {
if (*p == '\\') {
p++;
if (*p == 'u') {
memmove(buff, ++p, 4);
hexString = [NSString stringWithUTF8String:buff];
pScanner = [NSScanner scannerWithString: hexString];
[pScanner scanHexInt: &unicodeValue];
[theString appendFormat:#"%C", unicodeValue];
p += 4;
continue;
}
}
[theString appendFormat:#"%c", *p];
p++;
}
return [NSString stringWithString:theString];
}