I'm pretty new to iPhone development but I'm close to releasing my first app (related to a website I run). The app requires a very large database and as such I've decided to store only the most commonly used data locally, retrieving the other data via a JSON web service call from the database my website runs off.
Whilst performing OK using the simulator (hitting the live DB) searches relying on the web service call have been taking longer than I'd have hoped they would when running on the phone. These calls look much worse when compared to the native searches which are instantaneous. To reduce the relative difference I wanted to put in a fake interstitial (page with activity indicator) for the native searches (the web service searches already use one) but I've been having an issue with the timing of navigation controller pushes when combined with sleep(n).
Anyway, the search section of my app is a navController wihin a tabController tab. When trying to use code like this:
[[tabBarController.viewControllers objectAtIndex:0] pushViewController:(UIViewController *)waitingController animated:YES];
sleep(2);
I find that the push always waits for the sleep to finish before executing when the effect I want is for the viewcontroller to be pushed and then the app to wait for two seconds before continuing to simulate the search process.
I've had some other weird results with navController pushes, on a few occasions I've experienced what seems to be a mashing of two separate viewcontrollers when the one I've pushed as interstitial remains in place content-wise with only the title of the one I want pushed in it's place remaining.
I'm sure that there is a fundamental lack of understanding on my part to blame so I'm hoping for a bit of guidance here.
Cheers,
Alan.
sleep(float) blocks the main thread causing the UI to freeze up. You should instead schedule some action to be performed later using -[NSObject performSelector:withObject:afterDelay:]
Example:
[label performSelector:#selector(setText:) withObject:#"Delayed Hello World!" afterDelay:2.0f];
I would look at changing your architecture and not sleeping on the main thread - not a great user experience!
Look into using NSOperation to control your interactions with the web service - this will allow you to queue up operations and have them run in a specific sequence or in parallel.
NSOperation is a very easy way to provide robust threading operations - and you could choose to make a call back to the main thread with any updates you want to do along the way.
I think this architecture will help you make the user interface much better - but once you have reconfigured your thinking to use operations you will find lots of other benefits.
NB: It took me a few times to really get to grips with NSOperation and NSOperationQueue - but the time I invested was definitely worth it.
Related
Objective: I want to develop an UI application that runs a service/ task/method
periodically to update database. This service should start after
periodically even if my application is not active/visible/user exits
app. Similar to an Android Service .
I'm using BlackBerry Java 7.1 SDK eclipse plugin .
The options I came across are the following:
1) How to run BlackBerry application in Background
This link suggests that I extend Application instead of UIApplication. But I can't do that as my application has a user interface.
2) Make application go in background
I don't want my UI application to go in background, instead i just want my application to call the service periodically .
3) Run background task from MainScreen in BlackBerry?
This link suggests to run I a thread, but I don't think that if user exits my application then the thread will run in background
4) Blackberry Install background service from UI application?
This suggests using CodeModuleManager ,whose usage I'm unable to figure .
Please suggest what is the best way to achieve this objective or suggests any other better method .
I am new to blackberry so please pardon my ignorance.
To expand on Peter's Answer:
You will need to create two classes :
class BgApp extends Applicaton
class UiApp extends UiApplication
I guess you have already created the class that extends UiApplicaiton. So add another class that extends Application.
Then create a class that extends TimerTask and implement its run method to call the method that updates the database.
class UpdateDatabaseTask extends TimerTask
In the BgApp constructor, create a Timer. And schedule the UpdateDatabaseTask using the schedule(TimerTask, long, long) method.
Define alternate entry points, check the "Do not show on homescreen" and "auto run on startup" checkboxes for the bgapp's entry point.
It is easiest and simplest to use the builtin persistence mechanism (PersistentStore and Persistable interface) for storing data. Even if you use any other means like RecordStore or SQLDb, both UiApp and BgApp can use access the same database. The values updated by the bgapp will be accessible by the uiapp and vice-versa, automatically.
If you want to send a signal from bgapp to uiapp (for example when bgapp downloads new data you want the uiapp to reload the data instantaneously), post a Global Event (ApplicationManager.postGlobalEvent()) when the download is complete and listen for it in the screen that is displaying the data (GlobalEventListener interface).
There are code samples for each of these available as part of the SDK or search on the internet and you'll find a lot of implementations.
Good research, lots of interesting thoughts.
I think the best thing to do is to try the simple standard approaches and only make something more sophisticated if you need to.
Here are two options that would be regarded as 'standard', with brief advantages and disadvantages:
a) Make your UiApplication go to the Background
Instead of exiting when the user presses the 'close' button, your UiApplication will "requestBackground()". it will automatically be bought to the foreground when the user clicks on the icon, or selects your application from the task switcher. Then you can run a Thread whenever you want or in fact leave one running to update the database.
This is my preferred method. But you have to careful with memory management to make sure there are no leaks. And some people don't like the idea that the Application is visible on the Task Switcher all the time.
b) Alternate Entry
With this option, your one Application package contains two Applications, or more accurately, one Application and one UiApplication. The UiApplication is run when the user clicks on the icon. The Application runs as a background task, and updates the database for your UiApplication.
This looks like a more elegant solution, but introduces some possible communication issues, and is more difficult to debug.
In your case, since you are relatively new to BB, I would suggest that you use option a, and if you find it doesn't work for you, you will not find it that difficult to swap to option b.
And to comment on the Options you have already presented:
Sort of covered with option b
Option a
You are correct - if an Application exits, all the Threads are killed
Leaves the problem of creating the application in the first place and then debugging it. This is not really a solution for you, more an implementation method.
The above is brief, please ask if it is not clear.
This might help with b:
http://supportforums.blackberry.com/t5/Java-Development/Set-up-an-alternate-entry-point-for-an-application/ta-p/444847
Edit:
Editing this to respond to the questions and to expand on the alternative answer, which expanded on this one (bit circular I know...).
To answer the second question first, I agree with the other answer which states the alternate entry (background) and the foreground app can share an SQLite database.
With respect to how these two communicate, while they work just fine, personally I am not a great fan of Global Events because they are propagated to all Applications on the BlackBerry. You can achieve similar things in many alternative ways - the trick is to find something that is common to both applications so that they can communicate. To this end, I recommend using RuntimeStore. See this KB article:
http://supportforums.blackberry.com/t5/Java-Development/Create-a-singleton-using-the-RuntimeStore/ta-p/442854
Regarding how you persist your database, I like PersistentStore because it is present on all devices. But if you really have a database, and not persistent Objects, then SQLite seems the ideal thing to use. Personally I would not use RecordStore, but here is a discussion of the options:
http://supportforums.blackberry.com/t5/Java-Development/Introduction-to-Persistence-Models-on-BlackBerry/ta-p/446810
And just a clarification - in the example given, you have two applications, BgApp and UiApp. You will only have one main() method. This main method will use the args that you specify to determine which one to start, which it will create and have it "enter the dispatcher". If I could make a recommendation - use "gui" as the argument to specify that you will start your UiApplication. I have experienced a circumstance that the OS attempted to start my alternate entry Ui application with this String, regardless of what I had actually specified. Might have been a one off, but I have stuck to doing that ever since.
Finally two comments on the use of Timers and Timertask to provide triggered events. The first comment to make is whatever you run in the TimerTask should not take that long - so you should just use the TimerTask to initiate the download Thread (which might take a long time). Secondly for me, in this situation, I would not use Timer/TimerTask. I would rather just have a single Thread, which 'waits', and then processes. The advantage to me is that this can be adaptive. For example, if you fail to connect, then you might shorten the time till the next connection attempt. Or if it is after hours, then you might lengthen the time between connections to reduce battery usage. Or you might stop connecting completely when the battery is very low.
Hope this helps.
Me and my team are currently rookie developers in Objective-C (less than 3 months in) working on the development of a simple tab based app with network capabilities that contains a navigator controller with a table view and a corresponding detailed view in each tab. The target is iOS 4 sdk.
On the networking side, we have a single class that functions as a Singleton that processes the NSURLConnection for each one of the views in order to retrieve the data we need for each of the table views.
The functionality works fine and we can retrieve the data correctly but only if the user doesn't change views until the petition is over or the button of the same petition (example: Login button) is pressed on again. Otherwise, different mistakes can happen. For example, an error message that should only be displayed on the root view of one of the navigation controllers appears on the detailed view and vice versa.
We suspect that the issue is that we are currently handling only a single delegate on the Singleton for the "active view" and that we should change it to support a behavior based on the native Mail app in which you can change views while the data that was asked for in each one of the views keeps loading and updating correctly separately.
We have looked over stackoverflow and other websites and we haven't found a proper methodology to follow. We were considering using an NSOperationQueue and wrapping the NSURLConnections on an NSOperation, but we are not sure if that's the proper approach.
Does anyone have any suggestions on the proper way to handle multiple asynchronous NSURLConnections to update multiple views, both parent and child, almost simultaneously at the whim of the user's interaction? Ideally, we don't want to block the UI or disable the buttons as we have been recommended.
Thank you for your time!
Edit - forgot to add, one of the project restrictions set by our client is that we can only use the native iOS sdk network framework and not the ASIHTTPRequest framework or similar. At the same time, we also forgot to add that we are not uploading any information, we are only retrieving it from the WS.
One suggestion is to use NSOperations and a NSOperationsQueue. The nice thing about this arrangement is you can quickly cancel any in-process or queued work (if say the user hits the back button.
There is a project on github, NSOperation-WebFetches-MadeEasy that makes this about as painless as it can be. You incorporate one class in your classes - OperationsRunner - which comes with a "how-to-use-me" in OperationsRunner.h, and two skeleton NSOperations classes, one the subclass of another, with the subclass showing how to fetch an image.
I'm sure others will post of other solutions - its almost a problem getting started as there are a huge number of libraries and projects doing this. That said, OperationsRunner is a bit over 100 lines of code, and the operations about the same, so this is really easy to read, understand, use, and modify.
You say that your singleton has a delegate. Delegation is inappropriate when multiple objects are interested in the result. If you wish to continue using a singleton for fetching data, you must switch your pattern to be based on notifications. Your singleton will have responsibility for determining which connection corresponds to which task, and choosing an appropriate notification to be posted.
If you still need help with this, let me know, I'll try to post some sample code.
Need your help.
In my application, i want to implement a background process which keeps running continuously and downloads the updated data and stores it in document folder.
And my main thread should keep checking the document folder and display the updated data in view control.
The child thread should end once the view disappears. and start again once the view appears.
What is the best way to do it? NSThread or NSOperationQueue? What precautions are required?
I also have to access few variables of the class. So is should be thread safe.
Thanks in advance.
Regards
If you do not need to update a progress bar or something in iOS5 there is one great API method + sendAsynchronousRequest:queue:completionHandler: that allows you to run async download as a block inside NSOperationQueue. If not you should look into third party libs such as ASIHTTP request or https://github.com/AFNetworking/AFNetworking(probably better the last one) or you need to build you own download manager, not a simple task
there are two ways to do it..
First: You use NSOperationQueue which is a bit bulkier as it is build on GCD but does have some extra features.
Second: You use GCD (grand central dispatch) looking at requirements I would say GCD seems fine as you can easily access any thread(main or background) this would be slightly quicker.
You can have a look at - (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg This method creates a new thread in your application, putting your application into multithreaded mode if it was not already. In your viewDidDisappear, you can stop the task when your view disappears
From Apple Docs. Apple encourages to investigate the alternative Mac OS X technologies for implementing concurrency. This is especially true if you are not already familiar with the design techniques needed to implement a threaded application. These alternative technologies simplify the amount of work you have to do to implement concurrent paths of execution and offer much better performance than traditional threads.
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/AboutThreads/AboutThreads.html#//apple_ref/doc/uid/10000057i-CH6-SW2
So I'm working on this application that requests and retrieves webservice content for iPhone. The problem I am running into is this: When I initially request data, it is spawned off as an independent thread so that the application does not become unresponsive due to the network being slow. What this means is that if the user navigates away from the current page before this data finishes downloading, unexpected things can happen.
I have managed to narrow down the problem cases to one relatively simple one: I have some nested tables, so if a user goes down into the "Messages" table, which can sometimes take a little while to download, then back out immediately, and select a different set of messages to view, the previous set of messages ends up loading, because it was still in the queue.
Here are things I have tried:
1) I tried cancelling the operations, but this is futile, because since I only allow one operation in the queue at the time, it triggers immediately
2) I tried validating that the recipient of the data is the same, but this doesn't work because the actual table object is the between the two selections, it just needs a different data set.
Anyone have any general programming suggestions on how to solve this tricky threading problem?
On an iPhone specific note: if I could just stop the user from being able to back out of the messages table, I wouldn't have this problem, because they would basically be locked into that view until the data has finished loading.
Thanks!
This post has some design advice relating to iOS networking and threading. The basic gist of it is "Don't use explicit threading", and I couldn't agree more. NSURLConnection has great built-in functionality for asynchronously loading data from a URL while managing all of the threading for you. They can also be cancelled easily at will.
If you were to use the NSURLConnection paradigm, you can simply cancel any pending request when you back out of the requesting view controller.
I am new to objective-c/cocoa programming. I am making an application which is to constantly sync with a server and keep its view updated.
Now in a nutshell, heres what I thought of: Initiate an NSTimer to trigger every second or two, contact the server, if there is a change, update the view. Is this a good way of doing it?
I have read elsewhere that you can have a thread running in the background which monitors the changes and updates the view. I never worked with threads before and I know they can be quite troublesome and you need a good amount of experience with memory management to get most out of them.
I have one month to get this application done. What do you guys recommend? Just use an NSTimer and do it the way I though of...or learn multithreading and get it done that way (but keep in mind my time frame).
Thanks!
I think using separate thread in this case would be too much. You need to use threads when there is some task that runs for considerable amount of time and can freeze your app for some time.
In your case do this:
Create timer and call some method (say update) every N seconds.
in update send asynchronous request to server and check for any changes.
download data using NSURLConnection delegate and parse. Note: if there is probability that you can receive a huge amount of data from server and its processing can take much time (for example parsing of 2Mb of XML data) then you do need to perform that is a separate thread.
update all listeners (appropriate view controllers for example) with processed data.
continue polling using timer.
Think about requirements. The most relevant questions, IMO, are :
does your application have to get new data while running in background?
does your application need to be responsive, that is, not sluggish when it's fetching new data?
I guess the answer to the first question is probably no. If you are updating a view depending on the data, it's only required to fetch the data when the view is visible. You cannot guarantee always fetching data in background anyway, because iOS can always just kill your application. Anyway, in your application's perspective, multithreading is not relevant to this question. Because either you are updating only in foreground or also in background, your application need no more than one thread.
Multithreading is relevant rather to the second question. If your application has to remain responsive while fetching data, then you will have to run your fetching code on a detached thread. What's more important here is, the update on the user interface (like views) must happen on the main thread again.
Learning multithreading in general is something indeed, but iOS SDK provides a lot of help. Learning how to use operation queue (I guess that's the easiest to learn, but not necessarily the easiest to use) wouldn't take many days. In a month period, you can definitely finish the job.
Again, however, think clearly why you would need multithreading.