Uma estrutura Swift inspirada na sessão de NSOPERAÇÕES Avançadas do WWDC 2015. Anteriormente conhecido como operações , desenvolvido por @danthorpe com muita ajuda de nossa comunidade fantástica.
| Recurso | Onde encontrar |
|---|---|
| Vídeo de sessão | desenvolvedor.apple.com |
| Documentação de referência antiga, mas mais completa | docs.danthorpe.me/operations |
| Documentos de referência atualizados, mas ainda não completos | procedimento.kit.run/development |
| Guia de programação | operações.readme.io |
O ProcedureKit suporta todas as plataformas atuais da Apple. Os requisitos mínimos são:
A versão atual lançada do ProcedureKit (5.1.0) suporta Swift 4.2+ e Xcode 10.1. A filial development é Swift 5 e Xcode 10.2 Compatível.
O ProcedureKit é uma estrutura "multi-module" (não se preocupe no Google, acabei de inventar). O que quero dizer é que o projeto Xcode possui vários alvos/produtos, cada um dos quais produz um módulo SWIFT. Alguns desses módulos são de plataforma cruzada, outros são dedicados, por exemplo, ProcedureKitNetwork vs ProcedureKitMobile .
Consulte o guia de instalação do ProcedureKit.
Procedure é uma subclasse Foundation.Operation . É uma classe abstrata que deve ser subclassificada.
import ProcedureKit
class MyFirstProcedure : Procedure {
override func execute ( ) {
print ( " Hello World " )
finish ( )
}
}
let queue = ProcedureQueue ( )
let myProcedure = MyFirstProcedure ( )
queue . add ( procedure : myProcedure )Os principais pontos aqui são:
Procedure da subclasseexecute , mas não chame super.execute()finish() após o trabalho , ou se o procedimento for cancelado. Isso pode ser feito de forma assíncrona.ProcedureQueue . Os observadores são anexados a uma subclasse Procedure . Eles recebem retornos de chamada quando os eventos do ciclo de vida ocorrem. Os eventos do ciclo de vida são: Anexos , executaram , executaram, executaram , cancelaram , adicionarão uma nova operação , adicionaram uma nova operação , terminarão e terminarão .
Esses métodos são definidos por um protocolo; portanto, as classes personalizadas podem ser gravadas para estar em conformidade com vários eventos. No entanto, existem métodos baseados em blocos para adicionar observadores mais naturalmente. Por exemplo, para observar quando um procedimento termina:
myProcedure . addDidFinishBlockObserver { procedure , errors in
procedure . log . info ( message : " Yay! Finished! " )
} O Framework também fornece BackgroundObserver , TimeoutObserver e NetworkObserver .
Veja o wiki em [[observadores | observadores]] para obter mais informações.
As condições são anexadas a uma subclasse Procedure . Antes de um procedimento estar pronto para executar, ele avaliará de maneira assíncrona todas as suas condições. Se alguma condição falhar, ela termina com um erro em vez de executar. Por exemplo:
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()
}As condições podem ser mutuamente exclusivas. Isso é semelhante a uma trava sendo mantida impedindo outras operações com a mesma exclusão sendo executada.
A estrutura fornece as seguintes condições: AuthorizedFor , BlockCondition , MutuallyExclusive , NegatedCondition , NoFailedDependenciesCondition Condição, SilentCondition e UserConfirmationCondition (em ProcedureKitmobile ).
Veja o wiki em [[Condições | Condições]] ou o antigo Guia de Programação sobre Condições | Para mais informações.
Um recurso representa a capacidade do aplicativo de acessar o dispositivo ou as habilidades da conta de usuário, ou potencialmente qualquer tipo de recurso fechado. Por exemplo, serviços de localização, contêineres de kits em nuvem, calendários etc. ou um serviço da web. O CapabiltiyProtocol fornece um modelo unificado para:
GetAuthorizationStatusProcedure ,AuthorizeCapabilityProcedureAuthorizedFor .Por exemplo:
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 fornece os seguintes recursos: Capability.CloudKit e Capability.Location .
Nas operações (uma versão anterior dessa estrutura), existia mais funcionalidades (calendário, saúde, fotos, catálogo de endereços, etc.), e ainda estamos considerando como oferecê -los no ProcedureKit .
Consulte o wiki em [[recursos | recursos]] ou o antigo guia de programação sobre recursos para obter mais informações.
Procedure possui sua própria funcionalidade interna de registro exposta por meio de uma propriedade log :
class LogExample : Procedure {
override func execute ( ) {
log . info ( " Hello World! " )
finish ( )
}
}Consulte o Guia de Programação para obter mais informações sobre o registro e o suporte a estruturas de log de terceiros.
Freqüentemente, os procedimentos precisam de dependências para executar. Como é típico com aplicativos assíncronos/baseados em eventos, essas dependências podem não ser conhecidas no momento da criação. Em vez disso, eles devem ser injetados após a inicialização do procedimento, mas antes de ser executado. O ProcedureKit suporta isso através de um conjunto de protocolos e tipos que funcionam juntos. Achamos que esse padrão é ótimo, pois incentiva a composição de pequenos procedimentos de finalidade única. Estes podem ser mais fáceis de testar e potencialmente ativar a maior reutilização. Você encontrará injeção de dependência usada e incentivada ao longo desta estrutura.
De qualquer forma, em primeiro lugar, um valor pode estar pronto ou pendente. Por exemplo, quando um procedimento é inicializado, ele pode não ter todas as suas dependências, portanto, elas estão em um estado pendente. Espero que eles fiquem prontos quando for executado.
Em segundo lugar, se um procedimento estiver adquirindo a dependência exigida por outro procedimento, ele poderá ter sucesso ou falhar com um erro. Portanto, existe um tipo de resultado simples que suporta isso.
Em terceiro lugar, existem protocolos para definir as propriedades input e output .
InputProcedure associa um tipo Input . Uma subclasse Procedure pode estar em conformidade com isso para permitir a injeção de dependência. Observe que apenas uma propriedade input é suportada, portanto, crie tipos de estrutura intermediária para conter várias dependências. Obviamente, a propriedade input é do tipo de valor pendente.
OutputProcedure expõe o tipo Output associado por meio de sua propriedade output , que é um tipo de resultado pendente.
Reunir tudo isso é um conjunto de APIs no InputProcedure , que permite encadear dependências. Assim:
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 ) No exposto, supõe -se que o tipo Input corresponda ao tipo Output , neste caso, CLLocation . No entanto, também é possível usar um fechamento para massagear o tipo de saída para o tipo de entrada necessário, por exemplo:
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 ) Ok, então o que aconteceu? Bem, a API injectResult tem uma variante que aceita um fechamento à direita. O fechamento recebe o valor de saída e deve retornar o valor de entrada (ou lançar um erro). Portanto, { $0.speed } retornará a propriedade de velocidade da instância CLLocation do usuário.
A coisa importante a ser observada aqui é que esse fechamento é executado de maneira síncrona. Então, é melhor não colocar nada oneroso nele. Se você precisar fazer mapeamentos de dados mais complexos, consulte TransformProcedure e AsyncTransformProcedure .
Consulte o Guia de Programação sobre os resultados de injeção para obter mais informações.