Skip to content

Commit

Permalink
edition 2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
trozware committed Jul 22, 2024
0 parents commit eee1a33
Show file tree
Hide file tree
Showing 512 changed files with 28,793 additions and 0 deletions.
190 changes: 190 additions & 0 deletions 01-designing-the-data-model/final/OnThisDay.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
//
// OnThisDay.playground
// macOS by Tutorials
// Version 2.0
//
// by Sarah Reichelt
//

import Cocoa

let appState = AppState()
let monthNum = 2
let dayNum = 29

func testData() {
if let day = appState.getDataFor(
month: monthNum, day: dayNum
) {
print(day.displayDate)
print("\(day.deaths.count) deaths")
} else {
print("No data available for that month & day.")
}
}

extension String {
// String extension to decode HTML entities
var decoded: String {
let attr = try? NSAttributedString(
data: Data(utf8),
options: [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
],
documentAttributes: nil)

return attr?.string ?? self
}
}

enum FetchError: Error {
case badURL
case badResponse
case badJSON
}

func getDataForDay(month: Int, day: Int) async throws -> Day {
let address = "https://today.zenquotes.io/api/\(month)/\(day)"
guard let url = URL(string: address) else {
throw FetchError.badURL
}
let request = URLRequest(url: url)

let (data, response) = try await URLSession.shared.data(for: request)
guard
let response = response as? HTTPURLResponse,
response.statusCode < 400
else {
throw FetchError.badResponse
}

if let jsonString = String(data: data, encoding: .utf8) {
saveSampleData(json: jsonString)
}

do {
let day = try JSONDecoder().decode(Day.self, from: data)
return day
} catch {
throw FetchError.badJSON
}
}

// Task {
// do {
// try await getDataForDay(month: 2, day: 29)
// } catch {
// print(error)
// }
// }

// if let data = readSampleData() {
// do {
// let day = try JSONDecoder().decode(Day.self, from: data)
// // print(day.displayDate)
// // print(day.births.count)
// // print(day.births[0].links)
// // print(day.births[0].text)
// // print(day.births[0].year)
//
// appState.days[day.displayDate] = day
// testData()
// } catch {
// print(error)
// }
// }

Task {
do {
let day = try await getDataForDay(
month: monthNum,
day: dayNum
)
appState.days[day.displayDate] = day
testData()
} catch {
print(error)
}
}

struct Day: Decodable {
let date: String
let data: [String: [Event]]

var events: [Event] { data[EventType.events.rawValue] ?? [] }
var births: [Event] { data[EventType.births.rawValue] ?? [] }
var deaths: [Event] { data[EventType.deaths.rawValue] ?? [] }

var displayDate: String {
date.replacingOccurrences(of: "_", with: " ")
}
}

struct Event: Decodable, Identifiable {
let id = UUID()
let text: String
let year: String
let links: [EventLink]

enum CodingKeys: String, CodingKey {
case text
case links
}

init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)

let rawText = try values.decode(String.self, forKey: .text)
let textParts = rawText.components(separatedBy: " &#8211; ")
if textParts.count == 2 {
year = textParts[0]
text = textParts[1].decoded
} else {
year = "?"
text = rawText.decoded
}

let allLinks = try values.decode(
[String: [String: String]].self,
forKey: .links
)

var processedLinks: [EventLink] = []
for (_, link) in allLinks {
if
let title = link["2"],
let address = link["1"],
let url = URL(string: address) {
processedLinks.append(
EventLink(id: UUID(), title: title, url: url)
)
}
}
links = processedLinks
}
}

struct EventLink: Decodable, Identifiable {
let id: UUID
let title: String
let url: URL
}

enum EventType: String {
case events = "Events"
case births = "Births"
case deaths = "Deaths"
}

// 1
class AppState {
// 2
var days: [String: Day] = [:]
// 3
func getDataFor(month: Int, day: Int) -> Day? {
let monthName = Calendar.current.monthSymbols[month - 1]
let dateString = "\(monthName) \(day)"
return days[dateString]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// Files.swift
// macOS by Tutorials
// Version 2.0
//
// by Sarah Reichelt
//

import Foundation

public var sampleFileURL: URL {
URL.downloadsDirectory.appending(path: "SampleData.json")
}

public func saveSampleData(json: String) {
do {
try json.write(to: sampleFileURL, atomically: true, encoding: .utf8)
print("Sample JSON data saved to \(sampleFileURL.path)")
} catch {
print(error)
}
}

public func readSampleData() -> Data? {
do {
let data = try Data(contentsOf: sampleFileURL)
return data
} catch {
print(error)
return nil
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='macos' buildActiveScheme='true' importAppTypes='true'>
<timeline fileName='timeline.xctimeline'/>
</playground>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// OnThisDay.playground
// macOS by Tutorials
// Version 2.0
//
// by Sarah Reichelt
//

import Cocoa

extension String {
// String extension to decode HTML entities
var decoded: String {
let attr = try? NSAttributedString(
data: Data(utf8),
options: [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
],
documentAttributes: nil)

return attr?.string ?? self
}
}

enum FetchError: Error {
case badURL
case badResponse
case badJSON
}

func getDataForDay(month: Int, day: Int) async throws {
let address = "https://today.zenquotes.io/api/\(month)/\(day)"
guard let url = URL(string: address) else {
throw FetchError.badURL
}
let request = URLRequest(url: url)

let (data, response) = try await URLSession.shared.data(for: request)
guard
let response = response as? HTTPURLResponse,
response.statusCode < 400
else {
throw FetchError.badResponse
}

if let jsonString = String(data: data, encoding: .utf8) {
saveSampleData(json: jsonString)
}
}

Task {
do {
try await getDataForDay(month: 2, day: 29)
} catch {
print(error)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// Files.swift
// macOS by Tutorials
// Version 2.0
//
// by Sarah Reichelt
//

import Foundation

public var sampleFileURL: URL {
URL.downloadsDirectory.appending(path: "SampleData.json")
}

public func saveSampleData(json: String) {
do {
try json.write(to: sampleFileURL, atomically: true, encoding: .utf8)
print("Sample JSON data saved to \(sampleFileURL.path)")
} catch {
print(error)
}
}

public func readSampleData() -> Data? {
do {
let data = try Data(contentsOf: sampleFileURL)
return data
} catch {
print(error)
return nil
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='macos' buildActiveScheme='true' importAppTypes='true'>
<timeline fileName='timeline.xctimeline'/>
</playground>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit eee1a33

Please sign in to comment.