Search code examples
nativescript-vue

Send a POST request, receive a PDF in return and save it to the filesystem in NativeScript


I want to send a POST request containing JSON to a server for processing. The server will then return a PDF which I want to receive, save to the filesystem and display using nativescript-pdf-view. However, File.writeSync() will not accept the ArrayBuffer that I get from the server response. Instead, it expects the native variants NSData (iOS) and ByteArray (Android). How can I convert the ArrayBuffer to the native byte arrays?


Solution

  • Sending the POST request

    As explained by the official documentation, a POST request can be sent using the getBinary() function:

    import { getBinary } from 'tns-core-modules/http'
    
    getBinary({
      url: 'https://example.com/foo/bar/generate-pdf',
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      content: JSON.stringify(someObject),
    })
    

    Here, someObject is the object you want to convert to JSON and send to the server.

    Receiving, converting and saving the file / PDF

    The response will contain the PDF or any other file as an ArrayBuffer. As mentioned in the question, the File.writeSync() method expects NSData (iOS) and ByteArray (Android). So, we need to get a file handler, convert our file and save it to the filesystem.

    import { getBinary } from 'tns-core-modules/http'
    import { isAndroid } from 'tns-core-modules/platform'
    import { File, knownFolders, path } from 'tns-core-modules/file-system'
    
    getBinary({
      url: 'https://example.com/foo/bar/generate-pdf',
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      content: JSON.stringify(someObject),
    }).then((buffer) => {
    
      // Get file handler
      const documentsFolder = knownFolders.documents()
      const destination = path.join(documentsFolder.path, 'result.pdf')
      const file = File.fromPath(destination)
    
      // Convert buffer to byte array
      let byteArray
      if (isAndroid) {
        byteArray = Array.create('byte', buffer.byteLength)
        for (let i = 0; i < buffer.length; i++) {
          byteArray[i] = new java.lang.Byte(buffer[i])
        }
      } else {
        byteArray = NSData.dataWithBytesLength(buffer, buffer.byteLength)
      }
    
      // Save file
      file.writeSync(byteArray, (error) => {
        console.log(error)
      });
    })
    

    Why the File.writeSync() method doesn't convert this automatically is a miracle to me. Maybe someone else can explain!

    Displaying the PDF

    To display the PDF, you can use nativescript-pdf-view. Just use the filehandler to get the path file.path or the destination variable and pass it to the src property of the PDFView component. This might look like this:

    <template>
      <Page>
        <ActionBar title="PDF" />
        <GridLayout rows="*" columns="*">
          <PDFView :src="path" row="0" col="0" />
        </GridLayout>
      </Page>
    </template>
    
    <script lang="ts">
    export default {
      name: 'PdfViewer',
      props: {
        path: {
          type: String,
          required: true,
        },
      },
    }
    </script>
    

    Make sure to install the plugin, of course and introduce the element to Vue.js, first:

    // main.ts
    
    Vue.registerElement('PDFView', () => require('nativescript-pdf-view').PDFView)