So with iOS 7 supporting a broader background mode, is it possible to finally have an equivalent to Android Service on iOS?
What I am after is essentially running app A in the background and have one or more apps B and C talk to that app (without showing the GUI of app A).
Please note that using connectivity and push notifications may not be an option although this is the recommended way of doing so.
Any ideas?
EDIT: Not working as expected. See this answer for best solution: Push Notifications
EDIT: The next solution is only useful while the user is in the app to maintain it synced.
There is no way to perform tasks in the background permanently, but you can use the finite-length tasks to do that, when you make a finite-length, this gonna run always while the app is active, but when you click home button, ios gives you only 10 min to perform your task and invalidate it, but it gives you a chance to make a 'invalidate handler block' where you can do last actions before finish definitely.
So, if you use that handler block to call a finite-length task other time, you can simulate a service by run a task for 10 min and when its end, call its same for other 10 min and consequently.
I use that in a project creating a interface 'Service'. I let you here the code:
Service.h
//
// Service.h
// Staff5Personal
//
// Created by Mansour Boutarbouch Mhaimeur on 30/09/13.
// Copyright (c) 2013 Smart & Artificial Technologies. All rights reserved.
//
#import <Foundation/Foundation.h>
#interface Service : NSObject
#property (nonatomic) UIBackgroundTaskIdentifier backgroundTask;
#property (nonatomic) NSInteger frequency;
#property (nonatomic, strong) NSTimer *updateTimer;
- (id) initWithFrequency: (NSInteger) seconds;
- (void) startService;
- (void) doInBackground;
- (void) stopService;
#end
Service.m
//
// Service.m
// Staff5Personal
//
// Created by Mansour Boutarbouch Mhaimeur on 30/09/13.
// Copyright (c) 2013 Smart & Artificial Technologies. All rights reserved.
//
#import "Service.h"
#implementation Service
#synthesize frequency;
-(id)initWithFrequency: (NSInteger) seconds{
if(self = [super init]){
self.frequency = seconds;
return self;
}
return nil;
}
- (void)startService{
[self startBackgroundTask];
}
- (void)doInBackground{
//Español //Sobreescribir este metodo para hacer lo que quieras
//English //Override this method to do whatever you want
}
- (void)stopService{
[self.updateTimer invalidate];
self.updateTimer = nil;
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}
- (void) startBackgroundTask{
self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:frequency
target:self
selector:#selector(doInBackground)
userInfo:nil
repeats:YES];
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundTask];
}];
}
- (void) endBackgroundTask{
[self.updateTimer invalidate];
self.updateTimer = nil;
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
[self startBackgroundTask];
}
#end
With this class i perform my services, but i don't test it for a really long time. The best test i does lasted 16 hours in simulator and everything works fine!
EDIT: That was tested on the simulator, but in phone doesnt work after the application has been terminated.
I let you a example:
// SomeService.h
#interface SomeService : Service
#end
// SomeService.m
#import "SomeService.h"
#implementation SomeService
// The method to override
- (void)doInBackground{
NSLog(#"Background time remaining = %.1f seconds", [UIApplication sharedApplication].backgroundTimeRemaining);
NSLog(#"Service running at %.1f seconds", [self getCurrentNetworkTime]);
}
// Your methods
- (long) getCurrentNetworkTime{
return ([[NSDate date] timeIntervalSince1970]);
}
#end
And in your app delegate or where you need to raise the service, you write the next line:
Service myService = [[SomeService alloc] initWithFrequency: 60]; //execute doInBackground each 60 seconds
[myService startService];
And if you need to stop it:
[myService stopService];
May have explained more than necessary, but i want to keep it clear for anyone!
I hope its help and sorry for my english.
No, there is no equivalent to an Android Service. MansApps code does not work, at least not on iOS7. A call of [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask]; in the expiration handler will only return when the app comes back to the foreground, i.e., the call of [self startBackgroundTask]; will not be executed when the app stays in the background.
Basically it's impossible if your app doesn't implement any of the functionalities listed bellow. And they hardly investigate your app before upload it to the store, you need to justify the use of that permissions
This is what Apple say about that:
Implementing Long-Running Tasks
For tasks that require more execution time to implement, you must request specific permissions to run them in the background without their being suspended. In iOS, only specific app types are allowed to run in the background:
Apps that play audible content to the user while in the background, such as a music player app
Apps that record audio content while in the background
Apps that keep users informed of their location at all times, such as a navigation app
Apps that support Voice over Internet Protocol (VoIP)
Apps that need to download and process new content regularly
Apps that receive regular updates from external accessories
Apps that implement these services must declare the services they support and use system frameworks to implement the relevant aspects of those services. Declaring the services lets the system know which services you use, but in some cases it is the system frameworks that actually prevent your application from being suspended.
I found the best and standard solution:
Push notifications
(original post by Matthijs Hollemans, update by Ali Hafizji).
In iOS, apps can’t do a lot in the background. Apps are only allowed to do limited set of activities so battery life is conserved.
But what if something interesting happens and you wish to let the user know about this, even if they’re not currently using your app?
For example, maybe the user received a new tweet, their favorite team won the game, or their dinner is ready. Since the app isn’t currently running, it cannot check for these events.
Luckily, Apple has provided a solution to this. Instead of your app continuously checking for events or doing work in the background, you can write a server-side component to do this instead.
And when an event of interest occurs, the server-side component can send the app a push notification! There are three things a push notification can do:
Display a short text message
Play a brief sound
Set a number in a badge on the app’s icon
Tutorial link: http://maniacdev.com/2011/05/tutorial-ios-push-notification-services-for-beginners
Related
I'm trying to build a video calling app with Agora, I need to show acceptance screen like WhatsApp when user calling, if the app is exited i need to show calling screen when user is calling, I tried lot of thing but nothing works, I trying to do i flutter and but there is nothing much information on this,Please help me
First things first. You need to learn about some concepts before delving into your solution. Actually there isn't an out of the box solution.
You need to use a couple of things together:
Use push notifications to "wake up" your app:
https://pub.dev/packages/firebase_messaging
To start your app using push notifications refers to this post:
https://stackoverflow.com/a/48405551/4335775
Use CallKit (IOS) or ConnectionServices (Android) to show the upcoming call screen. By the day of this answer there are only a few packages to handle these things, here is one that can handle both platforms:
https://pub.dev/packages/flutter_callkeep
If you want a completely different thing and need to run some background process, there are bunch whole of things you should know first. I suggest beginning here: https://flutter.dev/docs/development/packages-and-plugins/background-processes
Here is a usefull package to work with background processes that should be constantly running:
https://pub.dev/packages/background_fetch
Currently there are two packages which provides integration for agora.io:
https://pub.dev/packages/agora_rtc_engine (for Agora Real Time Communication, vídeo call included)
https://pub.dev/packages/agora_rtm for real-time messaging
I hope this can help you.
You can try WorkManager plugin.
You can register an call back function to the os when the app is closed.
const myTask = "syncWithTheBackEnd";
void main() {
Workmanager.initialize(callbackDispatcher);
Workmanager.registerOneOffTask(
"1",
myTask, //This is the value that will be returned in the callbackDispatcher
initialDelay: Duration(minutes: 5),
constraints: WorkManagerConstraintConfig(
requiresCharging: true,
networkType: NetworkType.connected,
),
);
runApp(MyApp());
}
void callbackDispatcher() {
Workmanager.executeTask((task) {
switch (task) {
case myTask:
print("this method was called from native!");
break;
case Workmanager.iOSBackgroundTask:
print("iOS background fetch delegate ran");
break;
}
//Return true when the task executed successfully or not
return Future.value(true);
});
}
Maybe this can help you.
The complete article medium article
I am using Delphi 10.2 to create Android app that uses Rest components to read returning data from post form. When I press on a button to load the data it load them normally after about 3 seconds freeze. The problem if the user try to click (or touch) any control on the form the app exit immediately after the 3 seconds freeze but if the user did not touch the app the data was loaded normally !
What is the reason for that and how I should fix it ?
The code I use for the button is
RESTRequest1.Execute;
I use 3 components RESTClient , RESTRequest and RESTResponse
and here is the code I use to get the data:
procedure TfrmMain.RESTRequest1AfterExecute(Sender: TCustomRESTRequest);
var
return_response: string;
begin
if RESTResponse1.StatusCode = 200 then begin
//fill years
return_response := RESTResponse1.Content;
memo1.text := return_response;
end;
end.
On mobile platforms you should always use ExecuteAsync because it does not run in the same thread as the UI. Execute instead runs on the same thread as the UI so it freezes while the request is processing. Android closes the app if it is not responsive (= freezed) after some seconds, and this is your problem!
To be more precise, here's the doc:
The use of the ExecuteAsync method is strongly recommended on mobile
platforms. iOS (and likely Android) will terminate an application if
it considers the main thread to be unresponsive, i.e. if a running
request takes more than a second or two to return
You can find more info here.
The function ExecuteAsync, as you can see in the doc, has an useful parameter which takes an anonymous procedure. The code of this procedure will be called once the ExecuteAsync has finished his task. Here's an example:
RESTRequest1.ExecuteAsync(
procedure
begin
ShowMessage('Finished!');
end;);
This is very easy and also you don't need to type the other parameters since they alrady have a value by default. Again, if you look at the doc you'll see for example ASynchronized: Boolean = True;, so setting the second parameter after the anonymous proc to True would be not relevant.
On our application there's a service that is normally started during Application.OnCreate (directly calling context.startService) and also later on via AlarmManager (refactor is in progress to migrate some of its work to JobScheduler).
Our application also have a BroadcastReceiver that gets launched with its direct intent.
Given the new limitations in Android Oreo (https://developer.android.com/about/versions/oreo/android-8.0-changes.html) we're having an issue as follows:
app/process is in background/dead
BroadcastReceiver gets fired by the OS
Application.onCreate() executes before the BroadcastReceiver
Application.onCreate() code tries to run the Service
this leads to crash with "IllegalStateException: Not allowed to start service Intent".
I'm aware of the new recommended ways of launching a Service as answered by CommonsWare here https://stackoverflow.com/a/44505719/906362, but for this specific case, I simply want to have if(process in foreground) { startService }. I'm currently using the following method and it seems to work:
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static boolean isProcessInForeground_V21(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.AppTask> tasks = am.getAppTasks();
return tasks.size() > 0;
}
But I can't find the exact checks Android Oreo is doing (I got as far as here https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ContextImpl.java on the startServiceCommon method, but from there requireForeground flag seems to go to some native implementation)
So my question:
For the specific purpose of Android Oreo new limitations, how to check if my process is foreground before calling startService?
To continue your investigation: (TL;DR: see between horizontal lines at the bottom)
Disclaimer, I don't know too much about Android, I just like digging in the source code.
Note: you can also navigate the code in Android Studio if you jump to file instead of class:
or searching for text in Project and Libraries.
IActivityManager is defined by AIDL, that's why there are no sources for it:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/app/IActivityManager.aidl#145
Based on how AIDL needs to be implemented I found that ActivityManagerService extends IActivityManager.Stub (God bless Google indexing).
Note I also found this, which might be an interesting read if you're really interested how things work internally.
https://programmer.group/android-9.0-source-app-startup-process.html
ActivityManagerService sources reveal that in Oreo startService is forwarded to ActiveServices which is located in the same package.
Assuming we're looking for an exception like this:
java.lang.IllegalStateException: Not allowed to start service Intent {...}: app is in background uid UidRecord{af72e61 u0a229 CAC bg:+3m52s273ms idle procs:1 seq(0,0,0)}
we have to continue down the rabbit hole: requireForeground gets assigned to fgRequired parameter and the message is here. The condition to allow this depends on the start mode returned by ActivityManagerService.getAppStartModeLocked(packageTargetSdk = 26 or greater, disabledOnly = false, forcedStandby = false).
There are 4 start modes:
APP_START_MODE_NORMAL (needs to be different than this, i.e. !=)
APP_START_MODE_DELAYED (this is ok, i.e. return null)
APP_START_MODE_DELAYED_RIGID
APP_START_MODE_DISABLED
Ephemeral apps will immediately return APP_START_MODE_DISABLED, but assuming this is a normal app, we end up in appServicesRestrictedInBackgroundLocked.
Note: this is where some of the whitelist mentioned in https://stackoverflow.com/a/46445436/253468 is decided.
Since all branches but last return APP_START_MODE_NORMAL, this redirects to appRestrictedInBackgroundLocked where we find our most likely suspect:
int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Apps that target O+ are always subject to background check
if (packageTargetSdk >= Build.VERSION_CODES.O) {
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
So the reason for denial is simply targeting O. I think the final answer to your question of how the OS decides if your app is foreground or background is this condition in getAppStartModeLocked
UidRecord uidRec = mActiveUids.get(uid);
if (uidRec == null || alwaysRestrict || uidRec.idle) {
My guess is that a missing record means it's not running (but then how is it starting a service?!), and idle means it's backgrounded. Notice that in my exception message the UidRecord is saying that it's idle and has been backgrounded for 3m52s.
I peeked into your getAppTasks and it's based on TaskRecord.effectiveUid, so I'm guessing that's quite close to listing UidRecords for your app.
Not sure if this helps, but I'll post it anyway, so if anyone wants to investigate more, they have more info.
As per documentation for app shortcuts
Rate Limiting
When using the setDynamicShortcuts(), addDynamicShortcuts(), or
updateShortcuts() methods, keep in mind that you might only be able to
call these methods a specific number of times in a background app, an
app with no activities or services currently in the foreground. In a
production environment, you can reset this rate limiting by bringing
your app to the foreground.
What is rate limiting in concern with app shortcuts? when isRateLimitingActive() should be used?
Looking at the source code it seems that the isRateLimitingActive() method returns false if you do not have any remaining calls left to the ShortcutManager API (hence the "0"). I guess rate limiting is needed because the API is resource intensive. I can imagine that at least the following will happen if you update a shortcut:
The launcher app (and other listeners) needs to be notified and starts updating it's UI or whatever is needed (depends on the launcher);
The system needs to store the new dynamic shortcut information;
You could use this method to find out if a call to setDynamicShortcuts(), addDynamicShortcuts() or updateShortcuts() will succeed before even trying to do so.
Source:
/**
* Return {#code true} when rate-limiting is active for the caller application.
*
* <p>See the class level javadoc for details.
*
* #throws IllegalStateException when the user is locked.
*/
public boolean isRateLimitingActive() {
try {
return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId())
== 0;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
Bonus: setDynamicShortcuts(), addDynamicShortcuts() or updateShortcuts() return false if they did not succeed due to Rate Limiting.
The recommended maximum number of shortcuts is 4, although it is possible to publish up to 5. You can read more here.
I am developing an application for my friend who is in sales, this application will make phone calls one after another, as soon as one phone call gets disconnected, it will automatically make call to another number from the list. This list can be read from and xml data source or json or mongodb or even from excel sheet.
This could be an ios app that reads data from an end point and stores them and can initiate the call at any point and it wont stop until all the calls are made.
Next call will be made only after the first call has been finished.
I am thinking about using node based web app using google voice to trigger the chain.
I've no experience with ios / android apis but Im willing to work on that if it's a viable thing on that platform.
Note: what we're trying to avoid is whole process of
looking up the phone number.
touch hangup and then click for another phone number.
It should self trigger the next call as soon as current call gets disconnected.
Also we're trying to avoid any paid services like twillo.
Thanks in advance :)
for IOS, you could use CTCallCenter
self.callCenter = [[CTCallCenter alloc] init];
self.callCenter.callEventHandler = ^(CTCall *call){
if ([call.callState isEqualToString: CTCallStateConnected])
{
//NSLog(#"call stopped");
}
else if ([call.callState isEqualToString: CTCallStateDialing])
{
}
else if ([call.callState isEqualToString: CTCallStateDisconnected])
{
//NSLog(#"call played");
}
else if ([call.callState isEqualToString: CTCallStateIncoming])
{
}
};
Download phone list, loop inside phone list, make a call, listening for CTCallCenter and appdelegate's Event, detect user have finish last call, our app active again, then make the next call.
Or you can try in Demo here !