Table of Contents
En el artículo de hoy me gustaría dar una pequeña introducción a Combine y mostrar sus diferencias con RxSwift, y la mejor manera de empezar es mostrando la definición de Combine proporcionada por Apple:
A unified, declarative API for processing values overtime
Seguro que te suena familiar, y es que como ya hemos avanzado, en este artículo vamos a hablar de algunas características y diferencias que comparten Combine
y RxSwift
, empezando por Combine y sus características principales.
Combine vs RxSwift: Introducción a Combine
Lo primero a destacar son sus tres cualidades
- Generic.
- Type safe.
- Composition first.
Apple nos cuenta en su keynote sobre Combine que los conceptos principales son sencillos y fáciles de entender pero combinados unos con otros se pueden hacer cosas más complejas e interesantes.
Dentro de combine encontraremos:
- Publishers
- Subscribers
- Operators
Publishers
Son la parte más declarativa de la API de Combine. Definen cómo son producidos los valores o los errores.
Son Value type, en Swift, Structs.
Los Publishers permiten la subscripción de Subscribers, esto nos permitirá recibir los valores cuando estos se emitan.
protocol Publishers {
associatedtype Output
associatedtype Failure: Error
func subscribe<S: Subscriber>(_ subscriber: S)
where S.input == Output, S.Failure == Failure
}
Subscribers
Los subscribers son la otra cara de la moneda de los publishers, estos son los que reciben valores por el stream, y dado que estos valores pueden acabar mutando son de tipo Reference type como las clases.
protocol Subscriber {
associatedtype Input
associatedtype Failure: Error
func receive(subscription: Subscription)
func receive(_ input: Input) -> Subscribers.Demand
func receive(completion: Subscribers.Completion<Failure>)
}
Me recuerda a algo…
Con estos conceptos básicos de Combine podemos deducir que es muy parecido a otros frameworks ya existentes de programación reactiva como RxSwift o ReactiveCocoa. Para este artículo voy a centrarme en la comparación con RxSwift.
Si estás familiarizado en RxSwift puedes ver que los Publishers
son los Observables
y los Subscribers
los Observers
, tienen diferentes nombres pero ambos funcionan con la misma mecánica.
Un Publisher expone valores que pueden cambiar y un Subscriber
se suscribe para recibir todos los cambios.
Combine vs RxSwift: Diferencias
Compatibilidad actual
Para empezar, Combine no dispone de backward compatibility, es decir, no está disponible en sistemas anteriores a iOS 13 / macOS Catalina dado que necesita el nuevo proceso de runtime introducido para funcionar, y tampoco hay planes futuros de proporcionar esta compatibilidad a sistemas anteriores.
Por el contrario RxSwift funciona en iOS 8 y posteriores.
Gestión de errores
Si nos fijamos en la especificación del protocolo de Observable
veremos las primeras diferencias.
En Combine cada Publisher
necesita especificar un error type, mientras que en RxSwift Observables
no usan un error type, pueden lanzar (throw
) cualquier tipo de error en cualquier momento. Esto hace que en RxSwift los Observables
sean más fáciles de usar, dado que no tienes que pensar qué tipos de error deben ser lanzados. Sin embargo esto significa que debes tener cuidado de gestionar los errores por tu cuenta, el compilador no te ayudará si te olvidas de alguno, con Combine al ser más estricto esto no sucedería.
En Combine si tu stream no lanza errores de ningún tipo puedes marcarlo como tipo Never
.
Podríamos entender esta especificación explícita de los tipos de error de Combine como una capa más de seguridad pero que añade complicación al código.
En RxSwift se puede lograr algo similar usando el tipo Result
, este añade un tipo de error adicional pero tu stream no pararía después de lanzarse un error, o sino usando un stream específico para la gestión de errores.
Sobre la gestión de errores hay una diferencia más, Combine separa las funciones como throwing
o non-throwing
. Por ejemplo, hay operadores que tienen una versión que pueden lanzar errores y la versión sin el throw
.
Performance
En cuanto a performance no podemos negar que RxSwift usado correctamente es un framework muy optimizado, pero Combine ha sido construido por los ingenieros de Apple fijándose 100% en su performance.
Tal y como podemos ver en el blog de Flawless iOS han hecho una comparativa ejecutando dos bloques de código que realizan el mismo trabajo, uno en Combine y otro en RxSwift y podemos ver que la performance en tiempo de Combine resulta ganadora.
Uno de los principales motivos de esta mejora es debido a que RxSwift usa Swift como su lenguaje principal y necesita hacer muchos sinks
en las capas de bajo nivel del framework, esto afecta a su rendimiento. En el otro lado Combine es un proyecto de código cerrado, y puede no estar desarrollado necesariamente en Swift pero sí exponer una interfaz pública en Swift. Apple puede usar muchas optimizaciones de rendimiento que no están disponibles para los programadores fuera de la empresa.
Operadores
Combine y RxSwift tienen muchos operadores que realizan el mismo trabajo o muy similar, sin embargo el naming es diferente. En muchos casos en Combine hay operadores con nombre diferentes que en RxSwift pero que tienen su equivalente en cuanto a funcionalidad.
Por suerte la tabla de guía creada por Shai Mishali nos puede ayudar a relacionar todos esos operadores con namings diferentes.
Open source
Otro hecho importante es que Combine no es open source, y es bastante lógico si nos fijamos en todos los otros frameworks de Apple, los cuales no son open source. Como hemos comentado, Combine es muy posible que use features del propio sistema, que no están disponibles para todos. Del mismo modo que aunque fuera open source ellos saben más que nadie cómo lidiar con los problemas que puedan surgir si están relacionados con su core.
DisposeBag
Seguramente ya estáis familiarizados con el patrón de gestión de memoria de RxSwift, DisposeBag. En lugar de guardar cada subscripción separadamente y finalizar con todas ellas cuando el controller o class se desinicializa, simplemente escribimos .disposed(by: disposeBag)
y el framework se encargará de detectar el deinit
y deshacerse de todas esas dependencias de Rx.
No hay nada similar a DisposeBag
en Combine.
UI Framework
Dentro del framework reactivo necesitamos algún modo de vincular los flujos reactivos a la vista y vice versa. RxCocoa
es la solución para RxSwift
.
Combine no dispone de un framework concreto para realizar estos binds, disponemos del método assign
para vincular un stream
a un key path y a una propiedad de la vista.
publisher.assign(to: \.text, on: label)
No hay ningún modo (aún) en Combine de obtener un stream de un componente de UI, de modo que si podemos hacerlo con RxCocoa
Conclusión
Mi opinión dejando de lado las diferencias entre ambos frameworks, es que es muy positivo para Swift que aparezcan más herramientas de programación reactiva. Es muy posible que la aparición de Combine proporcione a RxSwift más popularidad, y en los próximos años con la madurez que irá adquiriendo Combine, quizá en algún punto determinado valdrá la pena saltar a este framework de Apple. Por ahora, y dado que, como hemos comentado, no tiene compatibilidad con versiones anteriores, podemos seguir disfrutando de RxSwift.
Author
-
More than 10 years on software development field, and working on mobile development since 2013. Experience creating apps from scratch and working on big applications with several dependencies. Used to work with the latest technologies and take care of architectural decisions. Able to lead small iOs teams, always from the tech side.
Ver todas las entradas