5

I'm writing an ImageRecognizer using Firebase Cloud Functions API in Xcode 12.4 with Swift as follows:

import Firebase
import UIKit
import Foundation

class ImageRecognizer {
    let imageName: String
    lazy var functions = Functions.functions()
    
    init(imageName: String) {
        self.imageName = imageName
    }
    
    func recognize() {
        print("RECOGNIZING")
        if let userImage = UIImage(named: imageName) {
            print("IMAGE VALID")
            guard let imageData = userImage.jpegData(compressionQuality: 1.0) else { return }
            print("IMAGE DATA VALID")
            let base64encodedImage = imageData.base64EncodedString()
            
            let requestData = [
              "image": ["content": base64encodedImage],
              "features": ["type": "TEXT_DETECTION"],
              "imageContext": ["languageHints": ["sa"]]
            ]
            
            functions.httpsCallable("annotateImage").call(requestData) { (result, error) in
              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]
                    print("ERROR \(message), CODE \(code), DETAILS \(details)")
                }
                print("RESULT \(result)")
              }
              
                guard let annotation = (result?.data as? [String: Any])?["fullTextAnnotation"] as? [String: Any] else { return }
                print("%nComplete annotation:")
                let text = annotation["text"] as? String ?? ""
                print("%n\(text)")
            }

        }
        
    }
}

My cloud function in index.js is as follows:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.annotateImage = void 0;
const functions = require("firebase-functions");
const vision_1 = require("@google-cloud/vision");
const client = new vision_1.default.ImageAnnotatorClient();
// This will allow only requests with an auth token to access the Vision
// API, including anonymous ones.
// It is highly recommended to limit access only to signed-in users. This may
// be done by adding the following condition to the if statement:
//    || context.auth.token?.firebase?.sign_in_provider === 'anonymous'
//
// For more fine-grained control, you may add additional failure checks, ie:
//    || context.auth.token?.firebase?.email_verified === false
// Also see: https://firebase.google.com/docs/auth/admin/custom-claims
exports.annotateImage = functions.https.onCall(async (data, context) => {
    console.log("DATA: " + data);
    if (!context.auth) {
        throw new functions.https.HttpsError("unauthenticated", "annotateImage must be called while authenticated.");
    }
    try {
        return await client.annotateImage(JSON.parse(data));
    }
    catch (e) {
        throw new functions.https.HttpsError("internal", e.message, e.details);
    }
});

The JSON.parse(data) part does not work - it returns the error:

RECOGNIZING IMAGE VALID IMAGE DATA VALID 2021-03-13 07:57:37.915895+0530 ImageReader[10575:10270760] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed ERROR Unexpected token o in JSON at position 1, CODE Optional(__C.FIRFunctionsErrorCode), DETAILS nil RESULT nil

Even when I change to any other dictionary as my requestData, still the JSON doesn't go through. Does anyone know how to call Firebase cloud functions properly from iOS?

NatashaTheRobot
  • 6,879
  • 4
  • 32
  • 27

1 Answers1

5

The "features": ["type": "TEXT_DETECTION"] needs to be an array of features, which Swift doesn't like:

            let requestData = [
              "image": ["content": userImage],
              "features": [["type": "TEXT_DETECTION"]],
              "imageContext": ["languageHints": ["sa"]]
            ]

New code that works (not finished refactoring):

import Firebase
import UIKit
import Foundation

class ImageRecognizer: Codable {
    let imageName: String
    lazy var functions = Functions.functions()
    
    init(imageName: String) {
        self.imageName = imageName
    }
    
    func recognize() {            
            struct data: Encodable {
                let image: [String: Data]
                let features = [["type": "TEXT_DETECTION"]]
                let imageContext = ["languageHints": ["sa"]]
                
                init() {
                    let userImage = UIImage(named: "onlytext.jpg")!
                    let imageData = userImage.jpegData(compressionQuality: 1.0)!
                    image = ["content": imageData]
                }
            }
            
            let encoder = JSONEncoder()
             
            let encodedData = try! encoder.encode(data())
            let string = String(data: encodedData, encoding: .utf8)!
            
            functions.httpsCallable("annotateImage").call(string) { (result, error) in
              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]
                    print("ERROR \(message), CODE \(code), DETAILS \(details)")
                }
                
              }
                
                print("SUCCESS")
                print("RESULT \(result?.data)")
              
                guard let annotation = (result?.data as? [String: Any])?["fullTextAnnotation"] as? [String: Any] else { return }
                print("%nComplete annotation:")
                let text = annotation["text"] as? String ?? ""
                print("%n\(text)")
            }

        }
        
    }
}
NatashaTheRobot
  • 6,879
  • 4
  • 32
  • 27
  • Hello...@NatashaTheRobot....i had face same problem & now that problem is resolved but i had face new problem and that was it always throw return while parsing response with string:any...any idea about solution ? – anil72178 Jun 11 '21 at 14:09