RailsとiOS(Swift)とAWS間をAPIモードで、データファイル保存呼び出しさせるまで③

### 前書き
初心者向けに自分なりの言葉で柔らかく書いたつもりです。非エンジニアですが、メモ目的とアウトプットの恩恵のために記事にしました。間違っているところなどありましたら、コメントをください。
あとコードにコメントが多く可読性が悪いかもしれません。そこは申し訳なく…

iosからAWSまでの一連の流れを記事にしているためかなり長くなっているので、記事を分けて書いています。

- [RailsとiOS(Swift)とAWS間をAPI通信で、データファイル保存させるまで①devise token auth編](https://qiita.com/Rafflesia/items/a80a495a8488755a4891)
- [RailsとiOS(Swift)とAWS間をAPI通信で、データファイル保存させるまで②Alamofire、Swiftyjson編 devise_token_auth(認証)のデータの送受信](https://qiita.com/Rafflesia/items/a175a15a46c10d5a8609)
- [RailsとiOS(Swift)とAWS間をAPI通信で、データファイル保存させるまで④carrierwave編](https://qiita.com/Rafflesia/items/6cd5d6e25f15e1ba876f)

③までのものがこちら
https://github.com/ToruMizuno/railswiftapi
④までのものがこちら
https://github.com/ToruMizuno/railswiftapi2

## apiモードを使ってdevise_token_auth(認証)以外のデータの送受信(Rails側)

### 認証用のUser以外のモデルの作成

Postをscaffoldで作成

```:
$ rails generate scaffold api/post
```






### scaffoldで作成されたControllerの編集

全て外部からRails、Railsから外部への通信のためのコントローラーアクション
RailsのViewでブラウザに表示させる処理は、また別にコントローラーを作成してアクションで処理させるようにすると良い

```Ruby:app/controllers/api/posts_controller.rb

class Api::PostsController < ApplicationController
  # コメント
  # before_action :set_api_post, only: [:show, :update, :destroy]
  # 追加
  before_action :set_api_post, only: [:update, :destroy]
  before_action :authenticate_api_user!# 認証制限

  # GET /api/posts
  # GET /api/posts.json
  def index
    # 全てのPostを取ってくる
    @api_posts = Api::Post.all
    # 追加
    render 'index', :formats => [:json], :handlers => [:jbuilder]
  end

  # GET /api/posts/1
  # GET /api/posts/1.json
  def show
    # 追加
    # Postのidで検索
    @api_post = Api::Post.find_by(id: params[:id])
    render 'show', :formats => [:json], :handlers => [:jbuilder]
  end

  # createは書き換えて使用する
  # POST /api/posts
  # POST /api/posts.json
  def create
    # @api_post = Api::Post.new(api_post_params)
    #
    # if @api_post.save
    #   render :show, status: :created, location: @api_post
    # else
    #   render json: @api_post.errors, status: :unprocessable_entity
    # end

    @api_post = Api::Post.new(create_params)

    begin
      Api::Post.transaction do
        @api_post.save!# save!で例外を出力する
      end
      # 正常に動作した場合の処理

    rescue ActiveRecord::RecordInvalid => e
      # 例外が発生した場合の処理
      @api_post = e.record
     end
  end

  # updateはupdate_web_post_idを使うので必要ないかも？
  # PATCH/PUT /api/posts/1
  # PATCH/PUT /api/posts/1.json
  def update
    if @api_post.update(api_post_params)
      render :show, status: :ok, location: @api_post
    else
      render json: @api_post.errors, status: :unprocessable_entity
    end
  end

  # destroyはそのまま使用する
  # DELETE /api/posts/1
  # DELETE /api/posts/1.json
  def destroy
    @api_post.destroy
  end

  # updateはこれを使う
  # 追加
  def update_post_id
    # post_idで1つのレコードを検索して取って来てもらう
    post = Api::Post.find_by(id: params[:id])
    post.update_attributes(update_params)
  end

  # 追加
  # web_user_idを使って自分のpostのみを、全て取得する
  def my_post_show_web_user_id
    # webuseridで引っかかる全てのレコードを検索して取って来てもらうようにする
    @api_posts = Api::Post.where(web_user_id: params[:id])
  end

  private

    # 冒頭のbefore_actionで使用する
    # Use callbacks to share common setup or constraints between actions.
    def set_api_post
      @api_post = Api::Post.find(params[:id])
    end

    # 標準のupdateであるが、必要でないかも？
    # Never trust parameters from the scary internet, only allow the white list through.
    def api_post_params
      params.fetch(:api_post, {})
    end



    # createで使用する
    # 追加
    # postを新規作成した場合、これだけのカラムを作ってくれる
    def create_params
      params.permit(
        :web_user_id,
        :post_image,# 画像しか作成しない。画像を作成と共に他のデータを送信しないから。更新でデータ保存する
        :title_name
      )
    end

    # update_post_idで使用する
    # 追加
    # postを更新する場合、これだけのカラムを更新する
    def update_params
      params.permit(
        :web_user_id,
        :post_image,
        :title_name
       )
    end

end
```





### scaffoldで出来たマイグレーションファイルを編集

```Ruby:
class CreateApiPosts < ActiveRecord::Migration[5.1]
  def change
    create_table :api_posts do |t|

      t.integer  :web_user_id
      t.string   :post_image# 画像保存はstring
      t.string  :title_name
      
      t.timestamps
    end
  end
end
```






### テーブル作成

```:
$ rake db:migrate
```






### コントローラーアクションに対応したjbuilderを作成して編集

******.json.jbuilderファイルを新規作成して下記を記述


```Ruby:app/views/api/posts/index.json.jbuilder
# コメント
# json.array! @api_posts, partial: 'api_posts/api_post', as: :api_post
json.set! :posts do
  json.array! @api_posts, :id, :user_id, :post_image, :title_name
end
```

```Ruby:app/views/api/posts/show.json.jbuilder
# コメント
# json.partial! "api_posts/api_post", api_post: @api_post
json.extract! @api_post, :id, :user_id, :post_image, :title_name
```

```Ruby:app/views/api/posts/create.json.jbuilder
# 送るjsonの設定
json.merge! @api_post.attributes
```

```Ruby:app/views/api/posts/my_post_show_web_user_id.json.jbuilder
json.array! @api_posts, :id, :web_user_id, :post_image, :title_name
```






### 作成したコントローラーアクションのルートを定義

```Ruby:config/routes.rb
Rails.application.routes.draw do
  # コメント
  # namespace :api do
  #   resources :posts
  # end

  # namespaceは標準のルートの/の間にフォルダー名を入れられる
  namespace :api, default: {format: :json} do
    # resources :posts, only: :create# createのみのルートを作る
    resources :posts do# 7つのアクションのルートを作る
      member do
        get :my_post_show_web_user_id# 追加でid付き(web_user_id)のルートを作る
        put :update_post_id# 追加でid付き(web_user_id)のルートを作る
      end
    end
  end

  # コメント
  # mount_devise_token_auth_for 'User', at: 'auth'
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
  namespace :api do
    mount_devise_token_auth_for 'User', at: 'auth', controllers: {
        registrations: 'api/auth/registrations'
    }
  end
end
```





## apiモードを使ってdevise_token_auth(認証)以外のデータの送受信(iOS側)

PostViewController.swiftファイルを作成して記述していく

### ライブラリーをimportする

```Swift:PostViewController.swift
import UIKit
import Alamofire
import SwiftyJSON
```






### パラメータの保存先

Post.swiftファイルを作成して記述する

```Swift:model/Post.swift
import UIKit

class Post: NSObject {
    var id: Int = 0
    var web_user_id: Int = 0//所属しているUserのidか何か
    var postImage: String! = ""//プロパティ
    var titlename: String = ""//プロパティ
}
```






### ストーリーボードとの接続とパラメータ用の変数の用意

```Swift:PostViewController.swift

    @IBOutlet weak var idTextField: UITextField!
    
    var accesstoken: String!
    var client: String!
    var uid: String!
    var webuser: Websuer!

    
    var posts = [Post]()//全てのPostデータを入れる変数
    var myposts = [Post]()//自分のPostデータを入れる変数
```






### 認証時に取ってきたトークンとユーザー情報の取得

```Swift:PostViewController.swift
override func viewDidLoad() {
        super.viewDidLoad()        
        
        //appDelegateからアクセス制限用のトークンと、通信したいuserの情報を取ってくる
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        webuser = appDelegate.webuser
        accesstoken = appDelegate.accesstoken
        client = appDelegate.client
        uid = appDelegate.uid
        print("\(webuser)")
        print("\(accesstoken)")
        print("\(client)")
        print("\(uid)")
   }
```






### 文字列のデータの手入力を省くために、テキスト自動生成用の関数を用意

```Swift:PostViewController.swift
//ランダムに文字列を生成してくれる関数
    func generate(length: Int) -> String {
        let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        var randomString: String = ""
        
        for _ in 0..<length {
            let randomValue = arc4random_uniform(UInt32(base.count))
            randomString += "\(base[base.index(base.startIndex, offsetBy: Int(randomValue))])"
        }
        return randomString
    }
```




### ボタンでRails側のposts_controller.rbの各種アクションを発動
Railsの各種postのroutesにアクセスして、アクションを実行してもらう。
各種アクションで取得した値をjbuilderを通してレスポンスする。

indexアクション

```Swift:PostViewController.swift
    //indexアクション
    //全てのpostを取ってくる
    @IBAction func indexPostButtonClicked(_ sender: Any) {
        
        //ログインやサインアップでこれらの情報を取ってこれていた場合は
        if self.accesstoken != nil && self.client != nil && self.uid != nil {
            
            //取り出したトークン情報をヘッダーに格納
            var headers: [String : String] = [:]
            headers["access-token"] = self.accesstoken
            headers["client"] = self.client
            headers["uid"] = self.uid
            
            print("accesstoken: \(String(describing: self.accesstoken))")
            print("client: \(String(describing: self.client))")
            print("uid: \(String(describing: self.uid))")
            
            //GET /api/posts(.:format)
            Alamofire.request("http://localhost:3000/api/posts", method: .get, headers: headers).responseJSON { response in
                print("Request: \(String(describing: response.request))")
                print("Response: \(String(describing: response.response))")
                print("Error: \(String(describing: response.error))")
                
                //アクセスできた場合
                if response.response?.statusCode == 200 {
                    print("通信に成功しました")
                    
                    print("Request: \(String(describing: response.request))")
                    print("Response: \(String(describing: response.response))")
                    print("Error: \(String(describing: response.error))")
                    
                    //エラーがなかった場合
                    if response.error == nil {
                        
                        self.posts.removeAll()
                        
                        //responseから保存したデータのIDを抜き出す
                        let json:JSON = JSON(response.result.value ?? kill)
                        
                        json.forEach { (arg) in
                            let (_, json) = arg
                            
                            //Postに保存
                            let post = Post()
                            
                            //パラメーターセット
                            post.id = json["id"].intValue
                            post.web_user_id = json["web_user_id"].intValue
                            post.titlename = json["title_name"].stringValue
                            
                            print("id: \(String(describing: post.id))")
                            print("web_user_id: \(String(describing: post.web_user_id))")
                            print("title_name: \(String(describing: post.titlename))")
                            
                            self.posts.append(post)
                        }
                        
                    }
                    
                    //アクセスできなかった場合
                } else {
                    
                    print("アクセスできませんでした")
                    
                }
                
            }
            
            //ヘッダー情報を取ってこれていなかった場合
        } else {
            
            //ログインしていないと警告を出す
            
        }
        
    }
```





showアクション

```Swift:PostViewController.swift
    //showアクション
    //postのidで取ってくる
    @IBAction func showPostButtonClicked(_ sender: Any) {
        
        //ログインやサインアップでこれらの情報を取ってこれていた場合は
        if self.accesstoken != nil && self.client != nil && self.uid != nil {
            
            let postid: Int = Int(self.idTextField.text!)!//TextFieldにIDを入力しないとエラーになるよ
            let accesstoken: String = self.accesstoken
            let client: String = self.client
            let uid: String = self.uid
            
            print("postid: \(String(describing: postid))")
            print("accesstoken: \(String(describing: accesstoken))")
            print("client: \(String(describing: client))")
            print("uid: \(String(describing: uid))")
            
            //取り出したトークン情報をヘッダーに格納
            var headers: [String : String] = [:]
            headers["access-token"] = accesstoken
            headers["client"] = client
            headers["uid"] = uid
            
            print("requestURL_post: http://localhost:3000/api/posts/\(postid)")
            
            //GET /api/posts/:id(.:format)
            //ログインした際にとって来たトークン情報と、自分のwebuserid(アカウントID)を使って自分のpostのみを取ってくる
            Alamofire.request("http://localhost:3000/api/posts/\(postid)", method: .get, encoding: JSONEncoding.default, headers: headers).responseJSON { response in
                
                //アクセスできた場合
                if response.response?.statusCode == 200 {
                    
                    print("Request: \(String(describing: response.request))")
                    print("Response: \(String(describing: response.response))")
                    print("Error: \(String(describing: response.error))")
                    print("Value: \(String(describing: response.result.value))")
                    
                    //responseから保存したデータのIDを抜き出す
                    let json:JSON = JSON(response.result.value ?? kill)
                    print("json: \(String(describing: json))")
                    
                    //Postに保存
                    let post = Post()
                    
                    //パラメーターセット
                    post.id = json["id"].intValue
                    post.web_user_id = json["web_user_id"].intValue
                    post.titlename = json["title_name"].stringValue
                    
                    print("id: \(String(describing: post.id))")
                    print("web_user_id: \(String(describing: post.web_user_id))")
                    print("title_name: \(String(describing: post.titlename))")
                    
                    //アクセスできなかった場合は再度取ってくるように通信する
                } else {
                    
                    //アクセスできなかった場合は再度取ってくるように通信する
                    
                    print("Error: \(String(describing: response.error))")
                }
                
            }
            
            //ヘッダー情報を取ってこれていなかった場合
        } else {
            
            //ログインしていないと警告を出す
            
        }
        
        
    }
```





createアクション

```Swift:PostViewController.swift
    //createアクション
    //普通に保存する
    @IBAction func createPostButtonClicked(_ sender: Any) {
        
        let rndstr = generate(length: 32)
        
        //パラメーターを辞書で入れていく//左辺がカラム名、右辺が値
        var params: [String: Any] = [:]
        params["web_user_id"] = webuser.web_user_id
        params["title_name"] = rndstr
        
        //ログインやサインアップでこれらの情報を取ってこれていた場合は
        if self.accesstoken != nil && self.client != nil && self.uid != nil {
            
            //取り出したトークン情報をヘッダーに格納
            var headers: [String : String] = [:]
            headers["access-token"] = self.accesstoken
            headers["client"] = self.client
            headers["uid"] = self.uid
            
            print("accesstoken: \(String(describing: self.accesstoken))")
            print("client: \(String(describing: self.client))")
            print("uid: \(String(describing: self.uid))")
            
            //POST /api/posts(.:format)
            Alamofire.request("http://localhost:3000/api/posts", method: .post, parameters: params, headers: headers).responseJSON { response in
                print("Request: \(String(describing: response.request))")
                print("Response: \(String(describing: response.response))")
                print("Error: \(String(describing: response.error))")
                
                //アクセスできた場合
                if response.response?.statusCode == 200 {
                    print("通信に成功しました")
                    
                    print("Request: \(String(describing: response.request))")
                    print("Response: \(String(describing: response.response))")
                    print("Error: \(String(describing: response.error))")
                    
                    //エラーがなかった場合
                    if response.error == nil {
                        
                        //Postに保存
                        //保存後にjsonが返って来ないのでそのまま保存前の値を代入する
                        let post = Post()
                        
                        post.web_user_id = self.webuser.web_user_id
                        post.titlename = rndstr
                        
                        print("web_user_id: \(String(describing: post.web_user_id))")
                        print("title_name: \(String(describing: post.titlename))")
                    }
                    
                    //アクセスできなかった場合
                } else {
                    
                    print("アクセスできませんでした")
                    
                }
                
            }
            
            //ヘッダー情報を取ってこれていなかった場合
        } else {
            
            //ログインしていないと警告を出す
            
        }
        
    }
```





updateアクション

```Swift:PostViewController.swift
    //updateアクション
    //post_idで更新する
    @IBAction func updatePostButtonClicked(_ sender: Any) {
        
        let rndstr = generate(length: 32)
        
        //パラメーターを辞書で入れていく//左辺がカラム名、右辺が値
        var params: [String: Any] = [:]
        //params["post_image"] =
        params["title_name"] = rndstr
        
        //ログインやサインアップでこれらの情報を取ってこれていた場合は
        if self.accesstoken != nil && self.client != nil && self.uid != nil {
            
            let postid: Int = Int(idTextField.text!)!
            
            //取り出したトークン情報をヘッダーに格納
            var headers: [String : String] = [:]
            headers["access-token"] = self.accesstoken
            headers["client"] = self.client
            headers["uid"] = self.uid
            
            print("accesstoken: \(String(describing: self.accesstoken))")
            print("client: \(String(describing: self.client))")
            print("uid: \(String(describing: self.uid))")
            
            //PUT /api/posts/:id/update_post_id(.:format)
            Alamofire.request("http://localhost:3000/api/posts/\(postid)/update_post_id", method: .put, parameters: params, headers: headers).responseJSON { response in
                print("Request: \(String(describing: response.request))")
                print("Response: \(String(describing: response.response))")
                print("Error: \(String(describing: response.error))")
                
                //型を指定した定数にいれないと比較演算子が使えない
                let res: Int = (response.response?.statusCode)!
                
                //アクセスできた場合(更新ができた場合)
                if res >= 200 && res < 300 {
                    print("通信に成功しました")
                    
                    print("Request: \(String(describing: response.request))")
                    print("Response: \(String(describing: response.response))")
                    print("Error: \(String(describing: response.error))")
                    
                    //アクセスできなかった場合
                } else {
                    
                    print("アクセスできませんでした")
                    
                }
                
            }
            
            //ヘッダー情報を取ってこれていなかった場合
        } else {
            
            //ログインしていないと警告を出す
            
        }
        
    }
```





destroyアクション

```Swift:PostViewController.swift
    //destroyアクション
    //post_idで削除する
    @IBAction func destroyPostButtonClicked(_ sender: Any) {
        
        //ログインやサインアップでこれらの情報を取ってこれていた場合は
        if self.accesstoken != nil && self.client != nil && self.uid != nil {
            
            let postid: Int = Int(idTextField.text!)!
            
            //取り出したトークン情報をヘッダーに格納
            var headers: [String : String] = [:]
            headers["access-token"] = self.accesstoken
            headers["client"] = self.client
            headers["uid"] = self.uid
            
            print("accesstoken: \(String(describing: self.accesstoken))")
            print("client: \(String(describing: self.client))")
            print("uid: \(String(describing: self.uid))")
            
            //DELETE /api/posts/:id(.:format)
            Alamofire.request("http://localhost:3000/api/posts/\(postid)", method: .delete, headers: headers).responseJSON { response in
                print("Request: \(String(describing: response.request))")
                print("Response: \(String(describing: response.response))")
                print("Error: \(String(describing: response.error))")
                
                //型を指定した定数にいれないと比較演算子が使えない
                let res: Int = (response.response?.statusCode)!
                
                //アクセスできた場合(更新ができた場合)
                if res >= 200 && res < 300 {
                    print("通信に成功しました")
                    
                    print("Request: \(String(describing: response.request))")
                    print("Response: \(String(describing: response.response))")
                    print("Error: \(String(describing: response.error))")
                    
                    //アクセスできなかった場合
                } else {
                    
                    print("アクセスできませんでした")
                    
                }
                
            }
            
            //ヘッダー情報を取ってこれていなかった場合
        } else {
            
            //ログインしていないと警告を出す
            
        }
        
    }
```





my_post_show_web_user_idアクション

```Swift:PostViewController.swift
    //my_post_show_web_user_idアクション
    //自分のpostのみ取ってくる
    @IBAction func myPostShowButtonClicked(_ sender: Any) {
        
        //ログインやサインアップでこれらの情報を取ってこれていた場合は
        if self.accesstoken != nil && self.client != nil && self.uid != nil {
            
            let webuserid: Int = self.webuser.web_user_id
            let accesstoken: String = self.accesstoken
            let client: String = self.client
            let uid: String = self.uid
            
            print("WebuserID: \(String(describing: webuserid))")
            print("accesstoken: \(String(describing: accesstoken))")
            print("client: \(String(describing: client))")
            print("uid: \(String(describing: uid))")
            
            //取り出したトークン情報をヘッダーに格納
            var headers: [String : String] = [:]
            headers["access-token"] = accesstoken
            headers["client"] = client
            headers["uid"] = uid
            
            print("requestURL_post: http://localhost:3000/api/posts/\(webuserid)/my_post_show_web_user_id.json")
            
            //GET /api/posts/:id/my_post_show_web_user_id(.:format)
            //ログインした際にとって来たトークン情報と、自分のwebuserid(アカウントID)を使って自分のpostのみを取ってくる
            Alamofire.request("http://localhost:3000/api/posts/\(webuserid)/my_post_show_web_user_id.json", method: .get, encoding: JSONEncoding.default, headers: headers).responseJSON { response in
                
                //アクセスできた場合
                if response.response?.statusCode == 200 {
                    
                    print("Request: \(String(describing: response.request))")
                    print("Response: \(String(describing: response.response))")
                    print("Error: \(String(describing: response.error))")
                    print("Value: \(String(describing: response.result.value))")
                    
                    self.myposts.removeAll()
                    
                    //responseからとって来たデータを抜き出す
                    let json:JSON = JSON(response.result.value ?? kill)
                    json.forEach { (arg) in
                        let (_, json) = arg
                        
                        //Postに保存
                        let post = Post()
                        
                        //パラメーターセット
                        post.id = json["id"].intValue
                        post.web_user_id = json["web_user_id"].intValue
                        post.titlename = json["title_name"].stringValue
                        
                        print("id: \(String(describing: post.id))")
                        print("web_user_id: \(String(describing: post.web_user_id))")
                        print("title_name: \(String(describing: post.titlename))")
                        
                        self.myposts.append(post)
                        
                    }
                    
                    //アクセスできなかった場合
                } else {
                    
                    //アクセスできなかった場合
                    
                    print("Error: \(String(describing: response.error))")
                }
                
            }
            
            //ヘッダー情報を取ってこれていなかった場合
        } else {
            
            //ログインしていないと警告を出す
            
        }
        
    }
```





￼![スクリーンショット 2018-09-18 20.31.24.png.png](https://qiita-image-store.s3.amazonaws.com/0/245458/3d298add-b141-e92c-9b3b-b43b82059c8a.png)

## carrierwave編

記事の初めにも記述しましたが、iosからAWSまでの一連の流れを記事にしているためかなり長くなってしまったので他の記事でも紹介しています。
こちらを続けてご覧下さい。

- [RailsとiOS(Swift)とAWS間をAPI通信で、データファイル保存させるまで④carrierwave編](https://qiita.com/Rafflesia/items/6cd5d6e25f15e1ba876f)