I have an app built that uses the UIDocumentBrowser. It was working fine until I tried to add an ImagePickerController to allow the app to use the camera and take photos. The Documents folder is now no where to be found on the "Files" app or in the app I am developing. I could find it in the Finder on the Mac I am using for developing the app.
I tried deleting the entire app and data and installing a cleaned build.
I have this sitting at the top of the app launch method
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
_datapath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
if (![NSFileManager.defaultManager fileExistsAtPath:_datapath])
[NSFileManager.defaultManager createDirectoryAtPath:_datapath withIntermediateDirectories:YES attributes:nil error:nil];
When I run it in debug, it finds the already created documents folder so skips over the createDirectoryAtPath method.
Any ideas what's happening and/or how I can fix it?
Note: I never got to the point in the app where I could test the ImagePicker before this problem happened and removing the code I added didn't fix the problem.
Must have been cached information. Powered down the iPad and restarted fixed the problem.
Related
So, yesterday my app was running fine, but today it started to run really slow for no reason.
I've been trying to find the cause using CACurrentMediaTime() and now I know that its between didFinishLaunchingWithOptions on AppDelegate and my viewDidLoad of my first ViewController.
From here on it always takes about 10 seconds to load any new View, but inside the view it runs fluid.
I can post my logs here:
app[311:25582] 4665.159450
app[311:25582] Splash - 4673.318206
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSLog(#"%#",[paths objectAtIndex:0]);
NSLog(#"%f", CACurrentMediaTime());
return YES;
}
I've solved the problem by deleting the ViewController and creating a new one.
What I did to find the possible cause of the problem:
Went to an older version of the app to see it running (it was running ok)
Copied the Storyboard to the old project (started to run slow again)
Copied only the ViewController (again, it was running slow)
Copied only view by view (it worked!!)
My guess is that the ViewController was with some error and was messing up the rest of the app.
Just posting here the answer so that I can help someone else.
I created a WatchKit Application with the default XCode Template.
I added an app group entitlement to the iOS Target, to the Watchkit App Target and to the Watchkit App Extension Target. (this is the app group name: group.com.lombax.fiveminutes)
Then, I tried to access the shared folder URL with both the iOS App and the WatchKit Extension:
Extension:
#implementation ExtensionDelegate
- (void)applicationDidFinishLaunching {
// Perform any final initialization of your application.
NSURL *test = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.com.lombax.fiveminutes"];
}
iOS App:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSURL *test = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.com.lombax.fiveminutes"];
// ...
}
However, the test NSURL is different:
On iOS:
file:///Users/Lombardo/Library/Developer/CoreSimulator/Devices/38B983DB-342F-4A47-8C26-5D2C92CDB666/data/Containers/Shared/AppGroup/8DEE182E-AFE6-47DD-BA2B-6B0520158A8B/
on Watch:
file:///Users/Lombardo/Library/Developer/CoreSimulator/Devices/BF52D613-25FF-4092-A5B2-9C3F1B616160/data/Containers/Shared/AppGroup/CECB5EFC-7FBD-4C84-A878-1314CB7CF211/
And for this reason I'm unable to share data between the iOS App and the WatchKit Extension.
I cannon try on a real device since I don't have WatchOS 2.0 on my Apple Watch.
Any advice?
Thanks
UPDATE
I did some other tests:
Installed WatchOS 2, the issue still persists on real devices.
This is the store url for my iPhone:
NSURL
* #"file:///private/var/mobile/Containers/Shared/AppGroup/3D05D159-94D6-409C-9A38-90E0830D0C3F/FiveMinutes.sqlite"
And this is the store url for my Watch:
NSURL
* #"file:///private/var/mobile/Containers/Shared/AppGroup/F1E89377-F456-4FC2-BAAC-3DD705EF381A/FiveMinutes.sqlite"
The two apps reads and write to-from two different .sqlite files.
On simulator, if I hard-code one of the URLs, both iOS simulator and Watch simulator are able to read-write the same .sqlite file and share the content. However, this is not possible on real devices since the Watch extension cannot write to the iOS path:
URL:file:///private/var/mobile/Containers/Shared/AppGroup/3D05D159-94D6-409C-9A38-90E0830D0C3F/FiveMinutes.sqlite options:(null) ... returned error Error Domain=NSCocoaErrorDomain Code=512 "The file couldn’t be saved." UserInfo={reason=Failed to create file; code = 2} with userInfo dictionary {
reason = "Failed to create file; code = 2";
}
Ok, I think I've found my answer. I remembered that with the transition to Watch OS 2 the extension code is now executed directly on the Apple Watch, and no more on the paired iPhone. So it's seems obvious that the two devices doesn't share the same storage.
The first thing I did was to create a new project, starting from a base iOS Project, and then adding a Watch OS 1 (old version) App Target.
In this case, the directories were identical and they could communicate:
Watch Path: file:///Users/Lombardo/Library/Developer/CoreSimulator/Devices/BF52D613-25FF-4092-A5B2-9C3F1B616160/data/Containers/Shared/AppGroup/30B39103-CEEB-4C64-9531-FB27DC40180D/
iOS Path file:///Users/Lombardo/Library/Developer/CoreSimulator/Devices/BF52D613-25FF-4092-A5B2-9C3F1B616160/data/Containers/Shared/AppGroup/30B39103-CEEB-4C64-9531-FB27DC40180D/
Then, I did the first thing every programmer should do: read the docs.
In the FIRST PAGE of the WatchOS 2 transition guide there is this sentence:
Your extension now stores files and data on Apple Watch. Any data that is not part of your Watch app or WatchKit extension bundle must be fetched from the network or from the companion iOS app running on the user’s iPhone. You cannot rely on a shared group container to exchange files with your iOS app. Fetching files involves transferring them wirelessly to Apple Watch.
Is there any way to know that iOS app is launched after being updated?
I think i can save app current version every time i launch the application for example in NSUserDefaults and check this version every time i open the application.
And what about the case:
1) User installs app version 1.0 , but doesn't launch it.
2) User installs app version 2.0.
How to handle that case for example?
Thanks in advance.
If always done what you have suggested, saving the app version in the NSUserDefaults.
And about your other case, if the app does not start with version 1 then it does with version 2 you could just see it as a new install.
Since your app never started in the first place you can just treat it as a fresh install. If you doing this to track update in some kind of analytics tool you will have an issue. But you could use apple install/update reports to get the correct list of install/updates.
Just be sure that if you do any updates from any version you make you code in such a way that you can upgrade from any previous version. So installing verion 4 from 1 will preform any and all changes for version 2 and 3 as well.
I found the following note at this website from Apple.
When a user downloads an app update, iTunes installs the update in a new app directory. It then moves the user’s data files from the old installation over to the new app directory before deleting the old installation. Files in the following directories are guaranteed to be preserved during the update process:
/Documents
/Library
Although files in other user directories may also be moved over, you should not rely on them being present after an update.
In every version you release, you can put a txt file with a unique name (unique for every version) in one of these update-persistent directories and check for the previous version txt file(s) at initial launch of application. This should work even in the case where your application was not launched between the download and an initial update.
Every time your application is launched, the following function in your appDelegate class gets called after the launching process is complete:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
This is a point where you can check the version of the application, probably using somoething like:
[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleShortVersionString"]
I'm a bit late to the party, but if this is still an issue, I use a saved Boolean to see if this is the app's first launch:
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"]) {
NSLog(#"First launch");
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:YES forKey:#"HasLaunchedOnce"];
[defaults synchronize];
}
I can then deal with an install or update as you already mention in your question.
I think this could be useful: MTMigration manages blocks of code that need to run once on version updates in iOS apps. This could be anything from data normalization routines, "What's New In This Version" screens, or bug fixes.
It is available through CocoaPods: pod 'MTMigration'
Please take a look to MTMigration repository at GitHub (https://github.com/mysterioustrousers/MTMigration) for usage and examples.
[MTMigration applicationUpdateBlock:^{
/* This code run on every version change. */
}];
[MTMigration migrateToVersion:#"1.0" block:^{
/* This code only run once in version 1.0 */
}];
[MTMigration migrateToVersion:#"2.0" block:^{
/* This code only run once in version 2.0 */
}];
If a user was at version 1.0 , skipped 2.0 , and upgraded to 3.0 , then both the 1.0 and 2.0 blocks would run.
In my iCloud-based app, I noticed that if a crash were to occur while a UIDocument is open (and has not yet been closed), the status of the document sometimes becomes UIDocumentStateSavingError and it never opens successfully again.
When I run [UIDocument openWithCompletionHandler:] after this, the document never opens, returning NO for success and 5 (UIDocumentStateClosed and UIDocumentStateSavingError) for documentState.
On iOS 5, I also observed that NSMetadataQueryDidUpdateNotification is constantly being called (every second) until the file is deleted and the app is restarted. This causes additional usability problems in the app.
One more thing, on developer.icloud.com I noticed that the file is showing a second, conflicted version existing. This conflict isn't found on any of my devices, though, not even if the app is restarted or reinstalled.
What do I do to get the UIDocument to open normally?
Here is what I tried so far:
Checking [NSFileVersion unresolvedConflictVersionsOfItemAtURL:] for conflicts and removing all older version using [NSFileVersion removeOtherVersionsOfItemAtURL:].
Calling [[NSFileManager defaultManager] evictUbiquitousItemAtURL:] before calling [UIDocument openWithCompletionHandler:] to re-download the file to the device.
Downloading the main and conflicted versions from developer.icloud.com to see if any of them are corrupt or partial files. Both open fine.
Banging my head against my desk. Preliminary results are unsuccessful.
first time user of PLCrashReporter. I have written the code in my appDelegate.m file
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
NSLog(#"inside applicationdidfinishlaunching to do crash reporting");
PLCrashReporter *crashReporter = [PLCrashReporter sharedReporter];
NSError *error;
if ([crashReporter hasPendingCrashReport])
[self handleCrashReport];
if (![crashReporter enableCrashReporterAndReturnError:&error])
NSLog(#"Warning: Could not enable crash reporter %#",error);
}
I wrote handleCrashReport as it is in the example. I also on purpose crashed my app on clicking a button.(NSRangeException index 0 beyond bounds for empty array) like this:
NSMutableArray *mtestArray = [NSMutableArray new];
NSLog(#"%#",[mtestArray objectAtIndex:0]);
It throws exception (i.e. goes to main.m file. Doesn't go to home screen. It stays on the main.m file). I stop the execution and restart the app and it goes to a screen (this screen occurs if it were correct actually). I dont see a pop up saying send crash report or don't. I added CrashReporter.framework and libcrashreporter-iphonesimulator.a to my project. I changed mach-o and perfom single...as told in the article. I have been at this for more than 2 hours. Why dont i see the error dialog?
I put breakpoints at my applicationdidfinishlaunching and they NEVER get hit. I deleted my app and reinstalled (re-run) and still no go. Also my project is an iPad project with iOS 6.1. I read that crash report dialog appears on second time start of the app. But it won't any time,2nd or 3rd. If you need more information, please ask. Thanks
EDIT : Does plcrash reporter work for iPads also? I took the help from this blogpost
http://mandeepdhiman.blogspot.com/2011/10/crash-reporter-for-ios.html