iOS 7 - Proper stop of stop and 'clear' dispatch_queue_t - ios7

I am having a long method which download a set of data and files from server and I have a Reachability class to monitor the network connection. What I want to do is if network disconnected, stop the queue and 'clear' it. When user click a button to retry, it start everything from beginning. So, what is the proper steps?
Is it
in .h file
#property (nonatomic) dispatch_queue_t background_q;
#property (nonatomic) Reachability *reachMonitor;
in .m file
- (void)downloadBigDataAndFiles {
self.reachMonitor = [Reachability reachabilityForInternetConnection];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reachabilityValueChanged:) name:kReachabilityChangedNotification object:nil];
[reachMonitor startNotifier];
self.background_q = dispatch_queue_create("backgroundMasterQueue", NULL);
dispatch_async(background_q, ^{
// Do whatever
});
}
When disconnected
- (void)reachabilityValueChanged:(NSNotification *)notification {
if ([self.reachMonitor currentReachabilityStatus] == NotReachable) {
if (self.background_q) { // <- is it necessary?
dispatch_suspend(self.background_q);
self.background_q = nil; // <- is it necessary?
}
} else if (([self.reachMonitor currentReachabilityStatus] == ReachableViaWWAN) || ([self.reachMonitor currentReachabilityStatus] == ReachableViaWiFi)) {
// show up retry button...
}
}

Related

Connecting multiple keyboards in iOS 14 Objective-C

iOS 14 introduced GCKeyboard. I was able to successfully connect a BlueTooth NumberPad in my app but I need to be able to connect two of them in tandem. Could anyone please point me to sample code?
In the GCController class there is a "controllers" method which returns an array of all connected controllers, but there isn't anything similar for GCKeyboard.
Here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
[[UIApplication sharedApplication]setIdleTimerDisabled:YES];
// notifications for keyboard (dis)connect
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(keyboardWasConnected:) name:GCKeyboardDidConnectNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(keyboardWasDisconnected:) name:GCKeyboardDidDisconnectNotification object:nil];
}
GCKeyboard *leftKeyboard;
GCKeyboard *rightKeyboard;
- (void)keyboardWasConnected:(NSNotification *)notification {
if (!self.leftKeyboard) {
leftKeyboard = (GCKeyboard *)notification.object;
NSLog(#"Left Keyboard connected: %#\n", leftKeyboard.description);
NSString *keyboardStatus = [NSString stringWithFormat:#"Left Keyboard CONNECTED:\n%#", leftKeyboard.description];
self.statusLabel.text = keyboardStatus;
self.leftKeyboard = leftKeyboard;
[self reactToKeyboardInput];
}
else if (!self.rightKeyboard) {
NSLog(#"Right Keyboard connected: %#\n", rightKeyboard.description);
rightKeyboard = (GCKeyboard *)notification.object;
NSString *keyboardStatus = [NSString stringWithFormat:#"Right Keyboard CONNECTED:\n%#", rightKeyboard.description];
self.statusLabel.text = keyboardStatus;
self.rightKeyboard = rightKeyboard;
[self reactToKeyboardInput];
}
}
- (void)keyboardWasDisconnected:(NSNotification *)notification {
if (self.leftKeyboard) {
GCKeyboard *keyboard = (GCKeyboard *)notification.object;
NSString *status = [NSString stringWithFormat:#"Left Keyboard DISCONNECTED:\n%#", keyboard.description];
self.statusLabel.text = status;
self.leftKeyboard = nil;
}
else if (self.rightKeyboard) {
GCKeyboard *keyboard = (GCKeyboard *)notification.object;
NSString *status = [NSString stringWithFormat:#"Right Keyboard DISCONNECTED:\n%#", keyboard.description];
self.statusLabel.text = status;
self.rightKeyboard = nil;
}
}
When a keyboard or number pad is connected I get the message:
2020-09-18 13:09:15.845943-0700 Controller[1653:351628] Left Keyboard connected: GCController 0x280bb8dd0 ('Keyboard' - 0x27c3dc28cec4d818)
"Bring keyboard and mouse gaming to iPad" WWDC video transcript states:
In the case of keyboards, you can't differentiate multiple keyboards, and all keyboard events from multiple keyboards are instead coalesced for you. So while you might use notifications to notice when a keyboard disconnects and perhaps pause the game to prompt for different input, in general, you'll find that just using GCKeyboard-Device.coalesced to check on the key state when non-nil is the right path.
So while you may have multiple keyboards connected, it sounds like they all get coalesced into a single GCKeyboard instance and you can't differentiate between them. That is why there's no +[GCKeyboard keyboards] method. Instead there is +[GCKeyboard coalescedKeyboard]

OCMock notification observer - verifying the object

I've recently started working with OCMock framework and trying to figure out how to use the notification observer. So in my source code I'm observing some notification:
typedef enum {
OperationStatusCompleted,
// Some other statuses
} SomeOperationStatus;
- (void)addObserverForSomeNotification {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(someOperationStatusDidChange:)
name:#"someOperationStatusDidChange"
object:nil];
- (void)backgroundOperationStatusDidChange:(NSNotification *)notification {
id<SomeOperationProtocol> operation = [notification object];
if (operation.status == OperationStatusCompleted) {
// Do something.
}
}
Now I want to add the expectation for that notification in my test:
- (void)testNotification {
id observerMock = OCMObserverMock();
[[NSNotificationCenter defaultCenter] addMockObserver:observerMock name:#"someOperationStatusChanged" object:nil];
[[observerMock expect] notificationWithName:#"someOperationStatusChanged" object:[OCMArg any]];
// run the test ...
}
That by itself works fine and I'm receiving the notification in the test but what I actually want to check is the status of the object of the notification (meaning that I've received a notification with specific object with specific status). Is it possible?
I finally figured out how to do it:
[[observerMock expect] notificationWithName:#"someOperationStatusChanged" object:[OCMArg checkWithBlock:^BOOL(id<IMBBackgroundOperation> param) {
return param.status == OperationStatusCompleted;
}]];

How to check celluar/wifi internet strength in Objective c?

I am developing an iot app ,I want to show a pop-up in case wifi/celluar internet is slow .
How to implement the slow networks alert in objective c ?
I think you could try the Reachbility application to help you out checking if you have internet or not. As for the server itself, you can use the NSUrlConnection Delegate methods to check if there was a problem with your request (by seeing the kind of HTTP code that comes)
Reachability *reachability = [Reachability reachabilityForInternetConnection];
NetworkStatus internetStatus = [reachability currentReachabilityStatus];
if(internetStatus == NotReachable) {
UIAlertView *errorView;
errorView = [[UIAlertView alloc]
initWithTitle: NSLocalizedString(#"Network error", #"Network error")
message: NSLocalizedString(#"No internet connection found, this application requires an internet connection to gather the data required.", #"Network error")
delegate: self
cancelButtonTitle: NSLocalizedString(#"Close", #"Network error") otherButtonTitles: nil];
[errorView show];
[errorView autorelease];
}
Full example to check internet connection.
please download reachability class .h and .m from here
Follow these steps
viewcontroller.h
#class Reachability;
#interface DashboardVC : UIViewController
{
Reachability* internetReachable;
Reachability* hostReachable;
}
-(void) checkNetworkStatus:(NSNotification *)notice;
viewcontroller.m
-(void) viewWillAppear:(BOOL)animated
{
// check for internet connection
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkNetworkStatus:) name:kReachabilityChangedNotification object:nil];
internetReachable = [Reachability reachabilityForInternetConnection];
[internetReachable startNotifier];
// check if a pathway to a random host exists
hostReachable = [Reachability reachabilityWithHostName:#"www.apple.com"];
[hostReachable startNotifier];
// now patiently wait for the notification
}
-(void) checkNetworkStatus:(NSNotification *)notice
{
// called after network status changes
NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
switch (internetStatus)
{
case NotReachable:
{
NSLog(#"The internet is down.");
break;
}
case ReachableViaWiFi:
{
NSLog(#"The internet is working via WIFI.");//your device is connected with wifi
break;
}
case ReachableViaWWAN:
{
NSLog(#"The internet is working via WWAN.");//your internet is connected with mobile data
break;
}
}
}
You can send a request to your server and given that it's about 5-10 KB of data you expect to be returned, then create a timer callback that is scheduled for say 20 seconds.
If you don't get a response within 20 seconds, then let's consider that a slow connection.
// make POST request to server, the POST request should have a callback method assigned
[self testSpeed];
// schedule a method to be called after 20 seconds
myTimer = [NSTimer scheduledTimerWithInterval:20.0 selector:#selector(stopSpeedTest) .... ];
// your POST request callback method
-(void)speedTestCallback
{
[myTimer invalidate];
myTimer = nil;
[self alertGoodSpeed];
}
// your stopSpeedTest method to identify app didn't receive response within 20 seconds
-(void)stopSpeedTest
{
[self alertTooSlow];
}

How to interrupt AVPlayer for incoming call manually

I've used this method How do I get my AVPlayer to play while app is in background? to have an AVPlayer play in the background so the user can listen while browsing safari etc. Only problem is now AVPlayer continues to play when there is an incoming call and stays playing during the call. Is there a way to catch the incoming call and end call events so AVPlayer can be stopped and started manually?
Through coreTelephony framework we have to find or detect the incoming call. From there you have to initiate your local notification to stop your AVPlayer.
after importing do like this
CTCallCenter * _callCenter = [[CTCallCenter alloc] init];
_callCenter.callEventHandler = ^(CTCall* call)
{
if ([call.callState isEqualToString:CTCallStateDisconnected])
{
NSLog(#"Call has been disconnected");
}
else if([call.callState isEqualToString:CTCallStateDialing])
{
NSLog(#"Call start");
}
else if ([call.callState isEqualToString:CTCallStateConnected])
{
NSLog(#"Call has just been connected");
}
else if([call.callState isEqualToString:CTCallStateIncoming])
{
NSLog(#"Call is incoming");
// You have to initiate/post your local notification through NSNotification center like this
[[NSNotificationCenter defaultCenter] postNotificationName:#"stopAVPlayer" object:nil];
} else
{
NSLog(#"None of the conditions");
}
};
Refer this :https://developer.apple.com/library/ios/navigation/#section=Frameworks&topic=CoreTelephony
You could use the CallKit for getting phone call event now. (iOS 10.0+)
This is good for me.
#import <CallKit/CallKit.h>
#interface ViewController ()<CXCallObserverDelegate> {
CXCallObserver *_center;
}
-(void)viewDidLoad
_center = [[CXCallObserver alloc] init];
dispatch_queue_t queue = dispatch_queue_create("THIS_IS_A_CALL",NULL);
[_center setDelegate:self queue:queue];
Delegate
- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call {
NSString *tag = #"callObserver";
NSString *content = #"";
if (call.isOutgoing) {
content = #"call.isOutgoing";
}
if (call.hasEnded) {
content = #"call.hasEnded";
}
if (call.hasConnected) {
content = #"call.hasConnected";
}
if (call.isOnHold) {
content = #"call.isOnHold";
}
NSLog(#"%# - %#", tag, content);
}
More detail: https://developer.apple.com/documentation/callkit

How to check if NSOperationQueue is finished and if any operation failed?

I'm trying to parse some XML files in the background so that the UI doesn't freeze. I have to check two things:
NSOperationQueue is finished?
NSOperation - parsing did fail?
I have a class that subclasses NSOperation and a delegate is called if the parsing failed. Operations in the queue are limited to one simultaneously.
My problem is that I can't rely on the fact that the failed message is executed before I get the queue did finish message. Sometimes I don't get a failed message before I get the finished message. Then, for example, I have this order:
Operation 1 Successful
Operation 2 Successful
OperationQueue finished
Operation 3 Failed
All messages are sent to the main thread. After I get the finished message I want to proceed in my code, but only if all operations were successful. How can I handle the problem that the delegate message is called after my queue is finished.
This are some parts of my code:
//XMLOperation.m
- (void)main {
NSLog(#"Operation started");
if ([self parseXML]) {
[self performSelectorOnMainThread:#selector(finishedXMLParsing) withObject:nil waitUntilDone:NO];
} else {
[self performSelectorOnMainThread:#selector(failedXMLParsing) withObject:nil waitUntilDone:NO];
}
}
NSLog(#"Operation finished");
}
//StartController.m
[self.xmlParseQueue addObserver:self forKeyPath:#"operations" options:0 context:NULL];
...
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (object == self.xmlParseQueue && [keyPath isEqualToString:#"operations"]) {
if ([self.xmlParseQueue.operations count] == 0) {
// Do something here when your queue has completed
NSLog(#"queue has completed");
}
}
else {
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
}
This probably happens because your KVO notification for the operations property of the queue is not necessarily delivered on the main thread while your finished/failed notifications are.
You should ensure that the completion notification is performed on the main thread as well, so that the order of your notifications is defined.
Resume this question and paste my code solution here.
void RunInMainThread(void (^block)(void))
{
if (!block) {
return;
}
if ([NSThread isMainThread]) {
block();
} else {
dispatch_async(dispatch_get_main_queue(), block);
}
}
#interface MyOperationQueue : NSOperationQueue
#property (nonatomic, assign) NSUInteger totalCount;
#property (nonatomic, copy) dispatch_block_t allOperationCompletionBlock;
#end
#implementation MyOperationQueue
- (void)addOperation:(NSOperation *)op
{
[super addOperation:op];
__weak typeof(self) weakSelf = self;
self.totalCount++;
DDLogVerbose(#"Added a operation, total: %ld operation(s).", self.totalCount);
op.completionBlock = ^ {
weakSelf.totalCount--;
DDLogVerbose(#"Finished a operation, left: %ld operation(s).", weakSelf.totalCount);
if (weakSelf.totalCount == 0) {
if (weakSelf.allOperationCompletionBlock) {
RunInMainThread(weakSelf.allOperationCompletionBlock);
}
}
};
}
#end