Hi guys,
I'm struggling a lot with challenge on day 77,
1: Why the array is not sorted? In the struct I added the function < but it doesn't work
2: The Data that I save on disk is not going to appear when I restart the app. Why?
3: I can't assign a MapMarker using the coordinate in the struct because it gives me errors.
Below the complete code:
import CoreLocation
import SwiftUI
struct ContentView: View {
@State private var image: UIImage?
@State private var showingImagePicker = false
@State private var showingAlert = false
@State private var faces = [Face]().sorted()
@State private var name = ""
let locationFetcher = LocationFetcher()
var body: some View {
NavigationView {
List(faces) { face in
NavigationLink {
DetailView(face: face)
} label: {
HStack {
Image(uiImage: face.image ?? UIImage(systemName: "person")!)
.resizable()
.frame(width: 100, height: 100)
.clipShape(Circle())
.overlay(Circle().strokeBorder(.yellow, lineWidth: 3))
Spacer()
Text(face.name)
.font(.title2)
.padding(.trailing)
}
}
}
.navigationTitle("Facename")
.sheet(isPresented: $showingImagePicker) {
ImagePicker(image: $image)
}
.toolbar {
Button() {
showingImagePicker = true
} label: {
Image(systemName: "plus")
}
}
.onChange(of: image) { _ in
showingAlert = true
}
.alert("Person's name", isPresented: $showingAlert) {
TextField("Enter name", text: $name)
Button("Save") {
self.locationFetcher.start()
addFace()
name = ""
}
}
}
}
let savePath = FileManager.documentsDirectory.appendingPathComponent("faces")
init() {
do {
let data = try Data(contentsOf: savePath)
faces = try JSONDecoder().decode([Face].self, from: data)
} catch {
faces = []
print(error)
}
}
func addFace() {
if let location = self.locationFetcher.lastKnownLocation {
let newFace = Face(id: UUID(), name: name, image: image, latitude: location.latitude, longitude: location.longitude)
faces.append(newFace)
save()
} else {
print("Unable to see location.")
}
}
func save() {
do {
let data = try JSONEncoder().encode(faces)
try data.write(to: savePath, options: [NSData.WritingOptions.atomic, .completeFileProtection])
} catch {
print("Unable to save data")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
import MapKit
import SwiftUI
struct DetailView: View {
@State private var mapRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 50, longitude: 0), span: MKCoordinateSpan(latitudeDelta: 25, longitudeDelta: 25))
@State var face: Face
var body: some View {
VStack {
Image(uiImage: face.image ?? UIImage(systemName: "person")!)
.resizable()
.scaledToFit()
.padding()
Map(coordinateRegion: $mapRegion)
}
.navigationTitle("\(face.name)")
.navigationBarTitleDisplayMode(.inline)
}
}
struct DetailView_Previews: PreviewProvider {
static var previews: some View {
DetailView(face: Face.example)
}
}
import CoreLocation
import SwiftUI
struct Face: Identifiable, Codable, Equatable, Comparable {
var id: UUID
var name: String
var image: UIImage?
let latitude: Double
let longitude: Double
var coordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}
static let example = Face(id: UUID(), name: "Giovanni", image: UIImage(systemName: "person"), latitude: 50.50, longitude: 50.50)
enum CodingKeys: CodingKey {
case id
case name
case image
case latitude
case longitude
}
init(id: UUID, name: String, image: UIImage?, latitude: Double, longitude: Double) {
self.id = id
self.name = name
self.image = image
self.latitude = latitude
self.longitude = longitude
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(UUID.self, forKey: .id)
self.name = try container.decode(String.self, forKey: .name)
let imageData = try container.decode(Data.self, forKey: .image)
self.image = UIImage(data: imageData)
self.latitude = try container.decode(Double.self, forKey: .latitude)
self.longitude = try container.decode(Double.self, forKey: .longitude)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .name)
try container.encode(name, forKey: .name)
let imageData = image?.jpegData(compressionQuality: 0.8)
try container.encode(imageData, forKey: .image)
try container.encode(latitude, forKey: .latitude)
try container.encode(longitude, forKey: .longitude)
}
static func ==(lhs: Face, rhs: Face) -> Bool {
lhs.id == rhs.id
}
static func <(lhs: Face, rhs: Face) -> Bool {
lhs.name < rhs.name
}
}
import PhotosUI
import SwiftUI
struct ImagePicker: UIViewControllerRepresentable {
@Binding var image: UIImage?
class Coordinator: NSObject, PHPickerViewControllerDelegate {
var parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
guard let provider = results.first?.itemProvider else { return }
if provider.canLoadObject(ofClass: UIImage.self) {
provider.loadObject(ofClass: UIImage.self) { image, _ in
self.parent.image = image as? UIImage
}
}
}
}
func makeUIViewController(context: Context) -> PHPickerViewController {
let configurator = PHPickerConfiguration()
let picker = PHPickerViewController(configuration: configurator)
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
import Foundation
extension FileManager {
static var documentsDirectory: URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
}
import CoreLocation
class LocationFetcher: NSObject, CLLocationManagerDelegate {
let manager = CLLocationManager()
var lastKnownLocation: CLLocationCoordinate2D?
override init() {
super.init()
manager.delegate = self
}
func start() {
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
lastKnownLocation = locations.first?.coordinate
}
}