I know that this question has been already asked many times.
I use Socket.IO client for Xcode (https://github.com/pkyeck/socket.IO-objc). And problem is that I need socketIO connection in many view controllers.
In ViewControllerA (.h file) I have this:
#import "SocketIO.h"
#inteface ViewControllerA : UIViewController <SocketIODelegate> {
SocketIO *socketIO;
}
(.m file):
- (void) viewDidLoad {
...
[socketIO connectToHot:#"localhost" onPort:1234];
}
And ViewControllerB is made for chat room, I need to get this socketIO object to use for communicate with server. Am I right that I need to do this things?
1) In ViewControllerB make the same as in ViewControllerA
#import "SocketIO.h"
#inteface ViewControllerA : UIViewController <SocketIODelegate> {
SocketIO *socketIO;
}
2) In ViewControllerA before seque make this:
#import "viewControllerB.h"
...
viewControllerB *viewControllerB = (viewControllerB *)[storyboard instantiateViewControllerWithIdentifier:#"viewControllerB"];
viewControllerB.socketIO = socketIO;
[self pushViewController:viewControllerB animated:YES];
Or maybe there are some easy methods to access data from many view controllers?
Even though some people don't like Singletons, I find them very useful in these types of situation, where there is no obvious "owner" of a piece of data. A Singleton is an object that will only exist as one and only one instance. It's accessed through a static method in the singleton class itself:
SocketKeeperSingleton.h
#interface SocketKeeperSingleton : NSObject
#property (nonatomic, strong) SocketIO *socketIO;
+ (SocketKeeperSingleton *) sharedInstance;
#end
SocketKeeperSingleton.m
#import "SocketKeeperSingleton.h"
#implementation SocketKeeperSingleton
// The synthesize will automatically generate a getter and setter
#synthesize socketIO = _socketIO;
+ (SocketKeeperSingleton *)sharedInstance {
static SocketKeeperSingleton *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {
NSLog(#"In SocketKeeperSingleton init");
}
return self;
}
// Override the getter for your socket
- (SocketIO *)socketIO {
if (!_socketIO || check-to-see-if-your-socket-has-been-disconnected-or-failed) {
_socketIO = code-to-create-and-connect-your-socket-goes-here
}
return _socketIO;
}
#end
Then, get the socket from anywhere in your project, like this:
SocketIO *mySocketIO = [SocketKeeperSingleton sharedInstance].socketIO;
Good luck!
Related
This is a SpriteKit game.
I am using a singleton to store a single 'Show' object to be accessible throughout the game. The 'Show' class has an NSString property 'showTitle'.
In ViewController1, I set the 'Show' property of the singleton. To test.. I then printed out a string property of 'Show' (showTitle) from the singleton and it prints the string correctly.
After segueing to ViewController2, I again print out the same string property of 'Show' (showTitle) from the singleton and it again prints the string correctly.
THEN, the spritekit scene is initialized from ViewController2. I attempt to print the same string property
of 'Show' from the singleton, and it prints null instead of the string. I went further and segued to ViewController3, tried to print the showTitle from the singleton..... NULL.
Why am I able to access the 'Show' property of the singleton in ViewControllers 1 & 2, but not from within the sprite kit scene or ViewController3. Where am I going wrong?
ShowSingleton.h
#import <Foundation/Foundation.h>
#import "Show.h"
#interface ShowSingleton : NSObject
#property (nonatomic, strong) Show* currentShow;
+(ShowSingleton *)singleton;
#end
ShowSingleton.m
#import "ShowSingleton.h"
#implementation ShowSingleton
#synthesize currentShow;
+(ShowSingleton *)singleton {
static dispatch_once_t pred;
static ShowSingleton *shared = nil;
dispatch_once(&pred, ^{
shared = [[ShowSingleton alloc] init];
});
return shared; }
#end
ViewController1:
- (IBAction)openShow:(UIButton*)sender
{
//showsarray is an array of 'Show' objects retrieved through core data in another class
[ShowSingleton singleton].currentShow = [showsarray objectAtIndex:sender.tag];
NSLog(#"Opened show: %#", [ShowSingleton singleton].currentShow.showTitle);
//The above line correctly prints the selected showTitle from the singleton
}
After openShow: completes, a segue opens ViewController2. ViewController2 initializes the SpriteKit scene.
ViewController2:
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
SKView * skView = (SKView *)self.view;
if (!skView.scene)
{
skView.showsFPS = YES;
skView.showsNodeCount = YES;
SKScene * scene = [iMarchMyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
[skView presentScene:scene];
}
}
- (void)viewDidLoad
{
//The following line correctly prints the showTitle from ViewController2
NSLog(#"processing show: %#", [ShowSingleton singleton].currentShow.showTitle);
}
myScene.m:
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
if ([ShowSingleton singleton].currentShow)
{
//This always gets called, which tells me the object exists, but null is printed for showTitle
NSLog(#"show title from scene: %#", [ShowSingleton singleton].currentShow.showTitle);
}
}
return self;
}
Consider using this singleton implementation:
// ShowSingleton.h
#import <Foundation/Foundation.h>
#import "Show.h"
#interface ShowSingleton : NSObject
+ (instancetype)singleton;
#end
// ShowSingleton.m
#import "ShowSingleton.h"
#implementation ShowSingleton
+ (instancetype)singleton {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
#end
ShowSingleton *shared is scoped to the method, not the class.
Try declaring it as a class property of your AppDelegate, and then override the getter with something like this:
#property (strong, nonatomic) Show *currentShow;
and then override the getter as:
+(Show*)currentShow
{
static id _currentShow = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
currentShow = [showsarray objectAtIndex:sender.tag]; //probably not this, not sure where in your app flow this info is ready...
});
return _currentShow;
}
Now you can leverage the proper singleton UIApplicationDelegate that Apple provided, and end up with an unchanging instance of Show that is accessible anywhere in your app by calling (YourApplicationClass*)[[UIApplication sharedApplication] currentShow]
Keep in mind that dispatch_once is tied to the App Lifecycle. It will be purged when and only when the app terminates, which may be while your app is in the background.
Singleton's are fiddly to implement correctly, and even more difficult to know when their use is actually warranted, so you may want to take a look at NSUserDefaults to see if it has something you could bend to your purposes instead.
I have a protocol in one class:
#protocol DataStorageManager
- (void) saveFile;
#end
#interface DataManager : NSObject
{
id <DataStorageManager> delegate;
}
#property (nonatomic, assign) id<DataStorageManager> delegate;
//methods
#end
and its implementation:
#implementation DataManager
#synthesize delegate;
#end
and I have another class which is the adapter between the first and the third one:
#import "DataManager.h"
#import "DataPlistManager.h"
#interface DataAdapter : NSObject <DataStorageManager>
#property (nonatomic,strong) DataPlistManager *plistManager;
- (void) saveFile;
#end
and its implementation
#import "DataAdapter.h"
#implementation DataAdapter
-(id) initWithDataPlistManager:(DataPlistManager *) manager
{
self = [super init];
self.plistManager = manager;
return self;
}
- (void) saveFile
{
[self.plistManager savePlist];
}
#end
So when I in first method try to call my delegate method like this
[delegate saveFile];
Nothing happened. I don't understand what's wrong with the realization - it's a simple adapter pattern realization. So I need to use the delegate which will call the methods from the third class. Any help?
You are not setting the delegate property. You need to do this,
-(id) initWithDataPlistManager:(DataPlistManager *) manager
{
self = [super init];
self.plistManager = manager;
self.plistManager.delegate = self;
return self;
}
Also, in DataManager class remove the ivar declaration, just declaring property is sufficient, the ivar gets automatically created. Call the delegate method as below,
if([self.delegate respondsToSelector:#selector(saveFile)] {
[self.delegate saveFile];
}
Hope that helps!
In your case you forget to set your protocol delegate and also need to call protocol method
by self.delegate....
I just Give Basic Idea for how to Create Protocol
Also Read This Question
#DetailViewController.h
#import <UIKit/UIKit.h>
#protocol MasterDelegate <NSObject>
-(void) getButtonTitile:(NSString *)btnTitle;
#end
#interface DetailViewController : MasterViewController
#property (nonatomic, assign) id<MasterDelegate> customDelegate;
#DetailViewController.m
if([self.customDelegate respondsToSelector:#selector(getButtonTitile:)])
{
[self.customDelegate getButtonTitile:button.currentTitle];
}
#MasterViewController.m
create obj of DetailViewController
DetailViewController *obj = [[DetailViewController alloc] init];
obj.customDelegate = self;
[self.navigationController pushViewController:reportTypeVC animated:YES];
and add delegate method in MasterViewController.m for get button title.
#pragma mark -
#pragma mark - Custom Delegate Method
-(void) getButtonTitile:(NSString *)btnTitle;
{
NSLog(#"%#", btnTitle);
}
can't understand what's wrong in this case, so I cant use the delegate - there is an exception in self.plistManager.delegate = self; Property 'delegate' not found on object of type 'DataPlistManager *'
#import "DataManager.h"
#import "DataPlistManager.h"
#interface DataAdapter : NSObject <DataStorageManager>
#property (nonatomic,strong) DataPlistManager *plistManager;
- (void) saveFile;
#end
and its implementation
#import "DataAdapter.h"
#implementation DataAdapter
-(id) initWithDataPlistManager:(DataPlistManager *) manager
{
self = [super init];
self.plistManager = manager;
self.plistManager.delegate = self;
return self;
}
- (void) saveFile
{
[self.plistManager savePlist];
}
#end
Your DataPlistManager needs a property delegate:
#property (weak) id<DataStorageManager> delegate;
If you add #import "DataAdapter.h" in your "DataPlistManager.h" file then remove it and add it to "DataPlistManager.m" file, I don't know but some days ago i have same issue, and i solved it by using this trick :)
DataManager class contains delegate property so you should set your object as delegate of DataManager class and call method (send message) saveFile inside delegate class:
#implementation DataAdapter
- (void)someMethod) {
DataManager *dataManagerObject = [[DataManager alloc] init];
dataManagerObject.delegate = self;
}
#implementation DataManager
- (void)someDelegateMethod {
[self.delegate saveFile];
}
Are you sure you understand concept of delegation pattern?
https://developer.apple.com/library/ios/documentation/general/conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html
I have been searching all morning how to do this. I have 2 View Controllers. From the root View Controller (ViewControllerA - which is a table view controller) you can go push to the second view controller (ViewControllerB).
In the ViewControllerB, there are two fields: contacts & textBody. When the user is done they can click on "Add". This will then go back to ViewControllerA. What I am trying to do now, is for every time that process occurs, all the information from ViewControllerB the user just added goes into a cell in ViewControllerA. The user can then add as many cells as they like.
What I can't do however, is get the information across the view controllers. I have been looking all morning at using the app delegate, singletons??, protocols, sharing properties, etc! But I am still stuck.
What I want to do, but can't, is for every time the user clicks "Add" on ViewControllerB, contacts & texts are put into an array. This array is then put into another array which holds all the smaller arrays which the user has created? If you have an ideas, or links to similar/sample code or tutorials, that would be much appreciated!
Try this using the delegate method as follows
Download Sample Project with XIBs
Download Sample Project With Storyboard
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController {
NSMutableArray *dataArray;
}
- (void)passData:(NSMutableArray *)array;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "ChildViewController.h"
#implementation ParentViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
dataArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnGoToSecondView:(id)sender {
ChildViewController *secondVC = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
secondVC.delegate = self;
[self presentViewController:secondVC animated:YES completion:nil];
}
- (void)passData:(NSMutableArray *)array {
[dataArray addObject:array];
NSLog(#"Data Passed = %#",dataArray);
}
#end
ChildViewController.h
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#class ParentViewController;
#interface ChildViewController : UIViewController {
NSMutableArray *tempArray;
}
#property (strong, nonatomic) IBOutlet UITextField *txtContact;
#property (strong, nonatomic) IBOutlet UITextField *txtTextBody;
#property(nonatomic, assign) ParentViewController *delegate;
#end
ChildViewController.m
#implementation ChildViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
tempArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)viewDidUnload {
[self setTxtContact:nil];
[self setTxtTextBody:nil];
[super viewDidUnload];
}
#end
With Storyboard
If you are using storyboard then create a ParentViewController segue ChildViewController and give it a identifier in my sample it showChildView
Then use the following code to set the delegate
// Calling the segue to go to the child view and setting up the delegate.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showChildView"]) {
ChildViewController *childVC = segue.destinationViewController;
childVC.delegate = self;
}
}
Then to dismiss back to the ParentViewController use the following code (from my sample)
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self.navigationController popToRootViewControllerAnimated:YES];
}
I would recommend using a singleton instance of your NSMutableDictionary as they have bailed me out of your exact situation multiple times (including custom frameworks and UITabBarControllers). Here is an example I'm currently using to implement a singleton. This methodology is also ARC-safe as well
mySingleton.h
#import <Foundation/Foundation.h>
#interface mySingleton : NSObject {
}
+ (NSMutableDictionary *) myMutableDict;
#end
mySingleton.m
#import "mySingleton.h"
#implementation mySingleton
+ (NSMutableDictionary *)myMutableDict
{
static NSMutableDictionary *singletonInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singletonInstance = [[NSMutableDictionary alloc]init];
});
return singletonInstance;
}
#end
As long as you include mySingleton.h in all of your view controllers you can access the data via [mySingleton myMutableDict]. For example: [[mySingleton myMutableDict] setObject:myObject forKey:myKey];
Good luck!
If the information is really "global" - it has only one instance across the whole app - then you should create a singleton as DB80Buckeye suggested.
If the information is something that truly belongs to ViewController1 and you want it to be modified in ViewController2 (ie ViewController2 is really part of ViewController1, it just happens to be on another screen), then you should pass that as part of the constructor of ViewController2.
-(void)view_controller_1_that_push_view_controller_2_onto_the_stack {
ViewController2* vc2 = [[ViewController2 alloc] initWithInformation:your_information];
[self.navigationController pushViewController:vc2 animated:YES];
}
#interface ViewController2
-(id)initWithInformation:(YourInformationClass*)info;
#end
Another way is to use notifications.
There are two ways to go here. The standard pattern for doing this is delegation. You don't need a singleton. ViewControllerA manages and lists your data. ViewControllerB doesn't need to know anything about all of that data so there's no reason to expose it via a singleton, etc.
Create a delegate protocol in ViewControllerB's header file. Something like this:
#protocol ViewControllerBDelegate
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody;
#end
Now, specify that ViewControllerA will implement the delegate protocol in its header:
#interface ViewControllerA : UIViewController <ViewControllerBDelegate>
Don't forget to import ViewControllerB.h at the top of ViewControllerA's header.
In ViewControllerA's implementation, implement the delegate method you specified in the protocol:
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody {
[self.someArray addObject:[[SomeObject alloc] initWithContact:contact body:textBody]];
[self.tableView reloadData];
}
That's obviously just an example -- not sure how you're managing your data structure and it's probably better to insert the cell someplace that makes sense.
Declare a delegate reference in ViewControllerB's header:
#property (weak, nonatomic) id<ViewControllerBDelegate> delegate;
When you present ViewControllerB, set ViewControllerA as the delegate.
ViewControllerB *b = [[ViewControllerB alloc] init...];
b.delegate = self;
In the selector triggered by the add button in ViewControllerB, call back on the delegate before popping the view controller off the navigation stack:
[self.delegate addContact:contact withBody:text];
where contact and text are the values the user entered.
One could also use a block instead of a delegate but the principle is the same -- have the second view controller only be responsible for taking input, in your case, and pass it back to the view controller managing the data.
Alternatively for delegate suggest using the following:
ViewControllerA.h:
#property (nonatomic, strong) ViewControllerB* viewControllerB;
In ViewControllerA.m
if (!self.viewControllerB)
{
self.viewControllerB = [[ViewControllerB alloc] initWithNibName: #"ViewControllerBr" bundle: nil];
}
[self.navigationController pushViewController: self.viewControllerB
animated: YES];
...
- (void) viewWillAppear: (BOOL) animated
if (self.viewControllerB)
{
NSString* contact = self.viewControllerB.contact;
NSLog(#"%#", contact);
}
...
I have tried using a singleton class in my app delegate but I haven't been able to get that to work. I've also checked out the iAdSuite examples (particularly the containerBanner example because it seemed to be the most relative) but I can't figure it out. If there's a better way to accomplish this without using a singleton class and you can point me in the right direction I'd really appreciate it. Some of my singleton class code is below. Thank you!
#interface App Delegate
#property (assign) iAdController *iadc;
+ (AppDelegate*) sharedApplication;
- (iAdController*)sharedAd;
#end
#implementation AppDelegate
#synthesize iadc;
+ (AppDelegate*) sharedApplication
{
return [[UIApplication sharedApplication] delegate];
}
-(iAdController*)sharedAd
{
if(iadc==nil){
iadc=[iAdController new];
}
return iadc;
}
#interface ViewController
iAdController*iadc=[[AppDelegate sharedApplication] sharedAd];
//here i get an error saying, "initializer element is not a compile-time constant.
Everything is imported correctly. If there's anything else I should post let me know.
try changing your singleton creation to this:
+ (LocationManagerSingleton*)sharedInstance {
static LocationManagerSingleton *_sharedInstance;
if(!_sharedInstance) {
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[super allocWithZone:nil] init];
});
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)init
{
self = [super init];
if (self != nil)
{
// PERFORM any custom initialization here
}
return self;
}
Obviously change the name of the class.
Whenever you want to use your singleton in any of your viewcontrollers just call it like this:
locationManager = [LocationManagerSingleton sharedInstance];
Dont forget to add
+ (LocationManagerSingleton*) sharedInstance;
on the header.
EDIT
well it seems i misunderstood your code (forget my answer, you simply want to be able to access your iAdController from everywhere. so just place
Add inside the .m of the ViewController
#interface ViewController()
{
iAdController *iadc;
}
And inside the
-(void)viewDidLoad
{
iadc=[[AppDelegate sharedApplication] sharedAd];
}
but import the app delegate.h on whichever viewcontroller you want to use it in.
#import "AppDelegate.h"
also there shouldnt be a space in the AppDelegate on the #interface