2020.8.9
特定の種類のCodableデータの読み込み
Loading a specific kind of Codable data
2つの異なる種類のJSONをSwift構造体にロード
1つは宇宙飛行士用
もう1つはミッション用
project8-files
astronauts.jsonとMissions.json
astronauts.json
import Foundation
extension Bundle {
func decode(_ file: String) -> [Astronaut] {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) from bundle.")
}
let decoder = JSONDecoder()
guard let loaded = try? decoder.decode([Astronaut].self, from: data) else {
fatalError("Failed to decode \(file) from bundle.")
}
return loaded
}
}
/-----------------------------------------------------------
import SwiftUI
struct Astronaut: Codable, Identifiable {
let id: String
let name: String
let description: String
}
struct ContentView: View {
let astronauts = Bundle.main.decode("astronauts.json")
var body: some View {
Text("\(astronauts.count)")
}
}
ジェネリックを使用してあらゆる種類のCodableデータをロードする
Using generics to load any kind of Codable data
missions.json
struct CrewRole: Codable {
let name: String
let role: String
}
struct Mission: Codable, Identifiable {
let id: Int
let launchDate: String?
let crew: [CrewRole]
let description: String
}
This is called a nested struct
// Bundle-Decodable.swift
// Moonshot
import Foundation
extension Bundle {
// ジェネリック版
func decode<T: Codable>(_ file: String) -> T {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) from bundle.")
}
let decoder = JSONDecoder()
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError("Failed to decode \(file) from bundle.")
}
return loaded
}
// 非ジェネリック版
/*
func decode(_ file: String) -> [Astronaut] {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) from bundle.")
}
let decoder = JSONDecoder()
guard let loaded = try? decoder.decode([Astronaut].self, from: data) else {
fatalError("Failed to decode \(file) from bundle.")
}
return loaded
}
*/
}
import SwiftUI
//------------------------------------
struct Astronaut: Codable, Identifiable {
let id: String
let name: String
let description: String
}
//------------------------------------
struct Mission: Codable, Identifiable {
struct CrewRole: Codable {
let name: String
let role: String
}
let id: Int
let launchDate: String?
let crew: [CrewRole]
let description: String
}
//------------------------------------
struct ContentView: View {
let astronauts: [Astronaut] = Bundle.main.decode("astronauts.json")
let missions: [Mission] = Bundle.main.decode("missions.json")
var body: some View {
Text("\(astronauts.count)")
}
}
ミッションビューのフォーマット
Formatting our mission view
// Bundle-Decodable.swift
import Foundation
extension Bundle {
// ジェネリック版
func decode<T: Codable>(_ file: String) -> T {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) from bundle.")
}
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "y-MM-dd"
decoder.dateDecodingStrategy = .formatted(formatter)
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError("Failed to decode \(file) from bundle.")
}
return loaded
}
import SwiftUI
//------------------------------------
struct Astronaut: Codable, Identifiable {
let id: String
let name: String
let description: String
}
//------------------------------------
struct Mission: Codable, Identifiable {
struct CrewRole: Codable {
let name: String
let role: String
}
let id: Int
let launchDate: Date?
//let launchDate: String?
let crew: [CrewRole]
let description: String
//----------------------
var displayName: String {
"Apollo \(id)"
}
var image: String {
"apollo\(id)"
}
//----------------------
var formattedLaunchDate: String {
if let launchDate = launchDate {
let formatter = DateFormatter()
formatter.dateStyle = .long
return formatter.string(from: launchDate)
} else {
return "N/A"
}
}
}
//------------------------------------
struct ContentView: View {
let astronauts: [Astronaut] = Bundle.main.decode("astronauts.json")
let missions: [Mission] = Bundle.main.decode("missions.json")
var body: some View {
NavigationView {
List(missions) { mission in
NavigationLink(destination: Text("Detail view")) {
Image(mission.image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 44, height: 44)
VStack(alignment: .leading) {
Text(mission.displayName)
.font(.headline)
//Text(mission.launchDate ?? "N/A")
Text(mission.formattedLaunchDate)
}
}
}
.navigationBarTitle("Moonshot")
}
}
}