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];
}
Related
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 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 ..
It seems as though I can create a dispatch_source with a type of DISPATCH_SOURCE_TYPE_VNODE and provide the file descriptor for the directory I'm interested in but I'm not sure what flags I ought to set to detect a file creation.
What I ultimately want to do is to attach a start using dispatch_io_read to read data from the file as it is written but to do this I need the file descriptor for the file which is not available until it's created.
For clarity, I do not directly create the file and so can't easily determine (without polling) when it exists.
Here's an example of how you would use GCD for that:
#implementation AppDelegate
{
dispatch_source_t _source;
NSArray* mFiles;
NSString* mPath;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Add an observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(directoryDidChange:) name:#"FilesChanged" object:self];
mPath = #"/your/dir";
int fildes = open(mPath.UTF8String, O_RDONLY);
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fildes, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE, queue);
dispatch_source_set_event_handler(source, ^{
[self updateListWithNotification: YES];
});
dispatch_source_set_cancel_handler(source, ^{
close((int)dispatch_source_get_handle(source));
});
_source = source;
dispatch_resume(source); // Start monitoring
dispatch_async(queue, ^{
[self updateListWithNotification: NO];
});
}
- (void)applicationWillTerminate:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"FilesChanged" object:self];
dispatch_source_cancel(_source); // Stop monitoring
}
- (void)updateListWithNotification: (BOOL)withNotification
{
// Our manipulation of state here is OK because we know this only ever gets called on a serial queue
mFiles = mFiles ?: [NSArray array];
NSArray* contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath: mPath error: nil] ?: [NSArray array];
if (withNotification)
{
NSSet* oldFiles = [NSSet setWithArray: mFiles];
NSSet* newFiles = [NSSet setWithArray: contents];
NSMutableSet* addedFiles = [newFiles mutableCopy]; [addedFiles minusSet: oldFiles];
NSMutableSet* removedFiles = [oldFiles mutableCopy]; [removedFiles minusSet: newFiles];
NSDictionary* ui = #{ #"FilesRemoved" : removedFiles, #"FilesAdded" : addedFiles };
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName: #"FilesChanged" object: self userInfo: ui];
});
}
mFiles = contents;
}
- (void)directoryDidChange: (NSNotification*)n
{
NSLog(#"Directory %# changed.\nFiles removed: %#\nFiles added: %#", mPath, n.userInfo[#"FilesRemoved"], n.userInfo[#"FilesAdded"]);
}
#end
This uses GCD to monitor the directory and then posts an NSNotification to the main thread with a set of files added and removed since the last notification.
Answered from this link.
In short, no but...
You cannot use dispatch_io_create to handle this as it does not work with directories, you will get a posix error 21 when trying to set up the channel.
You can however use dispatch_source_create with type DISPATCH_SOURCE_TYPE_VNODE and mask DISPATCH_VNODE_WRITE as follows:
// Open FD for events only
int dirFD = open(filename, O_EVTONLY);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_source_t src = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,
dirFD,
DISPATCH_VNODE_WRITE,
queue);
// Set the block to be submitted in response to an event
dispatch_source_set_event_handler(src, ^{
fprintf(stderr, "Directory changed!\n");
// TODO: Identify new file here
dispatch_source_cancel(src);
});
// Set the block to be submitted in response to source cancellation
dispatch_source_set_cancel_handler(src, ^{
close(dirFD);
fprintf(stderr, "Cancel!");
});
dispatch_resume(src);
I have a complex C structure and it's hard to convert this structure to NSDictionary one by one.
How do I post this C structure via NSNotificationCenter and retrieve it back from the NSNotificationCenter callback function?
Any suggestion will be appreciated, thanks!
Store and retrieve as NSValue
Store:
CustomStruct instanceOfCustomStruct = ...;
NSValue * valueOfStruct = [NSValue valueWithBytes:&instanceOfCustomStruct objCType:#encode(CustomStruct)];
[[NSNotificationCenter defaultCenter] postNotificationName:#"YourNotification" object:self userInfo:#{#"CustomStructValue" : valueOfStruct}];
Retrieve:
NSValue * valueOfStruct = note.userInfo[#"CustomStructValue"];
CustomStruct instanceOfCustomStruct;
[valueOfStruct getValue:&instanceOfCustomStruct];
Personally, I find it easiest to stuff the structure into an NSData and then send it as part of the notification. Here's the declaration of the sample structure used in the code that follows.
typedef struct
{
int a;
int b;
}
ImportantInformation;
Here's the code to send the structure as part of a notification. The last part of the third line puts the NSData object into an NSDictionary and passes that dictionary as the userInfo. The key for the NSData in the dictionary is #ImportantInformation.
ImportantInformation info = { 555, 321 };
NSData *data = [NSData dataWithBytes:&info length:sizeof(info)];
[[NSNotificationCenter defaultCenter] postNotificationName:#"ImportantChange" object:self userInfo:#{ #"ImportantInformation" : data }];
Here's the code that adds an observer for the notification and defines the block that runs when the notification is received.
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
self.observer = [center addObserverForName:#"ImportantChange"
object:nil
queue:nil
usingBlock:^(NSNotification *notif)
{
// this is the structure that we want to extract from the notification
ImportantInformation info;
// extract the NSData object from the userInfo dictionary using key "ImportantInformation"
NSData *data = notif.userInfo[#"ImportantInformation"];
// do some sanity checking
if ( !data || data.length != sizeof(info) )
{
NSLog( #"Well, that didn't work" );
}
else
{
// finally, extract the structure from the NSData object
[data getBytes:&info length:sizeof(info)];
// print out the structure members to prove that we received that data that was sent
NSLog( #"Received notification with ImportantInformation" );
NSLog( #" a=%d", info.a );
NSLog( #" a=%d", info.b );
}
}];
Note: be sure to remove the observer at some point.
[[NSNotificationCenter defaultCenter] removeObserver:self.observer];
Can i safely post messages containing code blocks within my program address space? Tests works but is this legal?
typedef void (^EmitBlock)(NSDictionary* args);
- (void) subscribe {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(on_emit:) name:kEmit object:nil];
}
- (void) on_emit:(NSNotification*) notification
{
EmitBlock completion = [[notification userInfo] valueForKey:#"completion"];
completion(#{#"result" : #"Ok"});
}
- (void) post:(EmitBlock) completion {
[[NSNotificationCenter defaultCenter] postNotificationName:kEmit
object:nil userInfo:#{#"completion":
^(NSDictionay* args) { NSLog(#"%#", args); }
}];
}
Blocks are Objective-C objects, so you can put them into an dictionary and use them
in the userinfo of a notification.
But note that according to the Transitioning to ARC Release Notes:
… You still need to use [^{} copy] when passing “down” the stack into
arrayWithObjects: and other methods that do a retain.
you should put a copy of the block into the dictionary:
[[NSNotificationCenter defaultCenter] postNotificationName:#"kEmit"
object:nil
userInfo:#{
#"completion" : [^(NSDictionary* args) { NSLog(#"%#", args); } copy]
}
];
You can use one from libraries, for example https://github.com/cflesner/NSNotificationCenter-CLFBlockNotifications for using blocks with notifications
I think it is more safer approach