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
Related
I am trying to perform what I thought was a simple task. I have a handful of repeating timers that need to be started and stopped.
I created methods for starting and stopping the timers, and attempt to pass the timers to the methods as parameters.
The problem is that the timers never seem to be stopped. Any ideas why this might not be working properly? Thank you!
Top of File:
#import "ViewController.h"
NSTimer *launchTimer;
NSTimer *transactionTimer;
Starting Method:
-(void) startingMethod {
NSString *urlString = #"http://google.com";
[[AsynchRequestService sharedInstance]
performAsynchronousURLRequest:urlString completion:^(BOOL success,
NSString *responseBody, NSString *responseStatus) {
if (success) {
[self stopResponseTimer:launchTimer];
}
else {
[self startResponseTimer:launchTimer
method:#selector(startingMethod)
interval:10];
}
}];
}
Method to Start the Timer:
-(void)startResponseTimer:(NSTimer *) timer method:(SEL) method
interval:(int) interval {
[timer invalidate];
timer = nil;
timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self
selector:method userInfo:nil repeats:YES];
}
Method to Stop the Timer:
-(void)stopResponseTimer:(NSTimer *) timer {
NSLog(#"STOP TIMER");
[timer invalidate];
timer = nil;
}
Make startResponseTimer and `stopResponseTimer' take the pointer to the object pointer intead.
-(void)startResponseTimer:(NSTimer **) timer method:(SEL) method
interval:(int) interval {
[*timer invalidate];
*timer = nil;
*timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self
selector:method userInfo:nil repeats:YES];
}
-(void)stopResponseTimer:(NSTimer **) timer {
NSLog(#"STOP TIMER");
[*timer invalidate];
*timer = nil;
}
then invoke it like
[self startResponseTimer:&launchTimer];
[self stopResponseTimer:&launchTimer];
This should make sure that you retain the right NSTimer object.
NOTE:It is always a good idea to check a pointer to a pointer to an object for NULL in a public method
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.
I cant work out to stop the Gyro updates in my Cocos2D game,
I have got the following code in my init: ` //gyro
motionManager = [[CMMotionManager alloc] init];
referenceAttitude = nil;
[motionManager startGyroUpdates];
timer = [NSTimer scheduledTimerWithTimeInterval:0.2
target:self
selector:#selector(doGyroUpdate)
userInfo:nil
repeats:YES];
Then in the gyro update itself I check when the progress is more than 100% if (progress > 100) {
[self pauseSchedulerAndActions];
[self stopGyroUpdates];
Then:
- (void)stopGyroUpdates{
NSLog(#"Stop gyro update");
}
but it keeps checking... So the code within the if statement keeps getting called.
I found the solution with the following code: `
-(void) callgyro:(int)gyroprocess {
NSLog(#"%i", gyroprocess);
if (gyroprocess < 100 ) {
motionManager = [[CMMotionManager alloc] init];
referenceAttitude = nil;
[motionManager startGyroUpdates];
timertwee = [NSTimer scheduledTimerWithTimeInterval:0.2
target:self
selector:#selector(doGyroUpdate)
userInfo:nil
repeats:YES];
}
else {
[motionManager stopGyroUpdates];
[timertwee invalidate];
NSLog(#"Gyro moet stoppen!");
}`
and within the gyro update itself:
if (progress > 100) {
[self callgyro:progress];
[self.timer invalidate];
self.timer = nil;
[Blikje setImage:[UIImage imageNamed:#"pop3.png"]];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setFloat:numhundreds forKey:#"score"];
progress = 0;
[self stopGyroUpdates];
}
Like rickster said, you need to call stopGyroUpdates on the CMMotionManager instance.so you need to create an instance variable or property in the interface implementation.
In your .m file where your declare the interface:
#interface ViewController ()
#property (strong, nonatomic) CMMotionManager *motionManager;
#end
Then initialize it as you require
motionManager = [[CMMotionManager alloc] init];
Then when you need to stop updates you call
[self.motionManager stopGyroUpdates]
You need to call stopGyroUpdates on your instance of CMMotionManager, not (or in addition to) on self. (This means that instance needs to persist beyond the scope if the method in which you call startGyroUpdates -- say, in a property or ivar -- if it doesn't already.)
I have an application that connects to a server using NSStream on another thread. The application also closes the connection should the user decide to log out. The problem is that I am never able to successfully close the stream or the thread upon having the user disconnect. Below is my code sample on how I approach creating a thread for my network and trying to close the stream:
+ (NSThread*)networkThread
{
static NSThread *networkThread = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
networkThread = [[NSThread alloc] initWithTarget:self selector:#selector(networkThreadMain:) object:nil];
[networkThread start];
});
return networkThread;
}
+ (void)networkThreadMain:(id)sender
{
while (YES)
{
#autoreleasepool {
[[NSRunLoop currentRunLoop] run];
}
}
}
- (void)scheduleInThread:(id)sender
{
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[inputStream open];
}
- (void)closeThread
{
[inputStream close];
[inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[inputStream release];
inputStream = nil;
}
Call made when trying to connect the inputstream:
[self performSelector:#selector(scheduleInThread:) onThread:[[self class] networkThread] withObject:nil waitUntilDone:YES];
Any advice is greatly appreciated.
The way you're mixing static and instance variables is confusing. Are you married to doing it that way? If you put this inside an NSOperation and ran it using an NSOperationQueue I think you'd get much cleaner encapsulation. The operation will manage its own async thread so you don't have to. Also, I highly recommend using ARC if you can.
A few notes:
Make sure to set the stream's delegate and handle delegate events. You should probably do that inside the operation (make the operation the delegate) and close the stream and finish the operation when necessary.
There may be other failure conditions for the stream besides NSStreamStatusClosed, such as NSStreamStatusNotOpen, etc. You will probably need to add additional handling, which can be done by listening to the delegate methods.
Your code is probably not working right mainly because your while loop runs the runloop forever. You have to have conditions in which to break out. NSOperation gives you some pretty good standardized ways of doing this.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#interface AsyncStreamOperation : NSOperation
#end
NS_ASSUME_NONNULL_END
#import "AsyncStreamOperation.h"
#interface AsyncStreamOperation ()
#property (atomic, strong) AsyncStreamOperation *config;
#property (atomic, strong) NSInputStream *stream;
#property (atomic, assign, getter=isExecuting) BOOL executing;
#property (atomic, assign, getter=isFinished) BOOL finished;
#end
#implementation AsyncStreamOperation
#synthesize executing = _executing;
#synthesize finished = _finished;
- (instancetype)initWithStream:(NSInputStream *)stream
{
self = [super init];
if(self) {
_stream = stream;
}
return self;
}
- (BOOL)isAsynchronous
{
return YES;
}
- (BOOL)isExecuting
{
#synchronized (self) {
return _executing;
}
}
- (void)setExecuting:(BOOL)executing
{
#synchronized (self) {
[self willChangeValueForKey:#"isExecuting"];
_executing = executing;
[self didChangeValueForKey:#"isExecuting"];
}
}
- (BOOL)isFinished
{
#synchronized (self) {
return _finished;
}
}
- (void)setFinished:(BOOL)finished
{
#synchronized (self) {
[self willChangeValueForKey:#"isFinished"];
_finished = finished;
[self didChangeValueForKey:#"isFinished"];
}
}
- (void)start
{
// Get runloop
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
// Schedule stream
[self.stream scheduleInRunLoop:runLoop forMode:NSDefaultRunLoopMode];
[self.stream open];
// Loop until finished
// NOTE: If -cancel is not called, you need to add your own logic to close the stream so this loop ends and the operation completes
while(self.executing && !self.finished && !self.cancelled && self.stream.streamStatus != NSStreamStatusClosed) {
#autoreleasepool {
// Maximum speed once per second or CPU goes through the roof
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}
}
self.executing = NO;
self.finished = YES;
}
- (void)cancel
{
[super cancel];
[self.stream close];
self.executing = NO;
self.finished = YES;
}
#end
I want a timer class that can post messages to a delegate when there are 1/2/3 seconds to go.
My test target consistently crashes.
iOS logic unit test target.
Tests class that times a duration using a repeating NSTimer
One test with no asserts. The test passes, but then the target crashes with:
/Developer/Tools/RunPlatformUnitTests.include: line 415: 770 Bus error "${THIN_TEST_RIG}" "${OTHER_TEST_FLAGS}" "${TEST_BUNDLE_PATH}"
It seems to me that it's some kind of memory allocation error, but I can't figure out what I'm missing. The problem is associated with the stop timer routine somehow. It's only when the timer runs out that the target crashes.
Things I've tried
Build and Analyze - no errors reported
Remove -framework and UIKit from the linker flags
Removing dealloc - this has no effect
Test Code
-(void)testGivenThreeSecondDurationAtOneSecondDelegateShouldBeToldToShowGreenCard {
JGTimerController *timer = [JGTimerController timerWithDurationValue:1 delegate:nil];
[timer startTimer];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.1]];
}
Class Code
#interface JGTimerController : NSObject {
NSNumber *duration;
NSTimer *timer;
id <NSObject, JGTimerControllerDelegate> _delegate;
}
#property (nonatomic, retain) NSNumber *duration;
... public methods...
#end
#implementation JGTimerController
#synthesize duration;
+(JGTimerController *)timerWithDurationValue:(NSUInteger)durationValue delegate:(id <JGTimerControllerDelegate>)delegate_ {
JGTimerController *instance = [[[JGTimerController alloc] initWithDurationValue:durationValue delegate:delegate_] autorelease];
return instance;
}
-(JGTimerController *)initWithDurationValue:(NSUInteger)durationValue delegate:(id <JGTimerControllerDelegate>)delegate_ {
self = [super init];
timer = nil;
[self setDurationValue:durationValue];
_delegate = delegate_;
return self;
}
-(NSUInteger)durationValue {
NSNumber *result = [self duration];
return result ? [result intValue] : 0;
}
-(void)setDurationValue:(NSUInteger)value_ {
[self setDuration:[NSNumber numberWithInt:value_]];
}
-(BOOL)stopTimerAtZeroDuration:(NSTimer *)timer_ {
if ([self durationValue] == 0) {
[self stopTimer];
return YES;
}
return NO;
}
-(void)startTimer {
if ([self stopTimerAtZeroDuration:nil])
return;
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerDidCountDownByASecond:) userInfo:nil repeats:YES];
}
-(void)stopTimer {
if ([self durationValue] == 0 && [_delegate conformsToProtocol:#protocol(JGTimerControllerDelegate)])
[_delegate showRedCard];
[timer invalidate];
[timer release];
}
-(BOOL)timerIsRunning {
return (timer != nil);
}
-(void)timerDidCountDownByASecond:(NSTimer *)timer_ {
[self setDurationValue:[self durationValue] - 1];
[self stopTimerAtZeroDuration:timer_];
}
-(void)dealloc {
[_delegate release];
[timer release];
[duration release];
[super dealloc];
}
#end
There should be no [_delegate release] in dealloc because you did not retain it.
Likewise, timer should not be released. NSTimer is like every other NSObject, if you did not alloc, copy or retain, you do not need to release.