Un marco Swift inspirado en la sesión avanzada de NSOPERATIONS WWDC 2015. Anteriormente conocido como operaciones , desarrolladas por @Danthorpe con mucha ayuda de nuestra fantástica comunidad.
| Recurso | Donde encontrarlo |
|---|---|
| Video de sesión | desarrollador.apple.com |
| Documentación de referencia antigua pero más completa | docs.danthorpe.me/operations |
| Documentos de referencia actualizados pero aún no completos | procedimiento.kit.run/develogment |
| Guía de programación | operaciones.readme.io |
ProcedureKit admite todas las plataformas de Apple actuales. Los requisitos mínimos son:
La versión actualizada actual de ProcedureKit (5.1.0) admite Swift 4.2+ y Xcode 10.1. La rama development es SWIFT 5 y Xcode 10.2 compatible.
ProcedureKit es un marco de "múltiplo múltiples" (no se moleste en Google, simplemente lo inventé). Lo que quiero decir es que el proyecto XCode tiene múltiples objetivos/productos, cada uno de los cuales produce un módulo Swift. Algunos de estos módulos son multiplataforma, otros están dedicados, por ejemplo, ProcedureKitNetwork vs ProcedureKitMobile .
Consulte la Guía del Procedimiento de Instalación.
Procedure es una Foundation.Operation . Es una clase abstracta que debe ser subclase.
import ProcedureKit
class MyFirstProcedure : Procedure {
override func execute ( ) {
print ( " Hello World " )
finish ( )
}
}
let queue = ProcedureQueue ( )
let myProcedure = MyFirstProcedure ( )
queue . add ( procedure : myProcedure )Los puntos clave aquí son:
Procedure de subclaseexecute pero no llamar a super.execute()finish() después de que se realice el trabajo , o si el procedimiento se cancela. Esto podría hacerse de manera asincrónica.ProcedureQueue . Los observadores están conectados a una subclase Procedure . Reciben devoluciones de llamada cuando ocurren eventos del ciclo de vida. Los eventos del ciclo de vida son: Did , se adjuntarán, se ejecutará , se ejecutaron , canceló , agregará una nueva operación , agregará una nueva operación , terminará y terminó .
Estos métodos están definidos por un protocolo, por lo que se pueden escribir clases personalizadas para ajustarse a múltiples eventos. Sin embargo, existen métodos basados en bloques para agregar observadores de manera más natural. Por ejemplo, observar cuándo termina un procedimiento:
myProcedure . addDidFinishBlockObserver { procedure , errors in
procedure . log . info ( message : " Yay! Finished! " )
} El marco también proporciona BackgroundObserver , TimeoutObserver y NetworkObserver .
Consulte el wiki on [[Observadores | Observadores]] para obtener más información.
Las condiciones se unen a una subclase Procedure . Antes de que un procedimiento esté listo para ejecutar, evaluará asincrónicamente todas sus condiciones. Si alguna condición falla, termina con un error en lugar de ejecutar. Por ejemplo:
myProcedure . add ( condition : BlockCondition {
// procedure will execute if true
// procedure will be ignored if false
// procedure will fail if error is thrown
return trueOrFalse // or throw AnError()
}Las condiciones pueden ser mutuamente excluyentes. Esto es similar a un bloqueo que se celebra que evita otras operaciones con la misma exclusión que se ejecuta.
El marco proporciona las siguientes condiciones: AuthorizedFor , BlockCondition , MutuallyExclusive , NegatedCondition , NoFailedDependenciesCondition , SilentCondition y UserConfirmationCondition (en procedimientokitmobile ).
Consulte el wiki on [[condiciones | condiciones]], o la antigua guía de programación sobre las condiciones | Para más información.
Una capacidad representa la capacidad de la aplicación para acceder a las capacidades de la cuenta del dispositivo o del usuario, o potencialmente cualquier tipo de recurso cerrado. Por ejemplo, servicios de ubicación, contenedores de kits en la nube, calendarios, etc. o un servicio web. El CapabiltiyProtocol proporciona un modelo unificado para:
GetAuthorizationStatusProcedure ,AuthorizeCapabilityProcedureAuthorizedFor .Por ejemplo:
import ProcedureKit
import ProcedureKitLocation
class DoSomethingWithLocation : Procedure {
override init ( ) {
super . init ( )
name = " Location Operation "
add ( condition : AuthorizedFor ( Capability . Location ( . whenInUse ) ) )
}
override func execute ( ) {
// do something with Location Services here
finish ( )
}
} ProcedureKit proporciona las siguientes capacidades: Capability.CloudKit y Capability.Location .
En las operaciones , (una versión anterior de este marco), existía más funcionalidad (calendario, salud, fotos, libreta de direcciones, etc.), y todavía estamos considerando cómo ofrecerlos en procedimientokit .
Consulte el wiki on [[capacidades | capacidades]], o la antigua guía de programación sobre capacidades para obtener más información.
Procedure tiene su propia funcionalidad de registro interna expuesta a través de una propiedad log :
class LogExample : Procedure {
override func execute ( ) {
log . info ( " Hello World! " )
finish ( )
}
}Consulte la Guía de programación para obtener más información sobre el registro y el soporte de los marcos de registro de terceros.
A menudo, los procedimientos necesitarán dependencias para ejecutar. Como es típico con las aplicaciones asincrónicas/basadas en eventos, estas dependencias podrían no ser conocidas en el momento de la creación. En su lugar, deben inyectarse después de que se inicialice el procedimiento, pero antes de ejecutarse. ProcedureKit lo admite a través de un conjunto de protocolos y tipos que funcionan juntos. Creemos que este patrón es excelente, ya que fomenta la composición de pequeños procedimientos de un solo propósito. Estos pueden ser más fáciles de probar y potencialmente permitir una mayor reutilización. Encontrará la inyección de dependencia utilizada y alentada en todo este marco.
De todos modos, en primer lugar, un valor puede estar listo o pendiente. Por ejemplo, cuando se inicializa un procedimiento, es posible que no tenga todas sus dependencias, por lo que están en un estado pendiente. Esperemos que se estén listos cuando se ejecute.
En segundo lugar, si un procedimiento está adquiriendo la dependencia requerida por otro procedimiento, puede tener éxito o puede fallar con un error. Por lo tanto, hay un tipo de resultado simple que admite esto.
En tercer lugar, hay protocolos para definir las propiedades input y output .
InputProcedure asocia un tipo Input . Una subclase Procedure puede ajustarse a esto para permitir la inyección de dependencia. Tenga en cuenta que solo una propiedad input es compatible, por lo tanto, crea tipos de estructura intermedia para contener múltiples dependencias. Por supuesto, la propiedad input es un tipo de valor pendiente.
OutputProcedure expone el tipo asociado Output a través de su propiedad output , que es un tipo de resultado pendiente.
Reunirlo todo es un conjunto de API en InputProcedure que permite encadenar dependencias juntas. Como esto:
import ProcedureKitLocation
// This class is part of the framework, it
// conforms to OutputProcedure
let getLocation = UserLocationProcedure ( )
// Lets assume we've written this, it
// conforms to InputProcedure
let processLocation = ProcessUserLocation ( )
// This line sets up dependency & injection
// it automatically handles errors and cancellation
processLocation . injectResult ( from : getLocation )
// Still need to add both procedures to the queue
queue . add ( procedures : getLocation , processLocation ) En lo anterior, se supone que el tipo Input coincidía con el tipo Output , en este caso, CLLocation . Sin embargo, también es posible usar un cierre para masajear el tipo de salida al tipo de entrada requerido, por ejemplo:
import ProcedureKitLocation
// This class is part of the framework, it
// conforms to OutputProcedure
let getLocation = UserLocationProcedure ( )
// Lets assume we've written this, it
// conforms to InputProcedure, and
// requires a CLLocationSpeed value
let processSpeed = ProcessUserSpeed ( )
// This line sets up dependency & injection
// it automatically handles errors and cancellation
// and the closure extracts the speed value
processLocation . injectResult ( from : getLocation ) { $0 . speed }
// Still need to add both procedures to the queue
queue . add ( procedures : getLocation , processLocation ) Bien, ¿qué acaba de pasar? Bueno, la API injectResult tiene una variante que acepta un cierre final. El cierre recibe el valor de salida y debe devolver el valor de entrada (o lanzar un error). Entonces, { $0.speed } devolverá la propiedad de velocidad de la instancia CLLocation del usuario.
La clave a tener en cuenta aquí es que este cierre se ejecuta sincrónicamente. Por lo tanto, es mejor no poner nada oneroso en él. Si necesita hacer asignaciones de datos más complejas, consulte TransformProcedure y AsyncTransformProcedure .
Consulte la Guía de programación sobre los resultados de inyección de más información.