iOS5 Saving images to custom folder, ALAssetsLibrary fail - ios5

<><> ----
Aalok has answered this question, I'm just waiting for him to write it up so I can chose that as the correct answer. Until then, along with making the changes he describes below I had to call -- self.library = [[ALAssetsLibrary alloc] init]; -- before every attempt to save my image. As a precaution I also stopped the AVSession running until after the save, when is was restarted (using [session stopRunning]; and [session startRunning];
----- <><>
<><> ----- EDIT 2: You do not need to stop and restart the AV session, tested this thoroughly and it's working perfectly. ----- <><>
<><> ----- EDIT 3: after testing this on my device thoroughly, and it working perfectly, the code is not working once it's been through the review process and put on the store. Two identical devices (2x iPhone 4) running the same OS, one using my dev build, one off the app sotre, the ap store version still has the bug. Giving up with this for now ----- <><>
I'm using the category in this link to try to save to a custom folder:
http://www.touch-code-magazine.com/ios5-saving-photos-in-custom-photo-album-category-for-download/
Now it works some of the time, but not all. In the comments it's been suggested that the following code will detect if the group properties are nil:
if ([group valueForProperty:ALAssetsGroupPropertyURL] == nil)
{
NSLog(#”group properties are nil!”);
} else {
[group addAsset:asset];
}
Which I have, and it does detect if the properties are nil. So all good. What I'm struggling with is at that point setting the properties and saving the images. I'm guessing that at this point we can manually set the albumName and save the image, which I've tried, but the error still occurs.
Any ideas? Thank you.

I have facing the same problem with this same .h and .m file but find one solution for this after working on it for 2 to 3 days and the solution is very simple what I have to change is in .m file in
-(void)saveImage:(UIImage*)image toAlbum:(NSString*)albumName withCompletionBlock:(SaveImageCompletion)completionBlock
and in
-(void)addAssetURL:(NSURL*)assetURL toAlbum:(NSString*)albumName withCompletionBlock:(SaveImageCompletion)completionBlock
methods i just add this before calling the inside methods
//THE CHANGE dispatch_async(dispatch_get_main_queue(),^{ //
[self writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)image.imageOrientation completionBlock:^(NSURL* assetURL, NSError* error)
{
//error handling
if (error!=nil)
{
completionBlock(error);
return;
}
//add the asset to the custom photo album
[self addAssetURL: assetURL toAlbum:albumName withCompletionBlock:completionBlock];
}];
//THE CHANGE }); //
AND Same for the other one
Happy Coding :)
EDIT
In second method I add a line below every completionBlock(nil); line
[[NSNotificationCenter defaultCenter] postNotificationName:kSaveSuccess object:nil];
And I use this notification to make sure that image is saved in album too. Till that time I Shows UIActivityIndicator with some text message and after successfully saved image one popup message is shown indicated that image is saved in album with album name. And while this time the UI is unresponsive i.e. user can't do any thing other then pressing home button of device :) ;)
Happy Coding :)

Related

Photomania- Stanford Paul Hegarty's class

is there something wrong with core data for xcode 4.3.2
I'm following the Stanford Paul Hegarty class for ios 5.0
doing the core data walkthrough (Video 14 Core data demo)
I downloaded the file here
I ran it in xcode 4.3.2 but the core data doesn't seem to work because the entries in the tableview do not appear.
I tried to run it in another computer with xcode 4.2 and ios 5.0
it's working perfectly
anybody who encountered the same problem? I'm pretty sure that xcode is at fault.
Interesting. I am having the same problems and I am also using XCode 4.3, but just thought it was because of the Flickr license that you need and which I don't have. (In FlickAPIKey.h, there is a #define FlickrAPIKey #"" and you won't download anything if you don't have that key.)
Update: I got myself a Flickr API key (you can just get one from their website) and tried the Photomania App on XCode 4.3: it works like a charm, so it looks like XCode is not your problem here. (Although on occasion I found that I had to stop and restart XCode to get rid of a strange bug or compiler error.) Anyway, maybe it is an idea to delete the data first before you try again: Just delete the App before you run it and the database file will get deleted as well.
Paul Hegarty after his class, has published an update version of his code which have a call to SAVE the CoreData database! This may be the reason why your CoreData informations doesn't persist.
His update note was:
// should probably saveToURL:forSaveOperation:(UIDocumentSaveForOverwriting)completionHandler: here!
// we could decide to rely on UIManagedDocument's autosaving, but explicit saving would be better
// because if we quit the app before autosave happens, then it'll come up blank next time we run
// this is what it would look like (ADDED AFTER LECTURE) ...
[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL];
So, you have to Add the 'document saveToURL' line in the 'fetchFlickrDataInDocument' function like this:
- (void)fetchFlickrDataIntoDocument:(UIManagedDocument *)document
{
dispatch_queue_t fetchQ = dispatch_queue_create("Flickr fetcher", NULL);
dispatch_async(fetchQ, ^{
NSArray *photos = [FlickrFetcher recentGeoreferencedPhotos];
[document.managedObjectContext performBlock:^{
// perform in the NSMOC's safe thread (main thread)
for (NSDictionary *flickrInfo in photos) {
[Photo photoWithFlickrInfo:flickrInfo inManagedObjectContext:document.managedObjectContext];
// table will automatically update due to NSFetchedResultsController's observing of the NSMOC
}
// should probably saveToURL:forSaveOperation:(UIDocumentSaveForOverwriting)completionHandler: here!
// we could decide to rely on UIManagedDocument's autosaving, but explicit saving would be better
// because if we quit the app before autosave happens, then it'll come up blank next time we run
// this is what it would look like (ADDED AFTER LECTURE) ...
[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL];
// note that we don't do anything in the completion handler this time
}];
});
}
If someone is still facing the same issue using Objective-C, there is one more thing you need to do after getting API key: change all http:// to https:// within files in FlickrFetcher folder. This worked for me.

File (.plist) stored at /Library/MyAppData/userstatus.plist keeps getting deleted in release builds. What's going on?

I'm facing a weird problem here. I'm saving some user information in a file called "userstatus.plist" which is saved in my application's /Library/MyAppData/userstatus.plist folder location.
In my app I check if the file exists using:
- (void)checkFile {
NSString *statusPath = [self statusPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:statusPath]) {
NSLog(#"Reading user status from file");
_userStatus = [[NSMutableArray alloc] initWithContentsOfFile:statusPath];
}
else {
NSLog(#"Creating new user status");
_userStatus = [[NSMutableArray alloc] init];
}
}
- (NSString*)statusPath {
return [[self libraryPath] stringByAppendingPathComponent:#"MyAppData/userstatus.plist"];
}
- (NSString*)libraryPath {
NSString* path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
return path;
}
Whenever I test my app in a debug build, I only get the "Creating new user status" message once, on the first build. In subsequent builds I get the "Reading user status from file" message.
However, when I switch to a release build, I keep getting the "Creating new user status" message for every build that I do, essentially wiping out any user data I have stored.
Any help would be much appreciated, since I'm at my wit's end... thanks! :)
EDIT:
I ran the release build on the emulator so I could keep a watch on my finder window to see what's going on in that location (/Library/MyAppData/). Turns out the plist file was never created, which is weird. I do have other files there which get created without any problems. Only the plist file does not get created. Hopefully this should help get a solution?
Sorry for wasting anyone's time here. The solution had nothing to do with what I thought while asking the question. During development, I used an NSAssert on the line where I write the file to the location; I forgot that NSAsserts are skipped in release builds.
Sorry everyone, my bad. I'm leaving this question up, though, if it might help anyone (in whatever way it can).

iCloud only seems to work on start up

I'm trying to create an iCloud app that uses Core Data. To do so, I use a single instance of UIManagedDocument. For persistentStoreOptions, I set "Database.data" for NSPersistentStoreUbiquitousContentNameKey and -iCloudURL-/CoreData_Logs for NSPersistentStoreUbiquitousContentURLKey. As by Apple suggested, I open the document like so:
if ([[NSFileManager defaultManager] fileExistsAtPath:[urlOfDatabase path]])
{
[_database openWithCompletionHandler:^(BOOL success) { ... }];
}
else
{
[_database saveToURL:urlOfDatabase
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) { ... }];
}
Also, I registered for NSPersistentStoreDidImportUbiquitousContentChangesNotifications, and merge the context from the notification in that method.
To test the app, I use an iPad and an iPhone. When I run it on one device, add some data, and open it up on the other device, the correct data is displayed. If I however add data while the app is running on both devices, it either doesn't update (I've waited for at least 10 minutes once) or it updates after a few minutes but only with a whole bunch of error messages: "The item failed to download.".
Did I miss something (hopefully obvious) in my set up?

UILocalNotifications playing Custom sound

I implemented local notification in my app but I am just wondering is there a way to play a sound that is not part of the main bundle of iPhone App.
Basically in my app, I want user to record a sound that gets played when the local notification is generated instead of playing a pre-recorded or default sound.
As far as i know this can be implementable because i have seen 2-3 App in app store which is doing the same thing which i want to do
- (void)alertSelector:(NSString *)AlertTitle WithFiringTime:(NSDate *)date
{
UILocalNotification *localNotification = [[[UILocalNotification alloc] init] autorelease];
[localNotification setFireDate:date];
[localNotification setTimeZone:[NSTimeZone defaultTimeZone]];
NSDictionary *data = [NSDictionary dictionaryWithObject:date forKey:#"payload"];
[localNotification setUserInfo:data];[localNotification setAlertBody:AlertTitle];
[localNotification setAlertAction:#"View"]; [localNotification setHasAction:YES];
localNotification.soundName=#"voice.aif";
if (!localNotification)
return;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
My guess is your sound isn't formatted appropriately to play by the OS. Make sure your sound is formatted in IMA4 format. Also, make sure your file is in your application bundle, and you should be good to go.
For more reference and similar question, see this SO question.
Choose custom sound for Local Notifications
Here is a link for Apple's Local and Push Notification Guide. It explains the sounds in detail and the default sounds that can be used.
From the description of the soundName property -
For this property, specify the filename (including extension) of a
sound resource in the application’s main bundle or
UILocalNotificationDefaultSoundName to request the default system
sound.
It doesn't seem that sounds other than those present in the bundle can be used. Are you sure that the apps are using local notifications with their custom sounds? Maybe they are playing sounds with alerts or something like that.
HTH,
Akshay
notif.soundName = #"sound.caf";
That should work. Make sure the sound is actually in your app’s bundle, is in the correct format (linear PCM or IMA4—pretty much anywhere that explains how to convert sounds for iOS will tell you how to do that), and is under 30 seconds.
You can convert from wav and mp3 using terminal :
afconvert -f caff -d LEI16#44100 -c 1 in.wav sound.caf
'in.wav' is input sound which u want to convert and 'sound.caf' is output file ,change directory to where u want to have the out put file
Make sure your sound file is added to the "Copy Bundle Resources" list under Project Settings->(Target)->Build Phases.
This fixed the issue for me. At first I only got the default sound, even though I specified a custom file name (and the file was added to the project). But after I manually added the file to this list it started working.
I used a system sound from my mac to test with. I converted it using this command in a console:
afconvert /System/Library/Sounds/Basso.aiff ~/Downloads/alert2.caf -d ima4 -f caff -v
Well I was experiencing same Issue , It sounds like this was not supported before or not doable to play custom sound for local notification that is not included in App Bundle. But later this is supported and you can assign custom sounds from App container specifically in Library/Sounds directory . And it worked with me . you can either download sounds from server to this directory or test it by copying a .caf(Apple said other extensions may work) file from bundle to Library/sounds directory and then remove reference for the file from bundle so that you can make sure that if sounds played it is from directory out of bundle . This all works very well , the main issue with me if user force quoted the app it stops working for local notification while it still works with push kit notifications , Im scheduling local one though when push kit push arrives.
Here is the code for testing playing sound from directory :
func copyFileToDirectoryFromBundle()
{
// get reference to library directory
let LibraryDirectoryPaths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.LibraryDirectory, NSSearchPathDomainMask.UserDomainMask, true)
// see what is in there
for path in LibraryDirectoryPaths
{
print("di=\(path)")
}
// prepare for sound directory
let libraryDirectoryPath: AnyObject = LibraryDirectoryPaths[0]
let soundsDirectoryPath = libraryDirectoryPath.stringByAppendingPathComponent("Sounds")
// create sounds directory under library if not there already
if !NSFileManager.defaultManager().fileExistsAtPath(soundsDirectoryPath)
{
do {
try NSFileManager.defaultManager().createDirectoryAtPath(soundsDirectoryPath, withIntermediateDirectories: false, attributes: nil)
print("sounds library created,,,")
} catch let error as NSError {
print("creating sounds directory error = \(error.localizedDescription)");
}
}
else
{
print("Sounds directory is there already under Library")
}
do {
// sounds directory should have been added under library
let directoryContents = try NSFileManager.defaultManager().contentsOfDirectoryAtURL( NSURL(string: libraryDirectoryPath as! String)!, includingPropertiesForKeys: nil, options: [])
print("Library directs=\(directoryContents)")
// prepare for adding subdirectory with file name from bundle
// here is where you should save files if downloaded from server
// here im just moving one from bundle
let subDirectoryPath = directoryContents.last!
if let pathFromBundle = NSBundle.mainBundle().pathForResource("notification", ofType:"caf") {
let myFilePathUnderSounds = "\(subDirectoryPath.path!)\("/notification.caf")"
if !NSFileManager.defaultManager().fileExistsAtPath(myFilePathUnderSounds)
{
do {
print("bundle path=\(pathFromBundle)and datacontainer path=\(subDirectoryPath.path!)")
try NSFileManager.defaultManager().copyItemAtPath(pathFromBundle, toPath:myFilePathUnderSounds )
print("item copied")
} catch let error as NSError {
print(error.localizedDescription);
}
}
else
{
print("Your file from bundle path = \(pathFromBundle) is already there under Sounds")
}
}
else
{
print("Item not found in bundle")
}
// see item there after you add it
let subDirectories = try NSFileManager.defaultManager().contentsOfDirectoryAtURL( subDirectoryPath, includingPropertiesForKeys: nil, options: [])
print("files under sounds")
for fileDirectory in subDirectories
{
print(fileDirectory)
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
After making sure .caf file is under Library/Sounds directory . All you have to do is merely assigning file name with extension to local notification soundName property like if the file is in bundle.
are you cleaning the old notifications with?
UIApplication* app = [UIApplication sharedApplication];
NSArray* oldNotifications = [app scheduledLocalNotifications];
if ([oldNotifications count] > 0) {
[app cancelAllLocalNotifications];
}
else you can, delete the app from the phone (maybe need a reboot too)
the it should work :-)
beside that the code mentioned above should be correct.
and this line localNotification.soundName=#"voice.aif"; should be enough to play custom sounds.
but if you had notification tested first with no sound-file added, the default sound file (UILocalNotificationDefaultSoundName) is registered! and to re-register you have to cancel the previous notification (cancelAllLocalNotifications) or delete the app, to be able to "re"-register your notfication,
beside that, the apple doc says:
Sounds that last longer than 30 seconds are not supported. If you
specify a file with a sound that plays over 30 seconds, the default
sound is played instead.
hope it helps now :-)

Detect and Customize UIWebView's "Unable to Read Document" error

I'm testing a UIWebview with a number of different document types - .xlsx, .jpg, etc. - and it opens most of them just fine. From time to time, I open a local file and this message appears right in the web view:
Unable to Read Document
An error occurred while reading the document
I'm not concerned with "why" this error occurs - it happens, for instance, when I feed the UIWebView a garbage file (intentionally). The problem is that I can't figure out how to detect "when" this happens. It doesn't trigger webView:didFailLoadWithError, it doesn't trigger an NSException (via #try & #catch), and when I inspect the document in webViewDidFinishLoad, webView.request.HTTPBody is null.
Anyone know how to detect when UIWebView can't display content?
just call uiwebview delegate method
- (void)webViewDidStartLoad:(UIWebView *)webView
{
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSString *html = [webView stringByEvaluatingJavaScriptFromString:
#"document.body.innerHTML"];
if ([html rangeOfString:#"Unable to Read Document."].location == NSNotFound) {
NSLog(#"NO Error");
}
else
{
// NSLog(#"File contains Error");
}
}
If inspecting the log does not show anything out of the ordinary, run it on the actual device. As a bit of help, my log says
Cannot find data converter callback for uti public.data
Failed to generate preview
which means that the simulator is failing. After being frustrated with this same problem, I went thru the whole final certification and installation and provisioning process, and too confirm that Word 97 and Pages / Pages.zip files containing text do indeed display just fine on the actual device. SOLVED. The simulator itself is broken, which is very...troubling, that this little note didn't seem to make it into the release notes, and also complicates development a tad bit. However, the work around is to select the Device in Xcode and deploy out, and it should work.