I am writing a little systray application that fetches data from an API and updates its menu accordingly, and am having trouble with updating the menu while it's open.
I don't even know where to start, so let's start with the beginning.
I have a custom PNLinksLoader
class whose responsibility is to fetch the data and parse it:
- (void)loadLinks:(id)sender
{
// instance variable used by the NSXMLParserDelegate implementation to store data
links = [NSMutableArray array];
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
if (YES == [parser parse]) {
NSArray *modes = [NSArray arrayWithObject:NSRunLoopCommonModes];
[delegate performSelectorOnMainThread:@selector(didEndLoadLinks:) withObject:links waitUntilDone:NO modes:modes];
}
}];
}
The loader is ran one time at the application's startup (works perfectly), and then a timer is setup for periodic refresh:
loader = [[PNLinksLoader alloc] init];
[loader setRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://papyrus.pandanova.com/links"]]];
[loader setDelegate:self];
[loader loadLinks:self];
NSMethodSignature *signature = [loader methodSignatureForSelector:@selector(loadLinks:)];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:loader];
[invocation setSelector:@selector(loadLinks:)];
NSTimer *timer = [NSTimer timerWithTimeInterval:10 invocation:invocation repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
Now whenever the loader loads new data on the server, if the menu is closed, then everything is ok, I open the menu and the new data is there.
But if the menu happens to be open during the refresh, then nothing happens. If I close and reopen the menu, then I can see the new data.
I think I'm missing something about the RunLoop, but I fail to see what (my understanding of it is very sparse, as I'm actually writing this little app mainly to learn Objective-C).
EDIT
The problem here is not to update the menu while it is open, it actually kind of works when I use performSelector:withObject:
instead of performSelectorOnMainThread:withObject:waitUntilDone:modes:
in the loader. The thing is when I do that I get weird results when updating the menu while it's open (works perfectly when the menu is closed):
Adding an NSLog
call in my menu population loop fixes
the symptoms, and from what I read on the internet, it might be a sign that I have a race condition on my threads (that's why I tried using performSelectorOnMainThread
, but I can't seem to figure it out.