Using Upload API with a provided label - label is missing in Annotation View

Working on an iOS app with the Roboflow upload API:

 func classifyScreenshotByApp(_ screenshots: [Screenshot]) {
        let group = DispatchGroup()
        
        for screenshot in screenshots {
            group.enter()
            
            guard let image = screenshot.image,
                  let imageData = image.jpegData(compressionQuality: 0.1) else {
                print("Failed to process image data")
                return
            }

            let fileContent = imageData.base64EncodedString()
            let postData = fileContent.data(using: .utf8)
            
            var request = URLRequest(url: URL(string: "https://classify.roboflow.com/app-classification/17?api_key=\(API_KEY)&name=YOUR_IMAGE.jpg")!, timeoutInterval: Double.infinity)
            request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
            request.httpMethod = "POST"
            request.httpBody = postData
            
            // Execute Post Request
            URLSession.shared.dataTask(with: request, completionHandler: { [self] data, response, error in
                defer { group.leave() }
                
                if let error = error {
                    print("API error: \(error.localizedDescription)")
                    return
                }

When I look at the uploaded image in Roboflow Annotate, I can see the image, but the label shows as “Undefined”

CleanShot 2023-05-24 at 07.48.56

Ah whoops I pasted wrong code snippet

func uploadImage(image: UIImage, project: String, newLabel: String, completion: @escaping (UploadResult)->()) {
    
    let encodedImage = convertImageToBase64String(img: image)
    let uuid = UUID().uuidString
    
    if let url = URL(string: "https://api.roboflow.com/dataset/\(project)/upload?api_key=\(API_KEY)&name=\(uuid)&split=train&label=\(newLabel)") {
        var request = URLRequest(url: url, timeoutInterval: Double.infinity)
        
        request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        request.httpMethod = "POST"
        request.httpBody = encodedImage.toData()

Thanks for the code example!

As I understand, you’re aiming to upload an image to a classification project with the upload API and a new specific label in mind. Your request is succeeding in that your image is being uploaded successfully to your classification project, but the label you’re passing isn’t present.

To upload an image for a classification project and apply a label to it, you currently do this in two separate requests:

  1. The first request uploads the image to the project (as you’re doing). You’ll receive an imageID for that image in your project.
  2. The second request then applies a label to the imageID

As an example of #2 as a bash script:

cat YOUR_ANNOTATION.xml | curl -d @- \
"https://api.roboflow.com/dataset/your-dataset/annotate/abc123?\
api_key=YOUR_KEY&\
name=YOUR_ANNOTATION.xml"

I don’t know much Swift, and I asked chatGPT to give an example of the add classification label bash snippet in Swift instead. It provided this:

import Foundation

let url = URL(string: "https://api.roboflow.com/dataset/your-dataset/annotate/abc123?api_key=YOUR_KEY&name=YOUR_ANNOTATION.xml")!

let inputData = Data(/* data to be sent in the request */)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = inputData

let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
    if let error = error {
        print("Error: \(error)")
    } else if let data = data {
        let responseString = String(data: data, encoding: .utf8)
        print("Response: \(responseString ?? "")")
    }
}

task.resume()

Awesome; got it working like so:

func annotateImage(imageID: String, label: String, completion: @escaping (Bool)->()) {
    let url = URL(string: "https://api.roboflow.com/dataset/MY_PROJECT/annotate/\(imageID)?api_key=\(API_KEY)")
    var request = URLRequest(url: url!)
    request.httpMethod = "POST"
    
    // Specify the type of the body
    request.setValue("application/xml", forHTTPHeaderField: "Content-Type")
    
    // The new label should be passed as XML in the body
    let labelXML = "<annotation>\n\t<folder>images</folder>\n\t<filename>\(imageID)</filename>\n\t<path>/images/\(imageID).jpg</path>\n\t<source>\n\t\t<database>Unknown</database>\n\t</source>\n\t<size>\n\t\t<width>0</width>\n\t\t<height>0</height>\n\t\t<depth>3</depth>\n\t</size>\n\t<segmented>0</segmented>\n\t<object>\n\t\t<name>\(label)</name>\n\t\t<pose>Unspecified</pose>\n\t\t<truncated>0</truncated>\n\t\t<difficult>0</difficult>\n\t\t<bndbox>\n\t\t\t<xmin>0</xmin>\n\t\t\t<ymin>0</ymin>\n\t\t\t<xmax>0</xmax>\n\t\t\t<ymax>0</ymax>\n\t\t</bndbox>\n\t</object>\n</annotation>"
    
    let labelData = labelXML.data(using: .utf8)
    request.httpBody = labelData
    
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)
    
    let task = session.dataTask(with: request) { (data, response, error) in
        if error != nil {
            print(error!)
            completion(false)
            return
        }
        
        guard let data = data else {
            completion(false)
            return
        }
        
        do {
            guard let dict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
                return
            }
            print(dict)

        } catch {
            completion(false)
        }
        completion(true)
    }
    task.resume()
}