Auto redeploy on iOS Simulator XCode - ios

Is there any way to [edit] have a fresh [/edit] redeploy your app from xcode onto the iOS simulator when you hit run, without have to delete [edit] the existing data from the app [/edit] every time?

If you want it to start with empty data each time, you can use the Xcode organizer to take an application data snapshot of a freshly started app, add the snapshot to your project and then edit your scheme and go to Run -> Options and select the snapshot in from the Application Data popup menu. That way when the app is started, it always starts with the same, presumably empty, data.
I don't think it clears NSUserDefaults, though.

You shouldn't have to delete it every time… you should just be able to hit run and it will overwrite or otherwise update the version that is loaded there.
I normally have a debug button to remove all data and add in fake data if necessary and wrap them in a DEBUG flag. This allows me to control it without having to restart the app.
- (void)viewDidLoad
{
#if DEBUG
// Make Button and hook it to clearAllData
#endif
}
- (void)clearAllData
{
// Clear the Core Data databases
SDAppDelegate *myDelegate = [[UIApplication sharedApplication] delegate];
[myDelegate clearAllData];
// Clear the UserDefaults
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:[[NSBundle mainBundle] bundleIdentifier]];
// Run the initial Setup again
[self setup];
}

Related

Could not locate installed application Xcode

I am trying to build share extension. Created the target, fixed the project settings (provisioning profile, app group, NSExtension).
Tried to run and saw the logs I added to the three methods isContentValid, didSelectPost, and configurationItems. This is my first time to create a share extension so, I was just looking around first and see when each method is called.
Now for some reason (I don't know what I did wrong), I am unable to see the logs from the extension. When I hit the run button in Xcode, the message Finished running ... gets displayed in the status bar on top.
Tried to change the scheme settings to the extension in the executable field, I get the following message when trying to run:
Could not locate installed application
Install claimed to have succeeded, but application could not be found on device. bundleId = <my-bundle-id-value>
Followed every solution I could find on the internet. Tried the solution from here and here.
UPDATE
I wanted to check if the extension actually runs or just the logs don't work. So I added the following logic to isContentValid:
- (BOOL)isContentValid {
// Do validation of contentText and/or NSExtensionContext attachments here
NSLog(#"isContentValid");
NSUserDefaults * userDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"<group-id>"];
[userDefaults setObject:#"haha" forKey:#"key"];
[userDefaults synchronize];
return YES;
}
and read then read the haha in the main application it worked! So, the question is: Why can't I debug the application? Why doesn't the application print the log statements. Developing without logging and/or debugging could take 2x, if not more, time.
UPDATE 2
The best I was able to come up with is to manually attach the debugger to the extension, add breakpoints here and there (where I wanted to put logs) and edited the breakpoints to log as action and continue after evaluation.

NSUserDefaults objectForKey sometimes nil

I've just noticed that a key that exists in my **NSUserDefaults**
is returning nil quite often. It seems about half the time it's
correct and the other half it's not. I say half the time, but I dont
mean that its flip-flopping, its just 40-50% I see it not work.
When I write the initial value, I call synchronize immediately.
I use this key as the applications
revision I set when a new user signs up.
The following code returns nil:
#define kDBrevision #"revision"
NSString *rev = [[NSUserDefaults standardUserDefaults] objectForKey:kDBrevision];
When I launch the app and just monitor the value (without
writing any NSUserDefaults), the value sometimes is valid
with no modifications to the NSUserDefaults at all.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSString *r = [[NSUserDefaults standardUserDefaults] objectForKey:kDBrevision];
NSLog(#"revision %#", r);
_exit(1);
I have no idea why this is happening. Im running iOS 10 on my
device connected to Xcode 8.2.1. Anyone have any ideas?
Thanks
EDIT:
I started talking to apple about fixing this and found out that if you have
file protection complete turned on, it can be the cause of this issue
showing up from time to time, however apple told me that my particular
case (which is the only one I was sure of at this time) is a bug.
The case is when you use Xcode to launch the app on the device it should
not fail like this and it occasionally does. No idea when or if it will
be fixed. Instead I moved my critical strings from the defaults to
the keychain instead.
This appears to be an Xcode 8 and/or iOS 10 bug. I ran into it myself and conclusively narrowed the case to UserDefaults intermittently returning nil when in fact there was data for the key. Specifically, I set the data once, then removed the set logic and executed only the get logic, repeatedly, and sometimes got values, sometimes got nil.
I changed my Run scheme to use Release configuration and ran the app on device without the debugger attached and the issue disappeared, that is, the get logic produced the correct value every time I executed it (~30 times).
Here is some more discussion:
iOS 10 with XCode 8 GM caused NSUserDefaults to intermittently not work
iOS 10, NSUserDefaults Does Not Work
https://forums.developer.apple.com/thread/48700
https://forums.developer.apple.com/message/143155#143155
try below code in didFinishLaunchingWithOptions
Swift 3.0
UserDefaults.standard.synchronize()
Obective - C
[[NSUserDefaults standardUserDefaults] synchronize];

NSUserDefaults not cleared after app uninstall on simulator

this may sound real NOOB! I want to check if it's the second time the user enters my application, so to keep the run count I'm using NSUserDefaults. I have implemented the following code in my rootViewController's viewDidLoad method:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSLog(#"hello %ld", (long)[userDefaults integerForKey:#"runCount"]);
if ([userDefaults integerForKey:#"runCount"] != 1) {
//not the 2nd run
[userDefaults setInteger:1 forKey:#"runCount"];
NSLog(#"not 2nd run");
} else {
//second run or more
NSLog(#"2nd run");
}
[userDefaults synchronize];
everything works fine, but the problem is that when I uninstall(delete and re-install) the application according to here and here the data should be cleared, but it is not and after re-installing the app previous data is still showing up.
I'm running my app on iOS simulator using xCode6-beta and targeting the application on iOS 8
I think this is due to a bug in the iOS8 Beta Simulator.
The expected behavior is that when the app is deleted, the NSUserDefaults for that app are deleted as well.
However, NSUserDefaults are NOT deleted when you remove an app from the simulator.
They are correctly deleted when you delete them from a physical device running iOS8.
A quick and annoying solution for now is to click, iOS Simulator -> Reset Content and Settings.
Xcode 9.2 with Simulator 10 still presents this issue. Menu option is now Hardware .. Erase All Content and Settings
I submitted a bug report btw
Since Reset Content and Settings is a nuclear option, you could consider two other options until the bug on the iOS 8/Xcode 6 GM simulator is addressed:
You could manually delete the plist file where the NSUserDefaults are stored. This is currently located at ~/Library/Developer/CoreSimulator/Devices/*some_device_id*/Library/Preferences/com.yourcompany.yourapp.plist It's a little tedious to find the right simulator to work with among the UUID directory names. EDIT: 2014-10-28 20-34-52 Correct path:
~/Library/Developer/CoreSimulator/Devices/*some_device_id*/data/Library/Preferences/com.yourcompany.yourapp.plist
You could perform "surgery" on that plist (using a run script build phase perhaps) using plistbuddy e.g.
/usr/libexec/plistbuddy -c "Set :BSDidMoveSqliteDb 0" path_to_plist
For anyone facing the same issue.
If you have more than 1 app under the same group and all of them are using app groups (ON under capabilities), then you will have to remove all the apps from the device in order for the user defaults to be cleared.
Since the user defaults are shared, even if one of the app is on the device then it will not be deleted, as that app will be using the userdefaults.
The code should work fine on the device.
Maybe some bugs in the simulator.
Try to Reset Contents and Settings for the Simulator.
it is a bug, and you can delete NSUserDefaults with following code
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
While this is still a bug another option could be to remove the specific key(s) in NSUserDefaults. Most of the time, when testing/developing, we only care about a few keys and not everything in NSUserDefaults. If you do only care about a few keys than I propose adding removeObjectForKey:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
// ADD THIS TO SIMULATE CLEAN/EMPTY DEFAULTS, COMMENT OUT AFTER INITIAL EXECUTION.
// This will cause the TRUE case to be executed.
[userDefaults removeObjectForKey:#"runCount"]
NSLog(#"hello %ld", (long)[userDefaults integerForKey:#"runCount"]);
if ([userDefaults integerForKey:#"runCount"] != 1) {
//not the 2nd run
[userDefaults setInteger:1 forKey:#"runCount"];
NSLog(#"not 2nd run");
} else {
//second run or more
NSLog(#"2nd run");
}
[userDefaults synchronize];
Adding removeObjectForKey simulates the first run of the app, commenting it out will simulate all subsequent app executions.
In my case i found the *.plist in the following directory:
[1] /Users/SOME-USERNAME/Library/Developer/CoreSimulator/Devices/SOME-DEVICE-ID/data/Library/Preferences/SP.UserDefaultsTest.plist
Problem: Deleting the app in xCode 6(iOS 8 simulator) but the file stays on disk like mentioned above.
Solution: Deleting the located file from path [1] manually and the NSUserDefaults are gone.
So the annoying way to reset the simulator is no longer necessary.

How to handle iOS app launching after update?

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.

Debugging iOS app on first launch

I have code that is triggered on launch only after the app is upgraded or installed. I want to debug this code, but can't think of a way to do it.
I can't launch the app with Xcode because it would overwrite the existing app and wouldn't trigger the update.
I can't attach Xcode to the process manually because the code I want to debug will have executed by the time I get Xcode attached.
Is there someway of pulling this off that I'm missing?
Use the "Wait for MyApp.app to launch" option.
In Xcode, hold down the Option key and choose Product->Run... (the ellipses are added when you hold Option down). This will bring up the scheme editor, with the Run scheme selected. Click on the Info tab, and on the resulting Info panel you'll see a radio group labelled "Launch" with two options: "Automatically" and "Wait for MyApp.app to launch". If you select the second option, Xcode will start the debugger and wait for you to launch your application manually. You can set a breakpoint at the beginning of the code you want to debug, and the debugger will stop there.
Reset the app to its previous state.
You may want to consider adding some code that resets the app to whatever state it was in before the upgrade. This can be debug code that's excluded from the release build. Debugging problems that require you to re-install the app every time you want to run through your debug can take a lot of time. Even just having to restart the app at every debug cycle eats up a lot of your time. It's very often worthwhile to spend a little extra time adding a button to your app that undoes whatever changes you're making so that you can do them again.
Unit test.
One great way to debug code that deals with app states that are difficult to recreate is to write unit tests. In a test, you can create any state you want. You can even create app states that may be impossible to recreate in your app. And you can debug that code over and over again. Like the previous solution, it takes a little more time to write the code up front, but you'll save so much time on each iteration of your debug cycle that its well worth it. Even better, you can add the test to your build process so to help ensure that the functionality doesn't break later.
I also have some code that I only run on upgrades, i have this method below which I use at the end of applicationDidFinishLaunching. I can debug this too:
- (void) determineWhetherToForceSync {
NSString *currentVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleShortVersionString"];
NSString *savedVersionKey = #"savedVersionKey";
NSString *savedVersion = [[NSUserDefaults standardUserDefaults] valueForKey:savedVersionKey];
if (!savedVersion) {
//This is a fresh install....
} else {
//They have a saved version, so check if it's the same as current version
if (![currentVersion isEqualToString:savedVersion]) { //They have done an upgrade
//This is an update
} //.... else they are starting the app on a second attempt
}
//Save the current version
[[NSUserDefaults standardUserDefaults] setObject:currentVersion forKey:savedVersionKey];
}
To re-test, delete the app and re-install. Or change your version number.

Resources