I am trying to save an image in core data but after I select it in the simulator, it doesn't show up in the image view? Here is a sample of the code:
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (fugitive_.image != nil) {
self.fugitiveImage.image = [UIImage imageWithData:fugitive_.image];
}
}
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
fugitive.image = UIImagePNGRepresentation([info objectForKey:UIImagePickerControllerEditedImage]);
[self dismissModalViewControllerAnimated:YES];
[picker release];
}
First, do not store images (or any binary data) in Core Data; especially on iOS. You will get far better performance storing it on disk and then storing a reference to the file location in Core Data.
Second, your sample code does not show how you are putting the data into Core Data. Therefore it is hard to suggest a solution.
Update
I did not find a simple reference to how to do this so here is one:
Image Cache Pre iOS 5.0
To set up an image cache on disk in a pre-iOS 5.0 environment you want to first create an attribute on your entity that is a NSString. In this example we will name that attribute imageFilename. Once that is complete we will want to create a subclass of NSManagedObject for our entity so that we can implement the helper methods:
#interface MyEntity : NSManagedObject
#property (nonatomic, retain) NSString *imageFilename;
#property (nonatomic, retain) NSImage *image;
#end
We are going to let Core Data manage the imageFilename since it is defined in the model. However we are going to implement the accessors for image.
#implementation MyEntity
#dynamic imageFilename;
#synthesize image = _image;
- (void)setImage:(UIImage*)image
{
NSString *filename = [self imageFilename];
if (!filename) {
filename = [[NSProcessInfo processInfo] globallyUniqueString];
[self setImageFilename:filename];
}
[_image release];
_image = [image retain];
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [cachePath stringByAppendingPathComponent:filename];
NSData *data = UIImagePNGRepresentation(image);
NSError *error = nil;
if (![data writeToFile:filePath options:NSDataWritingAtomic error:&error]) {
NSLog(#"Failed to write image to disk: %#\n%#", [error localizedDescription], [error userInfo]);
return;
}
}
The -setImage: will save the image to disk into the cache directory (note that the cache directory is not backed up and can be delete by the system in the event of a low space situation). It selects a random filename if one has not been created already.
The path is intentionally not stored because the directory of the application's sandbox can change. Therefore we only want to store the filename in Core Data and resolve the path.
We also keep an in memory reference to the image so that we are not hitting disk if the image is asked for again during this entity's lifecycle. This is the reason for the #synthesize even though we are implementing the accessors.
Note that we store the images on disk as PNG. This can be expensive (the compression routines are relatively slow) but it keeps the image in a universal format which can be useful.
- (UIImage*)image
{
if (_image) return _image;
NSString *filename = [self imageFilename];
if (!filename) return nil;
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [cachePath stringByAppendingPathComponent:filename];
if (![[NSFileManager defaultFileManager] fileExistsAtPath:filePath]) return nil;
_image = [[UIImage alloc] initWithContentsOfFile:filePath];
return _image;
}
The implementation of the -image is pretty much the reverse. We check to see if we have a filename; resolve the full path and load the image into memory and then return it to the caller.
- (void)prepareForDeletion
{
[super prepareForDeletion];
NSString *filename = [self imageFilename];
if (!filename) return nil;
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [cachePath stringByAppendingPathComponent:filename];
NSError *error = nil;
if (![[NSFileManager defaultFileManager] removeItemAtPath:filePath error:&error]) {
NSLog(#"Potential error removing on disk image: %#\n%#", [error localizedDescription], [error userInfo]);
}
}
We want to keep our cache directory as clean as possible so that we do not create a low space situation. Therefore when the entity is going to be deleted from Core Data we want to remove the file from disk. The actual error that happens during a delete is a non-fatal issue for us. It could be an error because the file was already deleted or something else. There is no reason to completely fail for this error but it is important to log it.
- (void)willTurnIntoFault
{
[super willTurnIntoFault];
[_image release], _image = nil;
}
#end
Finally, we implement the -willTurnIntoFault method so that we can release our in-memory reference to the image at the end of this entity's lifecycle.
Image Cache iOS 5.0+
Create a binary attribute on the entity.
Turn on the "Store in External Record File" bit.
There is no step three
Related
I am trying to download only image and text(probably HTML string) of a Evernote's note in my iOS app. I have successfully downloaded image from a note . But I did not find any method or process which help me to get text which are written on the note . I have used
ENSDK.framework
-(void)findAllNotes {
NSLog(#"finding all notes..");
[self.session findNotesWithSearch:nil
inNotebook:nil
orScope:ENSessionSearchScopeAll
sortOrder:ENSessionSortOrderNormal
maxResults:255
completion:^(NSArray* findNotesResults,
NSError* findNotesError) {
if (findNotesError) {
[self.session unauthenticate];
NSAssert(NO, #"Could not find notes with error %#", findNotesError);
} else {
[self processFindNotesResults:findNotesResults];
}
}];
}
- (void)processFindNotesResults:(NSArray*)results {
NSParameterAssert(results);
NSLog(#"processing find notes results..");
for (ENSessionFindNotesResult* result in results) {
[self.session downloadNote:result.noteRef
progress:NULL
completion:^(ENNote* note,
NSError* downloadNoteError) {
NSAssert(!downloadNoteError, #"Could not download note with error %#",
downloadNoteError);
[self getDataFromNote:note];
}];
}
}
-(void)getDataFromNote:(ENNote*)note {
for (ENResource* resource in note.resources) {
if ([resource.mimeType hasPrefix:#"image"]) {
UIImage* image = [[UIImage alloc] initWithData:resource.data];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *docs = [paths objectAtIndex:0];
NSString* path = [docs stringByAppendingFormat:#"/image1.jpg"];
NSData* imageData = [NSData dataWithData:UIImageJPEGRepresentation(image, .8)];
NSError *writeError = nil;
if(![imageData writeToFile:path options:NSDataWritingAtomic error:&writeError]) {
NSLog(#"%#: Error saving image: %#", [self class], [writeError localizedDescription]);
}
}
}
}
The content of the note is available to you in the content property of your variable note; i.e. it's in the content property of an ENNote object.
Also note that in addition to accessing the content directly, the Evernote iOS SDK also includes a special method that makes it easy to display a note's content in a UIWebView:
We've made this easy-- rather than serializing it to HTML and fussing with attached image resources, we've provided a method to generate a single Safari "web archive" from the note; this is a bundled data type which UIWebView natively knows how to load directly.
I am Writing an app which has share extension to save selected photo to my app' local storage from iphone photo gallery.
NSData WriteToFile returns YES but I couldn't find the stored file into the directory in of which I gave path while writing.
So, in short NSData WriteToFile fails to save a photo at given path.
Below is my code.
- (IBAction)acceptButtonTapped:(id)sender
{
__block UIImage *photo;
for (NSExtensionItem *item in self.extensionContext.inputItems)
{
for (NSItemProvider *itemProvider in item.attachments)
{
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage])
{
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage options:nil completionHandler:^(UIImage *image, NSError *error) {
if(image)
{
dispatch_async(dispatch_get_main_queue(), ^{
photo = image;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy_MM_dd_hh_mm_ss"];
NSString *fileName;
fileName = [NSString stringWithFormat:#"%#.jpeg",[formatter stringFromDate:[NSDate date]]];
dataPath = [dataPath stringByAppendingPathComponent:fileName];
NSData * imageData = [NSData dataWithData:UIImageJPEGRepresentation(image, 1.0)];
BOOL isdone = [imageData writeToFile:dataPath atomically:NO];
NSLog(#"%u", isdone);
});
}
}];
break;
}
}
}
[self.extensionContext completeRequestReturningItems:#[] completionHandler:nil];
}
Any Help would be much appreciable.
Thank you.
If you're trying to access the Document directory from the share extension, NO you can't do that. Share extension or other widgets are separate application from their containing app and therefore have their own sandbox. So you will need to use App Groups to share files.
Application groups are primarily targeted for extensions, more specifically, for widgets.
NSFileManager has a method on it containerURLForSecurityApplicationGroupIdentifier: where you can pass in the identifier you created when turning on App Groups for your apps
NSURL *containerURL = [[NSFileManager defaultManager]
containerURLForSecurityApplicationGroupIdentifier:#"group.com.company.app"];
You can save the files to this location, because you can access the shared application groups from both extension and host app.
You're modifying dataPath on each pass through the loop, appending another filename to it. That will create an ever-growing series of badly formed paths that contain all the filenames.
Don't do that. Create a new local variable filePath, and construct a filename into filePath using
filePath = [docsPath stringByAppendingPathComponent: filename];
Log your path and LOOK AT IT. When your program doesn't behave as expected, don't trust any of your assumptions, because one or more of them may be wrong.
I want to upload or save image to FTP server from my iOS app. but every time I get error that ftp not connected
I use SCRFTPRequest library.
here is my code...
UIImage *image = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
NSData * imageData = UIImagePNGRepresentation(image);
NSFileManager * fileManager = [NSFileManager defaultManager];
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.png",image]];
[fileManager createFileAtPath:fullPath contents:imageData attributes:nil];
NSLog(#"image saved");
[picker dismissViewControllerAnimated:YES completion:nil];
ftpRequest = [SCRFTPRequest requestWithURL:[NSURL URLWithString:#"ftp://myURL"] toUploadFile:fullPath];
ftpRequest.username = #"DemoUser";
ftpRequest.password = #"DemoUser";
ftpRequest.customUploadFileName = #"inapp";
ftpRequest.delegate = self;
[ftpRequest startAsynchronous];
From White Raccoon,
Just Drag and Drop the WhiteRaccoon.h and WhiteRaccoon.m file and import CFNetwork framework in your project.
- (void) upload
{
//the upload request needs the input data to be NSData
//so we first convert the image to NSData
UIImage * ourImage = [UIImage imageNamed:#"space.jpg"];
NSData * ourImageData = UIImageJPEGRepresentation(ourImage, 100);
//we create the upload request
//we don't autorelease the object so that it will be around when the callback gets called
//this is not a good practice, in real life development you should use a retain property to store a reference to the request
WRRequestUpload * uploadImage = [[WRRequestUpload alloc] init];
uploadImage.delegate = self;
//for anonymous login just leave the username and password nil
uploadImage.hostname = #"xxx.xxx.xxx.xxx";
uploadImage.username = #"myuser";
uploadImage.password = #"mypass";
//we set our data
uploadImage.sentData = ourImageData;
//the path needs to be absolute to the FTP root folder.
//full URL would be ftp://xxx.xxx.xxx.xxx/space.jpg
uploadImage.path = #"/space.jpg";
//we start the request
[uploadImage start];
}
-(void) requestCompleted:(WRRequest *) request{
//called if 'request' is completed successfully
NSLog(#"%# completed!", request);
}
-(void) requestFailed:(WRRequest *) request{
//called after 'request' ends in error
//we can print the error message
NSLog(#"%#", request.error.message);
}
-(BOOL) shouldOverwriteFileWithRequest:(WRRequest *)request {
//if the file (ftp://xxx.xxx.xxx.xxx/space.jpg) is already on the FTP server,the delegate is asked if the file should be overwritten
//'request' is the request that intended to create the file
return YES;
}
Finally i got success to upload image file on ftp server.
To upload image on ftp i used Gold Raccoon external library.with this library you can easily upload image to ftp server.
https://github.com/albertodebortoli/GoldRaccoon
What is the right way to move a core data model that allows external storage into a UIManagedDocument? I have a core data store that I am trying to move into a UIManagedDocument. I have users with lots of data. Some of it is 2 - 3 minute audio clips. I am subclassing UIManaged document and overriding the configurePersistentStoreCoordinatorForURL. Then copying the files over into the UIManagedDocument bundle. It all seems to work great accept for the Audio files that are stored externally. In my Core Data Model, my audio files are set up to allow external storage. These files are no longer connected after the move and when I try to play them int the app after the move, I get an audio session error. Thanks for any help you can offer on the topic. Here is my code that I am using to override the UIMD…
- (BOOL)configurePersistentStoreCoordinatorForURL:(NSURL *)storeURL
ofType:(NSString *)fileType
modelConfiguration:(NSString *)configuration
storeOptions:(NSDictionary *)storeOptions
error:(NSError *__autoreleasing *)error{
[self printFileDir];
// If legacy store exists, create a UIManaged Document and store it there
NSURL *docsDir = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *legacyStoreURL = [docsDir URLByAppendingPathComponent:#"RRLevelBook.sqlite"];
NSFileManager* fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:legacyStoreURL.path])
{
NSLog(#"Old db exists");
//swap files
NSURL *storeURLshm = [NSURL URLWithString:[[storeURL absoluteString] stringByAppendingString:#"-shm"]];
NSURL *storeURLwal = [NSURL URLWithString:[[storeURL absoluteString] stringByAppendingString:#"-wal"]];
NSURL *supportFiles = [[storeURL URLByDeletingLastPathComponent] URLByAppendingPathComponent:#".persistenStore_SUPPORT"];
NSURL *legacyStoreURLshm = [NSURL URLWithString:[[legacyStoreURL absoluteString] stringByAppendingString:#"-shm"]];
NSURL *legacyStoreURLwal = [NSURL URLWithString:[[legacyStoreURL absoluteString] stringByAppendingString:#"-wal"]];
NSURL *legacySupportFiles = [[legacyStoreURL URLByDeletingLastPathComponent] URLByAppendingPathComponent:#".RRLevelBook_SUPPORT"];
NSError* thisError = nil;
//swap the sqlite file
[fileManager replaceItemAtURL:storeURL
withItemAtURL:legacyStoreURL
backupItemName:nil
options:NSFileManagerItemReplacementUsingNewMetadataOnly
resultingItemURL:nil
error:&thisError];
//swap the -shm file
[fileManager replaceItemAtURL:storeURLshm
withItemAtURL:legacyStoreURLshm
backupItemName:nil
options:NSFileManagerItemReplacementUsingNewMetadataOnly
resultingItemURL:nil
error:&thisError];
//swap the -wal file
[fileManager replaceItemAtURL:storeURLwal
withItemAtURL:legacyStoreURLwal
backupItemName:nil
options:NSFileManagerItemReplacementUsingNewMetadataOnly
resultingItemURL:nil
error:&thisError];
//Move in the Support files
[fileManager moveItemAtURL:legacySupportFiles toURL:supportFiles error:nil];
//delete old files that have been swapped
[fileManager removeItemAtURL:legacyStoreURL error:nil];
[fileManager removeItemAtURL:legacyStoreURLwal error:nil];
[fileManager removeItemAtURL:legacyStoreURLshm error:nil];
[fileManager removeItemAtURL:legacySupportFiles error:nil];
NSLog(#"%#",[thisError localizedDescription]);
}
[self printFileDir];
return [super configurePersistentStoreCoordinatorForURL:storeURL ofType:fileType modelConfiguration:configuration storeOptions:storeOptions error:error];
}
Well, here is what I ended up doing - for better or worse:
Open the new UIManagedDocument.
Open up the legacy Core Data Model.
Copy each audio file (NSData) from Legacy CoreData Context to the UIManagedDocument Context.
Reconnect all relationships based on the Legacy CoreData Context.
NSManagedObjectContext *legacyMOC = [[NSManagedObjectContext alloc]init];
[legacyMOC setPersistentStoreCoordinator:psc];
//fetch all audio recordings from legacyStore
NSArray *legacyRecordingArray = [self fetchAudioRecordingsfrom:legacyMOC];
//fetch all audio recordings form UIMDStore
NSArray *uimdRecordingArray = [self fetchAudioRecordingsfrom:self.managedObjectContext];
//for each audio recording, copy the audio object from legacy and save it to UIMDStore
for (int i = 0; i < legacyRecordingArray.count; i++) {
//save audio to core data
RunningRecord *legacyRR = (RunningRecord *)legacyRecordingArray[i];
RunningRecord *uimdRR = (RunningRecord *)uimdRecordingArray[i];
uimdRR.audioData = [NSData dataWithData:legacyRR.audio.file];
uimdRR.audio.file = nil;
}
if (![self.managedObjectContext save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
More SQLite issues. So my interface is as follows (this is all in .m):
#interface Search()
#property (strong, nonatomic) NSString *databasePath; //path to sqlite database file
#property (strong, nonatomic) NSString *databaseName;
#property (nonatomic) sqlite3 *database;
#end
and the init follows:
- (id)init
{
if ((self = [super init]))
{
self.databaseName = DB_NAME;
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
_databasePath = [documentsDir stringByAppendingPathComponent:self.databaseName];
[self checkAndCreateDatabase];
if (sqlite3_open_v2([self.databasePath UTF8String], &_database, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK)
{
[[[UIAlertView alloc]initWithTitle:#"Missing"
message:#"Database file not found"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil]show];
}
else
{
NSLog(#"%s: sqlite3_open_v2 error: %s", __FUNCTION__, sqlite3_errmsg(self.database));
}
}
The error that the Log in the init returns is: sqlite3_open_v2 error: not an error. In my searches, I've heard that SQLite doesn't return an error when it points to a non-existent database. But I'm not sure why the database wouldn't exist. The copy function I'm using (which I was given and had seemed to work before) is as follows:
-(void) checkAndCreateDatabase
{
// Check if the SQL database has already been saved to the users phone, if not then copy it over
BOOL dbExists;
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
NSFileManager *fileManager = [NSFileManager defaultManager];
// Check if the database has already been created in the users filesystem
dbExists = [fileManager fileExistsAtPath:_databasePath];
// If the database already exists then return without doing anything
if(dbExists)
{
return;
}
// If not then proceed to copy the database from the application to the users filesystem
// Get the path to the database in the application package
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:_databaseName];
// Copy the database from the package to the users filesystem
//[fileManager copyItemAtPath:databasePathFromApp toPath:_databasePath error:nil];
NSError *error = nil;
if (![fileManager copyItemAtPath:databasePathFromApp toPath:_databasePath error:&error])
{
NSLog(#"%s: copyItemAtPathError: %#", __FUNCTION__, error);
}
}
Finally, I have verified in the iOS Simulator Documents directory that the database exists, and the query I'm trying to execute on it works. Why might I be getting this error?
Having never worked with SQLLite like this, I only want to mention, that in the code above, your else statement gets called, when sqlite_open_v2 == SQL_OK. So maybe in that case, there is just no error to return and everything is fine?!
It turns out the problem was a very simple one. The object I was trying to assign the values to (and then pull them from) was not initialized. It was a very silly oversight on my part, but I'm still new to Objective C.
Additionally, I do recommend icodebuster's suggestions to use FMDB for SQLite in iOS. It cleaned up some of my SQLite mess and made it a lot nicer to use.