I am trying to pass data from one controller to another (without doing any type of animation or segue) to populate a table with an array. Populating the table is not an issue as I have been able to do that, but getting the data to the controller with the tableview has proven to be impossible.
I've been trying to figure out how to do this for the last 4 days. I've read through tons of helpful posts including: Passing Data between View Controllers, with absolutely no luck. I'm fairly new to iOS dev so maybe I'm just completely overlooking something that should be simple.
I am using SWRevealViewController for the sidebar.
WebAppViewController.m
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSString *html = [webView stringByEvaluatingJavaScriptFromString:
#"document.getElementById('json-ios').innerHTML"];
NSData *jsonData = [html dataUsingEncoding:NSUTF8StringEncoding];
NSError *e;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:nil error:&e];
NSArray *jsonArray = [dict objectForKey:#"results"];
// This is where I would like to pass jsonArray to my sidebar to populate a table (SidebarViewController) each time the page loads
.....
try using NSNotification or Singleton
NSNotification:
#implementation TestClass
- (void) dealloc
{
// If you don't remove yourself as an observer, the Notification Center
// will continue to try and send notification objects to the deallocated
// object.
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
- (id) init
{
self = [super init];
if (!self) return nil;
// Add this instance of TestClass as an observer of the TestNotification.
// We tell the notification center to inform us of "TestNotification"
// notifications using the receiveTestNotification: selector. By
// specifying object:nil, we tell the notification center that we are not
// interested in who posted the notification. If you provided an actual
// object rather than nil, the notification center will only notify you
// when the notification was posted by that particular object.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveTestNotification:)
name:#"TestNotification"
object:nil];
return self;
}
- (void) receiveTestNotification:(NSNotification *) notification
{
// [notification name] should always be #"TestNotification"
// unless you use this method for observation of other notifications
// as well.
if ([[notification name] isEqualToString:#"TestNotification"])
NSLog (#"Successfully received the test notification!");
}
#end
... somewhere else in another class ...
- (void) someMethod
{
// All instances of TestClass will be notified
[[NSNotificationCenter defaultCenter]
postNotificationName:#"TestNotification"
object:self];
}
source
Singleton:
The standard way of creating a singleton is like...
Singleton.h
#interface MySingleton : NSObject
+ (MySingleton*)sharedInstance;
#end
Singleton.m
#import "MySingleton.h"
#implementation MySingleton
#pragma mark - singleton method
+ (MySingleton*)sharedInstance
{
static dispatch_once_t predicate = 0;
__strong static id sharedObject = nil;
//static id sharedObject = nil; //if you're not using ARC
dispatch_once(&predicate, ^{
sharedObject = [[self alloc] init];
//sharedObject = [[[self alloc] init] retain]; // if you're not using ARC
});
return sharedObject;
}
#end
source
P.S.There is no way singleton won't work.
Singleton is like a static object. You would want to save data here that will be accessible by the whole app without much changes in code.(Its like a global object) whereas NSNotification is self explanatory and googlable
Use protocol or NSNotification for simple data passing ..
Related
When I receive data from a socket and pass the data to another VC via NSNotificationCenter, the passed object always logs (null), despite the object being present in the other class.
there is where I pass the data through.
UPDATED:
-(void) initSIOSocket {
[SIOSocket socketWithHost:#"http://192.168.1.4:8080" response:^(SIOSocket *socket) {
self.socket = socket;
NSLog(#"%# from initSIOSocket", self.socket);
[self.socket on:#"q_update_B" callback:^(NSArray *args) {
NSArray *tracks = [args objectAtIndex:0];
[[NSNotificationCenter defaultCenter] postNotificationName:#"qUpdateB" object:nil userInfo:[NSDictionary dictionaryWithObject:tracks forKey:#"tracksData"]];
}];
..
- (void)receiveUpdateBNotification:(NSNotification *)notification
{
// Do parse respone data method and update yourTableViewData
NSArray *tracks = [[notification userInfo] objectForKey:#"tracksData"];
NSLog(#"%#", tracks);
self.tracks = tracks;
[self.tableView reloadData];
}
Console is STILL logging as (null) object. The notification is successful, no data is sent.
For passing data using NSNotification you need to use the userInfo dictionary.
Post it like:
[[NSNotificationCenter defaultCenter] postNotificationName:#"qUpdateB" object:nil userInfo:[NSDictionary dictionaryWithObject:tracks forKey:#"MyData"]];
And retrieve it using:
- (void)receiveUpdateBNotification:(NSNotification *)notification
{
self.tracks = [[notification userInfo] objectForKey:#"MyData"];
[self.tableView reloadData];
}
Object property is not intended for passing data.
object
The object associated with the notification. (read-only) Declaration
#property(readonly, retain) id object Discussion;
This is often the object that posted this notification. It may be nil.
Typically you use this method to find out what object a notification
applies to when you receive a notification.
For example, suppose you’ve registered an object to receive the
message handlePortDeath: when the “PortInvalid” notification is posted
to the notification center and that handlePortDeath: needs to access
the object monitoring the port that is now invalid. handlePortDeath:
can retrieve that object as shown here:
- (void)handlePortDeath:(NSNotification *)notification
{
...
[self reclaimResourcesForPort:notification.object];
...
}
Reference
I needed to use my Singleton to pass the data using the NSNotificationCenter, like so.
-(void) initSIOSocket {
[SIOSocket socketWithHost:#"http://192.168.1.4:8080" response:^(SIOSocket *socket) {
self.socket = socket;
NSLog(#"%# from initSIOSocket", self.socket);
[self.socket on:#"q_update_B" callback:^(NSArray *args) {
NSArray *tracks = [args objectAtIndex:0];
self.setListTracks = tracks;
[[NSNotificationCenter defaultCenter] postNotificationName:#"qUpdateB" object:nil];
}];
}];
}
..
- (void)receiveUpdateBNotification:(NSNotification *)notification
{
if ([[notification name] isEqualToString:#"qUpdateB"])
NSLog (#"Successfully received the test notification!");
// Do parse respone data method and update yourTableViewData
NSArray *tracks = [[SocketKeeperSingleton sharedInstance]setListTracks];
self.tracks = tracks;
[self.tableView reloadData];
}
I have an array called 'venues' that I'm generating in the following method. I'm trying to reuse it in a different method, but every time I try to access it in a different method the array is 'null'. The method is:
- (void)startSearchWithString:(NSString *)string {
[self.lastSearchOperation cancel];
self.lastSearchOperation = [Foursquare2
venueSearchNearLocation:#"Chicago" query:nil limit:nil intent:intentCheckin radius:nil categoryId:string
callback:^(BOOL success, id result){
if (success) {
NSDictionary *dic = result;
NSArray *venuesDic = [dic valueForKeyPath:#"response.venues"];
self.venues = [SearchVenue placesWithArray:venuesDic];
NSLog(#"self.venues inside is %#",self.venues);
} else {
NSLog(#"%#",result);
}
}];
NSLog(#"self.venues outside is %#",self.venues);
}
self.venues inside logs content.
self.venues outside logs (null).
I'm trying __block but without success.
In my .h I have:
#property (strong, nonatomic) NSArray *venues;
The callback block is executed asynchronously after venueSearchNearLocation has finished.
your log code:
NSLog(#"self.venues outside is %#",self.venues);
probably gets executed before your callback block and therefore self.venues are still nil.
You must access self.venues after your callback-block has finished.
How you do that really depends on your app. One way of lose coupling is to post a notification after you have set your array.
[[NSNotificationCenter defaultCenter] postNotificationName:
#"VENUES_RECEIVED " object:nil];
and in some initializer, you need to listen to that notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receivedVenus)
name:#"VENUES_RECEIVED" object:nil];
and of course you need a method where you can work with your array
- (void) receivedVenus
{
NSLog(#"self.venues outside is %#",self.venues);
}
Do not forget to remove your observer (i.e. in dealloc)
[[NSNotificationCenter defaultCenter] removeObserver:self];
Try
NSArray *venuesDic = [NSArray arrayWithArray:[dic valueForKeyPath:#"response.venues"]];
I am using the following code in a few different view controllers to listen for Dropbox datastore changes.
Each view controller has a property defined like this:
#property (nonatomic, retain) DBDatastore *store;
And then I add an observer inside listenForRemoteDataChanges with this code:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//Listen for remote Dropbox changes
DBAccount *account = [[DBAccountManager sharedManager] linkedAccount];
if(account){
self.store = [DBDatastore openDefaultStoreForAccount:account error:nil];
__weak typeof(self) weakSelf = self;
[[PPDropboxSync sharedDropboxSync] listenForRemoteDataChanges:self.store weakController:weakSelf];
}
}
...and then remove the observer with these methods:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
//Stop listening for Dropbox changes
if(self.store) {
[self.store removeObserver:self];
[self.store close];
self.store = nil;
}
}
-(void)dealloc {
//Deallocate NSNotifications (prevents mistakenly calling unavailable notification which causes crashes)
[[NSNotificationCenter defaultCenter] removeObserver:self];
//Stop listening for Dropbox changes
if(self.store) {
[self.store removeObserver:self];
[self.store close];
self.store = nil;
}
}
I keep getting this error and the datastore sync subsequently fails:
ERR: DROPBOX_ERROR_ALREADYOPEN: database_manager.cpp:155: datastore default already open
It appears the DBDatastore stays open from controller to controller even though they each have their own self.store property. Why? I thought I was closing the datastore with the viewWillDisappear method using [self.store close]; Any idea what I'm doing wrong?
As Clifton tried to explain, this means that you are opening it a second time (for the second view controller) before you close the first one. A datastore may only be opened once, until it is closed, so that is what the error is trying to tell you.
Perhaps you can use a singleton pattern?
I have multiple views where I need to handle the network connection of socket.io, so I created singleton class namely MC_SocketHandler. Below is the code of the MC_SocketHandler class.
// MC_SocketHandler.h
#import <Foundation/Foundation.h>
#import "SocketIO.h"
#interface MC_SocketHandler : NSObject <SocketIODelegate>
// SocketIO
//#property (nonatomic) SocketIO *socketConnection;
+ (MC_SocketHandler *) sharedSocketHanderObj;
+ (SocketIO *) initHandShake;
+ (SocketIO *) getSocketConnection;
-(bool) isConnected;
-(void) disConnect;
-(void) fireAgentLeftChat;
#end
// MC_SocketHandler.m
#import "MC_SocketHandler.h"
#import "MC_APIUtility.h"
#implementation MC_SocketHandler
SocketIO *socketConnection = nil;
static MC_SocketHandler *sharedSocketObj = nil;
+ (MC_SocketHandler *) sharedSocketHanderObj {
if (sharedSocketObj == nil)
sharedSocketObj = [[MC_SocketHandler alloc] init];
return sharedSocketObj;
}
+(SocketIO*) initHandShake {
if (socketConnection == nil) {
NSDictionary *headers = [NSDictionary dictionaryWithObjectsAndKeys:[MC_APIUtility getApiToken], #"token", nil];
socketConnection = [[SocketIO alloc] initWithDelegate:(id)self ];
[socketConnection connectToHost:domain onPort:447 withParams:headers];
}
return socketConnection;
}
+ (SocketIO *) getSocketConnection {
return socketConnection;
}
-(bool) isConnected {
if (socketConnection == nil)
return socketConnection.isConnected;
return false;
}
-(void) disConnect {
if (socketConnection != nil && socketConnection.isConnected)
[socketConnection disconnect];
NSLog(#"Disconnected --- %hhd", socketConnection.isConnected );
return;
}
// SocketIO Delegate
-(void) socketIODidConnect:(SocketIO *)socket {
NSLog(#"Socket has Connected....");
}
-(void) socketIO:(SocketIO *)socket didReceiveEvent:(SocketIOPacket *)packet {
NSString *data = packet.data;
NSLog(#"---- didReceoveEvent - data - %#", data);
// Grab data from packet
NSDictionary *dict = packet.dataAsJSON;
NSLog(#"EVENT DATA :- %# DICT :- %#", data, dict);
/*
EVENTS To Listen
onSuccessInit
visitor_info
new_visitor
agent_online
agent_offline
agent_logout
*/
dict = nil;
// Pull out args fro mdict
//NSArray *args = dict[#"args"];
}
-(void) socketIO:(SocketIO *)socket didReceiveMessage:(SocketIOPacket *)packet {
NSLog(#"Rcvd Message - %#", packet.data);
}
-(void) socketIO:(SocketIO *)socket didSendMessage:(SocketIOPacket *)packet {
NSLog(#"Send Msg - %#", packet.dataAsJSON);
}
-(void) socketIO:(SocketIO *)socket onError:(NSError *)error {
NSLog(#"Error - %#", error);
}
-(void) socketIODidDisconnect:(SocketIO *)socket disconnectedWithError:(NSError *)error {
NSLog(#"Disconnected With Error - %#", error);
}
-(void) fireAgentLeftChat {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:[MainAppDataObject sharedAppDataObject].activeAgentChatItem.chatSessionId forKey:#"chat_session_id"];
[socketConnection sendEvent:#"agentLeftChat" withData:dict];
return;
}
- (void)dealloc {
socketConnection = nil;
}
#end
Code that I use it in 1 of my views :
// Init SocketIO
SocketIO *socket = [MC_SocketHandler initHandShake];
// Fire Agent Online event
[socket sendEvent:#"setAgentOnline" withData:nil];
Handshake is being done properly, setAgentOnline event is send properly. Other events that I fire are also done properly. BUT,
when socket gets connected thru initHandshake, I believe "Socket has Connected...." should be seen in logs as that is written in socketIODidConnect delegate method. Similarly, I receive event (I see logs of socket.m class), but my delegate method didReceiveEvent is never called. Same way I don't see any logs of any delegate methods.
In initHandShake method only I have set the delegate also :
socketConnection = [[SocketIO alloc] initWithDelegate:(id)self ];
yet why these methods aren't called.
I was also wondering, when I receive events, on different events I got to perform different actions. How will I transfer to particular view (View's obj won't be shared with this to call his method) ? And If I create delegate, then I will have to handle all delegate methods in all views. What's will be the best method to work out with this ? And why this Singleton & delegate methods aren't being linked & called when I have set the delegate. Where am I going wrong ?
Any help, guidance is highly appreciative. Thanks alot.
In SocketIO, you create a SocketIO
Is that right?
In fact called "socketConnection". Am i right?
AT THAT TIME...
you must set the delegate !!!
Essentially, your code must look like this,
socketConnection = make one of these.
socketConnection.delegate = self;
It's possible this is your fundamental problem. I hope it helps!
PS you should, almost certainly, use only properties in iOS development. get rid of your "traditional" variables and use only properties.
I have a view controller that's subscribing to notifications broadcast elsewhere. This is part of a large object graph that has some cleanup that needs to be done when the children are dealloc'd. But dealloc was never being called in one of the children (ARC environment), which (if I understand) means something somewhere was still retained, thus causing ARC to never dealloc even when the VC is dismissed. I've traced the offending code to the following two lines. The first version causes the VC and children to never be fully dealloc'd. In the second version, things work fine and everything gets dealloc'd when this VC is dismissed.
My question is why? With ARC I can't understand why this additional NSArray would not get properly released.
Hopefully this is enough code. Can post more if need be.
Here's the version that leads to the VC (and children, etc) never being fully dealloc'd:
// Subscribe to notifications that the number of trackables has changed
[[NSNotificationCenter defaultCenter] addObserverForName:kUpdatedNumberofTrackables object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
if ([note.object isKindOfClass:[NSArray class]]) {
NSArray *activeTrackableNames = note.object; // <-- offending line
[self trackableUpdate:activeTrackableNames]; // <-- offending line
} else {
NSLog(#"Observer error. Object is not NSArray");
}
}];
But when I do it the following way, everything gets released properly when the view is unloaded, and thus dealloc is called on child VCs:
// Subscribe to notifications that the number of trackables has changed
[[NSNotificationCenter defaultCenter] addObserverForName:kUpdatedNumberofTrackables object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
if ([note.object isKindOfClass:[NSArray class]]) {
[self trackableUpdate:note.object]; // <-- seems to work
} else {
NSLog(#"Observer error. Object is not NSArray");
}
}];
A few other relevant methods in this VC that may(?) play a role:
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
self.trackablesVisible_private = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - Target Handling
-(void)trackableUpdate:(NSArray *)trackablesArray {
NSLog(#"Trackable Changed");
if (([trackablesArray count] > 0) && [self.delegate respondsToSelector:#selector(foundValidTargets:)]) {
[delegate foundValidTargets:trackablesArray];
}
self.trackablesVisible_private = trackablesArray;
}
I don't understand what's going on here. Could someone please enlighten me?
Thanks!
EDIT:
So as noted in the replies, part of the issue is a retain cycle from failing to use a weak self in the block, however apparently there's another issue going on here because I'm using Notification Center. The solution is described here: Dealloc Not Running When Dismissing Modal View from Block
My final working code is as follows:
In #interface
__weak id observer;
In loadView
__weak ThisViewController *blockSelf = self; // Avoids retain cycle
observer = [[NSNotificationCenter defaultCenter] addObserverForName:kUpdatedNumberofTrackables object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
if ([note.object isKindOfClass:[NSArray class]]) {
ThisViewController *strongSelf = blockSelf; // Avoids retain cycle
NSArray *activeTrackableNames = note.object;
[strongSelf trackableUpdate:activeTrackableNames];
} else {
NSLog(#"Observer error. Object is not NSArray");
}
}];
And in viewWillDisappear
[[NSNotificationCenter defaultCenter] removeObserver:observer];
I don't yet fully understand why you have to save the returned observer object, rather than just removing self, but this works.
You have a retain cycle in your block...here is what you want
__weak MyViewControllerName *bSelf = self;
// Subscribe to notifications that the number of trackables has changed
[[NSNotificationCenter defaultCenter] addObserverForName:kUpdatedNumberofTrackables object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
if ([note.object isKindOfClass:[NSArray class]]) {
**NSArray *activeTrackableNames = note.object;
[bSelf trackableUpdate:activeTrackableNames];**
} else {
NSLog(#"Observer error. Object is not NSArray");
}
}];
For more info on blocks and retain cycles, check out http://zearfoss.wordpress.com/2012/05/11/a-quick-gotcha-about-blocks/