前提・実現したいこと
iOSアプリを作成しています。
ライブハウスの名前を検索して地図上に周辺のコインロッカーを表示させるアプリです。
場所の情報(検索ワード、経度、緯度、詳細)のデータベースを仮想サーバに作り、
サーバー側にPHPファイルを起いて自作APIを作り、JSON形式で出力するようにしています。
発生している問題・エラーメッセージ
swift側でJSONのパースが上手くできないのか、それとも構造体への格納ができないのか
do try catch エラーハンドリングで必ずエラーをキャッチしてしまいます。
http://192.168.33.10:8000/api.php/?livehouse=%E6%9C%AC%E5%85%AB%E5%B9%A1 2019-02-21 17:41:49.762064+0900 WhereistheCoinLocker[10697:390643] TIC Read Status [1:0x600000ae1380]: 1:57 php output: Optional("[{\"latitude\":35.720825,\"longitude\":139.927428,\"keyword\":\"JR本八幡駅改札付近北口方面\"},{\"latitude\":35.7211,\"longitude\":139.927267,\"keyword\":\"都営新宿線本八幡駅 地下1階 駅長事務室の左右\"},略 検索結果が続きます]") エラーがでました
該当のソースコード
swift
1//jsonの中身を受け取るデータ構造 2 struct Coinlocker: Codable { 3 let latitude : Double? 4 let longitude : Double? 5 let keyword : String? 6 } 7 8 //jsonのデータ構造 9 struct ResultJson: Codable { 10 //複数要素 11 let item:[Coinlocker]? 12 } 13 14 //ロッカーを検索するためPHPにリクエストするメソッド 15 func searchLocker (keyword: String) { 16 17 //検索キーワードをURLエンコードする 18 guard let keyword_encode = keyword.addingPercentEncoding(withAllowedCharacters: sNSCharacterSet.urlQueryAllowed) else { 19 return 20 } 21 22 //リクエストURLの組み立て 23 guard let req_url = URL(string: "http://192.168.33.10:8000/api.php/?livehouse=(keyword_encode)") else { 24 return 25 } 26 print(req_url) 27 28 //リクエストに必要な情報を生成 29 let req = URLRequest(url: req_url) 30 31 //データ転送を管理するためのセッションを生成 32 let session = URLSession(configuration: .default, delegate: nil 33 , delegateQueue: OperationQueue.main) 34 //リクエストをタスクとして登録 35 let task = session.dataTask(with: req, completionHandler: { 36 (data , response , error) in 37 38 //セッションを終了 39 session.finishTasksAndInvalidate() 40 //do try catch エラーハンドリング 41 do { 42 //data にちゃんと検索結果が入っているらしい 43 let phpOutput = String(data: data!, encoding: .utf8) 44 print("php output: (String(describing: phpOutput))") 45 46 //JsonDecoderのインスタンス取得 47 let decoder = JSONDecoder() 48 //受け取ったJSONデータをパース(解析)して格納 49 let json = try decoder.decode(ResultJson.self, from: data!) 50 51 print(json) 52 53 } catch { 54 //エラー処理 55 print("エラーがでました") 56 } 57 }) 58 //ダウンロード開始 59 task.resume() 60 }
PHP
1<?php 2 3if(isset($_GET['livehouse'])) { 4 5 $livehouse = htmlspecialchars($_GET['livehouse']); 6 7define("USERNAME","xxxxx"); 8define("PASSWORD", "xxxxxx"); 9 10try{ 11 $dbh = new PDO("mysql:host=192.168.33.10;dbname=coinlockerdb; 12 charset=utf8", USERNAME, PASSWORD); 13 14 $dbh-> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 15 $dbh-> setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 16 $stmt = $dbh->prepare("select latitude, longitude, keyword from coinlockers where livehouse like '%$livehouse%'"); 17 $stmt-> setFetchMode(PDO::FETCH_ASSOC); 18 $stmt-> execute(); 19 $rows = array(); 20 while($row = $stmt->fetch(PDO::FETCH_ASSOC)){ 21 $rows[] = $row; 22 } 23 # 接続成功ならjson形式で吐き出す 24 header('Content-Type: application/json; charset=UTF-8'); 25 echo $json = json_encode($rows, JSON_UNESCAPED_UNICODE); 26 return $json; 27} catch (PDOException $e){ 28 echo "JSON吐き出し失敗"; 29 echo $e->getMessage(); 30} 31$dbh =null; 32}else{ 33 echo "失敗"; 34}
試したこと
swift側の構造体の変数名とデータベースの各項目のスペルが間違っていないか確認しました。
デバッグエリアに出ている TIC Read Status [1:0x600000ae1380]: 1:57についても検索してみましたがこれはエラーではないのでしょうか。調べてもよくわかりませんでした。
初心者でどこがおかしいのか、何を調べていいのかもわからない状態です。
補足情報(FW/ツールのバージョンなど)
データベースのテーブルの情報は下記の通りです。
+-----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| latitude | double | NO | UNI | NULL | |
| longitude | double | NO | UNI | NULL | |
| livehouse | varchar(50) | NO | | NULL | |
| keyword | text | NO | | NULL | |
+-----------+------------------+------+-----+---------+----------------+
参考にしたものはこちらです。(たった2日でマスターできるiPhoneアプリ開発集中講座 Xcode 10 Swift 4.2対応 より)
swift
1 //Jsonのitem内のデータ構造 2 struct ItemJson: Codable { 3 //お菓子の名前 4 let name: String? 5 //メーカー 6 let maker: String? 7 //掲載URL 8 let url: URL? 9 //画像のURL 10 let image: URL? 11 } 12 13 //JSONのデータ構造 14 struct ResultJson: Codable { 15 //複数要素 16 let item:[ItemJson]? 17 } 18 19 //searchokashiメソッド 20 func searchOkashi (keyword: String) { 21 22 //お菓子の検索キーワードをURLエンコードする 23 guard let keyword_encode = keyword.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { 24 return 25 } 26 27 //リクエストURLの組み立て 28 guard let req_url = URL(string: 29 "http://www.sysbird.jp/toriko/api/?apikey=guest&format=json&keyword=(keyword_encode)&max=10&prder=r") else { 30 return 31 } 32 print(req_url) 33 34 //リクエストに必要な情報を生成 35 let req = URLRequest(url: req_url) 36 //データ転送を管理するためのセッションを生成 37 let session = URLSession(configuration: .default, delegate: nil, delegateQueue: OperationQueue.main) 38 //リクエストをタスクとして登録 39 let task = session.dataTask(with: req, completionHandler: { 40 (data, response, error) in 41 //セッション終了 42 session.finishTasksAndInvalidate() 43 //do try catcyエラーハンドリング 44 do{ 45 //JSONDecoderのインスタンス取得 46 let decoder = JSONDecoder() 47 //受け取ったJSONデータをパース(解析)して格納 48 let json = try decoder.decode(ResultJson.self, from:data!) 49 50 51 //お菓子の情報が取得できているか確認 52 if let items = json.item { 53 //お菓子のリストを初期化 54 self.okashiList.removeAll() 55 //取得したお菓子の数だけ処理 56 for item in items { 57 //お菓子の名前、メーカ名、掲載URL,画像URLをアンラップ 58 if let name = item.name, let maker = item.maker, let link = item.url, let image = item.image { 59 //1つのお菓子をタプルでまとめて管理 60 let okashi = (name, maker, link, image) 61 //お菓子の配列へ追加 62 self.okashiList.append(okashi) 63 } 64 } 65 //TableView を更新する 66 self.tableView.reloadData() 67 if let okashidbg = self.okashiList.first { 68 print ("-----------------") 69 print ("okashiList[0] = (okashidbg)") 70 } 71 } 72 73 } catch { 74 //エラー処理 75 print("エラーが出ました") 76 } 77 }) 78 //ダウンロード開始 79 task.resume() 80 } 81 82 //Cellに値を設定するdatasourceメソッド。必ず記述。 83 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 84 //お菓子リストの総数 85 return okashiList.count 86 } 87 88 //Cellに値を設定するdatasourceメソッド。必ず記述。 89 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 90 //今回表示を行う、Cellオブジェクト(1行)を取得 91 let cell = tableView.dequeueReusableCell(withIdentifier: "okashiCell", for: indexPath) 92 //お菓子のタイトル設定 93 cell.textLabel?.text = okashiList[indexPath.row].name 94 //お菓子の画像を取得 95 if let imageData = try? Data(contentsOf: okashiList[indexPath.row].image) { 96 //正常に取得できた場合は、UIImageで画像オブジェクトを生成してCellにお菓子画像を設定 97 cell.imageView?.image = UIImage(data: imageData) 98 } 99 //設定済みのCellオブジェクトを画像に反映 100 return cell 101 } 102 103 //Cellが洗濯された際に呼び出されるdelegateメソッド 104 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 105 // ハイライト解除 106 tableView.deselectRow(at: indexPath, animated: true) 107 108 //SFSafariViewを開く 109 let safariViewController = SFSafariViewController(url: okashiList[indexPath.row].link) 110 111 //dekegate の通知先を自分自身 112 safariViewController.delegate = self 113 114 //SafariViewが開かれる 115 present(safariViewController, animated: true, completion: nil) 116 } 117 118 // SafariViewが閉じられた時によばれるdelegateメソッド 119 func safariViewControllerDidFinish(_ controller: SFSafariViewController) { 120 // SafariViewを閉じる 121 dismiss(animated: true, completion: nil) 122 }
回答1件
あなたの回答
tips
プレビュー