Integrating Google Books API with HarmonyOS Next using Remote Communication Kit (RCP)

Introduction

Greetings everyone! This article will guide you through the process of integrating with the Google Books API to retrieve book data within an HarmonyOS Next application, utilizing Huawei’s Remote Communication Kit (RCP). We’ll demonstrate a practical example, showing how to fetch and display information about books.

Understanding the Remote Communication Kit (RCP)

The Remote Communication Kit (RCP) is a fundamental module in HarmonyOS that empowers applications with robust HTTP data request capabilities. It allows your app to perform various HTTP operations, including common methods like GET, POST, PUT, DELETE, and more. For our demonstration, RCP will be instrumental in making GET requests to the Google Books API.

Let’s dive into an example where we’ll use RCP to retrieve book details from the Google Books API and render them on the user interface.

Project Implementation Overview

To effectively manage data and UI, our project follows a structured approach involving data models, a network service layer, a ViewModel, and UI components.

Data Models

We define a series of TypeScript interfaces to represent the structure of the data we expect from the Google Books API:

  • BookModel: This interface encapsulates the essential details of a book, such as title, page count, image URL, description, and categories, in a simplified format suitable for our application.
  • ImagesModel: Represents the image links for a book, specifically smallThumbnail.
  • RSPModel: The top-level response structure from the API, containing an array of items.
  • volModel: An intermediate model representing an individual item from the API response, holding volumeInfo.
  • VolumeInfoModel: Contains the detailed information about a book’s volume, including title, page count, image links, description, and categories, directly mapping to the API’s volumeInfo object.

These models ensure type safety and clear data handling throughout the application.

Constants

To maintain a clean and configurable codebase, we define constants for our API endpoints:
* BASE_URL: The root URL for the Google Books API (https://www.googleapis.com/books/v1/`).
*
url`: The specific API endpoint, including query parameters, to fetch books and select specific fields like title, page count, image links, description, and categories.

Network Service with RCP

The core of our data fetching mechanism resides in a utility function, getHttp, which leverages the rcp module:

import { rcp } from '@kit.RemoteCommunicationKit';
import { BASE_URL } from './Constants';

function sessionConfig(): rcp.SessionConfiguration {
  return {
    baseAddress: BASE_URL
  }
}

export function getHttp(url: string) {
  const session = rcp.createSession(sessionConfig());
  return session.get(`${url}`).then((res) => res.toJSON()).finally(() => {
    session.close();
  });
}

This function initiates an RCP session with the defined base address, sends a GET request to the specified URL, converts the response to JSON, and critically, ensures the session is closed afterward to release resources.

ViewModel for Data Management

The BookViewModel acts as a central point for fetching and preparing book data for the UI. It’s implemented as a singleton and uses @Observed to enable reactive updates to the UI.

import BookModel from '../model/BookModel';
import { RSPModel } from '../model/RSPModel';
import { volModel } from '../model/volModel';
import { getHttp } from '../utils/RCP';
import { url } from '../utils/Constants';

@Observed
export class BookViewModel {
  private static _instance: BookViewModel;
  allBooks: BookModel[] = []

  private constructor() {
  }

  public static get instance(): BookViewModel {
    if (!BookViewModel._instance) {
      BookViewModel._instance = new BookViewModel();
    }
    return BookViewModel._instance;
  }

  async getBooks() {
    const response = await getHttp(url) ?? []
    const rsp = response as RSPModel
    this.allBooks = rsp.items.map<BookModel>((i: volModel) => {
      return {
        title: i.volumeInfo.title,
        pageCount: i.volumeInfo.pageCount,
        image: i.volumeInfo.imageLinks.smallThumbnail,
        description: i.volumeInfo.description,
        categories: i.volumeInfo.categories
      }
    })
    console.info('getBooks', JSON.stringify(this.allBooks, null, 3))
  }
}

The getBooks method asynchronously calls our getHttp service, processes the raw API response into our simplified BookModel array, and updates the allBooks property, which then notifies observing UI components.

UI Components

BookCardComponent

This is a reusable component responsible for displaying the details of a single book. It receives title, pageCount, image, and categories as properties.

@Component
export struct BookCardComponent {
  @Prop title: string
  @Prop pageCount: number
  @Prop image: string
  @Prop categories: Array<string>

  build() {
    Column() {
      Image(this.image).width(60).height(70).padding({ bottom: 3 })
      Text(this.categories.join(', ')).fontSize(9).fontColor(Color.White).padding({ bottom: 3 })
      Text(this.title).fontSize(11).fontColor(Color.White).padding({ bottom: 3 })
      Text(this.pageCount.toString() + ' pages').fontSize(9).fontColor(Color.White)
    }.padding(2).width('70%').borderRadius(20)
  }
}

It arranges an image, categories, title, and page count within a Column layout, styled for presentation.

BooksPage

The BooksPage is the main entry point for displaying the list of books. It utilizes the BookViewModel and the BookCardComponent.

import { BookCardComponent } from '../component/BookCardComponent'
import { BookViewModel } from '../viewmodel/BookViewModel';
import BookModel from '../model/BookModel';

@Entry
@Component
struct HomePage {
  scroller: Scroller = new Scroller()
  @State viewModel: BookViewModel = BookViewModel.instance;

  aboutToAppear() {
    this.viewModel.getBooks()
  }

  build() {
    Column() {
      Text($r('app.string.books'))

      Scroll(this.scroller) {
        Column() {
          ForEach(this.viewModel.allBooks, (book: BookModel, index: number) => {
            BookCardComponent({
              image: book.image,
              title: book.title,
              pageCount: book.pageCount,
              categories: book.categories
            }).padding({ bottom: 4 })
            Divider().height(2).color(0xCCCCCC).padding({ left: 40, right: 40, bottom: 10 });
          }, (item: string, index: number) => item)
        }.padding({ bottom: 25 })
      }.width('100%').height('100%')
    }.backgroundColor(Color.Black).padding({ top: 10, bottom: 10 }).width('100%').height('100%')
  }
}

Upon the page appearing (aboutToAppear), it triggers the getBooks method in the ViewModel. The build method then uses a Scroll container with a ForEach loop to render each book using the BookCardComponent, dynamically updating as allBooks in the ViewModel changes.

Output

The application successfully fetches and displays book information, presenting a scrollable list of book cards with their respective images, titles, page counts, and categories.

Conclusion

This article has demonstrated a complete workflow for integrating with external APIs, specifically the Google Books API, within an ArkTS-based HarmonyOS Next project. By leveraging Huawei’s Remote Communication Kit (RCP), we established an efficient network service. Combined with a clear ViewModel architecture and modular UI components, we achieved a clean, maintainable, and responsive application capable of fetching and displaying dynamic data.

References

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.
You need to agree with the terms to proceed