SCADE

Camera control

Change history

  • August 2018 - Added Upload example

Introduction

The camera control is not really a control that you pick from the control list, but a class we use to

  • take pictures using the native Android or iOS camera control
  • select from the picture library

The SCDPlatformCamera Class wraps the UIImagePickerController for iOS and the ACTION_IMAGE_CAPTURE intent for the Android os.

Minimal Camera app

The layout is simple. We use a bitmap control to show the picture we will take / the image we will select from the library. The two buttons are linked to the getPicture method:

Using is is very easy:

  1. Create a SCDPlatformCamera instance
  2. Use the getPicture method to invoke native camera api
  3. You have two options to choose from. Picture or PhotoLibrary.
  4. Create event handlers to react to success and failure

Here the sample code for taking pictures

import ScadeKit

class MainPageAdapter: SCDLatticePageAdapter {

  var bitmap : SCDWidgetsBitmap!
  var takePhotoButton : SCDWidgetsButton!
  var chooseFromLibraryButton : SCDWidgetsButton!
  
  override func load(_ path: String) {
    super.load(path)
    
    bitmap = page?.getWidgetByName("bitmap1") as! SCDWidgetsBitmap
    takePhotoButton = page?.getWidgetByName("button1") as! SCDWidgetsButton
    chooseFromLibraryButton = page?.getWidgetByName("button2") as! SCDWidgetsButton
    
    // Creating camera object
    let camera = SCDPlatformCamera()
    
    // Success handler
    let _onSuccess = SCDPlatformCameraSuccessHandler { result in
      self.bitmap.content = result
    }
    
    // Error handler
    let _onError = SCDPlatformCameraErrorHandler { error in
      print(error)
    }
    
    // Take photo when button1 is clicked
    takePhotoButton.onClick.append(SCDWidgetsEventHandler { event in
      camera.getPicture(SCDPlatformCameraOptions(sourceType: .camera), onSuccess: _onSuccess, onError: _onError)
    })
    
     // Selecting from library when button2 is clicked
    chooseFromLibraryButton.onClick.append(SCDWidgetsEventHandler { event in
      camera.getPicture(SCDPlatformCameraOptions(sourceType: .photolibrary), onSuccess: _onSuccess, onError: _onError)
    })
    
  }

}

Upload image to Cloud server

A common scenario is to upload images taken onto the web, for instance into Amazon S3. This chapter demonstrate how to use a Http multipart POST to send your image to the web (www.file.io)

📘

Important - Use isoLatin1 for encoding images from camera

The most important learning is to use .data(using: String.Encoding.isoLatin1) to convert the image received from the camera to the Data object.

First, we add a helper class that makes creating a MultiPart request easy. Please pay attention to the code at the end of the file ( isoLatin1 )

import ScadeKit

class SingleMultipartUrlRequest {
    
    /* Generate multipart request for single file */
    let fromUrl : String
    let fileName : String
    let data : String
    
    init(fromUrl:String, fileName : String, data : String) {
        self.fromUrl = fromUrl
        self.fileName = fileName
        self.data = data 
    }
    
    // Support for single impage
    func generate() -> URLRequest {
        
        // get URL object
        let url = URL(string:fromUrl)
        
        // create UrLRequest
        var request = URLRequest(url:url!) 
    
        // set boundary (boundary is abitrary ASCII string to devide multipe parts)
        let boundary = "--12345SCADE"
        
        // compute content type
        let contentType = "multipart/form-data; boundary=\(boundary)"
        
        // content body
        let body = generateBody(boundary: boundary, part: self.data, fileName: self.fileName)
        
        // compute MANDATORY content length
        let contentLength = String(body.count)
        
        // populate request
        request.setValue(contentType, forHTTPHeaderField: "Content-Type")
        request.setValue(contentLength, forHTTPHeaderField: "Content-Length")
        request.httpBody = body
        request.httpMethod = "POST"
        
        return request
    }
    
    func generateBody(boundary:String,part:String, fileName:String) -> Data {
        
        /*
            Build body
            1. Starting boundary 
            2. Content-Disposition (name and filename)
            3. Content-Type
            4. data
            5. Ending boundary prefixed by CR and enclosed in --
        
        */
        
        let cr = "\r\n"
        let imageType = "png"
        
        // 1. Starting boundary
        let startBoundary = "--" + boundary + cr
        
        // 2. Content-Disposition https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
        let contentDisposition = "Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"\r\n"
        
        // 3. ContentType
        let contentType = "Content-Type: image/" + imageType + "\r\n\r\n"
        
        // 4. data
        let content = part
        
        // 5. end
        let endBoundary = cr + "--" + boundary + "--"       

        // create body
        var body = Data()
        
        body.append(startBoundary.data(using: String.Encoding.utf8)!)
        body.append(contentDisposition.data(using: String.Encoding.utf8)!)
        body.append(contentType.data(using: String.Encoding.utf8)!)
        
        // <!-- USE isoLatin1 for the IMAGE 
        body.append(content.data(using: String.Encoding.isoLatin1)!)
        // USE isoLatin1 for the IMAGE -->
        
        body.append(endBoundary.data(using: String.Encoding.utf8)!)
    
        return body
    }
}

Now we can easily create a UrlRequest and use the regular Swift Foundation URLRequest to execute an POST to www.file.io

func uploadToFileIo(imageContent:String) {
      // Upload image to internet server using Swift Foundation and www.File.io
      // - curl -F "[email protected]" https://file.io
      
      // create MultiPart request for one file
      let request : URLRequest = SingleMultipartUrlRequest(
          fromUrl: "https://file.io", 
          fileName: "c1.png", 
          data: imageContent).generate()
      
      // create upload task
      let task = URLSession.shared.dataTask(with: request, completionHandler : { (data, response, error) in
            
          // print error
          if error != nil {
                print(error!.localizedDescription)
            }
            
          // return on empty data
          guard let data = data else { print("No data") ; return }
           
          // parse to json or return with error message
          guard let json = try? JSONSerialization.jsonObject(with: data, options: [])
                  as? [String: Any] else {
                        print("error trying to convert data to JSON")
                        return
                }
                    
         // print result
          print(json!)
            
        })

      // run task
      task.resume();
      
  }
  
  <img src="https://d5rrulwm8ujxz.cloudfront.net/UgLoggingSCADESimulator.png" width="100%" height="100%">

We added another button to send the captured image to the internet server.

Select a picture and press the upload button and the URL of the uploaded image shows up your IDE console.

You can doublecheck the successful upload by retrieving the image in the browser

Updated 2 years ago

Camera control


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.