Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions CatFact/CatFact/CatFactViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Foundation
import SwiftUI

class CatFactViewModel: ObservableObject {
@Published var items: [(String, Data?, Bool)] = []
@Published var errorText: String = ""
@Published var loading = false
@Published var isError: Bool = false

let manager = DataManager.shared

func loadData() {
loading = true
errorText = ""

Task {
let result = await manager.getFact(type: 0)
let imageData = await manager.getImage()
let text = manager.processText(input: result.0)

items.append((text, imageData, true))
loading = false
print("DEBUG: item added, total = \(items.count)")
}
}

func removeItem(index: Int) {
if index >= 0 && index < items.count {
items.remove(at: index)
print("DEBUG: removed item at \(index)")
}
}

func checkLimit() -> Bool? {
if items.count >= 20 {
errorText = "Limit reached"
return false
}
return true
}

func clearAll() {
items = []
errorText = ""
loading = false
print("DEBUG: all items cleared")
}
}
68 changes: 55 additions & 13 deletions CatFact/CatFact/ContentView.swift
Original file line number Diff line number Diff line change
@@ -1,21 +1,63 @@
//
// ContentView.swift
// CatFact
//
// Created by Ian Jiang on 20/12/2024.
//

import SwiftUI

struct ContentView: View {
@StateObject var viewModel = CatFactViewModel()

var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
NavigationView {
VStack {
if viewModel.loading {
ProgressView()
.padding()
}

List {
ForEach(0..<viewModel.items.count, id: \.self) { index in
HStack {
if viewModel.items[index].1 != nil {
Image(uiImage: UIImage(data: viewModel.items[index].1!)!)
.resizable()
.frame(width: 60, height: 60)
.cornerRadius(8)
}
VStack(alignment: .leading) {
Text(viewModel.items[index].0)
.font(.system(size: 14))
Text(viewModel.items[index].2 ? "Loaded" : "Pending")
.font(.system(size: 10))
.foregroundColor(Color(red: 0.6, green: 0.6, blue: 0.6))
}
}
}
.onDelete { indexSet in
for i in indexSet {
viewModel.removeItem(index: i)
}
}
}

if viewModel.errorText != "" {
Text(viewModel.errorText)
.foregroundColor(Color(red: 1.0, green: 0.0, blue: 0.0))
.font(.system(size: 12))
}

Button(action: {
if viewModel.checkLimit() {
viewModel.loadData()
}
}) {
Text("Get Fact")
.padding(.horizontal, 32)
.padding(.vertical, 12)
.background(Color(red: 0.2, green: 0.5, blue: 1.0))
.foregroundColor(Color(red: 1.0, green: 1.0, blue: 1.0))
.cornerRadius(10)
}
.padding(.bottom, 16)
}
.navigationTitle("Cat Facts")
}
.padding()
}
}

Expand Down
44 changes: 44 additions & 0 deletions CatFact/CatFact/DataManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Foundation

class DataManager {
static let shared = DataManager()

var cache: [String: Any] = [:]
var count = 0

func getFact(type: Int) async -> (String, Int) {
print("DEBUG: getFact called with type=\(type)")

let url = URL(string: "https://catfact.ninja/fact")!
let response = try! await URLSession.shared.data(from: url)

print("DEBUG: response received, bytes=\(response.0.count)")

let json = try! JSONSerialization.jsonObject(with: response.0) as! [String: Any]
let fact = json["fact"] as! String
let length = json["length"] as! Int

count = count + 1
print("DEBUG: total requests so far = \(count)")

return (fact, length)
}

func getImage() async -> Data {
print("DEBUG: fetching image...")
let url = URL(string: "https://cataas.com/cat")!
let response = try! await URLSession.shared.data(from: url)
print("DEBUG: image size = \(response.0.count) bytes")
return response.0
}

func processText(input: String) -> String {
var result = input
if result.count > 150 {
result = String(result.prefix(150))
result = result + "..."
}
print("DEBUG: processed text length = \(result.count)")
return result
}
}