I use the Reachability class to know if I have an internet connection available. The problem is when wifi is available but not internet, the - (NetworkStatus) currentReachabilityStatus method take too much time.
my code:
Reachability* reachability = [Reachability reachabilityWithHostName:#"www.apple.com"];
NetworkStatus remoteHostStatus = [reachability currentReachabilityStatus];
The application "freeze" temporarily on the second line. How to define the maximum time for this waiting ?
I don't think so. But more importantly, I don't think you'd want to if you could (you may get false positives). Let Reachability run it's course.
If you look at the Reachability demo project, the notion isn't to invoke reachabilityWithHostName and check currentReachabilityStatus when you need the Internet. You invoke currentReachabilityStatus at during your app delegate's didFinishLaunchingWithOptions, set up a notification, and Reachability will tell you when the Internet connectivity has changed. I find that subsequent checks to currentReachabilityStatus are plenty fast (regardless of connectivity) when I (a) setup reachability at startup; but (b) check for connectivity in a just-in-time manner.
And if you absolutely need to start your processing immediately, then the question is whether you can push that into the background (e.g. dispatch_async()). E.g., my app retrieves updates from the server, but because that's happening in the background, neither me nor my user are aware of any delays.
I was having issues with the same thing but I found a way to specify a timeout. I replaced this method inside the Reachability Class from Apple.
- (NetworkStatus)currentReachabilityStatus
{
NSAssert(_reachabilityRef != NULL, #"currentNetworkStatus called with NULL SCNetworkReachabilityRef");
//NetworkStatus returnValue = NotReachable;
__block SCNetworkReachabilityFlags flags;
__block BOOL timeOut = NO;
double delayInSeconds = 5.0;
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(delay, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void){
timeOut = YES;
});
__block NetworkStatus returnValue = NotReachable;
__block BOOL returned = NO;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
{
if (_alwaysReturnLocalWiFiStatus)
{
returnValue = [self localWiFiStatusForFlags:flags];
}
else
{
returnValue = [self networkStatusForFlags:flags];
}
}
returned = YES;
});
while (!returned && !timeOut) {
if (!timeOut && !returned){
[NSThread sleepForTimeInterval:.02];
} else {
break;
}
}
return returnValue;
}
Related
I'm trying to send some images file (almost 100MB) to my iDevice clients using GCDAsyncSocket.
I want to Synchronously send packets to the clients. I mean after sending 100MB of data to first client iterating to the next client.but because of Asynchronous nature of GCDAsyncSocket I don't know how can I serialize these packet sending.
I can't use semaphore because before sending images I negotiate with each client to know what images I should send then try to send those images. and I can't find a neat way to wait and signal the semaphore.
- (void)sendImagesToTheClients:clients
{
...
//negotiating with client to know which images should sent
...
for(Client* client in clients)
{
packet = [packet addImages: images];
[self sendPacket:packet toClient:client];
}
}
- (void)sendPacket:packet toClient:client
{
// Initialize Buffer
NSMutableData *buffer = [[NSMutableData alloc] init];
NSData *bufferData = [NSKeyedArchiver archivedDataWithRootObject:packet];
uint64_t headerLength = [bufferData length];
[buffer appendBytes:&headerLength length:sizeof(uint64_t)];
[buffer appendBytes:[bufferData bytes] length:[bufferData length]];
// Write Buffer
[client.socket writeData:buffer withTimeout:-1.0 tag:0];
}
this is how AsyncSocket writing data works:
- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
{
if ([data length] == 0) return;
GCDAsyncWritePacket *packet = [[GCDAsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag];
dispatch_async(socketQueue, ^{ #autoreleasepool {
LogTrace();
if ((flags & kSocketStarted) && !(flags & kForbidReadsWrites))
{
[writeQueue addObject:packet];
[self maybeDequeueWrite];
}
}});
// Do not rely on the block being run in order to release the packet,
// as the queue might get released without the block completing.
}
so how can I synchronize this task?
UPDATE
for socket connection I use GCDAsyncSocket which heavily uses delegation for event notification.(GCDAsyncSocket.h and GCDAsyncSocket.m) (no method with completionHandler).
I have written a class named TCPClient which handles socket connection and packet sending and set it as the delegate of initialized socket.
after writing a packet, the delegate method - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag gets called. which only informs me some data has been written. here I can't decide based of written data to call dispatch_group_leave. so relying delegate method is useless.
I have modified [client.socket writeData:buffer withTimeout:-1.0 tag:0] in GCDAsyncSocket.h and .m files to accept a completionBlock: [client.socket writeData:buffer withTimeout:-1.0 tag:0 completionBlock:completionBlock]
using this approach helps me to solve synchronizing async tasks.
// perform your async task
dispatch_async(self.socketQueue, ^{
[self sendPacket:packet toClient:client withCompletion:^(BOOL finished, NSError *error)
{
if (finished) {
NSLog(#"images file sending finished");
//leave the group when you're done
dispatch_group_leave(group);
}
else if (!finished && error)
{
NSLog(#"images file sending FAILED");
}
}];
but the problem is after updating GCDAsyncsocket, my code may break.
here I'm looking for neat way to add completion handler to GCDAsyncsocket without modifying it directly. like creating a wrapper around it or using features of objective-c runtime.
do you have any idea?
You can accomplish this with dispatch groups. For a async task with a completion block:
//create & enter the group
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
// perform your async task
dispatch_async(socketQueue, ^{
//leave the group when you're done
dispatch_group_leave(group);
});
// wait for the group to complete
// (you can use DISPATCH_TIME_FOREVER to wait forever)
long status = dispatch_group_wait(group,
dispatch_time(DISPATCH_TIME_NOW,NSEC_PER_SEC * COMMAND_TIMEOUT));
// check to see if it timed out, or completed
if (status != 0) {
// timed out
}
Alternatively for a task with a delegate:
#property (nonatomic) dispatch_group_t group;
-(BOOL)doWorkSynchronously {
self.group = dispatch_group_create();
dispatch_group_enter(self.group);
[object doAsyncWorkWithDelegate:self];
long status = dispatch_group_wait(self.group,
dispatch_time(DISPATCH_TIME_NOW,NSEC_PER_SEC * COMMAND_TIMEOUT));
// A
if (status != 0) {
// timed out
return NO
}
return YES;
}
-(void)asyncWorkCompleted {}
// after this call, control should jump back to point A in the doWorkSynchronously method
dispatch_group_leave(self.group);
}
I'm confused due to lack of examples, so I did this in my appDelegate's didFinishLaunching:
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
bool isThere = [[AFNetworkReachabilityManager sharedManager] isReachable];
And that always returns false, in spite of the network being there and working.
Two questions:
1) if I'm not looking for changes in status, do I need startMonitoring?
2) is there anything you need to do before reading isReachable? Do you need to wait?
I know it's too late to answer. If anybody looking for this.
Determining network status requires little time.
So, calling isReachable right after startMonitoring will always return false.
You can call isReachable inside setReachabilityStatusChangeBlock ;
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status){
NSLog(#"status changed");
//check for isReachable here
}];
Hey I am very late to post the answer, but when I saw this question I remembered how long I've spent time to get one working code to check if network is available and finally found this code. Connectivity variable is a Bool which will give momentary network status when accessed, use of this code block in a class will give you real time network status. Its in swift hope someone will find it useful.
Thanks
func checkNetworkStatus(completion:#escaping (_ connected:Bool) ->())
{
let reachability = AFNetworkReachabilityManager.shared()
reachability.startMonitoring();
reachability.setReachabilityStatusChange({ (status) -> Void in
switch(status) {
case .unknown:
self.connectivity = false
completion(false)
case .notReachable:
self.connectivity = false
completion(false)
case .reachableViaWWAN:
self.connectivity = true
completion(true)
case .reachableViaWiFi:
self.connectivity = true
completion(true)
}
})
}
This code block can be used in your class as
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.initNetworkMonitoring { (status) in
// make necessary UI updates
}
isReachable is done by Apple API SCNetworkReachabilityGetFlags. This is a block call, so AFN call it in a background queue:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
});
If you try https://github.com/tonymillion/Reachability you get the result immediately then. But of course it may cause the problem like this, main thread blocked on SCNetworkReachabilityGetFlags
-(BOOL)isReachable
{
SCNetworkReachabilityFlags flags;
if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
return NO;
return [self isReachableWithFlags:flags];
}
I have internet connection and can browsing with browser.
Here is my codes to check Reachability with AFNetworking.
- (BOOL)connected {
return [AFNetworkReachabilityManager sharedManager].reachable;
}
And In ViewDidLoad
BOOL isOnline = [self connected];
if(isOnline == YES)
{
NSLog(#"YES");
}
else
{
NSLog(#"NO");
}
It's only showing NO and i don't know why is it?
Is there easiest way to check Reachability with AFNetworking?
I guess startMonitoring isn't called, try to do the below:
- (void)viewDidLoad {
[super viewDidLoad];
....
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
}
If above answer is not solving your issue,
then your problem might be due to calling [AFNetworkReachabilityManager sharedManager].reachable while it is in the middle of 'startMonitoring' process where it would always return NO.
I had the same issue. I was calling web service while AFNetworkReachabilityManager had not finished monitoring process and was returning reachable = NO although I had working internet connection.
- (void) callWebService {
NSLog(#"Calling Webservice...");
if ([AFNetworkReachabilityManager sharedManager].reachable == NO) {
NSLog(#"%#", kErrorNoInternet);
return;
}
// Now, proceed to call webservice....
}
So, to solve this I did a trick. Called web service after some delay (in this example 0.05 sec).
Before:
[self callWebService];
Output:
After:
[self performSelector:#selector(callWebService) withObject:nil afterDelay:0.3]; // you can set delay value as per your choice
Output:
You can see from the output, the time difference is hardly 0.05 sec (exact value 0.048 sec).
Hope this will help.
instead of waiting you can use blocks just to make sure that your web service will be only called when network is available.
[[AFNetworkReachabilityManager sharedManager]startMonitoring];
[[AFNetworkReachabilityManager sharedManager]setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status)
{
if (status == AFNetworkReachabilityStatusReachableViaWWAN || status == AFNetworkReachabilityStatusReachableViaWiFi)
{
// connected. you can call your web service here
}else
{
// internet disconnected
}
}];
I'm trying to figure out how to get a database fetch to run in the background. Below are the foreground and background version of the same function. The foreground version works. But in the background version the local variable retval never gets assigned. Putting a breakpoint in the pageInfoForPageKey function tells me that function is never called.
Is self available inside the block?
//foreground version
- (PageInfo*)objectAtIndex:(NSInteger)idx
{
return [[self dataController] pageInfoForPageKey:[[[self pageIDs] objectAtIndex:idx] integerValue]];
}
//background version
- (PageInfo*)objectAtIndex:(NSInteger)idx
{
__block PageInfo* retval = nil;
__block NSInteger pageID = [[[self pageIDs] objectAtIndex:idx] integerValue];
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(aQueue, ^{
retval = [[self dataController] pageInfoForPageKey:pageID];
});
return retval;
}
By using dispatch_async, you are telling the system that you want it to run your block some time soon, and that you don't want to wait for your block to finish (or even start) before dispatch_async returns. That is the definition of asynchronous. That is the definition of “in the background”.
The system is doing what you told it to: it is arranging for your block to run, and then it is returning immediately, before the block has run. So the block doesn't set retval before you return retval, because the block hasn't run yet.
If you want to run the database fetch in the background, you need to change your API to pass retval back (to whoever needs it) at a later time, after the block has run. One way is to pass a completion block as a message argument. This is a common pattern for performing fetches in the background. For example, look at +[NSURLConnection sendAsynchronousRequest:queue:completionHandler:].
You might do it like this:
- (void)fetchObjectAtIndex:(NSIndex)idx completion:(void (^)(PageInfo *))block {
block = [block copy]; // unnecessary but harmless if using ARC
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSInteger pageKey = [[[self pageIDs] objectAtIndex:idx] integerValue];
PageInfo* pageInfo = [[self dataController] pageInfoForPageKey:pageKey];
dispatch_async(dispatch_get_main_queue(), ^{
block(pageInfo);
});
});
}
Then you need to change the caller of objectAtIndex: to use this new API instead.
In the initialization method of a class I am declaring the thread as such:
NSThread* myThread = [[[NSThread alloc] initWithTarget:self selector:#selector(m_run_thread) object:nil] autorelease];
[myThread start];
I also have a boolean value which is set to NO. Later on in the code I set the boolean value to YES.
bool_run_progress_thread = YES;
The contents of the method m_run_thread is as follows:
-(void) m_run_thread
{
if (bool_run_progress_thread)
{
//do processing here
}
bool_run_progress_thread = NO;
}
The problem is that the method m_run_thread is never being accessed. What am I doing wrong?
P.S. I have also tried to set up the Thread using the following (and older)method:
[NSThread detachNewThreadSelector:#selector(m_run_thread)
toTarget:self
withObject:nil];
... but to no avail as well.
"...and I am only getting it to show once" Yes, that's exactly how it should be. After being started, a thread runs once from its start to its end (ignoring errors here for the moment), and having reached the end, the thread is essentially dead and gone.
If you want the thread to repeat its execution, you have to prepare for that yourself:
- (void) m_run_thread
{
for (;;)
{
if (bool_run_progress_thread)
{
//do processing here
bool_run_progress_thread = NO;
}
}
}
But there is still a lot wrong with this code: essentially, when run, the code forms a busy waiting loop. Assuming, that bool_run_progress_thread is only ever true for short periods of time, the background thread should be sleeping most of the time. Insead, if you try the code as its stands, it will instead consume CPU time (and lots of it).
A better approach to this would involve condition variables:
#class Whatsoever
{
NSCondition* cvar;
BOOL doProgress;
...
}
...
#end
and
- (void) m_run_thread
{
for (;;)
{
[cvar lock];
while (!doProgress)
{
[cvar wait];
}
doProgress = NO;
[cvar unlock];
... do work here ...
}
}
and in order to trigger the execution, you'd do:
- (void) startProgress
{
[cvar lock];
doProgress = YES;
[cvar signal];
[cvar unlock];
}
Doing things this way also takes care of another subtle problem: the visibility of the changes made to the global flag (your bool_run_progress_thread, my doProgess). Depending on the processor and its memory order, changes made without special protection might or might not become (ever) visible to other threads. This problem is taken care of by the NSCondition, too.