Procesamiento de fondo Delphi Firemonkey iOS

Descripción del problema:

Actualmente estoy desarrollando y Android y la aplicación iOS con Delphi XE7 Firemonkey. Esta aplicación necesita trabajar sin conexión para que el usuario pueda trabajar con ella y cuando el teléfono se enciende la aplicación envía todo el trabajo a los servidores incluso si la aplicación está en segundo plano o el teléfono está en espera.

Solución de trabajo de Android:

Como me enteré, los objetos TThread y TTimer no funcionan ya que dejan de ejecutarse una vez que la aplicación va a fondo o el teléfono pasa a estar parado.

Terminé encontrando esta biblioteca para Android que funciona como un objeto TTimer, pero todavía funciona cuando la aplicación va al fondo o el teléfono está en espera.

Https://github.com/dkstar88/AsyncTask

Lamentablemente, no funciona en iOS, ya que se comporta como TThread y TTimer y se detiene una vez que la aplicación va a fondo.

IOS intentó soluciones

He intentado varios enfoques, pero he encontrado que mucha gente tiene mucha información sobre Android, pero mucho menos para iOS.

Lo primero que intenté fue añadir en el archivo plist los permisos necesarios para decir a iOS que quiero hacer algo en segundo plano. He intentado la propiedad "fetch".

Esto por sí solo no funciona con TTimer, TThread o AsyncTask probado en Android.

Por lo tanto, pensé que tienes que decir a iOS que desea hacer algo en segundo plano y qué procedimiento o código es que desea que iOS para ejecutar cuando en backgroud.

El código más promsinig que encontré fue en este enlaces (ver comentarios):

Http://qc.embarcadero.com/wc/qcmain.aspx?d=128968

Aquí está el código que usé, adaptado del enlace anterior. Simplemente agrega una línea a un campo de memo para la prueba.

unit uBackgroundiOS_2; interface uses iOSapi.UIKit,Macapi.ObjCRuntime,Macapi.ObjectiveC,iOSapi.CocoaTypes,FMX.Platform.iOS,iOSapi.Foundation,Macapi.Helpers, FMX.Memo, system.SysUtils; const UIBackgroundFetchResultNewData:NSUInteger = 0; UIBackgroundFetchResultNoData:NSUInteger = 1; UIBackgroundFetchResultFailed:NSUInteger = 2; UIApplicationBackgroundFetchIntervalMinimum:NSTimeInterval = 0; UIApplicationBackgroundFetchIntervalNever:NSTimeInterval = -1; type id=Pointer; IMP = function( self : id; cmd : SEL; Param1 : NSUInteger ) : id; cdecl; Var UIApp : UIApplication; memo: TMemo; function imp_implementationWithBlock( block :Pointer ) : IMP; cdecl; external libobjc name _PU + 'imp_implementationWithBlock'; function imp_removeBlock( anImp : IMP ) : integer; cdecl; external libobjc name _PU + 'imp_removeBlock'; function objc_addClass(Cls: Pointer): Integer; cdecl; external libobjc name _PU + 'objc_addClass'; procedure performFetchWithCompletionHandler(self:id; _cmd: SEL;application:id; handler:id); procedure Init(p_memo: Tmemo); implementation procedure Init(p_memo: Tmemo); var Res: Integer; begin { Info: use .\plist\BackgroundOPs.info.plist from Project Main Pfad and copy to Info.plist include the Keys: <key>UIBackgroundModes</key> <array> <string>fetch</string> <string>remote-notification</string> <string>newsstand-content</string> </array> } UIApp := TUIApplication.Wrap(TUIApplication.OCClass.sharedApplication); objc_msgSend((UIApp as ILocalObject).GetObjectId,sel_getUid('setMinimumBackgroundFetchInterval:'),UIApplicationBackgroundFetchIntervalMinimum); Res := class_addMethod( objc_getClass('DelphiAppDelegate') , sel_getUid('application:performFetchWithCompletionHandler:'), @performFetchWithCompletionHandler,'v@:@?'); memo := p_memo; end; procedure performFetchWithCompletionHandler(self:id; _cmd: SEL;application:id; handler:id); { Using your device you can fire application:performFetchWithCompletionHandler with the following steps: Put your app in the Background state. Lock your device and wait 5 minutes. (I know, it's a waste of time, get some coffee) Unlock your device, this is will fire the method. } var ahandlerimp: IMP; begin try // here your code max. 30 Sec. Memo.Lines.Add(FormatDateTime('hh:nn:ss', Now)); ahandlerimp := imp_implementationWithBlock(handler); ahandlerimp(self,_cmd,UIBackgroundFetchResultNewData); // return the Do- code imp_removeBlock(ahandlerimp); except end; end; end. 

Esto no funciona en mi iphone 4 con iOS 8.4.1. Dejé el teléfono en el soporte por un rato y cuando comprobé la aplicación el campo del memorándum estaba en blanco.

¿Alguna idea de lo que podría estar mal? Estoy un poco muerto aquí.

¡Muchas gracias!

PS: en mi post original pongo más enlaces y fuentes (incluyendo otros posts de stackoverflow) pero desafortunadamente solo dejé los dos más relevantes porque no tengo la 10 reputación necesaria para publicar más.


Mezclando el código de estos dos ejemplos hice la siguiente unidad:

Http://qc.embarcadero.com/wc/qcmain.aspx?d=128968 (ver comentarios) Objetivo de llamada Bloque de código C desde delphi

 unit uBackgroundiOS; interface uses fmx.platform.iOS,iOSapi.CocoaTypes, Macapi.ObjCRuntime, Macapi.ObjectiveC, iOSapi.UIKit; const UIBackgroundFetchResultNewData:NSUInteger = 0; UIBackgroundFetchResultNoData:NSUInteger = 1; UIBackgroundFetchResultFailed:NSUInteger = 2; UIApplicationBackgroundFetchIntervalMinimum:NSTimeInterval = 0; UIApplicationBackgroundFetchIntervalNever:NSTimeInterval = -1; type // copied from fmx.platform.iOS as it's on private declaration id = Pointer; SEL = Pointer; PUIApplication = Pointer; IMP = function( self : id; cmd : SEL; Param1 : NSUInteger ) : id; cdecl; function imp_implementationWithBlock( block :id ) : IMP; cdecl; external libobjc name _PU + 'imp_implementationWithBlock'; function imp_removeBlock( anImp : IMP ) : integer; cdecl; external libobjc name _PU + 'imp_removeBlock'; procedure performFetchWithCompletionHandler(self : id; _cmd : SEL; application: PUIApplication; handler : id ); procedure initializeBackgroundFetch; //to test if procedure is called in background var fecth_string_test: string; implementation procedure performFetchWithCompletionHandler(self : id; _cmd : SEL; application: PUIApplication; handler : id ); var ahandlerimp: IMP; begin //Code to perform fetch fecth_string_test := 'entered background code!!'; ahandlerimp := imp_implementationWithBlock( handler ); //Create c function for block ahandlerimp(self,_cmd, UIBackgroundFetchResultNewData); //Call c function, _cmd is ignored imp_removeBlock(ahandlerimp); //Remove the c function created two lines up end; procedure initializeBackgroundFetch; Var UIApp : UIApplication; Interval: Pointer; begin UIApp := TUIApplication.Wrap(TUIApplication.OCClass.sharedApplication); Interval := objc_msgSend((UIApp as ILocalObject).GetObjectId, sel_getUid('setMinimumBackgroundFetchInterval:')); // Interval); class_addMethod( objc_getClass('DelphiAppDelegate') , sel_getUid('application:performFetchWithCompletionHandler:'), @performFetchWithCompletionHandler, 'v@:@?' ); end; end. 

Este código no funciona. Llamo initializeBackgroundFetch cuando se inicia la aplicación. Estoy seguro de que estoy haciendo algo mal, pero no puedo imaginar lo que es.

También me di cuenta de mi aplicación no aparece en el fondo de permitir que las aplicaciones en la configuración del iPhone, debe aparecer? He añadido lo siguiente en el archivo info.plist:

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> [...] <key>UIBackgroundModes</key> <array> <string>fetch</string> <string>remote-notification</string> <string>newsstand-content</string> </array> </dict> </plist> 

¿Algunas ideas?

Solución de trabajo de iOS

Finalmente conseguí que funcionara bajo delphi 10 Seattle (necesitas esta versión, he intentado XE7 y no funciona, no he probado xe8). Estos son los pasos:

1- Proyecto> Opciones> Información de versión. Compruebe UIBackgroundModes que usted necesita (utilicé Fetch)

2- Este es el código que usé:

 unit uBackgroundiOS; interface uses fmx.platform.iOS,iOSapi.CocoaTypes, Macapi.ObjCRuntime, Macapi.ObjectiveC, iOSapi.UIKit; const UIBackgroundFetchResultNewData:NSUInteger = 0; UIBackgroundFetchResultNoData:NSUInteger = 1; UIBackgroundFetchResultFailed:NSUInteger = 2; UIApplicationBackgroundFetchIntervalMinimum:NSTimeInterval = 0; UIApplicationBackgroundFetchIntervalNever:NSTimeInterval = -1; type // copied from fmx.platform.iOS as it's on private declaration id = Pointer; SEL = Pointer; PUIApplication = Pointer; IMP = function( self : id; cmd : SEL; Param1 : NSUInteger ) : id; cdecl; function imp_implementationWithBlock( block :id ) : IMP; cdecl; external libobjc name _PU + 'imp_implementationWithBlock'; function imp_removeBlock( anImp : IMP ) : integer; cdecl; external libobjc name _PU + 'imp_removeBlock'; procedure performFetchWithCompletionHandler(self : id; _cmd : SEL; application: PUIApplication; handler : id ); procedure initializeBackgroundFetch; function objc_msgSend(theReceiver: Pointer; theSelector: Pointer): Pointer; cdecl; varargs; external libobjc name _PU + 'objc_msgSend'; //to test if procedure is called in background var fecth_string_test: string; implementation procedure performFetchWithCompletionHandler(self : id; _cmd : SEL; application: PUIApplication; handler : id ); var ahandlerimp: IMP; begin //Code to perform fetch HERE!!!! fecth_string_test := 'entered background code!!'; ahandlerimp := imp_implementationWithBlock( handler ); //Create c function for block ahandlerimp(self,_cmd, UIBackgroundFetchResultNewData); //Call c function, _cmd is ignored imp_removeBlock(ahandlerimp); //Remove the c function created two lines up end; procedure initializeBackgroundFetch; Var UIApp: UIApplication; begin UIApp := TUIApplication.Wrap(TUIApplication.OCClass.sharedApplication); objc_msgSend((UIApp as ILocalObject).GetObjectId, sel_getUid('setMinimumBackgroundFetchInterval:'), UIApplicationBackgroundFetchIntervalMinimum); class_addMethod(objc_getClass('DelphiAppDelegate') , sel_getUid('application:performFetchWithCompletionHandler:'), @performFetchWithCompletionHandler, 'v@:@?'); end; end. 
  • Servicio de notificación de Google / Apple Push (APNS / GCM)
  • Planificación de un complemento de cámara cordova con imagen de superposición transparente
  • ¿Cómo puede configurar TI SensorTag para recopilar datos para su posterior recuperación?
  • ¿Por qué mis Botones de Unidad necesitan varios grifos para funcionar finalmente?
  • Compruebe la conexión a Internet desde Unity
  • Generación de iconos de iOS y Android en Cordova / PhoneGap
  • Aplicación web para Android e iOS envuelta en una aplicación nativa
  • ¿La mejor manera de hacer aplicaciones multiplataforma?
  • Compartir traducciones entre servidor java, ios y android
  • Cómo guardar la entrada en el calendario nativo (Android e iOS) en reaccionar nativo?
  • ¿Qué tan seguras son las API de GeoLocation en dispositivos móviles?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.