iOS: Singleton Pattern for Data File - ios

Class name MyData has 75+ properties that are needed in throughout 7 Scenes.
Currently, I pass the the instance of MyData file with the code below:
in SceneOne:
MyData *myData = [[MyData alloc]init];
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destinationViewController = segue.destinationViewController;
SceneTwo *sceneTwo = [destinationViewController isKindOfClass:[SceneTwo class]] ? (SceneTwo*)destinationViewController : nil;
sceneTwo.myData = self.myData;
}
This allows me to access any properties as myData.anyProperty
When the 7th Scene is dismissed, I set myData = NULL, and the app returns to SceneOne and a new instance of MyData is initialized.
I'm trying to accomplish above via sharedInstance.
MyData.h
#import <foundation/Foundation.h>
#interface MyData : NSObject {
#property (nonatomic, retain) NSString *someProperty;
// 74 other properties
+ (id)sharedData;
#end
MyData.m
#import "MyData.h"
#implementation MyData
#synthesize someProperty;
+ (id)sharedData {
static Mydata *sharedData = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedData = [[self alloc] init];
});
return sharedData;
}
#end
Question 1: Will the code above work to access the same instance with the code below in any of the Scenes:
MyData *myData = [MyData sharedData];
Question 2: Can I still access the files as myData.anyProperty ?
Question 3: How do I trigger a new instance of MyData and delete the current instance?
Question 4: I'm downloading a myData file off the web that's a duplicate of MyData class but the properties contain data, and I want Singelton to provide a new instance with the data from the downloaded file, what would be the code for that? i.e myData = [newDownloadedFile copy];
Question 5: Is there an advantage of using Singleton Method Vs. my current method?

Yes
Yes
You don't. Why do you believe this is necessary? Can you instead add a reset method to MyData?
You shouldn't mix the singleton pattern with a multiple-instance usage case. If you truly want a singleton, think about adding an additional layer to your data set. For example, you may have a local data and remote data configuration. If this is what you want, you may have to change the interface (header) of MyData to make this possible.
A singleton is a single instance of a class across a process. When you want to access the same collection of data from multiple locations in your code, a singleton object is one way you can accomplish this. Otherwise you need to instantiate an object and pass its address to all interested classes so they are each accessing the same instance. That's an oversimplification but I believe it addresses your question.
Regarding your comment for number 3, if you have a singleton, you don't want to reset the data for the entire app if you simply don't need the data in one place anymore. So consider the impact that would have. If you no longer need the MyData object, simply don't use it anymore. Singleton objects typically persist for the lifetime of an app, so it is not common to release/dispose of the object.
For number 4, say you currently have a property called player with a method declaration like this:
- (Player *)currentPlayer;
If you have multiple configurations available, you would add a parameter to your method interface and implementation like this:
- (Player *)currentPlayerForConfiguration:(NSInteger)configuration;
You could decide to use a number, string, or something else to differentiate between different configurations of your data. If you use a number, 0 could be local, 1 could be remote, 2 could be either (for example, check local data first, and if not there, then check remote). If you only have two options, you could use a BOOL and define your method like this:
- (Player *)currentPlayerUsingLocalData:(BOOL)useLocalData;

Related

Passing data between segues without using PrepareForSegue

I'm creating an user setup account with 5 steps using storyboard. Each step have a ViewController: 1º)Input for Name, contact etc, 2º) Import photos, 3º)Input, etc 4º)more inputs 5º)Confirmation Page, if the user click "confirm" -> Get all the inputs and upload to Parse.
The only solution i get when i search online for this, is to create a func "Prepare for Segue" and pass the information...But for me, this doesnt make any sense:
If i had 1000 viewcontrollers, the first viewcontroller information will be passsed through all the 1000 viewcontrollers? Why not the nº1000 view controller getting all the information that was left behind? e.g: The viewcontroller nº50 dont need the information about the viewcontroller nº1... This is a nonsense.
Is there any other way to implement this?
Maybe there is a solution since i'm using Parse like when i do:
ParseClass["Name"] = textfield.text
It sends information to Parse and they hold it until i do something like SaveInBackground().
I'm trying to find a solution like this using viewcontrollers. Each viewcontroller send information to parse and hold the information until the "saveinbackground()" happens on Viewcontroller nº5.
Possible? thank you
Yes it is possible. You can use NSUserDefaults for that which will store your info into memory and once it is stored you can use it anywhere in your project.
Like you can store value with it this way:
NSUserDefaults.standardUserDefaults().setObject(yourObject, forKey: "yourKey")
Now you can retrive this info from any where with yourKey like this:
let yourInstance = NSUserDefaults.standardUserDefaults().objectForKey("yourKey")
And you can cast it's type as per your need.
For more Info read Apple Document for NSUserDefaults.
One more way to pass value from one view to another view without segue by using Class or Struct.
You can read more about Classes and Structures from Apple Documentation.
If our inputs are finite, create a model class with properties for it ex:
#interface UserDataInput : NSObject
#property (nonatomic)NSString *name;
#property (nonatomic)NSString *contactNumber;
....
blah blah bla
....
#end
then make it as a singleton class
#implementation UserDataInput
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static UserDataInput *sharedInstance = nil;
dispatch_once(&onceToken, ^{
sharedInstance = [[[self class] alloc] init];
});
return sharedInstance;
}
#end
Then set properties from a view controller on leaving that view controller like,
UserDataInput *sharedInput = [UserDataInput sharedInstance];
sharedInput.name = self.nameField.text;
etc....
In final view controller you can access these properties to upload to parse.
Try it.
let yourInstance = NSUserDefaults.standardUserDefaults().objectForKey("yourKey")
NSUserDefaults.standardUserDefaults().synchronize()

Where should I initialize my singleton in iOS/OS X app?

I want to initialize my singleton object which stores and manages the application settings over the entire class within my app. Also, the singleton instance should be initialized by loading the data from NSUserDefaults upon launch. However, I'm not fully sure where I should initialize the singleton upon launch.
In Cocoa app, I first wrote the singleton initialization code within applicationWillFinishLaunching:, taking parameters from NSUserDefaults. However, later I found that this doesn't work properly if I also write the singleton initialization code (taking no parameter!) within my initial view controller, set in storyboard, because the viewWillLoad:, viewDidLoad: etc. of the class of the view controller are called before the applicationWillFinishLaunching:.
So now I'm sure I should write the singleton initalization code within viewWillLoad: earlier than applicationWillFinishLaunching, but still not sure whether it is appropriate. Specifically, I know the NSApplicationMain is the first method to be called upon launch, but it seems that the next method is not anything within AppDelegate, at least if you use storyboard.
To summary, what I want to ask are the following:
What method from what class will be called after NSApplicationMain, if you use storyboard.
Where should I write my singleton initialization code within my app? I want to initialize it as soon as possible.
Does it differ between iOS and OS X app?
You should initialize it when it's first accessed. Something like this, maybe:
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
As a side note, if you're literally only using this class as an accessor to NSUserDefaults, you might want to consider using static methods instead.
+ (id)mySpecificDataPoint {
return [[NSUserDefaults standardUserDefaults] objectForKey:#"whatever"];
}
+ (void)setMySpecificDataPoint:(id)data {
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"whatever"];
}
Or maybe a more well-designed way might be to add a category to NSUserDefaults for this purpose.
#interface NSUserDefaults (MyData)
#property (nonatomic) NSString *someDataPoint;
#property (nonatomic) NSInteger somePrimitiveDataPoint;
#end
#implementation NSUserDefaults (MyData)
- (NSString *)someDataPoint {
return [self objectForKey:#"someDataPoint"];
}
- (void)setSomeDataPoint:(NSString *)someDataPoint {
[self setObject:someDataPoint forKey:#"someDataPoint"];
}
- (NSInteger)somePrimitiveDataPoint {
return [[self objectForKey:#"somePrimitiveDataPoint"] integerValue];
}
- (void)setSomePrimitiveDataPoint:(NSInteger)somePrimitiveDataPoint {
[self setObject:#(somePrimitiveDataPoint) forKey:#"somePrimitiveDataPoint"];
}
#end
You init the singleton when you have to use it. So as Daji Djan said: lazy wins. Just take attention that, you should not do a long-run process in your applicationWillFinishLaunching, it should return as soon as possible.
If the singleton is not mandatory during applicationWillFinishLaunching, you should call it in viewWillAppear of first view controller if you need to initialize it ASAP.
lazy always wins
if you can get away with it: as late as possible :) AND always do the minimum needed (but do as much as is reasonable to keep your code clean!)

How do I declare a global variable to be used in both the main view and the flipside view of an app? ios iphone

Okay, so I was reading here declaring global variables in iPhone project and I noticed the line with this code: [[[UIApplication sharedApplication] delegate] myNSString];.
Basically, I want a user to input something in a text field on the flipside view, then have it stored in a variable which is accessed on the main view. Ideally, the main view would be able to just read the text field from the flipside view, but this seems to be impossible (I've spent several hours each day for the past few days scouring the web and various books for an answer about how to do this and no one seems to be able to give a definitive answer). Therefore, I'm resorting to using a global variable to tackle this.
Will the code that I printed above somehow allow me to do this? I've been trying to adapt it for the past hour, but have come up with nothing except No known instance method for selector 'myNSString' and I'm not quite sure what that means in this case.
Can someone please help me out? I feel like I can keep trying different things but without some sort of help, I'm just shooting in the dark here. Thank you!
You may want to think about using a singleton to hold your data if you're set on using a global variable. There's a good tutorial on singletons here: http://www.galloway.me.uk/tutorials/singleton-classes/ -basically it's a class that can be shared throughout the application and accessed/modified by different controllers. You'd be able to create a property on it, write to that property from the flip view, and then access that property from your main view.
#import "Singleton.h"
#implementation Singleton
#synthesize yourTextField;
#pragma mark Singleton Methods
+ (id)sharedManager {
static Singleton *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
yourTextField = #"";
}
return self;
}
You could call it in code by importing its header file and:
Singleton *mySingleton = [Singleton sharedManager];
the mySingleton object will have the text field attached. It can be accessed by:
mySingleton.yourTextField;
.h file:
#import <Foundation/Foundation.h>
#interface Singleton : NSObject
#property (nonatomic, strong) NSString *yourTextField;
+ (id)sharedManager;
#end
Singleton (remember about dispatch_once), static variables or NSUserDefaults. It really depends what you really need.
If you are using storyboards and just want to pass data between VC, then you can use "prepareForSegue" method (described here https://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instm/UIViewController/prepareForSegue:sender:).
Segue has "destinationController" property, so you can setup VC before showing it.

Establishing a global array of strings [duplicate]

This question already has answers here:
How do I declare an array as a constant in Objective-c?
(6 answers)
Closed 9 years ago.
In my app I refer to a number of the same string variables from lots of different view controllers and I have created a number of global NSStrings using this method:
OpeningController.h (before #interface)
extern NSString *stringName;
OpeningController.m (after the #interface { } #end)
NSString *stringName =#"On";
I can then refer to/alter stringName anywhere in my application.
I want to be able to do the same with an array of strings but when I try the following I get the error Initializer is not a compile-time constant.
How do I achieve what I am trying to achieve?
OpeningController.h
extern NSArray *arrayName;
OpeningController.m (after the #interface { } #end)
NSArray *arrayName = [NSArray arrayWithObjects:
#"String1",
#"String2",
#"String3",
#"String4",
nil];
Assuming openingController is a class name (hint: it should be OpeningController) then you can initialize the array within the class's +initialize method, which will be invoked as soon as the class is referenced at runtime:
OpeningController.m:
#import "OpeningController.h"
NSArray *arrayName = nil;
#implementation OpeningController
+ (void)initialize {
arrayName = [NSArray arrayWithObjects:
#"String1",
#"String2",
#"String3",
#"String4",
nil];
}
....
#end
EDIT: Note that this isn't a particularly good example as referencing the array without referencing OpeningController first will access the un-initialized array. A better approach is a singleton pattern:
OpeningController.h:
#interface OpeningController : UIViewController // Not sure of the subclass
+ (NSArray *)array;
...
#end
OpeningController.m:
#import "OpeningController.h"
#implementation OpeningController
+ (NSArray *)array {
static NSArray *array = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
array = [NSArray arrayWithObjects:
#"String1",
#"String2",
#"String3",
#"String4",
nil];
});
return array;
}
...
#end
Don't use shared global variables. They create very strong coupling between your different classes, and force all sorts of interdependencies. That leads very quickly to spaghetti-code.
Instead, create a data container singleton. That is a singleton object (do a google search on the singleton design pattern in Objective C, or even search here) that contain properties that you want to share.
Then any time you want to read or write a global variable, you use your singleton's accessor method to fetch the singleton and then invoke the desired property.
NSUserDefaults is an example of a singleton in Apple's frameworks. The code
[NSUserDefaults sharedUserDefaults] is a class method call that returns a pointer to the user defaults singleton.
Your code might look something like [MyDataSingleton sharedDataSingleton].
You mus init the array, otherwise it will not work.
You can create a instance of the class and get that instance everytime you want.
If you have questions please tell me :)

General design - Where do I put centrally accessed objects

I have my main app delegate
I have a few UIViewController derived instances driven by a Storyboard
Say I'd like to provide a centralized persistence layer for my application - perhaps Core Data of SQLite. Where would I put those objects? I'm missing some centrally accessible "Application" class you can access from all the UIViewController instances.
Is there a pattern to follow here?
you should check the singleton pattern:
In software engineering, the singleton pattern is a design pattern
that restricts the instantiation of a class to one object. This is
useful when exactly one object is needed to coordinate actions across
the system. The concept is sometimes generalized to systems that
operate more efficiently when only one object exists, or that restrict
the instantiation to a certain number of objects. The term comes from
the mathematical concept of a singleton.
here is a source for a example implementation: What should my Objective-C singleton look like?
and here is the direct link for the modern solution:
https://stackoverflow.com/a/145395/644629
What you're describing is your model layer. There are two main ways to manage the model:
At application startup, create the main model object and hand it to the first view controller.
Make the main model object a Singleton.
The "main model object" in both cases is generally some kind of object manager. It could be a document, or it could be a PersonManager if you have a bunch of Person objects. This object will vend model objects from your persistence store (generally Core Data).
The advantage of a Singleton here is that it's a little easier to implement and you don't have to pass around the manager. The advantage of a non-Singleton is that it's easier to have more than one (for a document-based system), and it's easier to test and reason about non-singletons than singletons. That said, probably 80% of my projects use a singleton model manager.
As a side note, that you appear to already understand: never store the model in the application delegate, and never use the application delegate as a "rendezvous point" to get to the model. That is, never have a sharedModel method on the application delegate. If you find yourself calling [[UIApplication sharedApplication] delegate] anywhere in your code, you're almost always doing something wrong. Hanging data on the application delegate makes code reuse extremely difficult.
Go with a singleton pattern, which has scope of application lifetime.
#interface DataManager ()
#end
#pragma mark -
#implementation DataManager
#pragma mark - Shared Instance
static DataManager* sharedInstance = nil;
#pragma mark - Singleton Methods
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
+ (DataManager*)sharedInstance
{
#synchronized([DataManager class])
{
if (!sharedInstance) {
//[[self alloc] init];
sharedInstance = [[DataManager alloc] init];
}
return sharedInstance;
}
return nil;
}
+ (id)alloc
{
#synchronized([DataManager class])
{
NSAssert(sharedInstance == nil, #"Attempted to allocate a second instance \
of a singleton.");
sharedInstance = [super alloc];
return sharedInstance;
}
return nil;
}
#end
Declare your properties in .h file and synthesize them here in .m file.
To use that property just call:
// set value
[[DataManager sharedInstance] setSharedProperty:#"ABC"]; // If its a string
// get Value
NSLog(#"value : %#", [[DataManager sharedInstance] sharedProperty]);
Hope this is what you required.
Enjoy Coding :)

Resources