🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

Q&A

解決済

1回答

911閲覧

SwiftUIでWebAPIから結果を表示したい

yosuke302

総合スコア8

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

0グッド

0クリップ

投稿2019/11/09 04:52

編集2019/11/09 07:21

HotPepperAPIから取得した飲食店情報をリスト表示するために、下記コードを書きました。

swift

1import SwiftUI 2import Foundation 3import Combine 4 5class RestaurantStore: ObservableObject { 6 7 @Published var restaurants: [Restaurant] = [] 8 9 init() { 10 load() 11 //④ 12 } 13 14 func load() { 15 let url = URL(string: "http://webservice.recruit.co.jp/hotpepper/gourmet/v1/?key=[APIキー]&large_area=Z011&format=json") 16 URLSession.shared.dataTask(with: url!) { data, response, error in 17 DispatchQueue.main.async { 18 self.restaurants = [try! JSONDecoder().decode(Restaurant.self, from: data!)] 19       //③ 20 } 21 }.resume() 22 // ② 23 } 24} 25 26struct Restaurant: Decodable{ 27 var results: Results 28 29 struct Results: Decodable { 30 var shop: [Shop] 31 32 struct Shop: Decodable, Identifiable { 33 var id: String 34 var name: String 35 } 36 37 } 38} 39 40struct SearchView: View { 41 42 @ObservedObject var store = RestaurantStore() 43 44 var body: some View { 45 List(store.restaurants[0].results.shop) {(res) in //① 46 RestaurantRow(res: res) 47 } 48 } 49} 50 51struct RestaurantRow: View { 52 var res: Restaurant.Results.Shop 53 var body: some View { 54 Text(res.name) 55 } 56} 57

json

1{ 2results: { 3 api_version: "1.26", 4 results_returned: "10", 5 results_start: 1, 6 shop: [ 7  { 8   name_kana: "しんじゅく ぜんせきこしつ いざかや とりきち しんじゅくてん", 9   other_memo: "様々なご要望承ります◇チーズフォンデュと個室の肉バル横丁 新宿店◇", 10   name: "鶏吉 新宿店", 11   genre: { 12    name: "居酒屋", 13    catch: "歓迎会 送別会 新宿 肉バル 個室 女子会", 14    code: "G001" 15   }, 16   open: "月~木、日: 17:00~翌0:00 (料理L.O. 23:00 ドリンクL.O. 23:30)金、祝前日: 17:00~翌3:30 (料理L.O. 翌2:30 ドリンクL.O. 翌3:00)土: 16:00~翌3:30 (料理L.O. 翌2:30 ドリンクL.O. 翌3:00)祝日: 16:00~翌0:00 (料理L.O. 23:00 ドリンクL.O. 23:30)", 17   close: "無休 休日や週末でも使えるお得なクーポン有♪◇新宿 全席個室 居酒屋 鶏吉 新宿店◇【新宿 居酒屋 宴会 個室】【肉バル 個室】", 18   service_area: { 19    name: "東京", 20    code: "SA11" 21   }, 22   station_name: "新宿", 23  }, 24  {(別の飲食店情報)}, 25  {(別の飲食店情報)}, 26  {(別の飲食店情報)}, 27 ],  28} 29}

(上記jsonデータは内容を一部省略したものです。)

エラー

①で、Fatal error: Index out of rangeが発生します。

やったこと

そもそもデータが取得できていないのではと思い、
②、③、④の位置でprint(self.restaurants)を書いてみたところ、

③ではデコードされたデータを出力でき、print(self.restaurants[0].results.shop[0].name)で店名までアクセスできました。
しかし、②、④では[]とのみ出力され、データは出力されませんでした。

なぜ、②、④ではデータが出力されず、また、上記エラーが発生しているのかがわかりませんでした。
解決方法をお教えいただけますと幸いです。

補足情報

  • Xcode Version 11.2
  • Apple Swift version 5.1
  • macOS catalina version 10.15.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

②と④は通信が終わっていないので空配列のままですね。

下記コードで動くと思います。
おそらく必要な情報は Shop だけだと思うのでモデルを出しました。

class RestaurantStore: ObservableObject { // Shop の配列の変更をViewに通知 @Published var restaurants: [Shop] = [] init() { load() } func load() { let url = URL(string: "https://webservice.recruit.co.jp/hotpepper/gourmet/v1/?key=[APIキー]&large_area=Z011&format=json") URLSession.shared.dataTask(with: url!) { data, response, error in guard let data = data else { return } let decoder: JSONDecoder = JSONDecoder() do { // Restaurant型で一旦データを受ける let searchedResult = try decoder.decode(Restaurant.self, from: data) DispatchQueue.main.async { // 結果の[Shop]をプロパティに代入 self.restaurants = searchedResult.results.shop } } catch { print("json convert failed in JSONDecoder. " + error.localizedDescription) } }.resume() } } struct Restaurant: Decodable{ var results: Results struct Results: Decodable { var shop: [Shop] } } // ここを分けました; RestaurantRowのプロパティも短くできます struct Shop: Decodable, Identifiable { var id: String var name: String } struct SearchView: View { @ObservedObject var store = RestaurantStore() var body: some View { List(store.restaurants) {(res) in RestaurantRow(res: res) } } } struct: View { var res: Shop var body: some View { Text(res.name) } }

投稿2019/11/09 12:40

TakuyaAso

総合スコア1361

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yosuke302

2019/11/09 14:03

ありがとうございます!上記コードで動きました! DispatchQueue内でデータの取得&プロパティへの代入を一気にやっていたせいだったのですね。。。 このエラーでだいぶ悩ませれていたので、大変助かりました! ベストアンサーにさせていただきます!
TakuyaAso

2019/11/09 23:15

良かったです!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問