Reconoce puntos de referencia de forma segura con Cloud Vision mediante Firebase Auth y Functions en plataformas de Apple

Para llamar a una API de Google Cloud desde tu app, debes crear una API de REST intermedia que maneje la autorización y proteja los valores secretos, como las claves de API. Luego, debes escribir código en tu app para dispositivos móviles a fin de autenticarte en este servicio intermedio y comunicarte con él.

Una forma de crear esta API de REST es usar Firebase Authentication y Functions, lo que te brinda una puerta de enlace administrada y sin servidores a las APIs de Google Cloud que controla la autenticación y se puede llamar desde tu app para dispositivos móviles con SDK precompilados.

En esta guía, se muestra cómo usar esta técnica para llamar a la API de Cloud Vision desde tu app. Este método permitirá que todos los usuarios autenticados accedan a los servicios facturados de Cloud Vision a través de tu proyecto de Cloud, por lo que debes considerar si este mecanismo de autenticación es suficiente para tu caso de uso antes de continuar.

Antes de comenzar

Configura tu proyecto

Si aún no has agregado Firebase a tu app, sigue los pasos en la guía de introducción para hacerlo.

Usa Swift Package Manager para instalar y administrar las dependencias de Firebase.

  1. En Xcode, con tu proyecto de app abierto, navega a File > Add Packages.
  2. Cuando se te solicite, agrega el repositorio del SDK de Firebase para plataformas de Apple:
  3.   https://github.com/firebase/firebase-ios-sdk.git
  4. Elige la biblioteca de Firebase ML.
  5. Agrega la marca -ObjC a la sección Other Linker Flags de la configuración de compilación de tu destino.
  6. Cuando termines, Xcode comenzará a resolver y descargar automáticamente tus dependencias en segundo plano.

A continuación, realiza la configuración en la app:

  1. En tu app, importa Firebase:

    Swift

    import FirebaseMLModelDownloader

    Objective-C

    @import FirebaseMLModelDownloader;

Quedan unos pocos pasos de configuración para terminar:

  1. Si aún no habilitaste las API basadas en la nube en tu proyecto, hazlo de la siguiente manera:

    1. Abre la Firebase ML Página de APIs de Firebase console.
    2. Si todavía no actualizaste tu proyecto a un plan de precios Blaze, haz clic en Actualizar para hacerlo (se te pedirá que realices la actualización únicamente si tu proyecto no está en el plan Blaze).

      Solo los proyectos con un plan Blaze pueden usar las APIs de Cloud.

    3. Si las APIs de Cloud no están habilitadas, haz clic en Habilitar las APIs de Cloud.
  2. Configura tus claves de API de Firebase existentes para inhabilitar el acceso a la API de Cloud Vision siguiendo los pasos que se indican a continuación:
    1. Abre la página Credenciales de la consola de Cloud.
    2. Para cada clave de API de la lista, abre la vista de edición y, en la sección Restricciones de claves, agrega a la lista todas las APIs disponibles excepto la de Cloud Vision.

Implementa la función que admite llamadas

A continuación, implementa la Cloud Function que usarás para conectar tu app y la API de Cloud Vision. El repositorio functions-samples contiene un ejemplo que puedes usar.

De forma predeterminada, el acceso a la API de Cloud Vision a través de esta función permitirá que solo los usuarios autenticados de tu app accedan a la API de Cloud Vision. Puedes modificar la función para diferentes requisitos.

Sigue estos pasos para implementar la función:

  1. Clona o descarga functions-samples repo y cambia al directorio Node-1st-gen/vision-annotate-image:
    git clone https://github.com/firebase/functions-samples
    cd Node-1st-gen/vision-annotate-image
    
  2. Instala las dependencias:
    cd functions
    npm install
    cd ..
  3. Si no tienes Firebase CLI, instálalo.
  4. Inicializa un proyecto de Firebase en el directorio vision-annotate-image. Cuando se te solicite, selecciona tu proyecto en la lista.
    firebase init
  5. Sigue estos pasos para implementar la función:
    firebase deploy --only functions:annotateImage

Agrega Firebase Auth a tu app

La función que admite llamadas implementada anteriormente rechazará todas las solicitudes de usuarios no autenticados de tu app. Si aún no lo has hecho, tendrás que agregar Firebase Auth a tu app.

Agrega las dependencias necesarias a tu app

Usa Swift Package Manager para instalar la biblioteca de Cloud Functions para Firebase.

1. Prepara la imagen de entrada

Para llamar a Cloud Vision, la imagen debe tener el formato de una string codificada en base64. Para procesar una UIImage, sigue estos pasos:

Swift

guard let imageData = uiImage.jpegData(compressionQuality: 1.0) else { return }
let base64encodedImage = imageData.base64EncodedString()

Objective-C

NSData *imageData = UIImageJPEGRepresentation(uiImage, 1.0f);
NSString *base64encodedImage =
  [imageData base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength];

2. Invoca la función que admite llamadas para reconocer puntos de referencia

Para reconocer puntos de referencia en una imagen, invoca la función que admite llamadas pasando una solicitud de JSON de Cloud Vision.

  1. Primero, inicializa una instancia de Cloud Functions:

    Swift

    lazy var functions = Functions.functions()
    

    Objective-C

    @property(strong, nonatomic) FIRFunctions *functions;
    
  2. Luego, crea una solicitud con el Tipo configurado como LANDMARK_DETECTION:

    Swift

    let requestData = [
      "image": ["content": base64encodedImage],
      "features": ["maxResults": 5, "type": "LANDMARK_DETECTION"]
    ]
    

    Objective-C

    NSDictionary *requestData = @{
      @"image": @{@"content": base64encodedImage},
      @"features": @{@"maxResults": @5, @"type": @"LANDMARK_DETECTION"}
    };
    
  3. Finalmente, invoca la función:

    Swift

    do {
      let result = try await functions.httpsCallable("annotateImage").call(requestData)
      print(result)
    } catch {
      if let error = error as NSError? {
        if error.domain == FunctionsErrorDomain {
          let code = FunctionsErrorCode(rawValue: error.code)
          let message = error.localizedDescription
          let details = error.userInfo[FunctionsErrorDetailsKey]
        }
        // ...
      }
    }
    

    Objective-C

    [[_functions HTTPSCallableWithName:@"annotateImage"]
                              callWithObject:requestData
                                  completion:^(FIRHTTPSCallableResult * _Nullable result, NSError * _Nullable error) {
            if (error) {
              if ([error.domain isEqualToString:@"com.firebase.functions"]) {
                FIRFunctionsErrorCode code = error.code;
                NSString *message = error.localizedDescription;
                NSObject *details = error.userInfo[@"details"];
              }
              // ...
            }
            // Function completed succesfully
            // Get information about labeled objects
    
          }];
    

3. Obtén información sobre los puntos de referencia reconocidos

Si la operación de reconocimiento de puntos de referencia se ejecuta correctamente, se mostrará una respuesta JSON de BatchAnnotateImagesResponse en el resultado de la tarea. Cada objeto en el array landmarkAnnotations representa un punto de referencia que se reconoció en la imagen. Por cada punto de referencia, puedes obtener las coordenadas de los límites en la imagen de entrada, el nombre, la latitud y la longitud, el ID de entidad del Gráfico de conocimiento (si está disponible) y la puntuación de confianza de la coincidencia. Por ejemplo:

Swift

if let labelArray = (result?.data as? [String: Any])?["landmarkAnnotations"] as? [[String:Any]] {
  for labelObj in labelArray {
    let landmarkName = labelObj["description"]
    let entityId = labelObj["mid"]
    let score = labelObj["score"]
    let bounds = labelObj["boundingPoly"]
    // Multiple locations are possible, e.g., the location of the depicted
    // landmark and the location the picture was taken.
    guard let locations = labelObj["locations"] as? [[String: [String: Any]]] else { continue }
    for location in locations {
      let latitude = location["latLng"]?["latitude"]
      let longitude = location["latLng"]?["longitude"]
    }
  }
}

Objective-C

NSArray *labelArray = result.data[@"landmarkAnnotations"];
for (NSDictionary *labelObj in labelArray) {
  NSString *landmarkName = labelObj[@"description"];
  NSString *entityId = labelObj[@"mid"];
  NSNumber *score = labelObj[@"score"];
  NSArray *bounds = labelObj[@"boundingPoly"];
  // Multiple locations are possible, e.g., the location of the depicted
  // landmark and the location the picture was taken.
  NSArray *locations = labelObj[@"locations"];
  for (NSDictionary *location in locations) {
    NSNumber *latitude = location[@"latLng"][@"latitude"];
    NSNumber *longitude = location[@"latLng"][@"longitude"];
  }
}