Wifi callback not called in launch daemon - ios

I want to write a launch daemon in iOS which connects to networks programmatically. For connecting to networks, I'm using Cykey's (David Murray) WifiManager app. Before writing the daemon it worked perfectly (I have used it in a springboard tweak) but now it doesn't scan for available networks (the method - (void)_scanDidFinishWithError:(int)error isn't been called). By logging, I found that the method _scan in DMNetWorksManager is called but the static void DMScanCallback(WiFiDeviceClientRef device, CFArrayRef results, int error, const void *token) isn't called. I think it's because of the runloop I have in the main.m file. Here is my code:
main.m:
int main (int argc, const char * argv[])
{
#autoreleasepool
{
// insert code here...
MSManager *server = [[MSManager alloc] init];
//start a timer so that the process does not exit.
/*NSDate *now = [[NSDate alloc] init];
NSTimer *timer = [[NSTimer alloc] initWithFireDate:now
interval:.01
target:server
selector:#selector(start)
userInfo:nil
repeats:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[runLoop run];*/
[server start];
CFRunLoopRun();
}
return 0;
}
and the start method in MSManager.m:
- (void)start
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(managerDidFinishScanning)
name:kDMNetworksManagerDidFinishScanning
object:nil];
//Some other code here....
}
- (void)managerDidFinishScanning
{
//Do some work here
}
NOTE: Iv'e used both CFRunloop and NSRunLoop (commented in the code) but none of them worked.

Related

iOS Private API: wake app from background

I need to have a demo app that will wake up itself from background on timer event. Is it possible without jailbreak by using private API? Tried this code:
void* sbServices = dlopen(SBSERVPATH, RTLD_LAZY);
int (*SBSLaunchApplicationWithIdentifier)(CFStringRef identifier, Boolean suspended) = dlsym(sbServices, "SBSLaunchApplicationWithIdentifier");
int result;
result = SBSLaunchApplicationWithIdentifier(CFSTR("com.my.app"), false);
dlclose(sbServices);
Didn't worked
Finally I found a solution using private api. Here is an example code launching custom app every 10 seconds
#interface PrivateApi_LSApplicationWorkspace
- (bool)openApplicationWithBundleID:(id)arg1;
#end
#implementation ViewController {
PrivateApi_LSApplicationWorkspace* _workspace;
}
- (void)viewDidLoad {
[super viewDidLoad];
_workspace = [NSClassFromString(#"LSApplicationWorkspace") new];
NSTimer *timer = [NSTimer timerWithTimeInterval:10.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
[self openAppWithBundleIdentifier:#"com.app.my"];
}];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
- (BOOL)openAppWithBundleIdentifier:(NSString *)bundleIdentifier {
return (BOOL)[_workspace openApplicationWithBundleID:bundleIdentifier];
}
#end

iOS TestCase for method which includes NS Timer [duplicate]

This question already has answers here:
XCTest and asynchronous testing in Xcode 6
(4 answers)
Closed 7 years ago.
I have a IBAction in my view controller which looks like this
-(IBAction)signUpAction:(id)sender
{
AppDelegate *appDel = [[UIApplication sharedApplication]delegate];
//check for internet Connection
if(appDel.isReachable)
{
//Internet Connection available
//perform animation od buttons and imagie view
[self fallDownAnimation];
//after animation perform model segue to corresponding view controller
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:0.8f target:self selector:#selector(performRegistrationPageSegue) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
else
{
//No internet Connection
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:ALERT_VIEW_TITLE message:#"No Internet Connection" delegate:self cancelButtonTitle:nil otherButtonTitles:#"Okay", nil];
[alert show];
}
}
-(void)performRegistrationPageSegue{
[self performSegueWithIdentifier:#"registerVCSegue" sender:self];
}
I want to write a test case on signUpAction method and verify if the Segue is performed. Since it has a timer the test case i have written is failing.
I Need a way to test the following condition
My Current Testcase method is
-(void)testRegisterViewControllerSegueOnAvailableInternetConnection{
AppDelegate *appDel = [[UIApplication sharedApplication]delegate];
appDel.isReachable = YES;
id loginMock = [OCMockObject partialMockForObject:_initialViewControllerToTest];
[[loginMock expect] performSegueWithIdentifier:#"registerVCSegue" sender:[OCMArg any]];
[loginMock performSelectorOnMainThread:#selector(signUpAction:) withObject:_initialViewControllerToTest.signUpButton waitUntilDone:YES];
XCTAssert([loginMock verify],#"Segue to Register Page not Performed on Sign Up Click");
}
You need to enter the event loop for a period of time, so that the timer event can be processed. It is basically not possible to fully regression test code without doing this. Here is a simplified method:
// Wait inside the event loop for a period of time indicated in seconds
+ (void) waitFor:(NSTimeInterval)maxWaitTime
{
int numSeconds = (int) round(maxWaitTime);
if (numSeconds < 1) {
numSeconds = 1;
}
for ( ; numSeconds > 0 ; numSeconds--) #autoreleasepool {
const int maxMS = 1000;
const int incrMS = 1;
const double seconds = 1.0 / (maxMS / incrMS);
for (int ms = 0 ; ms < maxMS; ms += incrMS) #autoreleasepool {
// One pass through the run loop for each time interval
NSDate *maxDate = [NSDate dateWithTimeIntervalSinceNow:seconds];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:maxDate];
}
}
return;
}
A more complex impl with a selector that can be called to return when a test condition is true:
+ (BOOL) waitUntilTrue:(id)object
selector:(SEL)selector
maxWaitTime:(NSTimeInterval)maxWaitTime
{
NSAssert(object, #"object is nil");
NSAssert(selector, #"selector is nil");
NSMethodSignature *aSignature = [[object class] instanceMethodSignatureForSelector:selector];
NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
[anInvocation setSelector:selector];
[anInvocation setTarget:object];
// Invoke test condition method once before the timing loop is entered, so that the
// event loop will not be entered if the condition is initially TRUE.
BOOL state;
[anInvocation invoke];
[anInvocation getReturnValue:&state];
if (state) {
return TRUE;
}
// The condition is FALSE, so enter the event loop and wait for 1 second
// each iteration through the loop. The logic below makes sure that the
// 1 second wait will be done at least once, even if wait time is less
// than a full second.
int numSeconds = (int) round(maxWaitTime);
if (numSeconds < 1) {
numSeconds = 1;
}
for ( ; numSeconds > 0 ; numSeconds--) #autoreleasepool {
NSDate *maxDate = [NSDate dateWithTimeIntervalSinceNow:1.0];
[[NSRunLoop currentRunLoop] runUntilDate:maxDate];
[anInvocation invoke];
[anInvocation getReturnValue:&state];
if (state) {
return TRUE;
}
}
return FALSE;
}

Is there a way, in background, to get my app notified when the CMMotionActivity changes?

I'd like to know if the system can wake an application in the background if the CMMotionActivity changes, for example, if the user starts walking/running after sitting, I'd like to be able to execute some code and schedule a local notification.
Is there a way to ask the system to wake my app in the background for that ?
EDIT : By looking at the reference, it doesn't seem to be possible ("[...] and updates are not delivered while your app is suspended."), but maybe is there an other way ?
I solved this creating a background timer and checking for the activity type in the selector called method. Just have a look to the code in case it can be useful for you. I accept suggestions, corrections and advices over this.
#define k_timer_time 10.0f
#property NSTimer *timer;
- (void) createBackGroundTimerToCheckMotion{
// create new uiBackgroundTask
__block UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
__weak __typeof(self) weakSelf = self;
// and create new timer with async call:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
weakSelf.timer = [NSTimer scheduledTimerWithTimeInterval:k_timer_time target:self selector:#selector(onTick:) userInfo:nil repeats:YES];
weakSelf.timer.tolerance = 5;
[[NSRunLoop currentRunLoop] addTimer:weakSelf.timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
}
- (void) onTick:(NStimer*)timer{
if([CMMotionActivityManager isActivityAvailable])
{
__weak __typeof(self) weakSelf = self;
CMMotionActivityManager *cm = [[CMMotionActivityManager alloc] init];
CMPedometer *sc = [[CMPedometer alloc] init];
NSDate *now = [NSDate date];
NSDate *last30Sec = [now dateByAddingTimeInterval:-30];
[cm queryActivityStartingFromDate:last30Sec toDate:now toQueue:[NSOperationQueue mainQueue] withHandler:^(NSArray *activities, NSError *error)
{
[activities enumerateObjectsUsingBlock:^(CMMotionActivity *a, NSUInteger idx, BOOL * _Nonnull stop) {
//Your stuff here
}];
}];
}
else
{
NSLog(#"Error accessing Motion data");
}
}

SIGSEGV in background thread NSRunLoop runMode:beforeDate:

I am using background threads with following pattern:
// Defined in .h-file as ivar
BOOL backgroundThreadIsAlive;
// .m file
static NSThread *backgroundThread = nil;
- (id)init
{
if (self = [super init])
{
if (backgroundThread == nil)
{
backgroundThreadIsAlive = YES;
backgroundThread = [[NSThread alloc] initWithTarget:self selector:#selector(threadMain:) object:nil];
[backgroundThread start];
}
}
return self;
}
- (void)threadMain:(id)data
{
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
while (backgroundThreadIsAlive)
{
[runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
My application sometimes crash with SIGSEGV in the line
[runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
I am using ARC. The crash is not reproducible. Just found it, when diving into testflight and saw this is the most often crash I got. It seems to appear independently from iOS version (I support iOS5+) and device type.
May someone have a hint for me, what I do wrong?
Is there a better solution doing background-threads? (maybe using GCD)
Is there a way to reproduce those issues?
Thank you for your time and guidiance. :)

problems with new thread

After trying many ways to call a function in new thread only the below code worked for me
[NSThread detacNewThreadSelector:#selector(temp:) toTarget:self withObject:self];
The below didn't work:
NSThread *updateThread1 = [[NSThread alloc] initWithTarget:self selector:#selector(temp:) object:self];
NSThread *updateThread1 = [[NSThread alloc] init];
[self performSelector:#selector(temp:) onThread:updateThread1 withObject:self waitUntilDone:YES];
Now when i try to call NSTimer or perform selector in timer: function it does not works Find below the code
int timeOutflag1 = 0;
-(void)connectCheckTimeOut
{
NSLog(#"timeout");
timeOutflag1 = 1;
}
-(void)temp:(id)selfptr
{
//[selfptr connectCheckTimeOut];
NSLog(#"temp");
//[NSTimer scheduledTimerWithTimeInterval:5 target:selfptr selector:#selector(connectCheckTimeOut) userInfo:nil repeats:NO];
[selfptr performSelector:#selector(connectCheckTimeOut) withObject:nil afterDelay:5];
}
- (IBAction)onUart:(id)sender {
protocolDemo1 *prtDemo = [[protocolDemo1 alloc] init];
//NSThread *updateThread1 = [[NSThread alloc] initWithTarget:self selector:#selector(temp:) object:self];
//[self performSelector:#selector(temp:) onThread:updateThread1 withObject:self waitUntilDone:YES];
// [updateThread1 start];
[self performSelector:#selector(temp:) withObject:self afterDelay:0];
while(1)
{
NSLog(#"Whieloop");
if(timeOutflag1)
{
timeOutflag1 = 0;
break;
}
if([prtDemo isConnected])
break;
}
}
If i use [self performSelector:#selector(connectCheckTimeOut) withObject:nil afterDelay:5];
in onUart function then it works properly i can see Timeout printf but inside temp it does not work.
NSTimer is run-loop based, so if you want to use one on a background thread that you're spawning and managing yourself, you will need to start a runloop on that thread. Read up on NSRunLoop. The short version might look something like:
- (void)timedMethod
{
NSLog(#"Timer fired!");
}
- (void)threadMain
{
NSRunLoop* rl = [NSRunLoop currentRunLoop];
NSTimer* t = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: #selector(timedMethod) userInfo:nil repeats:YES];
[rl run];
}
- (void)spawnThread
{
[NSThread detachNewThreadSelector: #selector(threadMain) toTarget:self withObject:nil];
}

Resources