【Swift UI】MySQLからデータを取得する方法!PHPでRESTAPIを実装

この記事からわかること

  • SwiftMySQLからデータ取得して表示させる方法
  • PHPを使ったRESTAPI実装方法

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

Swiftで開発するiOSアプリからMySQLに保存されているデータを取得する方法をまとめていきます。

今回のポイント

PHPで REST APIを構築する

また今回はこのサイトの記事情報をJSONデータで取得しiOSアプリで表示できるようにしてみます。

REST APIとは?

REST APIとはAPI(Application Programming Interface)の定義に使用される設計構造思想の1つです。RESTは「Representational State Transfer」の略称で日本語に訳すと「具体的な状態の転送」と言った意味になります。

RESTでは「統一されたインターフェース」や「URIを通して情報を提供する」などさまざまな原則が定められています。

色々な原則をまとめて1行で表すと「HTTP通信で定めた形式でデータを取得できるURIを構築すること」だと思います。

ちなみにAPIとはアプリやプログラム、Webサービス同士を繋ぐインターフェースのことです。またURLはファイルパスを示し、URIはファイル自体を示す総称です。

SwiftからMySQLのデータを取得する

前置きが長くなってしまいましたがSwiftアプリからMySQLのデータを取得する方法を見ていきます。ざっくりとした流れは以下の通りです。

実装の流れ

この方法は自身でWebサイトなどを運営していることが条件になるので注意してください。

PHPでREST APIを実装する

まずはPHP側でMySQLに格納されているデータを参照するためのREST APIを構築していきます。

私はLaravelを使用していますが、対象のURLアクセス時にJSON形式で必要なデータを出力できるような形式に持っていければOKです。

Laravelを使用している場合はコントローラーとルーティングを記述する必要があります。

コントローラー

コントローラー側ではMySQLに格納されているデータベースへアクセスし必要な情報を出力するようにしておきます。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;

use Illuminate\Support\Facades\View;

class techController extends Controller
{
    public function getAllArticles()
    {
        $articles = DB::table('テーブル名')
        ->orderBy('entryDay', 'DESC')
        ->get();
        return $articles;
    }
}

ルーティング

ルーティングは「web.php」の中に任意のURL(今回は/api/article)にアクセスされた時にコントローラーに繋がるように設定しておきます。

https://appdev-room.com/api/article

これでURLアクセス時にMySQLに格納されているデータがJSON形式で出力されるようになりました。

<?php

use App\Http\Middleware\TechMiddleware;
use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

// REST API
Route::get('/api/article', 'App\Http\Controllers\techController@getAllArticles');

SwiftでURLにアクセスし辞書型に変換

続いてSwift(iOSアプリ)側から先程ルーティングをしたURLにアクセスしデータをSwiftで扱いやすいようにJSONSerializationを使って変換していきます。JSONSerializationの使い方やAPIから辞書型などへの変換方法は以下の記事を参考にしてください。

import UIKit

class APIController: NSObject {

  func validationUrl (urlString: String) -> Bool {
      if let nsurl = NSURL(string: urlString) {
          return UIApplication.shared.canOpenURL(nsurl as URL)
      }
      return false
  }
  
  func getArticleAPI(completion: @escaping (Array<Any>) -> Void) {
            

        let urlString = "https://appdev-room.com/api/article"
        // 有効なURLかをチェック
        if validationUrl(urlString: urlString) == false {
            return
        }
        guard let url = URL(string: urlString) else {
            return
        }
        // リクエストを構築
        let request = URLRequest(url: url)
        
        // URLにアクセスしてレスポンスを取得する
        URLSession.shared.dataTask(with: request) { data, response, error in
        
            if let data = data {
                do {
                    let ary = try JSONSerialization.jsonObject(with: data, options: []) as? Array<Any>
                    completion(ary ?? [])
                } catch {
                  print(error.localizedDescription)
                }
            } else {
                // データが取得できなかった場合の処理
                print(error?.localizedDescription ?? "不明なエラー")
            }
        }.resume()
    }
}

これでこのクラスのメソッドから対象となる記事データにアクセスできるようになりました。

構造体に整形しビューに表示させる

今回は扱いやすいように必要になる情報だけを保持させたArticle構造体を定義してビューに充てがっていきます。

import UIKit

struct Article:Identifiable {
    var id:Int
    var title:String
    var descri:String
    var file:String
    var category:Int
    var imgPath:String
    var entryDate:Date
}

completionHandlerで受け取れるので今回はビューが表示されるタイミング(onAppear)で構造体に格納させ配列形式でプロパティなどに貯めていきます。

.onAppear{
    api.getArticleAPI { array in
      for article in array {
        let dic = article as? [String:Any]
        let obj = Article(id: Int(String(describing: dic!["id"]!))!,
                      title: dic!["title"] as! String,
                      descri: dic!["descri"] as! String,
                      file:  dic!["file"] as! String,
                      category:  Int(String(describing: dic!["category"]!))!,
                      imgPath:  dic!["imgPath"] as! String,
                      entryDate:Date())
        //dic!["entryDate"] as! String
        articles.append(obj)
      }
    }
}

全体のコードは以下になります。また今回はfilter機能をつけて検索ボックスも実装してみました。

 import SwiftUI

struct ListArticleView: View {
    
    let api = APIController()
    
    @State  var articles:[Article] = []
    @State  var text:String = ""
    @State  var isfilter:Bool = false
    
    @State  var isCategory:Int = 0
    var body: some View {
        VStack{
            ZStack(alignment: .trailing){
                TextField("word...", text: $text)
                    .environment(\.colorScheme, .light)
                    .frame(width: 270)
                    .padding()
                    .padding(.trailing,30)
                    .background(Color(red: 231/255, green: 231/255, blue: 231/255))
                    .cornerRadius(5)
                
                
                Button(action: {
                    if text.isEmpty{
                        isfilter = false
                    }else{
                        isfilter = true
                    }
                }, label: {
                    Image(systemName: "magnifyingglass")
                        .foregroundColor(isfilter ? .orange :.black)
                        .font(.system(size: 20).weight(.bold))
                }).padding(.trailing,20)
                
                
                
            }.padding([.trailing,.leading,.top])
            
            if articles.count != 0 {
                
                
                List(articles.filter (isfilter ? { $0.title.lowercased().contains(text.lowercased()) || $0.descri.lowercased().contains(text.lowercased()) } : (isCategory != 0 ? {$0.category == isCategory} : { $0.title != "" }))) { article in
                    RowArticleView(article: article)
                }
                .listStyle(GroupedListStyle())
                .padding([.trailing,.leading])
                
            }else{
                ProgressView()
                Text("記事情報の取得に失敗しました....\nオフラインの場合はオンラインにしてください").padding()
                Spacer()
                
            }

        }
        .preferredColorScheme(.dark)
        .onAppear{
            api.getArticleAPI { array in
                for article in array {
                    let dic = article as? [String:Any]
                    let obj = Article(id: Int(String(describing: dic!["id"]!))!,
                                      title: dic!["title"] as! String,
                                      descri: dic!["descri"] as! String,
                                      file:  dic!["file"] as! String,
                                      category:  Int(String(describing: dic!["category"]!))!,
                                      imgPath:  dic!["imgPath"] as! String,
                                      entryDate:Date())
                    //dic!["entryDate"] as! String
                    articles.append(obj)
                }
            }
        }
    }
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index