まず、Web API を直接叩いて、返ってくる JSON を観察します。
(やり方は、ブラウザで API を叩いて結果を VSCode などで整形する、ブラウザに機能拡張を入れる、Postman などの専用ツールを使う、ターミナルで curl と jq を使う、など色々あります。)
json
1{
2 "count": 2053,
3 "page": 1,
4 "first": 1,
5 "last": 30,
6 "hits": 30,
7 "carrier": 0,
8 "pageCount": 69,
9 "Items": [
10 {
11 "Item": {
12 "golfCourseId": 80004,
13 "golfCourseName": "アジア取手カントリー倶楽部",
14 "golfCourseAbbr": "アジア取手CC",
15 "golfCourseNameKana": "あじあとりでかんとりーくらぶ",
16 "golfCourseCaption": "常磐道の谷和原I.C.より15分!都心からのアクセスの良さが魅力。姉妹コースに続き、【セグウェイ】を100台導入!日本一の導入数を誇ります。フェアウェイ乗り入れ可能で移動も快適、【セグウェイでゴルフ】を是非体験してみて下さい。\r\n\r\n【お得プランの一部をご紹介】\r\n・全日廻り放題!*当日の天候、日没状況により、追加ラウンドできない場合もございます。\r\n・お誕生月特典あり!\r\n・平日は食べ放題のランチバイキング、4サム割引きあり。\r\nお得で楽しい一日をお気軽にお過ごし下さい。",
17 "address": "茨城県取手市稲1340",
18 "latitude": 35.9061664,
19 "longitude": 140.0397556,
20 "highway": "常磐自動車道谷和原",
21 "golfCourseDetailUrl": "https://booking.gora.golf.rakuten.co.jp/guide/disp/c_id/80004",
22 "reserveCalUrl": "https://search.gora.golf.rakuten.co.jp/cal/disp/c_id/80004",
23 "ratingUrl": "https://booking.gora.golf.rakuten.co.jp/voice/detail/c_id/80004",
24 "golfCourseImageUrl": "https://gora.golf.rakuten.co.jp/img/golf/80004/photo1.jpg",
25 "evaluation": 3.4
26 }
27 },
28 // 以下略
JSON の読み方は、{}
がオブジェクト (Swift では構造体)、[]
が配列です。
一番外側から見ていくと、こんな感じですね。
json
1{
2 "count": 2053,
3 "page": 1,
4 // 略
5 "Items": [ (省略) ]
6}
{}
つまりオブジェクトなので、struct にします。count や page などは不要なら省略しても構いません。
肝心の Items は []
なので配列になります。(要素の型はのちほど。)
swift
1struct Article: Codable {
2 var count: Int
3 var page: Int
4 // 略
5 var Items: [要素の型]
6}
また、デコードの際に decode([Article].self, ...)
としてますが、これだと一番外側が配列という意味になりますが、今回はそうではないので、decode(Article.self, ...)
でいいはずです。
次に、Items の要素ですが、こんな感じでオブジェクトの入れ子になってます。
json
1 {
2 "Item": {
3 "golfCourseId": 80004,
4 "golfCourseName": "アジア取手カントリー倶楽部",
5 // 略
6 }
7 },
ので、struct が 2 つ必要になります。外側は適当に ItemWrapper、内側は一番大事なもので、ゴルフコースの情報なので GolCourse でしょうか。なお、構造体を入れ子にする記事が多いようですが、個人的には入れ子にする必要はないかと。また、型名の最初の文字は大文字にしましょう。
swift
1struct Article: Codable {
2 var Items: [ItemWrapper]
3}
4
5struct ItemWrapper: Codable {
6 var Item: GolfCourse
7}
8
9struct GolfCourse: Codable {
10 var golfCourseName: String
11 var golfCourseCaption: String
12 var golfCourseDetailUrl: String
13 var golfCourseImageUrl: String
14}
(ちなみに、API のパラメーターに formatVersion=2 を指定すると ItemWrapper は不要になります。)
それから、型のテストですが、Playground 機能を使うと良いかも。Xcode の File メニューから New → Playground... して、次のコードを貼り付けて実行してみましょう。
swift
1import Foundation
2
3struct Article: Codable {
4 var Items: [ItemWrapper]
5}
6
7struct ItemWrapper: Codable {
8 var Item: GolfCourse
9}
10
11struct GolfCourse: Codable {
12 var golfCourseName: String
13 var golfCourseCaption: String
14 var golfCourseDetailUrl: String
15 var golfCourseImageUrl: String
16}
17
18// #""" と """# の間には複数行のテキストを書けるので、JSON をそのまま貼り付けられます。
19let json = #"""
20 {
21 "count": 2053,
22 "page": 1,
23 "first": 1,
24 "last": 30,
25 "hits": 30,
26 "carrier": 0,
27 "pageCount": 69,
28 "Items": [
29 {
30 "Item": {
31 "golfCourseId": 80004,
32 "golfCourseName": "アジア取手カントリー倶楽部",
33 "golfCourseAbbr": "アジア取手CC",
34 "golfCourseNameKana": "あじあとりでかんとりーくらぶ",
35 "golfCourseCaption": "常磐道の谷和原I.C.より15分!都心からのアクセスの良さが魅力。姉妹コースに続き、【セグウェイ】を100台導入!日本一の導入数を誇ります。フェアウェイ乗り入れ可能で移動も快適、【セグウェイでゴルフ】を是非体験してみて下さい。\r\n\r\n【お得プランの一部をご紹介】\r\n・全日廻り放題!*当日の天候、日没状況により、追加ラウンドできない場合もございます。\r\n・お誕生月特典あり!\r\n・平日は食べ放題のランチバイキング、4サム割引きあり。\r\nお得で楽しい一日をお気軽にお過ごし下さい。",
36 "address": "茨城県取手市稲1340",
37 "latitude": 35.9061664,
38 "longitude": 140.0397556,
39 "highway": "常磐自動車道谷和原",
40 "golfCourseDetailUrl": "https://booking.gora.golf.rakuten.co.jp/guide/disp/c_id/80004",
41 "reserveCalUrl": "https://search.gora.golf.rakuten.co.jp/cal/disp/c_id/80004",
42 "ratingUrl": "https://booking.gora.golf.rakuten.co.jp/voice/detail/c_id/80004",
43 "golfCourseImageUrl": "https://gora.golf.rakuten.co.jp/img/golf/80004/photo1.jpg",
44 "evaluation": 3.4
45 }
46 }
47 ]
48 }
49 """#
50
51let article = try! JSONDecoder().decode(Article.self, from: json.data(using: .utf8)!)
52for i in 0..<article.Items.count {
53 print(article.Items[i].Item.golfCourseName)
54 print(article.Items[i].Item.golfCourseCaption)
55 print(article.Items[i].Item.golfCourseDetailUrl)
56 print(article.Items[i].Item.golfCourseImageUrl)
57}