I have xml http://weather.yahooapis.com/forecastrss?w=20070458&u=c and I want that when xml is updated my data also gets updated.
Thanks.
As you can see this XML has ttl node, which tells that Time To Live is 60 seconds. So, you can periodically (once in a minute, according to the TTL value) check this URL and stay up to date.
Read this tutorial for xmlparser and NSXMLParser Class Reference. I think it will be helpful to you.
You can poll on it.
static void timerHandler(CFRunLoopTimerRef timer, void *info)
{
//request the xml here and compare it with the previous one
}
- (void)weatherMonitor
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CFRunLoopTimerContext context = {0, self, NULL, NULL, NULL};
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), 1, 0, 0, timerHandler, &context);//use your own time interval
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
CFRelease(timer);
CFRunLoopRun();
[pool drain];
}
Run weatherMonitor in a background thread.
You have 2 options:
Implement Easy APNS which will notify your app about any changes.
And you may deliver xml data directly along the notification
message, or you may launch a request to pull up xml as soon as you
get notified.
Set a timer in your app that will launch requests to check for xml
updates each 1-10-60 minutes, whatever.
Both have pros and cons, depending on your requirements and abilities. One thing is clear: you CANNOT receive data from exterior without sending requests, other than implementing Push Notifications. Implementing Easy APNS will provide your app with data even if the application is not running. On the other side, with the timer, will be the fastest/easiest way. You decide. Cheers!
Related
I'm trying to stream data to a server at regular intervals and do so in a way that is fast and does not block the UI. The UI is also pretty busy trying to display the data. Currently my implementation uses NSTimer that fires every 50ms, picks a network packet out of a circular array and sends it over:
//this is the timer
networkWriteTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(sendActivity:) userInfo:nil repeats:YES];
-(void)sendActivityInBackground:(id)sender
{
[[AppConfig getInstance].activeRemoteRoom.connection sendNetworkPacket:[circularArray objectAtIndex:arrayIndex%arrayCapacity]];
}
-(void)sendActivity:(NSTimer*)timer
{
// Send it out
[self performSelectorInBackground:#selector(sendActivityInBackground:) withObject:nil];
}
I'm not satisfied with the performance of this method. Time profiling has revealed that there's overhead associated with performing background selectors, and the performance can get quite choppy.
I'm thinking of additional ways to improve performance:
Improve the performance of the current timer based code
Try grand central dispatch
Implement NSOperationsQueue with a single operation.
Use a dedicated thread that wakes up, checks for update and sends it over if needed
Ideally, I would send data at even faster intervals (10ms or even for each activity update). This poses the question: What is the fastest way to implement a sequence of background send requests, where order matters? I want to make sure that one packet gets send before the next one is being sent.
Try a recurring dispatch timer (that is part of gcd):
self.synchronizerQueue = dispatch_queue_create("synchronizer queue", DISPATCH_QUEUE_SERIAL);
self.synchronizeTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.synchronizerQueue);
dispatch_source_set_timer(self.synchronizeTimer, DISPATCH_TIME_NOW, NSEC_PER_MSEC * 10, NSEC_PER_MSEC * 1);
dispatch_source_set_event_handler(self.synchronizeTimer, ^
{
// do your processing here
});
dispatch_resume(self.synchronizeTimer);
folks! I'm implementing a shared cache in my app. The idea is to get the cached data from the web in the background and then update the cache and the UI with the newly retrieved data. The trick is of course to ensure thread-safety, since the main thread will be continuously using the cache. I don't want to modify the cache in any fashion while someone else might be using it.
It's my understanding that using #synchronized to lock access to a shared resource is not the most elegant approach in ObjectiveC due to it trapping to the kernel and thus being rather sluggish. I keep reading that using GCD instead is a great alternative (let's ignore its cousin NSOperation for now), and I'd like to figure out what a good pattern for my situation would be. Here's some sample code:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// download the data in a background thread
dispatch_async(queue, ^{
CacheData *data = [Downloader getLatestData];
// use the downloaded data in the main thread
dispatch_sync(dispatch_get_main_queue(), ^{
[AppCache updateCache:data];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CacheUpdated" object:nil];
});
});
Would this actually do what I think it does, and if so, is this the cleanest approach as of today of handling this kind of situation? There's a blog post that's quite close to what I'm talking about, but I wanted to double-check with you as well.
I'm thinking that as long as I only ever access shared the shared resource on the same thread/queue (main in my case) and only ever update UI on main, then I will effectively achieve thread-safety. Is that correct?
Thanks!
Yes.
Other considerations aside, instead of shunting read/write work onto the main thread consider using a private dispatch queue.
dispatch_queue_t readwritequeue;
readwritequeue = dispatch_queue_create("com.myApp.cacheAccessQueue", NULL);
Then update your AppCache class:
- (void)updateCache:(id)data {
dispatch_sync(readwritequeue, ^{ ... code to set data ... });
}
- (id)fetchData:... {
__block id data = nil;
dispatch_sync(readwritequeue, ^{ data = ... code to fetch data ...});
return data;
}
Then update your original code:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// download the data in a background thread
dispatch_async(queue, ^{
CacheData *data = [Downloader getLatestData];
**[AppCache updateCache:data];**
// use the downloaded data in the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"CacheUpdated" object:nil];
});
});
If you ask 100 developers here was is the most elegant way to do this, you will get at least 100 different answers (maybe more!)
What I do, and what is working well for me, is to have a singleton class doing my image management. I use Core Data, and save thumbnails directly in the store, but use the file system and a URL to it in Core Data for "large" files. Core Data is setup to use the new block based interface so it can do all its work on a private thread managed by itself.
Possible image URLS get registered with a tag on the main thread. Other classes can ask for the image for that tag. If the image is not there, nil is returned, but this class sets a fetchingFlag, uses a concurrent NSOperation coupled to a NSURLConnection to fetch the image, when it gets it it messages the singleton on its thread with the received image data, and the method getting that message uses '[moc performBlock:...]' (no wait) to process it.
When images are finally added to the repository, the moc dispatches a notification on the main queue with the received image tag. Classes that wanted the image can listen for this, and when they get it (on the main thread) they can then ask the moc for the image again, which is obviously there.
I am trying to download files from remote, and I now can monitor every single files download success status
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
But any way to monitor the whole process of downloading? How should I know all downloads are finished?
And I tried start download request with
[afhttpClient enqueueBatchOfHTTPRequestOperations:operationArray
progressBlock:progressBlock
completionBlock:completionBlock];
seems not work, so what the difference between above code and start download request with [operation start] in a loop?
#mattt (if you can see this)
You most probably have an NSArray of URL objects that you use to download images. What you need to do is to create an integer value equal to the count of your URL objects. Each time you successfully download an image or absolutely fail to download it (for instance after few timeouts or upon receiving 404 HTTP status code) you need to decrement that integer (note that it should be an atomic property, since blocks are being executed on different threads). Once the count reaches zero - all requests are finished. You can also use that integer to update a progress bar or simply notify user that "#/15 images are downloaded". Let me know if you need any other clarifications.
And unfortunately I have not worked with AFHTTPClient, so I can't tell you the difference between the two operations precisely, but contextually, first one executes all the requests almost at the same time asynchronously and the latter one uses consecutive approach, where second request will only be launched upon completion of the first one.
You can create NSOperationQueue and put all AFHTTPRequestOperation into it.
To observe the "operations" by using KVO. When the count go to zero, that is the time to say all operations completed.
If what you want is a constant progress update for each operation with bytes downloaded and total expected then I can highly recommend Peter Steinberger's AFDownloadRequestOperation.
This class derives from AFHTTPRequestOperation and adds a progressiveDownloadProgressBlock per-operation rather than just a per-operation-completion progress at the operation queue level, which is what I think you're looking for. Another great bonus is that it makes resumable/partial downloads much more accessible than in the core AFNetworking implementation.
it's this easy to use (example from the GitHub project's README.md):
[pdfRequest setProgressiveDownloadProgressBlock:^(NSInteger bytesRead,
long long totalBytesRead, long long totalBytesExpected,
long long totalBytesReadForFile, long long totalBytesExpectedToReadForFile)
{
self.downloadProgress = totalBytesReadForFile/(float)totalBytesExpectedToReadForFile;
}];
I use this in a few enterprise iOS projects to download multi-gigabyte files and I can tell you that it works great with the 1.0.1 release of AFNetworking.
Hope that helps…
Am very new to iPhone application development. Am adding the events to iCal from my iphone app. The user can add multiple events at a time to iCal from the app. If the user add above 50 events to iCal, the event does not adding in iCal and the app getting crash. So, i want to avoid the app crash. So, i planned to delay the adding event to iCal inside the for loop(While the loop is in running state). How can i pause the For loop running and continue the for loop after 10 sec's delay. Meanwhile the for loop should continue after 10 sec's from where it was paused. How can i do this? Any ideas? Please help to solve my problem? Thanks in advance.
The best way to do what you're after is an NSOperationQueue but it's a bit technical. Something like this might be easier to understand:
Header:
NSMutableArray *events;
When you specify your list of events:
events = /*....*/;
[self nextOp];
The nextOp method:
-(void)nextOp {
// Do 10 events at a time.
int stop = MIN(10,[events count]);
for (int i = 0; i < stop; i++) {
//Add [events objectAtIndex:i] to iCal
}
// Remove these processed events.
[events removeObjectsInRange:NSMakeRange(0,stop)];
if ([events count] > 0) {
// We've still got more events to go, so schedule our next call
[self performSelector:#selector(nextOp) withObject:nil afterDelay:10];
} else {
[events release];
events = nil;
}
}
You should not pause loops on iOS. If you cannot complete your processing promptly and synchronously, you should exit the loop and exit the method containing the loop.
To complete your loop's processing, your app should start another iteration of the loop later, such as in a callback from a completion delegate, a timer, a performselector, or an operation queue. The latter methods can include a delay. You will have to know how to unstructure loops and how to save local state to do this. Exiting a loop with a callback in place is roughly equivalent to a delayed "GOTO" statement.
For an NSTimer call to be useful in loop processing, you need to exit your loop and method, as only then will the timer's callback selector be called.
I am trying to create class that will handle multiple downloads at same time (I need to download a lot of small files) and I have problems with "disappearing" connections.
I have function addDonwload that adds url to list of urls to download, and checks if there is free download slot available. If there is one it starts download immediately. When one of downloads finishes, I pick first url form list and start new download.
I use NSURLConnection for downloading, here is some code
- (bool) TryDownload:(downloadInfo*)info
{
int index;
#synchronized(_asyncConnection)
{
index = [_asyncConnection indexOfObject:nullObject];
if(index != NSNotFound)
{
NSLog(#"downloading %# at index %i", info.url, index);
activeInfo[index] = info;
NSURLRequest *request = [NSURLRequest requestWithURL:info.url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15];
[_asyncConnection replaceObjectAtIndex:index withObject:[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:TRUE]];
//[[_asyncConnection objectAtIndex:i] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
return true;
}
}
return false;
}
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
[self performSelectorOnMainThread:#selector(DownloadFinished:) withObject:connection waitUntilDone:false];
}
- (void)DownloadFinished:(id)connection
{
NSInteger index = NSNotFound;
#synchronized(_asyncConnection)
{
index = [_asyncConnection indexOfObject:(NSURLConnection*)connection];
}
[(id)activeInfo[index].delegate performSelectorInBackground:#selector(backgroundDownloadSucceededWithData:) withObject:_data[index]];
[_data[index] release];
[activeInfo[index].delegate release];
#synchronized(_asyncConnection)
{
[[_asyncConnection objectAtIndex:index] release];
[_asyncConnection replaceObjectAtIndex:index withObject:nullObject];
}
#synchronized(downloadQueue)
{
[downloadQueue removeObject:activeInfo[index]];
[self NextDownload];
}
}
- (void)NextDownload
{
NSLog(#"files remaining: %i", downloadQueue.count);
if(downloadQueue.count > 0)
{
if([self TryDownload:[downloadQueue objectAtIndex:0]])
{
[downloadQueue removeObjectAtIndex:0];
}
}
}
_asyncConnection is my array of download slots (NSURLConnections)
downloadQueue is list of urls to download
What happens is, at the beginning everything works ok, but after few downloads my connections start to disappear. Download starts but connection:didReceiveResponse: never gets called. There is one thing in output console that I don't understand I that might help a bit. Normaly there is something like
2010-01-24 21:44:17.504 appName[3057:207]
before my NSLog messages. I guess that number in square brackets is some kind of app:thread id? everything works ok while there is same number, but after some time, "NSLog(#"downloading %# at index %i", info.url, index);" messages starts having different that second number. And when that happens, I stop receiving any callbacks for that urlconnection.
This has been driving me nuts as I have strict deadlines and I can't find problem. I don't have many experiences with iphone dev and multithreaded apps. I have been trying different approaches so my code is kinda messy, but I hope you will see what I am trying to do here :)
btw is anyone of you know about existing class/lib I could use that would be helpful as well. I want parallel downloads with ability o dynamically add new files to download (so initializing downloader at the beginning with all urls is not helpful for me)
You've got a bunch of serious memory issues, and thread synchronization issues in this code.
Rather than go into them all, I'll ask the following question: You are doing this on a background thread of some kind? Why? IIRC NSURLConnection already does it's downloads on a background thread and calls your delegate on the thread that the NSURLConnection was created upon (e.g., your main thread ideally).
Suggest you step back, re-read NSURLConnection documentation and then remove your background threading code and all the complexity you've injected into this unnecessarily.
Further Suggestion: Instead of trying to maintain parallel positioning in two arrays (and some sketchy code in the above relating to that), make one array and have an object that contains both the NSURLConnection AND the object representing the result. Then you can just release the connection instance var when the connection is done. And the parent object (and thus the data) when you are done with the data.
I recommend that you take a look at this:
http://allseeing-i.com/ASIHTTPRequest/
It's a pretty sophisticated set of classes with liberal licensing terms (free too).
It may provide a lot of the functionality that you are wanting.
This snippet can be the source of the bug, you release the object pointed to by the activeInfo[index].delegate pointer right after issuing async method call on that object.
[(id)activeInfo[index].delegate performSelectorInBackground:#selector(backgroundDownloadSucceededWithData:) withObject:_data[index]];
[_data[index] release];
[activeInfo[index].delegate release];
Do you use connection:didFailWithError: ? There may be a timeout that prevents the successful download completion.
Try to get rid of the #synchronized blocks and see what happens.
The string inside the square brackets seems to be thread identifier as you guessed. So maybe you get locked in the #synchronized. Actually, I don't see a reason for switching thread - all the problematic code should run in the main thread (performSelectorOnMainThread)...
Anyhow, there is no need to use both the #synchronized and the performSelectorOnMainThread.
BTW, I didn't see the NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; line. Where do you initiate the connection?
As for the parallel downloads - I think that you can download more than one file in a time with the same code that you use here. Just create a separate connection for each download.
Consider just keeping a download queue along with a count of active connections, popping items off the top of the queue when downloads complete and a slot becomes free. You can then fire off NSURLConnection objects asynchronously and process events on the main thread.
If you find that your parallel approach prohibits doing all of the processing on the main thread, consider having intermediary manager objects between your main thread download code and NSURLConnection. Using that approach, you'd instantiate your manager and get it to use NSURLConnection synchronously on a background thread. That manager then completely deals with the downloading and passes the result back to its main thread delegate using a performSelectorOnMainThread:withObject: call. Each download is then just a case of creating a new manager object when you've a slot free and setting it going.