gcdasyncsocket background file transfer - cocoa

Having two devices that need to keep transferring data while in background or in LockScreen.
The main resource about backgrounding is available on https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
So far I'm looking forward to understand how is it expected to implement such above mentioned behaviour: in a scenario where a transfer is in progress and one of the apps (or both) goes into background. Obviously we have resumable transfer management working already.
I've been collecting stubs and answers about and I've ended up with the following:
Ensure every socket is backgroundable.
[socket performBlock:^{
[socket enableBackgroundingOnSocket];
}];
To keep backgrounding even when in Lock Screen, I read an answer saying that we should have something like at the end of didFinishLaunchingWithOptions but what code is in [self backgroundHandler] method?
BOOL backgroundAccepted = [[UIApplication sharedApplication]
setKeepAliveTimeout:600 handler:^{ [self backgroundHandler]; }];
if (backgroundAccepted)
NSLog(#"background handler accepted");
return YES;
The applicationDidEnterBackground delegate method of UIApplication shows
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"=== DID ENTER BACKGROUND ===");
if([[UIDevice currentDevice] respondsToSelector:#selector(isMultitaskingSupported)])
NSLog(#"Multitasking Supported");
else
return;
// Shall I remove my KVO observers when in background?? I guess NOT, right? :D
//[[NSNotificationCenter defaultCenter] removeObserver:self];
UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"End of tolerate time. Application should be suspended now if we do not ask more 'tolerance'");
// [self askToRunMoreBackgroundTask]; This code seems to be unnecessary. I'll verify it.
}];
if (bgTask == UIBackgroundTaskInvalid)
NSLog(#"This application does not support background mode");
else
NSLog(#"Application will continue to run in background");
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
});

I got it working following this tutorial but looks like GCDAsyncSocket is no longer maintained so it will work only on iOS7.
http://www.objc.io/issue-5/multitasking.html
To do background file transfer under iOS 8 I am using AFNetworking library (http://afnetworking.com)

Related

Update NSWindow content while asynchronously loading a file

In my document-based MacOS application, I have some big files that are loaded (especially recent files opened at application launch). I created ProgressController (a NSWindowController subclass) to inform user in a window that file loading is in progress. It is allocated by the makeWindowControllers method of the NSDocument subclass I use to manage documents. These are created when the user opens a file (and notably at startup when documents were displayed as the user did quit the application) and they load their content asynchronously in a background queue which in principle should not affect the main thread performance:
-(instancetype) initForURL:(NSURL *)urlOrNil withContentsOfURL:(NSURL *)contentsURL ofType:(NSString *)typeName error:(NSError *
{
if (self = [super init]){
... assign some variables before loading content...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^{
[[[NSURLSession sharedSession] dataTaskWithURL:urlOrNil completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
self.content = [[NSMutableString alloc] initWithData:data encoding:encoder];
dispatch_async(dispatch_get_main_queue(), ^(){
[self contentIsLoaded];
});
}] resume];
});
}
return self;
}
In contentIsLoaded, the progress window is closed.
- (void) contentIsLoaded
{
... do something ...
[self.progressController close];
}
This behaviour is OK, the window is displayed and closed when necessary.
The problem occurs when I want to update the content of this window on the main queue. I tried setting a NSTimer but is is never fired, even though it is created in the main queue. So, in MyApplicationDelegate I created a GCD timer like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
if (self->timer !=nil) dispatch_source_cancel(timer);
self.queue = dispatch_queue_create( "my session queue", DISPATCH_QUEUE_CONCURRENT);
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue);
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(timer, ^{
[self updateProgress];
});
dispatch_resume(timer);
…
}
In MyApplicationDelegate, the updateProgress method is defined as:
- (void) updateProgress
{
dispatch_async(self.queue, ^{
NSLog(#"timer method fired");
dispatch_async(dispatch_get_main_queue(), ^(){
NSLog(#"access to UI fired");
... update window (e.g. NSProgressIndicator)
}
});
if (self.shouldCancelTimer) dispatch_source_cancel(self->timer);
});
}
When running the application, The "Timer method fired" is logged every second. The "timer method fired" message (in the main queue) is logged once or twice only, then the logging seems suspended until the file has been loaded. Then this missing message appears several times in a row, as it was suspended before.
What did I wrong? I supposed that the background queue used for file loading should not affect the main queue and UI updates. Many applications behave like that and I need such behaviour as files in my App (strings, csv, json) can be hundreds of Mbytes!
For starters, have you looked into NSURLSessionTask's progress reporting APIs? There are a bunch of facilities for measuring and getting called back as data loads. It might be possible for you to avoid doing the polling, and just update your UI as these are called back - all on the main thread.
In fact, you might not even need one at all. As I recall, the networking operations carried out by NSURLSession are all done in the background anyways. I bet you can remove all of this, and just use some extra delegate callbacks from NSURLSession APIs to get this done. Way simpler too :)
Good luck!

PFUser currentUser nil after app restart on OS X

I've downloaded the latest Parse SDK for OS X, and I'm trying to retain my login after app restarts (obviously). I've used Parse before and I haven't faced this problem, neither on iOS nor OS X.
On my app start:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[Parse setApplicationId:#"XXX" clientKey:#"XXX"];
}
In my first view controller:
-(void)viewDidAppear{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if(![PFUser currentUser]){
[self performSegueWithIdentifier:#"login" sender:nil];
}else{
...
}
});
}
My login succeeds, and at that point [PFUser currentUser] is valid. Then, I close the app (tried both killing and gracefully quitting). When I open it again, [PFUser currentUser] is nil. I've tried this many times, it yields the same results. Why?
After struggling for a long while, I've found the solution. I need to dispatch_async the user checking block and it starts working. So instead of:
dispatch_once(&onceToken, ^{
if(![PFUser currentUser]){
[self performSegueWithIdentifier:#"login" sender:nil];
}else{
...
}
});
I did:
dispatch_once(&onceToken, ^{
dispatch_async(dispatch_get_main_queue(), ^{
if(![PFUser currentUser]){
[self performSegueWithIdentifier:#"login" sender:nil];
}else{
...
}
});
});
And it started working. Interesting to see that something is still not initialized on viewDidAppear synchronously on main queue (yes, it IS the main queue, double checked that), but is initialized somewhere after posting to the same queue asynchronously. Parse definitely needs more quality control of their SDK.

xcode presentModalViewController leaves old ViewController still active in background using AVCam

Have a problem with an application using AVCam. The application takes photos and saves them to the roll. Upon opening the app it takes the pictures perfectly and saves them just the way it should. However, once I leave that view controller to go to another view controller and then return, it saves images normal portrait images rotated 90 degrees. Very odd activities indeed.
After lots of hair pulling, I thought maybe I'm trying to run too many sessions, so I make sure I end the sessions in viewdidload and the viewdiddisappear:
-(void)viewDidUnload{
if([[self captureManager] session ]){
[[[self captureManager] session] stopRunning];
self.captureManager = nil;
}
}
-(void)viewDidDisappear:(BOOL)animated{
if([[self captureManager] session ]&&rollCancel==NO){
[[[self captureManager] session] stopRunning];
self.captureManager = nil;
}
}
Still not working. Still saving incorrectly after leaving.
I change views like so:
-(void)openMenu{
MenuViewController * vc = [[MenuViewController alloc] initWithNibName:#"MenuViewController" bundle:nil];
[self presentModalViewController:vc animated:YES];
[self dismissViewControllerAnimated:NO completion:nil];
}
Also tried:
-(void)openMenu{
[self dismissViewControllerAnimated:NO completion:^{
MenuViewController * vc = [[MenuViewController alloc] initWithNibName:#"MenuViewController" bundle:nil];
[self presentModalViewController:vc animated:YES];
[self dismissViewControllerAnimated:NO completion:nil];
}];
}
I don't need to release since I'm running on arc. This did not work as well, I wasn't even able to change screens with this option. SO, I added a timer to see if actions would keep acting after I leave the view controller:
updateTimer = [NSTimer timerWithTimeInterval: 5.0
target: self selector: #selector(autoTimer)
userInfo: nil repeats: YES];
[[NSRunLoop mainRunLoop] addTimer: updateTimer forMode: NSDefaultRunLoopMode];
-(void)autoTimer{
NSLog(#"blablabla");
}
And no big surprise, the blablabla continues to print into my console even after leaving the view controller. If I go back and forth, it will keep making new repeating timers and spam my console log.
This has got to have something to do with my application not saving correctly after returning to the screen. Which means that doing presentmodalviewcontroller does not actually remove everything from running.
Has anybody else ran into a similar issue like this or have any idea how to actually clear view controllers when switching between them?
didn't resolve the NSTimer still running in the background, but did fix the landscape thing which had nothing to do with stuff being open in the background
Found the solution here: https://stackoverflow.com/a/6335992/1688727

NSDistributedNotifications not distributed between instances of (same) app(s)

On 10.7.2 I have trouble getting standard NSDistributedNotifications to work out of the box.
Even when brought back to just (Full XCode version at https://github.com/dirkx/Example-NSDistribtuedNotification-Failing) to something as simple as below I get:
Splendidly working notifications 'locally' (Like with a NSNotificationCenter) but
No inter-app comms when I start the app up twice (e.g. from the command line)
No notifications when another apps registers for this (or for nill)
No debug/info in the distnoted daemon logs either.
What am I missing ?
NSString * kSayNotification = #"org.webweaving.sayExample";
// Send a Distributed Notification on button press.
//
-(IBAction)buttonChange:(NSButton *)sender {
NSString * str = (sender.state == NSOnState) ? #"Yes" : #"No";
[[NSDistributedNotificationCenter defaultCenter]
postNotificationName:kSayNotification
object:str
];
}
// Update a label on receiving a Notification.
//
-(void)notif:(NSNotification *)nf {
.. snipped time string ...
// Textfield with the time of arrival and the value passed in the notification.
//
textField.stringValue = [NSString stringWithFormat:#"%#: %#",
dStr, (NSString *)nf.object
];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Register for the notifications.
//
[[NSDistributedNotificationCenter defaultCenter]
addObserver:self
selector:#selector(notif:)
name:kSayNotification
object:nil];
}
As an aside - notification watcher (https://github.com/melo/notification-watcher) does not show the Notifiactions - but the notifications are processed within the app.
Turns out that when there are no other reasons for CFRunLoop to return - the messages are queued indefinitely. This seems by design.
I found three work arounds for this - none of which is that nice - they both involve
adding a deliverImmediately:YES to the posting,
a deliverImmediately: with NSNotificationSuspensionBehaviorDeliverImmediately to the observer or
setting a timer to intentionally interrupt the runLoop every seconds.
Obviously - none of this comes cheap.
Dw

Multiwindows problem, cocoa

I have a simple application, not document-based. I want to have a login window that allows people to login or add a user, and when they logged in successfully I want it to load the main page. If from the main page you click log out, it should destroy the main page and take you back to login page.
sounds like a simple plan, but for some reason I have a problem.
The way I have it right now, I check if the customer logged in or not in the main file AppDelegate and load different window controller. When customer logs in, I send a notification back to AppDelegate from Login Conntroller and load another window controller for main window.
Something like this:
if([[settings get:#"isLoggedIn"] isEqualToString:#"Yes"])
{
MainController *tmpMainController = [[MainController alloc] initWithWindowNibName:#"MainWindow"];
self.mainController = tmpMainController;
NSWindow *mainWindow = [tmpMainController window];
[mainWindow makeKeyAndOrderFront:self];
[tmpMainController release];
} else {
LoginController *tmpViewController = [[LoginController alloc] initWithWindowNibName:#"LoginWindow"];
self.loginController = tmpViewController;
loginWindow = [tmpViewController window];
[loginWindow makeKeyAndOrderFront:self];
[tmpViewController release];
}
Everything works fine, it displays the correct window. But the weird part happens when I log out from the main page, log in again and log out again. If I do it several times, instead of showing me 1 login window, it draws 2. If I continue the login process, on the second try I get 2 main windows. If I log out again, I see 4 cascade login windows, then I see 5 or 7 main windows. After all windows gets loaded all extra windows start getting destroyed one-by-one. It looks like when new window gets created it draws all old windows, then the new one and then destroys all old ones. I don't know why it happens. Would like some help.
Here is the code from my main controller when customer clicks log out:
-(IBAction)logOutClick:(id) sender
{
[settings set:#"isLoggedIn" value:#"No"];
[[self window] orderOut:self];
[[NSNotificationCenter defaultCenter] postNotificationName:#"NSUserLoggedOutNotification" object: self userInfo: nil];
}
the same thing for login controller:
if ([users verifyUser]) {
[settings set:#"isLoggedIn" value:#"Yes"];
[loginView removeFromSuperview];
[[self window] orderOut:self];
[[NSNotificationCenter defaultCenter] postNotificationName:#"NSUserLoggedInNotification" object: self userInfo: nil];
}
I have "Released when closed" checked off for both windows.
I added new nsnotification center observer every time I log out.
That was the problem.

Resources