Tutorials โฉ Model
Last updated
Last updated
Landmark.swift (8)
ModelData.swift (9)
Hike.swift (10)
Profile.swift (11)
import Foundation
import SwiftUI
import CoreLocation
struct Landmark: Hashable, Codable, Identifiable {
/// property required by `Identifiable` protocol;
var id: Int
var name: String
var park: String
var state: String
var description: String
var isFavorite: Bool
var isFeatured: Bool
var category: Category
/// Landmark.Category
enum Category: String, CaseIterable, Codable {
case lakes = "Lakes"
case rivers = "Rivers"
case mountains = "Mountains"
}
// MARK: - image
private var imageName: String
/// loads an image from the asset catalog.
var image: Image { Image(imageName) }
// MARK: - coordinates
/// for JSON decoding
struct Coordinates: Hashable, Codable {
var latitude: Double
var longitude: Double
}
private var coordinates: Coordinates
var locationCoordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(
latitude: coordinates.latitude,
longitude: coordinates.longitude
)
}
}
import Foundation
// 1. import `Combine` framework
import Combine // for `ObservableObject`
// 2. conforms to `ObservableObject`
final class ModelData: ObservableObject {
// โญ๏ธ load initial data from JSON
// 3. publish properties
/// An observable object needs to publish any changes to its data,
/// so that its subscribers can pick up the change.
@Published var landmarks: [Landmark] = load("landmarkData.json")
/// user profile that persists even after the user dismisses the profile view.
@Published var profile = Profile.default
/// hike data won't be modified, donโt need to mark it with `@Published`.
var hikes: [Hike] = load("hikeData.json")
/// dictionary of landmarks (by category name)
var categories: [String: [Landmark]] {
Dictionary(
grouping: landmarks,
by: { $0.category.rawValue }
)
}
/// featured landmarks
var features: [Landmark] {
landmarks.filter { $0.isFeatured }
}
}
/// โญ๏ธ fetches JSON data from appโs main bundle.
/// - Returns: an instance of a type conforming to `Decodable`
func load<T: Decodable>(_ filename: String) -> T {
// prepare to get data from file
let data: Data
// get file URL from app's main bundle
// (filename -> URL)
guard
let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle.")
}
// URL -> Data
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
// Data -> T
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
import Foundation
struct Hike: Codable, Hashable, Identifiable {
var id: Int
var name: String
var distance: Double
var difficulty: Int
var observations: [Observation]
static var formatter = LengthFormatter()
var distanceText: String {
Hike.formatter.string(fromValue: distance, unit: .kilometer)
}
/// Hike.Observation
struct Observation: Codable, Hashable {
var distanceFromStart: Double
var elevation: Range<Double>
var pace: Range<Double>
var heartRate: Range<Double>
}
}sw
import Foundation
/// user profile
struct Profile {
var username : String
var prefersNotifications = true
var seasonalPhoto = Season.winter
var goalDate = Date()
static let `default` = Profile(username: "g_kumar")
enum Season: String, CaseIterable, Identifiable {
case spring = "๐ท"
case summer = "๐"
case autumn = "๐"
case winter = "โ๏ธ"
var id: String { rawValue }
}
}