Swift で Hexabase にアクセスしてみる


# はじめに

とあるカンファレンスで `Hexabase` を知る機会があり、せっかくなので触ってみようと思いたち...

## Hexabase とは

> Hexabase は、企業システムの開発に利用できるエンタープライズ BaaS(Backend as a Service)です。Web アプリケーション開発に活用できる高品質なバックエンド機能をクラウドサービスとして提供します。Hexabase を利用することで、開発期間と開発コストをおさえながら、使い勝手の要となるフロントエンド開発に注力できます。

（引用：[Hexabase 開発ガイド](https://devdoc.hexabase.com/)）



今回は 「[Hexabase開発ツアー](https://devdoc.hexabase.com/docs/)」 を一通りやってみて、その過程で API のアクセスがあったので、せっかくなので Swift で操作してみました。

そんなに難しいことはしていないので、メモだと思って呼んでいただけますとmm

# Hexabase を触ってみる

## セットアップ

基本的にはサイトにある通りに進めていけば大丈夫です。

１点注意として、SNS 連携でアカウントを作ってしまうと、パスワードが必要な場面で困ってしまう（パスワードがない）ので、登録したアドレスからパスワードをリセットする必要があります。

## API で操作してみる

この部分は、[Step5.APIでバックエンドにアクセスする](https://devdoc.hexabase.com/docs/introduction/api) に相当します。

### 0. API の簡易クライアントを用意する

過去に色々と記事を書いてきたので、ちゃんとしたものを作りたい人は以下を参照ください🙏
- [URLSession+Combine+CodableでAPIクライアントを作る](https://qiita.com/hcrane/items/4778d7951cd0e1611f5b)
- [Alamofire+RxSwift+CodableでAPIクライアントを作る](https://qiita.com/hcrane/items/d2d316b086745417ffae)

今回は簡単にアクセスしたいので、簡易なものを用意します。

準備として以下を用意しておき

```.swift
import Combine

final class APIClient {
    static let shared = APIClient()
    private init() {}
    
    private let decorder: JSONDecoder = {
        let jsonDecoder = JSONDecoder()
        jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase
        return jsonDecoder
    }()
}
```

こちらにそれぞれのリクエストを追加していきます。

### 1. APIでloginする

以下が仕様書になっているので

- Method

```
Post    https://api.hexabase.com/api/v0/login
```

- Request

```
{
  "email": "foo@example.net",
  "password": "**********"
}
```

- Response

```.json
{
    "token": "xxxx"
}
```

それに対応してレスポンスクラスを用意し

```.swift
struct LoginResponseEntity: Decodable {
    let token: String
}
```

通信するために先ほど用意したクラスに、メソッドを追加します。

```APIClient.swift    
func login(email: String, password: String) -> AnyPublisher<LoginResponseEntity, Error> {
    let params = ["email": email, "password": password]
    var request = URLRequest(url: URL(string: "https://api.hexabase.com/api/v0/login")!)
    request.httpMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    request.httpBody = try! JSONSerialization.data(withJSONObject: params)
    
    return URLSession.shared
        .dataTaskPublisher(for: request)
        .map(\.data)
        .decode(type: LoginResponseEntity.self, decoder: decorder)
        .eraseToAnyPublisher()
}
```

実際にアクセスする際は

```.swift
APIClient.shared.login(email: "your_address@xxx.com", password: "your_password")
    .sink(receiveCompletion: { result in
        switch result {
        case .failure(let error):
            print("failure", error)
        case .finished:
            print("finished")
        }
    }, receiveValue: { response in
        print("receiveValue", response.token)
    })
    .store(in: &cancellables)
```

これでトークンを取得できるようになります。


### 2. APIでワークスペースを選択する

以下が仕様書になっているので

- Method

```
Get    https://api.hexabase.com/api/v0/workspaces
```

- Request Header

```
Authorization: Bearer XXXXXX_TOKEN_XXXXXX
```

- Response

```.json
{
    "workspaces": [
        {
            "workspace_id": "60594049dabf490001d84684",
            "workspace_name": "demo_workspace"
        },
        {
            "workspace_id": "60d45d336d66ed844b1190fe",
            "workspace_name": "demo_developer"
        }
    ],
    "current_workspace_id": "60d45d336d66ed844b1190fe"
}
```

それに対応してレスポンスクラスを用意し

```.swift
struct WorkspaceResponseEntity: Decodable {
    let workspaces: [WorkspaceEntity]
}

struct WorkspaceEntity: Decodable {
    let workspaceId: String
    let workspaceName: String
}
```

通信するために先ほど用意したクラスに、メソッドを追加します。

```APIClient.swift    
func getWorkspaces(token: String) -> AnyPublisher<WorkspaceResponseEntity, Error> {
    var request = URLRequest(url: URL(string: "https://api.hexabase.com/api/v0/workspaces")!)
    request.httpMethod = "GET"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    request.setValue( "Bearer \(token)", forHTTPHeaderField: "Authorization")

    return URLSession.shared
        .dataTaskPublisher(for: request)
        .map(\.data)
        .decode(type: WorkspaceResponseEntity.self, decoder: decorder)
        .eraseToAnyPublisher()
}
```

実際にアクセスする際は

```.swift
APIClient.shared.getWorkspaces(token: "your_token")
    .sink(receiveCompletion: { result in
        switch result {
        case .failure(let error):
            print("failure", error)
        case .finished:
            print("finished")
        }
    }, receiveValue: { workspace in
        print("receiveValue", workspace)
    })
    .store(in: &cancellables)
```

これでワークスペースの一覧を取得できます。

### 3. APIでアプリケーション情報を取得する

以下が仕様書になっているので

- Method

```
Get    https://api.hexabase.com/api/v0/workspaces/:workspace-id/applications
```

- Params

```
workspace_id: xxxx_your_workspace_id_xxxx
```

- Request Header

```
Authorization: Bearer XXXXXX_TOKEN_XXXXXX
```

- Response

```.json
[
    {
        "application_id": "60d45d336d66ed844b119101",
        "name": "新しいアプリケーション",
        "display_id": "new_app",
        "datastores": [
            {
                "datastore_id": "60d45d51dbf1fd0948253d8d",
                "name": "新しいデータベース",
                "display_id": "new_db"
            }
        ]
    },
    {
        "application_id": "60d5b2bbfc2b8d27e799d8b6",
        "name": "hexa-simple-example",
        "display_id": "APP-todo1",
        "datastores": [
            {
                "datastore_id": "60d5b2bb77d6297bc49699de",
                "name": "TODOサンプル",
                "display_id": "Db-todo1"
            }
        ]
    }
]
```

それに対応してレスポンスクラスを用意し

```.swift
struct WorkspaceObjectEntity: Decodable {
    let applicationId: String
    let name: String
    let displayId: String
    let datastores: [WorkspaceData]
}

struct WorkspaceData: Decodable {
    let datastoreId: String
    let name: String
    let displayId: String
}
```

通信するために先ほど用意したクラスに、メソッドを追加します。

```APIClient.swift
func accessWorkspace(token: String, id: String) -> AnyPublisher<[WorkspaceObjectEntity], Error> {
    var request = URLRequest(url: URL(string: "https://api.hexabase.com/api/v0/workspaces/\(id)/applications")!)
    request.httpMethod = "GET"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    request.setValue( "Bearer \(token)", forHTTPHeaderField: "Authorization")

    return URLSession.shared
        .dataTaskPublisher(for: request)
        .map(\.data)
        .decode(type: [WorkspaceObjectEntity].self, decoder: decorder)
        .eraseToAnyPublisher()
}
```

実際にアクセスする際は

```.swift
APIClient.shared.accessWorkspace(token: "your_token", id: "your_workspace_id")
    .sink(receiveCompletion: { result in
        switch result {
        case .failure(let error):
            print("failure", error)
        case .finished:
            print("finished")
        }
    }, receiveValue: { object in
        print("receiveValue", object)
    })
    .store(in: &self.cancellables)
```

ワークスペースにアクセスできました。

一旦ここまでとしますmm

# 終わりに

ドキュメントもしっかりしているので、比較的簡単に実装をすることができました！

チャットサービスも可能で、Webフロントはデモコードがあったので、興味がある人は呼んでみると面白いかと思います。

今回はデモコードとして簡単なコードになっていることをご容赦くださいmm