- URL
https://itunes.apple.com/lookup?id=XXXXXXXX&country=JP
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let url = URL(string: "https://itunes.apple.com/lookup?id=1242266654&country=JP")!
session.dataTask(with: url) { (data: Data?, response: URLResponse?, error: Error?) in
if let data = data {
let str = String(data: data, encoding: .utf8)!
print(str)
let obj = try? JSONDecoder().decode(AppDataResponse.self, from: data)
}
}.resume()
import Foundation
import XCTest
// ---------- Data -------------
guard let fileURL = Bundle.main.url(forResource: "response", withExtension: "json"),
let content = try? String(contentsOf: fileURL, encoding: String.Encoding.utf8),
let data = content.data(using: .utf8) else {
fatalError("JSON取得失敗!")
}
// ---------- extension -------------
extension KeyedDecodingContainer {
func decode<ResultType: Decodable>(forKey key: Key, defaultValue: ResultType) -> ResultType {
return (try? decode(ResultType.self, forKey: key)) ?? defaultValue
}
func convertStringDecode<ResultType: ConvertResultType>(forKey key: Key) -> ResultType {
let num = try? decode(Int.self, forKey: key)
return ResultType.toResultType(num: num)
}
}
typealias ConvertResultType = ConvertStringType & Decodable
internal protocol ConvertStringType {
static func toResultType(num: Int?) -> Self
}
extension String: ConvertStringType {
internal static func toResultType(num: Int?) -> String {
guard let num = num else { return "" }
return String(num)
}
}
// ---------- Data Class -------------
struct AppDataResponse: Codable {
let resultCount: Int
let results: [AppDataResults]
enum CodingKeys: String, CodingKey {
case resultCount
case results
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
resultCount = container.decode(forKey: .resultCount, defaultValue: 0)
results = container.decode(forKey: .results, defaultValue: [])
}
}
struct AppDataResults: Codable {
let screenshotUrls: [String]
let ipadScreenshotUrls: [String]
let appletvScreenshotUrls: [String]
let artworkUrl512: String
let artistViewUrl: String
let artworkUrl60: String
let artworkUrl100: String
let isGameCenterEnabled: Bool
let advisories: [String]
let kind: String
let features: [String]
let supportedDevices: [String]
let averageUserRatingForCurrentVersion: Float
let languageCodesISO2A: [String]
let fileSizeBytes: [String]
let sellerUrl: String
let userRatingCountForCurrentVersion: Int
let trackContentRating: String
let trackCensoredName: String
let trackViewUrl: String
let contentAdvisoryRating: String
let releaseNotes: String
let minimumOsVersion: String
let currentVersionReleaseDate: Date?
let genreIds: [String]
let primaryGenreName: String
let releaseDate: Date?
let wrapperType: String
let version: String
let currency: String
let description: String
let artistId: String
let artistName: String
let genres: [String]
let price: Float
let bundleId: String
let formattedPrice: String
let trackId: String
let trackName: String
let isVppDeviceBasedLicensingEnabled: Bool
let sellerName: String
let primaryGenreId: Int
let averageUserRating: Float
let userRatingCount: Int
enum CodingKeys: String, CodingKey {
case screenshotUrls
case ipadScreenshotUrls
case appletvScreenshotUrls
case artworkUrl512
case artistViewUrl
case artworkUrl60
case artworkUrl100
case isGameCenterEnabled
case advisories
case kind
case features
case supportedDevices
case averageUserRatingForCurrentVersion
case languageCodesISO2A
case fileSizeBytes
case sellerUrl
case userRatingCountForCurrentVersion
case trackContentRating
case trackCensoredName
case trackViewUrl
case contentAdvisoryRating
case releaseNotes
case minimumOsVersion
case currentVersionReleaseDate
case genreIds
case primaryGenreName
case releaseDate
case wrapperType
case version
case currency
case description
case artistId
case artistName
case genres
case price
case bundleId
case formattedPrice
case trackId
case trackName
case isVppDeviceBasedLicensingEnabled
case sellerName
case primaryGenreId
case averageUserRating
case userRatingCount
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
screenshotUrls = container.decode(forKey: .screenshotUrls, defaultValue: [])
ipadScreenshotUrls = container.decode(forKey: .ipadScreenshotUrls, defaultValue: [])
appletvScreenshotUrls = container.decode(forKey: .appletvScreenshotUrls, defaultValue: [])
artworkUrl512 = container.decode(forKey: .artworkUrl512, defaultValue: "")
artistViewUrl = container.decode(forKey: .artistViewUrl, defaultValue: "")
artworkUrl60 = container.decode(forKey: .artworkUrl60, defaultValue: "")
artworkUrl100 = container.decode(forKey: .artworkUrl100, defaultValue: "")
isGameCenterEnabled = container.decode(forKey: .isGameCenterEnabled, defaultValue: false)
advisories = container.decode(forKey: .advisories, defaultValue: [])
kind = container.decode(forKey: .kind, defaultValue: "")
features = container.decode(forKey: .features, defaultValue: [])
supportedDevices = container.decode(forKey: .supportedDevices, defaultValue: [])
averageUserRatingForCurrentVersion = container.decode(forKey: .averageUserRatingForCurrentVersion, defaultValue: 0.0)
languageCodesISO2A = container.decode(forKey: .languageCodesISO2A, defaultValue: [])
fileSizeBytes = container.decode(forKey: .fileSizeBytes, defaultValue: [])
sellerUrl = container.decode(forKey: .sellerUrl, defaultValue: "")
userRatingCountForCurrentVersion = container.decode(forKey: .userRatingCountForCurrentVersion, defaultValue: 0)
trackContentRating = container.decode(forKey: .trackContentRating, defaultValue: "")
trackCensoredName = container.decode(forKey: .trackCensoredName, defaultValue: "")
trackViewUrl = container.decode(forKey: .trackViewUrl, defaultValue: "")
contentAdvisoryRating = container.decode(forKey: .contentAdvisoryRating, defaultValue: "")
releaseNotes = container.decode(forKey: .releaseNotes, defaultValue: "")
minimumOsVersion = container.decode(forKey: .minimumOsVersion, defaultValue: "")
currentVersionReleaseDate = container.decode(forKey: .currentVersionReleaseDate, defaultValue: Date())
genreIds = container.decode(forKey: .genreIds, defaultValue: [])
primaryGenreName = container.decode(forKey: .primaryGenreName, defaultValue: "")
releaseDate = container.decode(forKey: .releaseDate, defaultValue: Date())
wrapperType = container.decode(forKey: .wrapperType, defaultValue: "")
version = container.decode(forKey: .version, defaultValue: "")
currency = container.decode(forKey: .currency, defaultValue: "")
description = container.decode(forKey: .description, defaultValue: "")
artistId = container.decode(forKey: .artistId, defaultValue: "")
artistName = container.decode(forKey: .artistName, defaultValue: "")
genres = container.decode(forKey: .genres, defaultValue: [])
price = container.decode(forKey: .price, defaultValue: 0.0)
bundleId = container.decode(forKey: .bundleId, defaultValue: "")
formattedPrice = container.decode(forKey: .formattedPrice, defaultValue: "")
trackId = container.decode(forKey: .trackId, defaultValue: "")
trackName = container.decode(forKey: .trackName, defaultValue: "")
isVppDeviceBasedLicensingEnabled = container.decode(forKey: .isVppDeviceBasedLicensingEnabled, defaultValue: false)
sellerName = container.decode(forKey: .sellerName, defaultValue: "")
primaryGenreId = container.decode(forKey: .primaryGenreId, defaultValue: 0)
averageUserRating = container.decode(forKey: .averageUserRating, defaultValue: 0.0)
userRatingCount = container.decode(forKey: .userRatingCount, defaultValue: 0)
}
}
// ---------- Decode -------------
let obj = try? JSONDecoder().decode(AppDataResponse.self, from: data)
if let obj = obj {
print(obj.resultCount)
print(obj.results.first!.screenshotUrls.forEach { print($0) })
print(obj.results.first!.releaseDate)
}
{
"resultCount": 1,
"results": [
{
"screenshotUrls": [
"https://is2-ssl.mzstatic.com/image/thumb/Purple115/v4/c7/68/41/c768410d-e23c-3f3d-5b65-eaa3a7ea05e6/source/392x696bb.jpg",
"https://is1-ssl.mzstatic.com/image/thumb/Purple115/v4/00/15/b4/0015b4d7-caac-ab05-b12a-f49daae1a55c/source/392x696bb.jpg",
"https://is3-ssl.mzstatic.com/image/thumb/Purple125/v4/0d/5f/76/0d5f767e-e70f-677d-8139-277021725478/source/392x696bb.jpg",
"https://is2-ssl.mzstatic.com/image/thumb/Purple125/v4/82/c4/d2/82c4d2d4-415f-2a38-2e35-7ab3f9aad4bd/source/392x696bb.jpg",
"https://is5-ssl.mzstatic.com/image/thumb/Purple115/v4/94/89/20/94892017-ae88-a453-41e5-47afe02ebe65/source/392x696bb.jpg"
],
"ipadScreenshotUrls": [],
"appletvScreenshotUrls": [],
"artworkUrl512": "https://is2-ssl.mzstatic.com/image/thumb/Purple118/v4/68/29/b6/6829b688-6a7f-de43-d76a-277fbc2db529/source/512x512bb.jpg",
"artistViewUrl": "https://itunes.apple.com/jp/developer/japan-professional-football-league/id1242266653?uo=4",
"artworkUrl60": "https://is2-ssl.mzstatic.com/image/thumb/Purple118/v4/68/29/b6/6829b688-6a7f-de43-d76a-277fbc2db529/source/60x60bb.jpg",
"artworkUrl100": "https://is2-ssl.mzstatic.com/image/thumb/Purple118/v4/68/29/b6/6829b688-6a7f-de43-d76a-277fbc2db529/source/100x100bb.jpg",
"isGameCenterEnabled": false,
"advisories": [
"ギャンブルまたはコンテスト"
],
"kind": "software",
"features": [],
"supportedDevices": [
"iPad2Wifi-iPad2Wifi",
"iPad23G-iPad23G",
"iPhone4S-iPhone4S",
"iPadThirdGen-iPadThirdGen",
"iPadThirdGen4G-iPadThirdGen4G",
"iPhone5-iPhone5",
"iPodTouchFifthGen-iPodTouchFifthGen",
"iPadFourthGen-iPadFourthGen",
"iPadFourthGen4G-iPadFourthGen4G",
"iPadMini-iPadMini",
"iPadMini4G-iPadMini4G",
"iPhone5c-iPhone5c",
"iPhone5s-iPhone5s",
"iPadAir-iPadAir",
"iPadAirCellular-iPadAirCellular",
"iPadMiniRetina-iPadMiniRetina",
"iPadMiniRetinaCellular-iPadMiniRetinaCellular",
"iPhone6-iPhone6",
"iPhone6Plus-iPhone6Plus",
"iPadAir2-iPadAir2",
"iPadAir2Cellular-iPadAir2Cellular",
"iPadMini3-iPadMini3",
"iPadMini3Cellular-iPadMini3Cellular",
"iPodTouchSixthGen-iPodTouchSixthGen",
"iPhone6s-iPhone6s",
"iPhone6sPlus-iPhone6sPlus",
"iPadMini4-iPadMini4",
"iPadMini4Cellular-iPadMini4Cellular",
"iPadPro-iPadPro",
"iPadProCellular-iPadProCellular",
"iPadPro97-iPadPro97",
"iPadPro97Cellular-iPadPro97Cellular",
"iPhoneSE-iPhoneSE",
"iPhone7-iPhone7",
"iPhone7Plus-iPhone7Plus",
"iPad611-iPad611",
"iPad612-iPad612",
"iPad71-iPad71",
"iPad72-iPad72",
"iPad73-iPad73",
"iPad74-iPad74",
"iPhone8-iPhone8",
"iPhone8Plus-iPhone8Plus",
"iPhoneX-iPhoneX",
"iPad75-iPad75",
"iPad76-iPad76",
"iPhoneXS-iPhoneXS",
"iPhoneXSMax-iPhoneXSMax",
"iPhoneXR-iPhoneXR"
],
"averageUserRatingForCurrentVersion": 4.5,
"languageCodesISO2A": [
"JA"
],
"fileSizeBytes": "98247680",
"sellerUrl": "https://www.jleague.jp/app/",
"userRatingCountForCurrentVersion": 535,
"trackContentRating": "17+",
"trackCensoredName": "Club J.LEAGUE",
"trackViewUrl": "https://itunes.apple.com/jp/app/club-j-league/id1242266654?mt=8&uo=4",
"contentAdvisoryRating": "17+",
"releaseNotes": "2018Jリーグアウォーズ応募に関する機能を追加しました。\nその他、軽微な修正を行っています。",
"minimumOsVersion": "9.3.5",
"currentVersionReleaseDate": "2018-10-18T04:25:04Z",
"genreIds": [
"6004"
],
"primaryGenreName": "Sports",
"releaseDate": "2017-07-31T15:44:36Z",
"wrapperType": "software",
"version": "1.2.13",
"currency": "JPY",
"description": "◆◆Jリーグ公式アプリ「Club J.LEAGUE」◆◆\n・お気に入りクラブを登録してニュースや試合速報、チケットの情報をまとめてチェック!\n・メダルを集めて、新しい観戦仲間が増やせる「明治安田生命Jリーグチャレンジ」に参加して日本のサッカーを盛り上げよう!\n・試合日にスタジアムでチェックインすれば、観戦メダルをGETできます!\n\n①お気に入りクラブを登録\n好きなクラブをお気に入りに登録しよう!\nそのクラブのニュースや試合の日程・速報をまとめてチェックできます。Push通知で試合開始やゴールの通知を受け取ることができます。\n\n②チケット購入\nお気に入りクラブの試合を一括管理!\nアプリから直接チケットを購入してスタジアムで応援しよう!\n\n③明治安田生命 Jリーグチャレンジ\n様々なミッションをクリアするとメダルがもらえます。\nメダルを3枚集めてJリーグチャレンジに挑戦すると、\n友達を誘えるペアチケットが当たるチャンスが!\n\n④観戦メダルをゲット\n試合日にスタジアムでチェックインするとメダルがもらえます!\nJチャレに挑戦するには観戦メダルが必須です。\n応援に来たときは必ずアプリをチェックしてください!\n\n⑤限定キャンペーン\nメダルをさらに集めるとランクがあがっていきます!\n上位のランクの方だけが参加できるキャンペーンもありますので\nどんどんメダルを集めてお得なキャンペーンに参加しましょう!\n\n--------------------------------------------------------\n■ご利用にあたっての注意事項\n--------------------------------------------------------\n・「Club J.LEAGUE」のご利用に際しては通信環境が必要となります。お客様に通信環境を整えていただく必要がございますので、あらかじめご了承ください。\n※お客様と通信会社とのご契約内容によって通信費が発生する場合がございますのでご注意ください。\n・通信環境が不安定だと必要な情報を読み取る事ができない場合がございます。圏外になりやすい場所や、WiFiなどでも通信環境が不安定ですとアプリが正常に稼働しない場合がございますのでご注意ください。\n・お客様のお持ちの機種の機能、OSなどのソフトウェアの環境によって、正常に稼働しない場合がございます。あらかじめご了承ください。\n・チェックイン機能をご利用の際は、アプリのGPS機能をONにする必要があります。通信環境によっては正確に位置情報を読み取れない場合がございますのでご注意ください。\n※WiFiをONにすることによってGPSの精度が高まります。\n\n--------------------------------------------------------\n著作権について\n--------------------------------------------------------\n本アプリに記載されている内容の著作権は、Jリーグおよび情報提供者に帰属し、いかなる目的であれ無断で複製、引用、転送、頒布、上演、改編、修正、追加など一切の行為を禁止いたします。",
"artistId": "1242266653",
"artistName": "Japan Professional Football League",
"genres": [
"スポーツ"
],
"price": 0.00,
"bundleId": "jp.jleague.club",
"formattedPrice": "無料",
"trackId": "1242266654",
"trackName": "Club J.LEAGUE",
"isVppDeviceBasedLicensingEnabled": true,
"sellerName": "JAPAN PROFESSIONAL FOOTBALL LEAGUE",
"primaryGenreId": 6004,
"averageUserRating": 4.5,
"userRatingCount": 17590
}
]
}