【Swift UI】MySQLからデータを取得する方法!PHPでRESTAPIを実装
この記事からわかること
- SwiftでMySQLからデータを取得して表示させる方法
- 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のデータを取得する方法を見ていきます。ざっくりとした流れは以下の通りです。
実装の流れ
- PHPでMySQLのデータをJSON形式でURL経由で出力
- SwiftでURLにアクセスし辞書型に変換
- 構造体に整形しビューに表示させる
この方法は自身で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)
}
}
}
}
}
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。