In my watch extension code I am trying to prevent the display of a second interface controller in certain scenarios. My solution requires looking through the list of active view controllers.
Does anyone know how to get a list of all active interface controllers? You can do this in iOS using self.navigationController.viewControllers but I could find no navigation controller in WatchKit.
I have read through the Apple Watch Programming Guide a number of times but they don't address this. Since you can push & pop interface controllers, there must be a list kept by the OS but I suspect there is no programming access to that list.
You could try to work around it by making your own list, and save it in NSUserDefaults for example:
You should clear it on the init of your initial ViewController:
- (instancetype)init {
self = [super init];
// get user defaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// empty the array at application launch
[defaults setObject:[[NSMutableArray alloc] init] forKey:#"activeVCs"];
[defaults synchronize];
return self;
}
And then in every other ViewController, you add and remove to this list on activate/deactivate of the controllers:
- (void)willActivate {
// get list of VCs
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *listOfVCs = [defaults objectForKey:#"activeVCs"];
[listOfVCs addObject:#"VC1"];
// save array and sync
[defaults setObject:listOfVCs forKey:#"activeVCs"];
[defaults synchronize];
[super willActivate];
}
- (void)didDeactivate {
// get list of VCs
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *listOfVCs = [defaults objectForKey:#"activeVCs"];
// remove VC if exists
if ([listOfVCs containsObject:#"VC1"])
[listOfVCs removeObject:#"VC1"];
// save array and sync
[defaults setObject:listOfVCs forKey:#"activeVCs"];
[defaults synchronize];
[super didDeactivate];
}
Because you can pass any valid context and access it in awakeWithContext:, I've used the technique of bundling a reference to the presenting view controller along with additional context in my WatchKit app. Not only does this allow you to build-up a list of presented interface controllers, but it also allows you to do things like assign delegates, inform controllers of interesting events, etc. I've published my JBInterfaceController project on GitHub: https://github.com/mikeswanson/JBInterfaceController
Related
I wanted to know how can I save a user's input when the user enters something from his mobile phone in the UITextField?
If I just use the text field and run the app I can enter data in the text field but when I close the application the data is gone. So how can I store that data permanently and show it again after the application is closed and reopened. Is there any way to save it?
At first, you should save the text when user did editing before user close the application(e.g. saved by NSUserDefaults):
self.yourTextView.delegate = self;
- (void)textViewDidChange:(UITextView *)textView
{
if (textView.markedTextRange == nil)
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:textView.text forKey:#"userText"];
[defaults synchronize];
}
}
Then, load the text that user saved before when user open your application again:
- (void)viewDidLoad
{
[super viewDidLoad];
self.yourTextView.text = [[NSUserDefaults standardUserDefaults]objectForKey:#"userText"];
}
We can save data into 3 data base
If you want to store single data into db, you can use
NSUserDefault
For store
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:textView.text forKey:#"textviewdata"];
[defaults synchronize];
For Retrieve
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *strTextViewText = [NSString stringWithFormat:#"%#",[defaults objectForKey:#"textviewdata"]];
Then Store a larger amount of data,we can use
SQLite
CoreData
Here are some ways to save data inside application.
create local database using sqlite or coredata both provides facilty to save data locally and before use please find the different situations to use these databases.
using NSUserDefaluts but not recomemded because NSUserDefaults aren’t meant to store sensitive information for more information see imp link see example also if you still.
To store data using NSUserDefaluts:
[[NSUserDefaults standardUserDefaults]setObject:self.textfield.text forKey:#"yourKey"];
To get data anywhere inside app:
object = [[NSUserDefaults standardUserDefaults] valueForKey:#"yourKey"];
Try with this:
-(void)viewDidDisappear:(BOOL)animated
{
[[NSUserDefaults standardUserDefaults]setObject:self.urtextfield.text forKey:#"savedtext"];
}
-(void)viewWillAppear:(BOOL)animated
{
self.urtextfield.text = [[NSUserDefaults standardUserDefaults]objectForKey:#"savedtext"];
}
I need to share an NSMutabaleArray between multiple ViewControllers, so I created a singleton. But when I re-launch my app, the array seems to clear itself which causes problems within my app. This is my singleton:
.h:
#import <Foundation/Foundation.h>
#interface playlistArray : NSObject {
NSMutableArray *playlistSongsArray;
}
#property (nonatomic, retain) NSMutableArray *playlistSongsArray;
+ (id)sharedArray;
#end
.m
#import "playlistArray.h"
#implementation playlistArray
#synthesize playlistSongsArray;
#pragma mark Singleton Methods
+ (id)sharedArray {
static playlistArray *sharedArray = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedArray = [[self alloc] init];
});
return sharedArray;
}
- (id)init {
if (self = [super init]) {
self.playlistSongsArray = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc {
// Should never be called, but just here for clarity really.
}
#end
I thought about storing it in NSUserDefaults - is this the correct route? If so, how would I do this?
Any help would be much appreciated, thanks!
I am not sure creating a separate singleton class for just passing an array to different view controllers is a great idea from code structure point of view. Much more cleaner approach would be to pass playlistSongsArray directly whenever you create a new instance of a view controller that may need it.
However, if you still want to use singleton in your implementation for some reason, I'd change playlistArray class to something like PlaylistManager (notice that the common convention is to capitalise first letters of each word in class names) — by doing this, you don't constraint yourself with array-only implementation and you can use it to share other playlist information between your view controllers.
As for persistency between app launches, it really depends on what kind of data you store in your array. For example, you can use NSUserDefaults if your array stores relatively small number of NSStrings (or other <NSCoding>-compliant classes). Other most common options are NSKeyedArchiver and Core Data. You can learn more about data persistency on iOS from Apple documentation or great online tutorials like this one on NSHipster.
I thought about storing it in NSUserDefaults - is this the correct route?
NSUserDefaults is certainly an option you have.
You can add the following two methods into your class and call it when appropriate (i.e., savePlayList when you modify the array;loadPlaylist` in the singleton init method):
- (void)savePlaylist {
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:self.playlistSongsArray forKey:#"playlistSongs"];
[defaults synchronize];
}
- (void)loadPlaylist {
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
self.playlistSongsArray = [[defaults arrayForKey:#"playlistSongs"] mutableCopy];
}
Have a look at this post for a discussion about different approaches you have.
You get singleton array blank when you relaunch the app because all memory allocated to the array is removed. These singleton classes are allocated once during the lifecycle of the app.
So the best answer to fetch the data every time you open the app is to save the data into the disk. To do so there are few ways
Database using Sqlite
plist
NSUSerDefaults
You can go for userdefaults or plist if you want to save the app.
Below is the example of saving the array into userdefault
Saving the array into userdefaults:
// Get the standardUserDefaults object, store your UITableView data array against a key, synchronize the defaults
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:array forKey:#"singletonArray"];
[userDefaults synchronize];
Retrieving the array:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSArray *singletonArray = [userDefaults objectForKey:#"singletonArray"];
Hope it will help you. Happy Coding :)
Is it possible to save and load data on Today Extension using NSUserDefaults?
After closing the Notification Center, the widget behaves like an app which is terminated, so any data results lost. How could I solve this issue?
This is my code:
NSUserDefaults *defaults;
- (void)viewDidLoad {
[super viewDidLoad];
defaults = [NSUserDefaults standardUserDefaults];
NSArray *loadStrings = [defaults stringArrayForKey:#"savedStrings"];
if ([loadStrings objectAtIndex:0] != nil) {
[display setText:[NSString stringWithFormat:#"%#", [loadStrings objectAtIndex:0]]];
}
if ([loadStrings objectAtIndex:1] != nil) {
calculatorMemory = [NSString stringWithFormat:#"%#", [loadStrings objectAtIndex:1]].doubleValue;
}
}
- (IBAction)saveData:(id)sender {
NSString *displayString;
NSString *memoryString;
NSArray *saveStrings = [[NSArray alloc] initWithObjects: displayString, memoryString, nil];
defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:saveStrings forKey:#"savedStrings"];
[defaults synchronize];
}
You need to use app group identifier instead of com.*
For instance:
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"group.company.appgroup"];
Don't forget to synchronise when you store data
[shared synchronize];
You need to add the App Group stuff detailed under here and then if it actually worked (pretty iffy under beta) it should allow you to share NSUserDefault data like normal between the host and widget.
Edit: Normal NSUserDefaults does not work. Apple has implemented a new method. To use, simply redefine your NSUserDefaults instance like this:
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"com.you.app.container"];
For anyone wondering how in the world do you save and get values then look at this code.
In your regular app add this to save whatever you like in your *.m file.
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"group.yourcompanyname.TodayExtensionSharingDefaults"];
//save dic
[shared setObject:dictionary2 forKey:#"dicForTodayWidget"];
//save array
[shared setObject:tempArray2 forKey:#"arrayForTodayWidget"];
//save some value
[shared setObject:#"1234" forKey:#"myValForTodayWidget"];
[shared synchronize];
In your today widget under TodayViewController.m in viewDidLoad add this.
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"group.yourcompanyname.TodayExtensionSharingDefaults"];
//get dic
NSMutableDictionary *dictionary = [shared objectForKey:#"dicForTodayWidget"];
You first need the App Groups set up for both targets (application and the extension).
Then, use the
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"group.company.myapp"];
to obtain the defaults object which you can read from/write to as usual.
If you want to be notified of changes to the defaults, use the NSUserDefaultsDidChangeNotification in your widget (or app).
For a step-by-step tutorial explaining all this, take a look at this blog post.
#edukulele
Today Extension and Main app run on two processes. Today Extension can't receive NSUserDefaultsDidChangeNotifications. I tried use MMWormhole. It is very good.
I'm creating a simple double value, saving it as NSUserDefault and trying to recover it...but it doesn't.
- (IBAction)try:(id)sender {
double value = 42.00;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setDouble:value forKey:#"kDoubleKey"];
// NSLog(#"loading %f",myDouble);
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSUserDefaults *fetchDefaults = [NSUserDefaults standardUserDefaults];
double intValue = [fetchDefaults doubleForKey:#"kDoubleKey"];
NSLog(#"douvle retrieve %f",intValue);
}
Do not forget to synchronise whenever you save something to the defaults:
put this at the end of your try method
[[NSUserDefaults standardUserDefaults]synchronize];
Here is what apple says about this: Because this method is automatically invoked at periodic intervals, use this method only if you cannot wait for the automatic synchronization (for example, if your application is about to exit) or if you want to update the user defaults to what is on disk even though you have not made any changes.
I need a way to initialize a lot of objects only the 1st time my app is installed. So I thought I use this:
-(id) init {
self = [super init];
if(self) {
NSUserDefaults *myUD = [NSUserDefaults standardUserDefaults];
[myUD setObject:#"5" forKey:#"Extra"]; // 5 giving 4 extra diaries
[myUD setObject:#"0" forKey:#"CatchingUp"]; // it's a live diary
[myUD setObject:#"888" forKey:#"SendingSuccess"];
[myUD synchronize];
}
return self;
}
I obviously have many more than theses to initialize but it's just to give you the idea of what I did.
I deleted the app from my phone in order to test, but the init wasn't performed. I put it before ViewDidLoad.
What am I doing wrong please or is there a better way to do it ?
Thank you very much in advance.
If you just want to set up some values at the first time your app runs,
Put this code in app delegate and use this method in didFinishLaunchingWithOptions method,
or you can put this code in your view controller and use this method in viewDidLoad method:)
- (void)setDefaultInfoValue {
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"everLaunched"]) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"everLaunched"];
//put you code here
NSUserDefaults *myUD = [NSUserDefaults standardUserDefaults];
[myUD setObject:#"5" forKey:#"Extra"]; // 5 giving 4 extra diaries
[myUD setObject:#"0" forKey:#"CatchingUp"]; // it's a live diary
[myUD setObject:#"888" forKey:#"SendingSuccess"];
[myUD synchronize];
}
}
At the moment you code will run whenever you create an instance of the class that contains the code - this is likely not what you want (and isn't what you describe).
Instead of setObject:forKey:, you should use registerDefaults: which installs the specified values only if values don't already exist and doesn't save the values to disk. This could be done in the app delegate, or separately in each class that uses different keys from user defaults.
The immediate problem you observe is probably that you aren't creating an instance of the class which contains your code.