Blog de Desarrollo en Swift para Plataformas Apple

Carthage – Instalación y Gestión de Dependencias

Get real time updates directly on you device, subscribe now.

En esta ocasión aprenderemos sobre el gestor de dependencias Carthage, una muy buena opción y de las mejores con las que contamos en el desarrollo de aplicaciones para plataformas Apple.

En este sitio ya hemos hablado sobre el gestor de dependencias CocoaPods y ahora es el turno de Carthage que, al igual que su competencia, nos viene a ayudar con la gestión de aquellas dependencias que puedan necesitar nuestras aplicaciones y a su vez con las dependencias de estas.

Las motivaciones son varias tras el uso de un gestor de dependencias y las veremos más adelante. Pero en pos de ir entrando en materia digamos que tenemos una aplicación y necesitamos que haga uso de la librería A, pero esta a su vez depende de B la cual también depende de C y D. Este escenario es el que básicamente nos ayuda a gestionar gestores como CarthageCocoaPods.

Pero esto no es todo amigos, también puede ocurrirnos que en ocasiones necesitemos importar versiones específicas de librerías en pos de hacer uso de funcionalidades recién incorporadas, siempre velando que no se genere un conflicto de dependencias ya que si D ha sufrido una actualización mayor quizás B no sea compatible con esto.

Un comportamiento que de seguro a muchos les recuerda a gestores de paquetes como Homebrew para macOS, el clásico apt-get en sistemas GNU / Linux basados en Debian como Ubuntu o dnf en aquellos basados en Red Hat como Fedora.

Carthage no surgió ayer, ya lleva algún tiempo junto a nosotros con un enfoque bien claro en la facilidad de uso. Ha sido creado por un grupo de desarrolladores de Github y fue el primer gestor de dependencias en trabajar con Swift, de hecho el propio Carthage está desarrollado en Swift.

Otro punto a destacar es que su simplicidad viene dada (en parte) por el uso de librerías dinámicas, algo que básicamente viene a responder a la falta de beneficios cuando trabajamos con versiones como iOS 8 o superiores, versiones donde ya no se pueden distribuir binarios creados con Swift que hagan uso de librerías o frameworks compilados como estáticos.

Gestores de Dependencias

A modo de definición propia podemos decir que los administradores de dependencias nos ayudan a realizar una serie de funciones bien útiles que realmente, a mi modo de ver, no tiene sentido hacer a mano:

  • Simplifican y estandarizan el proceso de extraer código de terceros e incorporarlo en nuestro proyecto. Sin esta herramienta, esto evidentemente aún podemos hacerlo pero (entre otras alternativas similares) tendríamos que copiar manualmente los códigos fuentes, buscar los binarios precompilados o utilizar mecanismos como los submódulos de Git.
  • Facilitan la actualización de librerías o frameworks que estemos utilizando. Solamente imagina tener que estar siguiendo todos estos proyectos en Slack, siguiendo las listas de correos de los desarrolladores o sencillamente visitando repetidas veces la página de GitHub de cada una de estas dependencias, descargar el código fuente y colocarlo adecuadamente en nuestro proyecto y esto cada vez que haya una actualización.
  • Seleccionan versiones apropiadas y compatibles de cada dependencia que utilices. Por ejemplo, si agregas manualmente dependencias, las cosas se pueden tornar bien feas cuando estas dependen unas de otras, comparten otra dependencia o peor aún cuando versiones específicas son necesarias, algo similar a lo que ocurría hace algunos años cuando no existía apt-get, yum o dnf y había que instalar los paquetes .deb o .rpm a mano junto a todas sus dependencias.

Creo que ha quedada clara la utilidad de los gestores de dependencias ¿Cierto?

Nota: En caso de tener alguna duda sobre esto último les recomiendo conocer más sobre las librerías y las diferencias entre las estáticas y las dinámicas. También me gustaría aclarar que en este Tutorial Carthage asumimos que el lector tiene un dominio relativamente fluido del sistema y sobre todo de la terminal, que sabe cómo instalar y configurar un entorno de desarrollo en macOS.

Instalar Carthage

Para comenzar a usar Carthage lo primero que necesitamos hacer es instalarlo en nuestro sistema, proceso que llevaremos a cabo mediante el gestor de paquetes Homebrew:

╭─josuevhn@WiseRatel ~
╰─$ brew update
Updated 2 taps (homebrew/core, homebrew/services).
==> New Formulae
ampl-mp               clojure               flann                 gromacs               libbitcoin            maxima                ott                   sbt@0.13              wxmaxima
apng2gif              cminpack              frugal                hdf5@1.8              libbitcoin-explorer   mimic                 packmol               snapcraft             xtensor
armadillo             cppad                 geth                  http-server           libmatio              mujs                  payara                stubby
ceres-solver          datamash              gmsh                  hwloc                 libpq                 nco                   pcb                   sundials
==> Updated Formulae
abcm2ps                      conan                        freeciv                      i3status                     lynis                        percona-server               stella
abcmidi                      confuse                      freeipmi                     iamy                         lz4                          percona-server-mongodb       stgit
ace                          conjure-up                   freeling                     icbirc                       lzlib                        percona-server@5.5           strongswan
acmetool                     consul                       freeswitch                   ice                          lzop                         percona-server@5.6           subnetcalc

... FRAGMENTO OMITIDO

cocoapods                    fonttools                    hunspell                     logtalk                      pcre2                        squid                        zorba
collector-sidecar            forego                       hyper                        luaver                       pdf2htmlex                   sshfs                        zsh
commandbox                   fossil                       i2p                          lumo                         pdftoedn                     sslyze                       zsh-syntax-highlighting
compcert                     fox                          i3                           lxc                          pdftoipe                     statik                       zstd
==> Renamed Formulae
gmt4 -> gmt@4                            opencv3 -> opencv                        root6 -> root                            srtp@1.5 -> srtp@1.6                     transfig -> fig2dev
==> Deleted Formulae
abi-compliance-checker                   go-gui                                   kafka@0.80                               phantomjs@1.92                           szl
gh                                       jpeg@9                                   mapnik@2                                 phantomjs@1.98
╭─josuevhn@WiseRatel ~
╰─$

Lo que acabamos de hacer y que es opcional o más bien una recomendación (casi obligación) pues no es más que actualizar la base de datos de Homebrew para asegurarnos de que instalaremos las últimas versiones de los paquetes que nos interesan, en este caso Carthage:

╭─josuevhn@WiseRatel ~
╰─$ brew install carthage
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/core).
==> Updated Formulae
tippecanoe

==> Downloading https://homebrew.bintray.com/bottles/carthage-0.25.0.sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring carthage-0.25.0.sierra.bottle.tar.gz
==> Caveats
Bash completion has been installed to:
  /usr/local/etc/bash_completion.d

zsh completions have been installed to:
  /usr/local/share/zsh/site-functions
==> Summary
?  /usr/local/Cellar/carthage/0.25.0: 69 files, 23MB
╭─josuevhn@WiseRatel ~
╰─$

Integrar Carthage con Xcode

Lo siguiente sería crear un archivo de nombre Cartfile y editarlo con nuestro editor favorito, en mi caso es mcedit:

╭─josuevhn@WiseRatel ~/Google Drive/Projects/Apple/iPhone/CarthageTest
╰─$ touch Cartfile
╭─josuevhn@WiseRatel ~/Google Drive/Projects/Apple/iPhone/CarthageTest
╰─$ mcedit Cartfile

…en pos de añadir la siguiente línea:

github "Alamofire/Alamofire" ~> 4.5

Especificando así que deseamos utilizar la versión 4.5 o cualquier otra superior compatible pero sin llegar a la 5.0. El formato que siguen las versiones es el siguiente:

Modo Descripción
== 1.0 Usar exactamente la versión 1.0
>= 1.0 Usar la versión 1.o u otra mayor
~> 1.0 Usar cualquier versión compatible con la 1.0 pero jamás la 2.0

Luego de esto lo que sigue es descargar y compilar las librerías que hemos especificado:

╭─josuevhn@WiseRatel ~/Google Drive/Projects/Apple/iPhone/CarthageTest
╰─$ carthage update
*** Fetching Alamofire
*** Checking out Alamofire at "4.5.1"
*** xcodebuild output can be found in /var/folders/1l/ffw7k5k90bl27r30kv5ykx2w0000gn/T/carthage-xcodebuild.fzpSrw.log
*** Building scheme "Alamofire watchOS" in Alamofire.xcworkspace
*** Building scheme "Alamofire macOS" in Alamofire.xcworkspace
*** Building scheme "Alamofire iOS" in Alamofire.xcworkspace
*** Building scheme "Alamofire tvOS" in Alamofire.xcworkspace
╭─josuevhn@WiseRatel ~/Google Drive/Projects/Apple/iPhone/CarthageTest
╰─$

Si listamos los directorios y archivos dentro de nuestro proyecto veremos:

╭─josuevhn@WiseRatel ~/Google Drive/Projects/Apple/iPhone/CarthageTest
╰─$ ls -lh
total 16
-rw-r--r--  1 josuevhn  staff    29B Sep  9 01:17 Cartfile
-rw-r--r--@ 1 josuevhn  staff    37B Sep  9 01:17 Cartfile.resolved
drwxr-xr-x  5 josuevhn  staff   170B Sep  9 01:14 Carthage
drwxr-xr-x  7 josuevhn  staff   238B Sep  9 00:42 CarthageTest
drwxr-xr-x  5 josuevhn  staff   170B Sep  9 00:28 CarthageTest.xcodeproj
╭─josuevhn@WiseRatel ~/Google Drive/Projects/Apple/iPhone/CarthageTest
╰─$

…como Carthage nos ha creado junto a nuestro Cartfile otro archivo de idéntico nombre pero de extensión .resolved en el cual se almacena la versión exacta que se ha descargado de cada una de las dependencias que estemos gestionando, este vendría a ser el homologo al Podfile.lock cuando trabajamos con CocoaPods.

Si le hacemos un cat a este archivo obtendríamos la siguiente salida:

╭─josuevhn@WiseRatel ~/Google Drive/Projects/Apple/iPhone/CarthageTest
╰─$ cat Cartfile.resolved
github "Alamofire/Alamofire" "4.5.1"
╭─josuevhn@WiseRatel ~/Google Drive/Projects/Apple/iPhone/CarthageTest
╰─$

…donde se nos informa que en efecto había una versión superior en la rama 4.5 y por ende se ha descargado la versión 4.5.1. Junto a este archivo también contamos con una carpeta llamada Carthage y dentro de la cual y específicamente dentro de Build vamos a tener nuestro framework ya compilado para todas las plataformas a las que Carthage brinda soporte.

Carthage – Plataformas Soportadas

Adentrándonos dentro de la plataforma para la cual estamos creando nuestra aplicación, que en nuestro caso es iOS, encontraremos el fichero Alamofire.framework el cual añadiremos a nuestro proyecto de Xcode. Por su parte la carpeta Checkouts es donde Carthage comprueba los código fuente de cada una de las dependencias que pueda tener el o los frameworks con los que estemos trabajando para su posterior descarga, de ser necesario.

Al mismo tiempo Carthage mantiene un repositorio interno, una especie de caché propia donde almacena el código fuente de todas estas dependencias, evitando tener que clonar el mismo código fuente varias veces para diferentes proyectos.

Por este motivo es que cada vez que ejecutamos “carthage update” obtenemos una respuesta mucho más rápida que la primera vez, ya que cuando las versiones locales y remotas son las mismas se aboga por la local y el proceso fluye mucho más rápido.

Si queremos que Carthage se enfoque en la plataforma para la que estamos trabajando basta con modificar el comando anterior al siguiente:

╭─josuevhn@WiseRatel ~/Google Drive/Projects/Apple/iPhone/CarthageTest
╰─$ carthage update --platform iOS
*** Fetching Alamofire
*** Checking out Alamofire at "4.5.1"
*** xcodebuild output can be found in /var/folders/1l/ffw7k5k90bl27r30kv5ykx2w0000gn/T/carthage-xcodebuild.RuPpbd.log
*** Building scheme "Alamofire iOS" in Alamofire.xcworkspace
╭─josuevhn@WiseRatel ~/Google Drive/Projects/Apple/iPhone/CarthageTest
╰─$

Lo otro que pudiéramos hacer en caso de no estar seguros de la última versión del framework o librería que deseamos utilizar, es omitir la versión en el fichero Cartfile, el cual luego de modificar la línea que habíamos establecido ahora luce así:

github "Alamofire/Alamofire"

Esto es más que suficiente para que Carthage automáticamente detecte la última versión compatible con el resto de dependencias que tengamos establecidas, las descarga y las compila en un framework binario de manera similar a como hemos visto anteriormente.

Continuando con nuestra integración, con Xcode abierto damos click sobre el nombre de nuestro proyecto (en el Navegador de Proyectos (Project Navigator)) y en los detalles a su derecha damos click nuevamente sobre el botón + asociado a la sección de Binarios Embebidos (Embedded Binaries):

Carthage - Añadiendo Binarios Embebidos a Xcode

…donde mediante el botón de Add Other localizaremos el fichero anteriormente mencionado de nombre Alamofire.framework:

Carthage - Añadiendo Binarios Embebidos a Xcode - Etapa Final

Finalizado este paso deberíamos ver algo como lo siguiente:

Carthage - Binarios Embebidos, Frameworks y Librerías Enlazadas

Sección donde se listan los binarios embebidos al igual que los framework o librerías que hemos añadido y en este caso figura con un estatus de requerido, es decir que Alamofire es una dependencia requerida para el correcto funcionamiento de nuestra aplicación.

Probando Alamofire

En este punto si compilamos y podemos ejecutar nuestra aplicación en el simulador sin ningún problema la integración ha sido todo un éxito. No obstante hagamos una sencilla prueba en nuestro ViewController:

//
//  ViewController.swift
//  CarthageTest
//
//  Created by Josué V. Herrera on 9/8/17.
//  Copyright © 2017 KodigoSwift. All rights reserved.
//

import UIKit
import Alamofire

class ViewController: UIViewController {

    override func viewDidLoad() {
        
        super.viewDidLoad()
        
        testAlamofire()

    } // viewDidLoad

    func testAlamofire() {
        
        Alamofire.request("https://httpbin.org/get").responseJSON { response in
            
            print("Request: \(String(describing: response.request))")   // original url request
            print("Response: \(String(describing: response.response))") // http url response
            print("Result: \(response.result)")                         // response serialization result
            
            if let json = response.result.value {
                
                print("JSON: \(json)") // serialized json response
                
            } // if let
            
            if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
                
                print("Data: \(utf8Text)") // original server data as UTF8 string
                
            } // if let
        
        } // closure
        
    } // testAlamofire

} // ViewController

Creo que el código se explica por sí mismo pero no obstante los puntos a señalar son: la importación de nuestro framework en la línea 10; de la línea 22 a la 44 hemos creado una función que nos servirá para probar el buen funcionamiento de Alamofire y para esto nos hemos valido de un código del propio sitio de Alamofire en Github.

En este código se hace una petición de red, se captura la respuesta y al final se muestra dicha respuesta en el último print de la línea 38. Por último en la línea 18 y dentro de viewDidLoad se ejecuta la llamada a nuestra función testAlamofire, cuyo resultado en mi caso es el siguiente:

Request: Optional(https://httpbin.org/get)
Response: Optional(<NSHTTPURLResponse: 0x610000025b40> { URL: https://httpbin.org/get } { status code: 200, headers {
    "Access-Control-Allow-Credentials" = true;
    "Access-Control-Allow-Origin" = "*";
    Connection = "keep-alive";
    "Content-Length" = 377;
    "Content-Type" = "application/json";
    Date = "Sat, 09 Sep 2017 05:42:50 GMT";
    Server = "meinheld/0.6.1";
    Via = "1.1 vegur";
    "X-Powered-By" = Flask;
    "X-Processed-Time" = "0.000771045684814";
} })
Result: SUCCESS
JSON: {
    args =     {
    };
    headers =     {
        Accept = "*/*";
        "Accept-Encoding" = "gzip;q=1.0, compress;q=0.5";
        "Accept-Language" = "en;q=1.0";
        Connection = close;
        Host = "httpbin.org";
        "User-Agent" = "CarthageTest/1.0 (com.kodigoswift.CarthageTest; build:1; iOS 10.3.1) Alamofire/4.5.1";
    };
    origin = "186.5.72.160";
    url = "https://httpbin.org/get";
}
Data: {
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip;q=1.0, compress;q=0.5", 
    "Accept-Language": "en;q=1.0", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "User-Agent": "CarthageTest/1.0 (com.kodigoswift.CarthageTest; build:1; iOS 10.3.1) Alamofire/4.5.1"
  }, 
  "origin": "186.5.72.160", 
  "url": "https://httpbin.org/get"
}

Los resultados son los esperados. Hasta ahora hemos visto un caso de uso común donde nos apoyamos en una librería o framework de terceros para dar soporte a ciertas funcionalidades de nuestra aplicación. Para esto hemos hecho uso de un gestor de dependencias de fácil integración que nos ayude con la descarga y compilación de las últimas versiones disponibles y sus futuras actualizaciones.

Actualizando Librerías y Frameworks

Si bien es cierto que pudiéramos adoptar ciertas rutinas de verificación, los más sencillo sería ejecutar el comando:

╭─josuevhn@WiseRatel ~
╰─$ carthage update

…exacto, el mismo comando que hemos usado hasta ahora. En caso de que solamente desees actualizar uno solo de los frameworks puedes variar el comando anterior:

╭─josuevhn@WiseRatel ~/Google Drive/Projects/Apple/iPhone/CarthageTest
╰─$ carthage update Alamofire
*** Fetching Alamofire
*** Checking out Alamofire at "4.5.1"
*** xcodebuild output can be found in /var/folders/1l/ffw7k5k90bl27r30kv5ykx2w0000gn/T/carthage-xcodebuild.n8MnPf.log
*** Building scheme "Alamofire iOS" in Alamofire.xcworkspace
*** Building scheme "Alamofire tvOS" in Alamofire.xcworkspace
*** Building scheme "Alamofire watchOS" in Alamofire.xcworkspace
*** Building scheme "Alamofire macOS" in Alamofire.xcworkspace
╭─josuevhn@WiseRatel ~/Google Drive/Projects/Apple/iPhone/CarthageTest
╰─$

…a esta versión donde especificamos el nombre.

Diferencias entre Carthage y CocoaPods

Pero ¿cuáles serían las diferencias principales entre estos dos gestores de dependencias? y ¿por qué deberíamos tomar en cuenta a Carthage sobre CocoaPods siendo este último el más usado ya desde hace años (desde los tiempos de Objective-C) en el mundo del desarrollo iOS?

En primer lugar, CocoaPods (de forma predeterminada) crea y actualiza automáticamente un proyecto de Xcode para nuestra aplicación y sus dependencias. Carthage por su parte construye un framework binario haciendo uso de xcodebuild, pero deja la responsabilidad de integrarlos al desarrollador. El enfoque de CocoaPods para algunos sin dudas es más fácil de usar, mientras que el de Carthage es mucho más flexible y no intrusivo.

CocoaPods aparte de fungir como manejador de dependencias también nos brinda acceso a librerías y frameworks a través de un repositorio centralizado donde podemos hacer búsquedas y localizar librerías de una manera sencilla y rápida. Por el contrario, Carthage se ha creado como un manejador descentralizado de dependencias.

No cuenta con un repositorio central de proyectos y se aboga por GitHub, lo que reduce el trabajo de mantenimiento y disminuye en gran medida los problemas de disponibilidad, o el posible evento donde deja de existir.

Sin embargo, descubrir nuevas librerías o frameworks se torna una tarea un poco más difícil: los desarrolladores que apuesten por Carthage deberán recurrir a las páginas trending de GitHub o similares.

A partir de aquí si aún tienen dudas con Carthage os recomiendo aprender más sobre CocoaPods, de esta forma tendrán una idea más clara de cada uno y cual de estos se adapta más a sus necesidades.

Falta aún mucho por aprender en nuestro camino a convertirnos en iOS Developer. Suscríbete a nuestra lista de correo mediante el formulario en el panel derecho y síguenos en nuestras redes sociales. Mantente así al tanto de todas nuestras publicaciones futuras.

Espero que todo cuanto se ha dicho aquí, de una forma u otra le haya servido de aprendizaje, de referencia, que haya valido su preciado tiempo.

Este artículo, al igual que el resto, será revisado con cierta frecuencia en pos de mantener un contenido de calidad y actualizado.

Cualquier sugerencia, ya sea errores a corregir, información o ejemplos a añadir será, más que bienvenida, necesaria!

Get real time updates directly on you device, subscribe now.

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More

RECIBE CONTENIDO SIMILAR EN TU CORREO

RECIBE CONTENIDO SIMILAR EN TU CORREO

Suscríbete a nuestra lista de correo y mantente actualizado con las nuevas publicaciones.

Se ha suscrito correctamente!