rails

Swift:シンプル!APIKitで画像投稿してみよう!


どうも、ささおです!

今回はAPIKitで画像投稿してみようと思います!

取り回しが効くようにとにかくシンプルに書くので、カメラ機能だったり、アルバム機能と簡単に組み合わせられると思います。

 

一応続きものになっているので、僕が今まで書いたAPIKit関連の記事を見ていただければと思います。

 

APIKitの始め方を書いた記事

1,APIKitの超簡単な使用方法

2,APIKitとHimotokiの基本のき!

3,RailsのWebアプリとSwiftのiOSアプリを連携させてみた

4,Webアプリ(API)へPOSTしてみる!

 

Railsでの画像アップロード機能を作成する

今回は画像アップロード機能ライブラリは『Shrine』を使用して作成します。

他の有名なアップロード機能ライブラリと言えば『ImageMagick』などがありますね!

:iOS側は画像アップロードライブラリがどちらでも変わらないので安心してください!

 

Shrineのインストール

gemfile内に

gem "shrine", github: 'janko-m/shrine'

を記述して、
$ bundle install

をしてください。

 

shrine.rbを作成する

shrineに関する初期設定を書いていきます。

app/config/initializers/下にshrine.rbを作成してください。

require "shrine"
require "shrine/storage/file_system"

# アップロードするディレクトリの指定
Shrine.storages = {
  cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
  store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"),
}

# プラグイン
Shrine.plugin :activerecord

 

image_dataカラムの追加

前回まで作成していたNoteテーブルに画像を保存するカラムを作成します。

画像が入るカラムなので、imageという名前で作成したいです。

しかし、ここで注意が必要です。

shrineを使用して画像を保存する場合、カラム名には「_data」をつけてあげなければいけません。

なので今回は「image_data」カラムを追加します!

こちらをターミナルで打ってください。

$ rails g migration add_image_data_to_notes image_data:text

$ rails db:migrate

 

uploaderを作成

app/下にuploadersディレクトリを作成してください。

そして、app/uploaders/下にnote_image_uploader.rbを作成してください。

class NoteImageUploader < Shrine
end

Shrineを継承させることが大事です!

 

modelを編集

app/models/Note.rbを編集していきます。

このように編集してください。

include NoteImageUploader[:image]

を追加しました。

NoteモデルとUploaderががっちゃんこしました!

class Note < ApplicationRecord
  include NoteImageUploader[:image]
end

 

ストロングパラメータ

次はapp/controllers/notes_controller.rbを少しだけ編集します。

note_paramsにimageを追加することでストロングパラメータに対応しました。

注意が必要なのが、ここでは「image_data」ではなく「image」にすることです。

def note_params
      params.require(:note).permit(:content, :image)
end

 

あとはformなどをいじればいい感じに画像投稿機能を実装できますが、画像投稿機能を実装するのはiOS側なので、今回は省きます。

 

iOS側に画像投稿機能を作成

まずはアップロードしたい画像を用意して、プロジェクトに入れておいてください。

 

基本的な流れ

1、Web側にデータを送るためにUIImageをDataに変換する。

その時に使用するのが、

 UIImagePNGRepresentation(引数にUIImageを入れる)

これによって、PNGの拡張子がついた画像データを作成することができる。

 

2、Session.send()の引数にDataを入れる。

 

3、bodyParametersに入れてWeb側に送る

 

こんな感じになっています!

 

CreateViewController.swift

前回のコードを編集するとこのようになります。

import UIKit
import APIKit

class CreateViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var titleTextField: UITextField!
    @IBOutlet weak var contentTextField: UITextField!

    // 画像データが入る
    var imageData: Data?
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // aaa.pngは画像の名前です。
        imageView.image = UIImage(named: "aaa.png")
        imageData = UIImagePNGRepresentation(imageView.image!)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    @IBAction func actionCreate(_ sender: Any) {
        Session.send(CreateNoteRequest(title: titleTextField.text, content: contentTextField.text, imageData: imageData!)) { result in
            switch result {
            case .success( _):
                print("作成完了!")
            case .failure(let err):
                print(err)
            }
        }
    }
}

 

まずStoryboardにimageViewを配置して、IBOutlet接続をしました。(ここは省きます

次にviewDidLoadないで配置したimageViewに、先ほど用意した画像を表示するように設定します。

その画像をUIImagePNGRepresentation()でpng形式の画像データに変換して、定義してある変数imageDataに格納します。

そのimageDataをSession.sendの引数に入れています。

 

 

Request.swift

struct CreateNoteRequest: WebRequest {
    
    var path: String {
        return "/notes"
    }
    typealias Response = Note
    
    var method: HTTPMethod {
        return .post
    }
    
    var title: String?
    var content: String?
    var imageData: Data?
    
    var bodyParameters: BodyParameters? {
        let titlePart = MultipartFormDataBodyParameters.Part(data: (title?.data(using: .utf8))!, name: "note[title]")
        let contentPart = MultipartFormDataBodyParameters.Part(data: (content?.data(using: .utf8))!, name: "note[content]")
        let imagePart = MultipartFormDataBodyParameters.Part(
            data: imageData!,
            name: "note[image]",
            mimeType: "image/png",
            fileName: "image.png")
        return MultipartFormDataBodyParameters(parts: [titlePart, contentPart, imagePart])
    }
    
    
    func response(from object: Any, urlResponse: HTTPURLResponse) throws -> CreateNoteRequest.Response {
        return try decodeValue(object)
    }
}

 

Request.swift内で変更した箇所はimageData関係です。

title, content. と同じように imageDataも変数として定義して、Session.sendからimageDataを初期化できるようにしました。

 

あとは、bodyParametersですね。

例によって、MultipartFormDataBodyParameters.PartでbodyParametersで使用できるようにしました。

他のtitleやcontentと違うところは、mimeTypeとfileNameですね。

mimeType

mimeTypeは送るファイルの形式です。

送るデータはpng形式なので、image/pngとしました。

fileName

これはそのまんまファイルの名前ですね。

適当でも問題なくアップロードできます。

 

 

 

これで画像投稿機能の作成完了です!

以外に大したことしないんですが(ライブラリが色々やってくれてる)、調べてもなかなか答えが出なかったのでメモしてみました!

 


ABOUT ME
ささお
3年目iOSエンジニア。 スタートアップで働いておりやす。 プログラミングスクールとエンジニアのキャリアを考えたい。 作ったアプリ - https://apps.apple.com/us/app/loverprofile/id1463563845?l=ja&ls=1