First, I am not asking internet reachability.
I have a lot internet requests in different places in my App. I want to show the ActivityIndicator on status bar, but I don't want to set
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES/NO
every time.
Is there a way I can detect my App is using the Internet?(Hook Methods or something?)
Thanks a lot!
Sadly — unless I'm behind the times — it's your own responsibility to keep track of this; there's no getter for quantity of network communications in flight and neither is there a notification or object you could observe to watch for changes.
Supposing you were just using NSURLSession, you might implement a function, YAURLCompletionForCompletion that takes a block of the form void (^)(NSURL *location, NSURLResponse *response, NSError *error) as a parameter and that increments a global activity count and returns another block of the same form which calls the original and then decrements the counter.
Display the spinner whenever the counter is non-zero. Always use YAURLCompletionForCompletion to wrap whatever completion handlers you pass when creating NSURLSessionDataTasks.
If you use AFNetworking, then AFNetworkActivityIndicatorManager is a good choice. You can implement similar function like that.
Related
I have the following code below that loops through an array. I need to check if the finish or fail selector has been called iterating to the next object in my dataArray.
for (id object in dataArray) {
[client setDidFinishSelector:#selector(getDataFinish:)];
[client setDidFailSelector:#selector(getDataFail:)];
[client getData:object];
}
In my getDataFinish method I assign values and I am trying to keep it in order. If I use the above method, the values can get out of order since the client response time can be different for each request..
I see two possible solutions, depending on what you're actually trying to do. It sounds like you're making calls to the internet, so yes you will get varied response time (or no response at all). Because of this, I would recommend using NSNotification. See this answer for more information about that.
Another option is making a flag in your code (AKA a BOOL) that you set to YES when your method has completed. Again, if you're making calls to the web I would not recommend this method as you are setting yourself up for an infinite loop if the user has no service and the BOOL never changes.
If you are still having trouble let me know and I can provide a more detailed answer.
excuse me for not knowing this, but I would like to know why there is a delay when I try to establish a connection to the database.
I am basically pulling data from a database to display the info back onto a UITableView but there seems to be a delay in the connection establishment.
- (void)viewDidLoad
{
[super viewDidLoad];
[self fetchFromDatabase];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
[self fetchFromDatabase];
NSLog(#"FOUR");
}
- (void)fetchFromDatabase
{
// The URL of the database
NSURL *url = [[NSURL alloc] initWithString:SDTET_DATABASE_SCHEDULE];
// Establish the connection to the database
[NSURLConnection sendAsynchronousRequest:[ [NSURLRequest alloc] initWithURL:url] queue:[ [NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"ONE !");
} ];
}
However, the output looks like so:
FOUR
FOUR
ONE !
Why doesn't ONE ! get printed out first?
How can I ensure the connection occurs before the numberOfSections is called, if possible?
Thanks
ONE gets printed last because you are calling an asynchronous method.
You can follow the flow of the program (which method calls which in what order). If we let our imagination stretch a bit, we could say that when a method is synchronous, it basically "says" to the next line of code to wait for it (the method) to finish.
However, if a method is asynchronous, it say "don't wait for me". The URL request method you are using is asynchronous. That means it starts to do its job but nobody waits, the execution of code continues in the next line immediately.
There is a piece in the puzzle that will clarify this more. The request method needs to be fed a completion block as a parameter. Explaining blocks is out of scope here, but you need to understand what block is to fully grasp the solution to your problem.
Suffice to say, once this asynchronous method finishes its job (which can be a long time and this is one of the reasons for asynchronous approach really), the block is invoked and executed.
So to answer your last question, you need to trigger a refresh on your table view, which you achieve by calling
[self.tableView reloadData]; inside the block. There is a nice logic here, since you know the block is executed ONLY after the asynchronous method concludes its work, you will refresh your table view knowing you have the updated data from db.
It's because it is an asynchronous request (meaning it doesn't necessarily happen immediately). It's a huge principle of computer science and it a much bigger concept than just Objective-C or iOS development.
In your case, you can get rid of the fetch being called in numberOfSectionsInTableView: and you can just call [self.tableView reloadData] in your completionHandler.
There is a delay because network access is slow. Sometimes it's REALLY slow.
Sometimes you can get a response in less than a second, and other times it can take the better part of a minute to get your response.
You should always write your network code to be asynchronous, which means that you submit the request with a method that returns immediately. Your program continues to run, and then you get notified once the response has been received.
That's what is going on with your code.
You don't want to send the request to load your data in numberOfSectionsInTableView. You should send the network request as early as possible, preferably before the view controller that will display the results is displayed.
However, it's pretty common to display a table view before you have the data to fill it. You might show an empty table view with a message that the data is loading, or with a progress dialog. In other cases you might know how many cells to display, and have the text data but not the images. In that case you might populate your cells with placeholder images. Once the data loads you can update the display.
In your case you should write your numberOfSectionsInTableView and numberOfRowsInSection: methods to return zero, and then make the completion method of your network call tell the table view to reload itself.
I am making a turnbased game for iOS with game center, 2 participants per match. I would like to implement a time limit on every turn, so that a player don't have to wait forever for the other player to finish its turn. I have tried:
currentMatch endTurnWithNextParticipants:[[NSArray alloc] initWithObjects:nextParticipant,nil] turnTimeout:GKTurnTimeoutDefault matchData:data completionHandler:^(NSError *error)
but nothing happens, the player still has forever to do their turn, so I am obviously missing something here.
What happens when the time limit is reached? How does gamecenter handle this, and where should I handle this?
That method updates the data stored on Game Center for the current match.
According to Apple Docs:
If the next player to act does not take their turn in the specified interval, the next player in the array receives a notification to act. This process continues until a player takes a turn or the last player in the list is notified.
When this method is called, it creates a new background task to handle the request. The method then returns control to your game. Later, when the task is complete, Game Kit calls your completion handler. Keep in mind that the completion handler may be called on a thread other than the one originally used to invoke the method. This means that the code in your block needs to be thread-safe.
I think you need to also end the players go on their end programatically.
I am an iOS newbie and I am trying to understand some of the "good practice" ways of doing things in an iOS app.
I have a ViewController which I made to be the controller that is called when the app starts. In it I saw a function called viewDidLoad and I tried to modify it to check if the user has had a user_id and then call a function that would eventually make an asynchronous request to manage that user in a remote db. Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
EmailUtil *email = [EmailUtil alloc];
email = [email init];
// This is just a test call to the function that would make a remote server request
[email setEmail: #"test" andBody: #"hello"];
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
if([standardUserDefaults objectForKey:#"user_id"] == nil)
{
NSLog(#"First time");
[standardUserDefaults setBool:YES forKey:#"user_id"];
}
else
{
NSString *subject = #"subject";
NSString *body = #"bod";
NSLog(#"Not first time");
}
}
So there are a few things I am unsure with here. Can the fact that I am calling a function to make a remote call from viewDidLoad cause problems in that function? Currently its not sending the remote request.
Also, should I deallocate memory for the objects I create right at the end of this function?
Or should I just move this code to another spot in the class where it would make more sense to have that code there?
I call the email object like this:
[email setEmail: #"test" andBody: #"hello"];
and here is the code for the EmailUtil class:
//
// EmailUtil.m
//
#import "EmailUtil.h"
#implementation EmailUtil
-(void) setEmail: (NSString *) subject andBody: (NSString *) body
{
NSString *final_url = [NSString stringWithFormat:#"http://www.my_url.com?subject=%#&body=%#",subject, body];
NSURL *url = [NSURL URLWithString:final_url];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url ];
// TODO: ok I dont really understand what this is
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"On return");
NSLog(#"This is data: %#" , data);
NSLog(#"This is response: %#" , response);
NSLog(#"This is error: %#" , error);
NSLog(#"OK");
}];
}
#end
Thank you!
You said:
So there are a few things I am unsure with here. Can the fact that I
am calling a function to make a remote call from viewDidLoad cause
problems in that function? Currently its not sending the remote
request.
Feel free to make whatever calls you want in viewDidLoad. Just make sure to not doing anything that will block the UI (e.g. some long, complicated method). Any long-running, non-UI related task would be done asynchronously in a separate queue, but that's for another day, in the distant future.
Whether you do this here, or in application:didFinishLaunchingWithOptions:, is a function of what your UI is going to do. We'd need a better sense of your app's flow to answer that question. And we need to understand what how the EmailUtil presents itself (often these sorts of classes use a modal presentation, which makes sense to do from another view controller and not from application:didFinishLaunchingWithOptions:).
But, bottom line, I personally have application:didFinishLaunchingWithOptions: always take me to what will be the app's "main" or "home" page, and if I have something that I want to present on first time usage, but not have that be the main page, then I'll invoke this from my main view controller's viewDidLoad. Other guys will customize application:didFinishLaunchingWithOptions: with the "first time used" logic. It's a matter of personal preference.
Also, should I deallocate memory for the objects I create right at the end of this function?
The first rule of memory management is that you always should deallocate anything you own (generally anything you create) and only the things you own. But, if you're using ARC (which I highly encourage you to do), this is taken care of for you (objects are deallocated as they fall out of scope).
Or should I just move this code to another spot in the class where it
would make more sense to have that code there?
Personally, I'm not crazy about having an app send an email just because the main view controller appeared, as I assume this was just an example and not a real app. But if you were contemplating putting this in a different place, the "send email" function is more logically hooked to the user tapping on a "send email" button, rather than running off and doing it itself.
On other points:
This code is curious, though, because you're creating EmailUtil, a local var, setting setting some its properties, but then not doing anything with it, and then letting it fall out of scope. I presume you'd want some method to make this EmailUtil do its stuff, e.g. [email sendMessage], or whatever the appropriate method is.
You're also creating two local vars, subject and body, and not using them and letting them fall out of scope. I presume you wanted to update your email object's properties accordingly but have not gotten to that.
Is there a reason you're writing your own EmailUtil class rather than using MessageUI.framework? Hopefully EmailUtil is just a nice wrapper for the MessageUI.framework, or doing something completely different, but not replicating functionality that Apple already provides.
You said that the app will "make an asynchronous request to manage that user in a remote db". Wow. I do that sort of stuff all the time, now, but I wouldn't suggest asynchronous coordination with a server as a good first-time project. Hopefully when you said "eventually" you meant "a couple of months from now" and not "in the next week or two." Looks like you're still getting your sea legs on basic view controller and memory management stuff. You might want to see if you can constrain the functionality of your initial version of the app to something a little less ambitious. If you don't have some basic skills yet and try to do something complicated, you can end up with a real mess on your hands that you'll find yourself completely rewriting later. So, no offense, but see if you can come up with something a little simpler for your first real project. Or do a couple of test apps first. Just a thought.
Lvsti is quite right that the typical object creation construct is [[EmailUtil alloc] init]. What you have is equivalent, but non-standard.
I know it's a work in progress, but it looks like your setEmail:andBody: is setting properties and sending the message. I'd suggest (a) having #property entries for subject and body (which will automatically generate setSubject and setBody methods, as well as let you do stuff like email.subject = #"This is the subject of the email";); (b) if you want, have an init convenience method, e.g., initWithSubject:body: which will do the init your object and set these two properties; and (c) have a separate method that actually sends the message. It's not a good practice to have something that looks like a setter, is sort of a setter, but also does more material stuff, all in a single method. Methods that begin with the word "set" are generally standard setters and iOS conventions will lead other programmers to misread your code (as I just did). By following some standard iOS conventions, it will be easier if you ever get iOS programmers involved in reviewing and revising code.
You should move this code to the application:didFinishLaunchingWithOptions: method of your appdelegate.
From iOS-Dev-Center
You should use this method to initialize your application and prepare
it for running. It is called after your application has been launched
and its main nib file has been loaded. At the time this method is
called, your application is in the inactive state.
And if you are not using arc, only thing you should release is EmailUtil, no reason to deallocate something by hand
I've searched a lot within SO but I can't find the right answer for my question.
Here the problem:
I'm figuring out the right mechanism to send multiple upload requests within an NSOperation subclass. In particular, this class performs two different operation within its main method:
First it retrieves data from a local db
Then it sends the composed data to a web server
Since, these two operations can take time to executed I wrapped them, as already said, within an NSOperation.
To upload data I decided to adopt a sync pattern (I need to sync my application with the number of upload requests that has been successfully submitted to the web server).
To perform a similar upload I'm using ASIHttpRequest in a synch fashion like the following.
for (int i = 0; i < numberOfUploads; i++) {
// 1-grab data here...
// 2-send data here
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request startSynchronous];
NSError *error = [request error];
if (!error) {
int response = [request responseStatusCode];
if(response == 200)
uploads++;
}
}
So, my questions are:
is this a valid solution to upload data to a web server?
is it valid to create ASIHTTPRequest *request within a background thread?
do I have to use an async pattern? If yes, how to?
Note I'm using ASIHttpRequest for sync requests but I think the same pattern could be applied with NSUrlConnection class through
sendSynchronousRequest:returningResponse:error:
Thank you in advance.
To answer your questions directly:
Yes, calling an NSUrlConnection (in your case, ASI wrapper) with a sync call is valid in an NSOperation.
You can create NSUrlConnections in background threads, but there are a couple of things to remember here:
If you use it on a background thread, you have to either call the synchronous methods, or you have to keep the thread alive yourself. Using the async in an NSOperation is explained pretty well here: How do I do an Asychronous NSURLConnection inside an NSOperation? I have used this pattern and it works well.
NSUrlConnnection Delegate callbacks call back to the thread that the NSUrlConnection was created on. Just something to remember.
You do not have to use the async pattern, but you can. The async pattern provides more flexibility. For example, if you need to cancel the operation, you have the ability to cancel the NSUrlConnection request with the async pattern. With the sync pattern, you are forced to let it run (unless you kill the thread explicitly).
One note: I would reconsider using ASI as it is no longer supported. AFNetworking seems to be the most popular replacement, though I chose to start using NSUrlConnection directly.
whenever you want to call using ASIHTTPRequest in background thread you have to call synchronous call only, because threads will close the request as soon they are sent,and about your questions
1, this is valid solution but call using synchronours only
2. you can call ASIHTTPRequest in background thread or you can call it using
nsurlconnection sendSynchronousRequest:returningResponse:error:
async pattern will not work for background thread you have to use them on main thread only .
hope it helps .