I am trying to use Apple's 'BTLE Transfer' sample project to understand CoreBluetooth programming. The app runs fine if I use an iOS 6 device as the Central, but if I run the same app with the iOS 7 device as the Central it doesn't work. The peripheral stops sending after two packets, and the central doesn't receive either one of them.
The only clue is this warning that I get only when running on iOS 7:
CoreBluetooth[WARNING] <CBCentralManager: 0x15654540> is disabling duplicate filtering, but is using the default queue (main thread) for delegate events
Can anyone tell me what needs to change to make this app compatible with iOS 7?
EDIT: When both devices are iOS7 there are no issues. This only breaks when an iOS7 central is talking to a iOS6 peripheral.
Okay I just ran it on an iOS 7 central to iOS 6 peripheral. If you want to make that warning about disabling duplicate filtering go away, just run it on a different thread. Do something like this:
dispatch_queue_t centralQueue = dispatch_queue_create("com.yo.mycentral", DISPATCH_QUEUE_SERIAL);// or however you want to create your dispatch_queue_t
_centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:centralQueue];
Now it will allow you to scan with duplicates enabled. However, you must call the textView setter on the main thread to be able to set the text without crashing:
dispatch_async(dispatch_get_main_queue(), ^{
[self.textview setText:[[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]];
});
Btw you probably also want to adopt the new iOS 7 delegate initialization:
_centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:centralQueue options:nil];//set the restoration options if you want
(Just check the iOS version and call the appropriate initialization method)
InscanForPeripheralsWithServices:options:,if you set CBCentralManagerScanOptionAllowDuplicatesKey:#YES then change it to
CBCentralManagerScanOptionAllowDuplicatesKey:#NOthat means scan should run without duplicate filtering.
For me,it works on iOS7 also.
Related
The following line of code prevents the app from automatically locking the screen after some idle time.
[UIApplication sharedApplication].idleTimerDisabled = YES; //write this in applicationDidFinishLaunching
It works well till iOS 5.0.
But iOS 5.1 does not respect this line and locks the screen after some idle time.
How to solve this irritating issue?
Thanks.
Edit:
The same code works fine when its installed in 5.0.1 device. But I dont know why it is not working with 5.1 device.
Just setting [UIApplication sharedApplication].idleTimerDisabled = YES; in
- (BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
works well for me. However, there is a caveat. I have noticed that every time I invoke camera utility to take a snapshot, idleTimerDisable gets set to NO behind the scene. So right after I upload my image, I had to call the following line of code again:
[UIApplication sharedApplication].idleTimerDisabled = YES;
I would not be surprised if there are more places throughout that require same strategy. So far this approach has worked without issues for me.
[[UIApplication sharedApplication] setIdleTimerDisabled: YES];
worked for me on iOS 5.1
No there should be no difference. Perhaps you have another mistake..
See iOS 5.0 to 5.1 API Diffs
Important: You should set this property only if necessary and should be sure to reset it to NO when the need no longer exists. Most
applications should let the system turn off the screen when the idle
timer elapses. This includes audio applications. With appropriate use
of Audio Session Services, playback and recording proceed
uninterrupted when the screen turns off. The only applications that
should disable the idle timer are mapping applications, games, or
similar programs with sporadic user interaction.
Maybe You exceeds the allowable time limit of being awake?
i know its is old, but i found this good and in Swift you can do it look a like this
application.idleTimerDisabled = true
Thanks for you answers! i use right now xcode 7 Beta 3 ( Swift 2 )
For Swift, I use this to do outside of delegate:
UIApplication.sharedApplication().idleTimerDisabled = true
Works fine if your application is registered for some background task, for example GPS location updating.
I have a question regarding iOS 4 and 5. I am really confused and hope someone will clear it out for me.
I am using iOS 5 SDK in my application. If i use the iOS 5 Twitter integration which is provided by apple, will it run on an iPhone that has iOS 4 installed ?
Does backward compatibility work ?
I have used Twitter as an example, but does backward compatibility really work with iOS 5 ?
If you set up your app properly, so that it can be run on devices running iOS 4 without crashing, then: yes, it will run on an iPhone that has iOS 4 installed.
Your app should implement logic such that the Twitter API is used when the app is being run on an iOS 5 device. When the app is running on an iOS 4 device, you can conditionally choose not to use the Twitter API.
Instead, you can use a different Twitter library (like MGTwitterEngine, or your own) - or just exclude Twitter functionality for those users.
To check whether the TWRequest Class exists, use NSClassFromString.
Class twRequestClass = NSClassFromString(#"TWRequest");
if (twRequestClass == nil) {
// TWRequest does not exist on this device (running iOS version < 5.0)
// ... do something appropriate ...
} else {
TWRequest *twRequest = [[twRequestClass alloc] init];
// ^ I didn't look up the proper initializer, so you should change that line if necessary
// ...
}
You would have to create ifs dependently of the iOS version the user is using. Exemple, in iOS 5 there is an Appearance API to modify most of the UI, but not in iOS 4, so you have to create a little if like that:
// not supported on iOS4
UINavigationBar *navBar = [myNavController navigationBar];
if ([navBar respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)])
{
[navBar setBackgroundImage:[UIImage imageNamed:#"bg.jpg"] forBarMetrics:UIBarMetricsDefault];
}
If you set up your app properly, so that it can be run on devices running iOS 4, it will crash. This is because you're trying to access methods/features that arn't available.
The way to get around this is to check if a feature is available using
if(NSClassFromString(#"UIPopoverController")) {
// Do something
}
(Popover controller is just an example)
You could also check the version using
float version = [[[UIDevice currentDevice] systemVersion] floatValue];
And then depending on the version run a specific piece of code (i.e. if iOS 5, preform twitter stuff,else do something different)
No, if you use the Twitter APIs available in iOS5, they will not be able to run on iOS4.
The reason being that when app will run on iOS4, the system will not be having the APIs availability.
if you check the documentation, you can see the iOS version from where this Class/API is available.
I hope this helps..
I don't mind using private API's or anything of the kind that Apple doesn't like, but would prefer a quick solution that doesn't stuff like playing silence in the background or swizzling.
Obviously this isn't for the app store so please no lecturing :)
So how do you run in the background without any restrictions like "backgrounder"? I didn't manage to find an answer besides some that point people to different directions, but maybe since then someone managed to dig it up already.
Update:
This solution no longer appears to be sufficient (~ iOS 7+ or 7.1+). I'm leaving the original answer for historical reference, and in case it helps produce a future solution based on this obsolete one:
It depends on what you mean by app. If you're talking about a non-graphical background service, then what you want is a Launch Daemon. See here for how to create a launch daemon.
If you have a normal UI application, but when the user presses the home button, you want it to stay awake in the background for an unlimited time, then you can use some undocumented Background Modes in your app's Info.plist file:
<key>UIBackgroundModes</key>
<array>
<string>continuous</string>
<string>unboundedTaskCompletion</string>
</array>
Then, when iOS is ready to put your app into the background (e.g. user presses home button), you can do this, in your app delegate:
#property (nonatomic, assign) UIBackgroundTaskIdentifier bgTask;
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Delay execution of my block for 15 minutes.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 15 * 60 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
NSLog(#"I'm still alive!");
});
self.bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
// should never get here under normal circumstances
[application endBackgroundTask: self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
NSLog(#"I'm going away now ....");
}];
}
Normally, iOS only gives you up to 10 minutes for your UI application to work in the background. With the undocumented background mode, you'll be able to keep alive past that 10 minute limit.
Note: this does not require hooking with MobileSubstrate. If you're using the second method (undocumented Background Modes), then it does require installing your app in /Applications/, not in the normal sandbox area (/var/mobile/Applications/).
Depending on what your "app" is going to do, you can hook MobileSubstrate. This will load with SpringBoard and essentially run "in the background".
If you want to write an actual application, then you can also write a "Dynamic Library" which will be loaded with SpringBoard by MobileSUbstrate. You can talk back and forth between this dylib and your app by using NSNotificationCenter; creating and posting notifications.
I created application to download files. For downloading i use ASIHTTPRequest. When I start download big file, and lock my device, after some time my download stops, wi-fi disables and i see Edge icon instead of Wi-fi icon. When I unlock my device, Wi-fi icon appears in 1-2 seconds. My application is not in background! How to solve my problem?
Two things come to mind:
Firstly enable persisten wifi connection for you app: My iPhone app needs a persistent network connection...how to specify UIRequiredDeviceCapabilities?
Secondly make the app request background time when it goes into the background so the actual download can continue:
Continuing a long running process in the background under iOS4
I'm not sure if 10 minutes after locking the device if the app would count as running in the background or not.
I'd at least try enabling background downloading in ASIHTTPRequest:
[request setShouldContinueWhenAppEntersBackground:YES];
It might help and you've nothing to lose :)
You can also prevent the IPhone to lock the screen. It'll use more battery but will solve your problem:
UIApplication *myApp = [UIApplication sharedApplication];
myApp.idleTimerDisabled = YES;
Hello I am planning to develop a simple iPhone game. I would like to be able to distinguish between a genuine crash, and the user killing the app ( by double tapping home screen and long-clicking it to kill it) .
Can someone please shed some light on what exactly happens when the user kill the app through the multitasking bar.
If your app is in the background and suspended when the user kills it, it will receive no notification. This accounts for the majority of cases.
If your app is currently running in the background (there are only very specific categories of apps that can do that), then it receives applicationWillTerminate.
Indeed, Apple is very clear as to the fact that you should save any relevant data before entering the background. Have a look at this (chapter "Responding to Application Termination"):
Even if you develop your application using iOS SDK 4 and later, you must still be prepared for your application to be killed without any notification. The user can kill applications explicitly using the multitasking UI. In addition, if memory becomes constrained, the system might remove applications from memory to make more room. If your application is currently suspended, the system kills your application and removes it from memory without any notice. However, if your application is currently running in the background state (in other words, not suspended), the system calls the applicationWillTerminate: method of your application delegate. Your application cannot request additional background execution time from this method.
EDIT:
about the "saying sorry" thing...
you can certainly do that on the next launch. simply store a key in NSUserDefaults and remove it when the app enters the background (I hope all this sounds familiar to you, otherwise look into the UIApplicationDelegate protocol).
when the app starts up, you check the key; if it is there, then the app was not closed by the user; if the app is not there, then the user at least moved the app to the background and did not experience any sudden termination...
For iOS6 and later there is a way to do this. A side effect of State Restoration is that it will delete the state when there is either a crash during restore or a user manually kills the app. You can use this to your advantage to detect a user manually killing the app.
From the documentation:
Be aware that the system automatically deletes an app’s preserved state when the user force quits the app. Deleting the preserved state information when the app is killed is a safety precaution. (The system also deletes preserved state if the app crashes at launch time as a similar safety precaution.) If you want to test your app’s ability to restore its state, you should not use the multitasking bar to kill the app during debugging. Instead, use Xcode to kill the app or kill the app programmatically by installing a temporary command or gesture to call exit on demand.
The following code assumes that you already have a BOOL for crash detection called _didCrashInLastSession. There are different approaches for getting this value such as this 3rd party library. In your code call the method [self getLaunchType] to see which type of launch you are dealing with and act on that accordingly. Put the following in your AppDelegate.m:
typedef NS_ENUM(NSInteger, LaunchType) {
LaunchTypeUnknown,
LaunchTypeNewInstall,
LaunchTypeNormalLaunch,
LaunchTypeCrashedLastSession,
LaunchTypeUserManualQuit,
};
static BOOL hadStateToRestore = NO;
static NSString * const kAppHasEverRunKey = #"appHasEverRun";
- (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder
{
// Called when going into the background
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:kAppHasEverRunKey];
[[NSUserDefaults standardUserDefaults] synchronize];
return YES;
}
- (BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder
{
// Called on start up
hadStateToRestore = YES;
return YES;
}
- (LaunchType)getLaunchType
{
if (_didCrashInLastSession) {
return LaunchTypeCrashedLastSession;
}
if (![[NSUserDefaults standardUserDefaults] boolForKey:kAppHasEverRunKey]) {
return LaunchTypeNewInstall;
}
if (!hadStateToRestore) {
return LaunchTypeUserManualQuit;
}
return LaunchTypeNormalLaunch;
}
Update: At least one 3rd party SDK breaks this technique: Urban Airship.
You can do it through your device also.
Connect your device to your machine.
Run xcode and go to organizer.
There select your device and device logs.
There you can also see crash logs of your app or game.