How to convert a keyedArchiver to a string and back again - nsstring

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?

Related

Sometimes PHAsset fetchAssetsWithLocalIdentifiers:options method return a empty fetch result

Has anyone encountered this problem? Any advice will be appreciated.
Code as below:
Sometimes the resultAsset is empty. Maybe happened on iOS9.3 occasionally.
- (PHAsset*)retrievePHAssetFromLocalIdentifier:(NSString*)localIdentifier {
if (!localIdentifier) {
return nil;
}
NSArray *localIdentifiers = #[localIdentifier];
PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:localIdentifiers options:nil];
PHAsset *resultAsset = [result firstObject]; //Sometimes the resultAsset is empty. Maybe happened on iOS9.3 occasionally.
if (!resultAsset) {
NSLog(#"can't retrieve PHAsset from localIdentifier:%#",localIdentifier);
}
return resultAsset;
}
This issue happened when choosing photos from "my photo stream". Finally, I got this workaround to solve it. I hope it helps you.
-(PHAsset*)workAroundWhenAssetNotFound:(NSString*)localIdentifier{
__block PHAsset *result = nil;
PHFetchOptions *userAlbumsOptions = [PHFetchOptions new];
userAlbumsOptions.predicate = [NSPredicate predicateWithFormat:#"estimatedAssetCount > 0"];
PHFetchResult *userAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumMyPhotoStream options:userAlbumsOptions];
if(!userAlbums || [userAlbums count] <= 0) {
return nil;
}
[userAlbums enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx1, BOOL *stop) {
PHFetchOptions *fetchOptions = [PHFetchOptions new];
fetchOptions.predicate = [NSPredicate predicateWithFormat:#"self.localIdentifier CONTAINS %#",localIdentifier];
PHFetchResult *assetsFetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:fetchOptions];
[assetsFetchResult enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop1) {
if(asset){
result = asset;
*stop1 = YES;
*stop = YES;
}
}];
}];
return result;
}
Reference link:How to get photo from My Photo Stream Album

Nested bridge transfer call with ARC

I'm trying to get the email address of a contact and the type of the email address (work/home). This is the code I've written
//Assume that 'personRef' of type ABRecordRef is available
....
ABMultiValueRef emailRef = ABRecordCopyValue(personRef, kABPersonEmailProperty);
NSMutableArray *emailAddresses = nil, *emailAddressLabels = nil;
int ctr = ABMultiValueGetCount(emailRef);
if(ctr!=0) {
emailAddresses = [[NSMutableArray alloc]init];
emailAddressLabels = [[NSMutableArray alloc]init];
for(int i=0; i<ctr; i++) {
NSString *eId = (__bridge_transfer NSString*)ABMultiValueCopyValueAtIndex(emailRef, i);
[emailAddresses addObject:eId];
CFStringRef label = ABMultiValueCopyLabelAtIndex (emailRef, i);
if(label!=NULL) {
NSString *eType = (__bridge_transfer NSString*)ABAddressBookCopyLocalizedLabel(label);
if([eType isEqualToString:#""]) {
[emailAddressLabels addObject:#"Email"];
} else {
[emailAddressLabels addObject:eType];
}
CFRelease(label);
}
}
}
The code crashes at CFRelease(label), but to prevent memory leak, I should be doing it. When I try the following
NSString *eType = (__bridge_transfer NSString*) ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex (emailRef, i));
I get the following warning from ARC
1. Call to function 'ABMultiValueCopyLabelAtIndex' returns a Core Foundation object with a +1 retain count
2. Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1
Now the question I have is, How to do a nesting __bridge_transfer call?
NSString *eType = (__bridge_transfer NSString*)
ABAddressBookCopyLocalizedLabel(
ABMultiValueCopyLabelAtIndex (emailRef, i) /* <-- this object is leaked */
);
This code is invalid because you leak the Label here (which I realise is your point I guess?).
You should run that code under the Instruments NSZombie instrument, it will trace all the retain/releases and you'll have a clue what's going on, because frankly, looking at the code, I don't see why it's wrong.

UITableView: cell.textLabel.text error: [__NSArrayI isEqualToString:]

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]);
}
}

How to store images and keys to NSMutableDictionary

I'm receiving an error when writing to my NSMutableDictionary "dictionary".
When implementing (below) from my item list class:
- (void)setImage:(UIImage *)i forKey:(NSString *)s
{
[dictionary setObject:i forKey:s];
// Create full path for image
NSString *imagePath = [self imagePathForKey:s];
// Turn image into JPEG data,
NSData *d = UIImageJPEGRepresentation(i, 0.5);
// Write it to full path
[d writeToFile:imagePath atomically:YES];
}
From DetailViewController:imagePickerController:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *oldKey = [_detailItem imageKey];
//did the item already have an image
if (oldKey) {
//delete old image
[[RoomList sharedStore] deleteImageForKey:oldKey];
}
//get picked image from info dictionary
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
//create CFUUID object; it knows how to create uique identifier strings
CFUUIDRef newUniqueID = CFUUIDCreate(kCFAllocatorDefault);
//create string from unique identifier
CFStringRef newUniqueIDString = CFUUIDCreateString(kCFAllocatorDefault, newUniqueID);
//use unique ID to set our item's imageKey
NSString *key = (__bridge NSString *)newUniqueIDString;
[_detailItem setImageKey:key];
NSLog(#"image key: %#", key);
[_detailItem setImage:image forKey:key];
//tell objects ointed to by newUniqueIDString and newUniqueID to lose an owner to avoid
//memory leak
CFRelease(newUniqueIDString);
CFRelease(newUniqueID);
//remove image picker (dismiss method)
[self dismissViewControllerAnimated:YES completion:nil];
[self configureView];
}
I am being met with the following error, that is tied to the [_detailItem setImage:image forKey:key]; operation in imagePickerController: -[NSManagedObject setImage:forKey:]: unrecognized selector sent to instance 0x8195770
My guess is that either the UIImage *image, NSString *key, or both are not able to be passed to the objectForKey method "setImage:forKey". Any way of fixing this, or getting around it?
Thanks!

Core Data Fetch is Returning Nothing

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_;
}

Resources