Пользовательский класс соответствует MKAnnotation и Codable - PullRequest
0 голосов
/ 28 апреля 2020

Я работаю над приложением группового проекта в Swift для iOS, которое позволяет пользователям сохранять местоположения в рамках тура, используя Firebase. Наш User объект является пользовательским классом, который, как мы надеемся, будет соответствовать Codable для использования с CodableFirebase CocoaPod. Однако проблема возникает при попытке согласовать объект Location с Codable, поскольку он также должен соответствовать MKAnnotation ... и мы все довольно новы в этом. Вполне возможно, что некоторые решения у меня над головой ... вполне возможно.

Это наш Location объект:

import Foundation
import MapKit
import CoreLocation
import CodableFirebase

class Location: NSObject, MKAnnotation {

    var coordinate: CLLocationCoordinate2D
    var locationName: String?
    var locationDescription: String?
    var locationImage: UIImage?

    init(coordinate: CLLocationCoordinate2D, locationName: String, locationDescription: String, locationImage: UIImage) {

        self.coordinate = coordinate
        self.locationName = locationName
        self.locationDescription = locationDescription
        self.locationImage = locationImage

    }
}

А это наш User объект модели:

import UIKit
import CloudKit

class User {

    //MARK: - Editable Public Profile Info
    var firstName: String
    var lastName: String
    var userStateOfOrigin: States
    var userImage: UIImage?
    var aboutMe: String
    var languages: [Language]

    //MARK: - Editable Personal Info (Includes all Public Profile info)
    var email: String?
    var phoneNumber: String?

    //MARK: - Hidden Variables
    var userId: String
    var userCreatedTours: [Tour]
    var savedLocations: [Location]
    var savedTours: [Tour]
    var bookedTours: [Tour]
    var previouslyExperiencedTours: [Tour]
    var userRating: [Rating]

    init(firstName: String, lastName: String, userStateOfOrigin: States, userImage: UIImage? = nil, aboutMe: String, languages: [Language] = [], email: String?, phoneNumber: String? = "", userId: String, userCreatedTours: [Tour] = [], savedLocations: [Location] = [], savedTours: [Tour] = [], bookedTours: [Tour] = [], previouslyExperiencedTours: [Tour] = [], userRating: [Rating] = []) {

        // editable Public info
        self.firstName =  firstName
        self.lastName = lastName
        self.userStateOfOrigin = userStateOfOrigin
        self.userImage = userImage
        self.aboutMe = aboutMe
        self.languages = languages

        // editable personal info
        self.email = email
        self.phoneNumber = phoneNumber

        // Hidden Variables
        self.userId = userId
        self.userCreatedTours = userCreatedTours
        self.savedLocations = savedLocations
        self.savedTours = savedTours
        self.bookedTours = bookedTours
        self.previouslyExperiencedTours = previouslyExperiencedTours
        self.userRating = userRating

    }
}

У нас есть несколько других пользовательских объектов, но я думаю, что как только эта проблема будет решена, мы сможем легко согласовать их с Codable. В конечном счете, было бы здорово иметь возможность сделать так, чтобы они оба соответствовали Codable для удобного использования с Firebase. Однако, если это невозможно, или самый разумный путь, я очень открыт для предложений. Я действительно ценю это.

1 Ответ

0 голосов
/ 29 апреля 2020

Пара наблюдений:

  1. MKAnnotation протокол имеет свойства title и subtitle (которые используются во многих представлениях аннотаций). Я мог бы предложить переименовать locationName и locationDescription, соответственно.

  2. Я бы предложил сделать эти свойства dynamic, так что если вы когда-нибудь их обновите, изменения будут обновлены на их соответствующих представлениях аннотаций.

    Например, документация для coordinate гласит:

    Ваша реализация этого свойства должна быть наблюдением значения ключа ( КВО) соответствует. Для получения дополнительной информации о том, как реализовать поддержку KVO, см. Руководство по программированию наблюдения значения ключа .

    Простой способ сделать это в Swift - с ключевым словом dynamic.

  3. Чтобы сделать это Codable, либо:

    Учитывая, что ни CLLocationCoordinate2D, ни UIImage не соответствуют Codable, я бы склонялся к этому второму варианту.

  4. A очень незначительный момент, но я мог бы не использовать имя типа Location, а вместо этого использовать что-то с Annotation в имени, чтобы прояснить, что это за объект. Это также помогает избежать путаницы с объектами местоположения, например, CLLocation. Я использовал CustomAnnotation ниже, что не очень хорошее имя, но, надеюсь, вы можете придумать имя, которое имеет больше смысла в вашем приложении, но также содержит Annotation в названии.

Таким образом, собрав все это вместе, вы можете сделать что-то вроде:

class CustomAnnotation: NSObject, MKAnnotation, Codable {
    dynamic var coordinate: CLLocationCoordinate2D
    dynamic var title: String?
    dynamic var subtitle: String?
    dynamic var image: UIImage?

    init(coordinate: CLLocationCoordinate2D, title: String?, subtitle: String?, locationImage: UIImage?) {
        self.coordinate = coordinate
        self.title = title
        self.subtitle = subtitle
        self.image = locationImage
    }

    enum CodingKeys: String, CodingKey {
        case title, subtitle, image, latitude, longitude
    }

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

        title = try values.decodeIfPresent(String.self, forKey: .title)
        subtitle = try values.decodeIfPresent(String.self, forKey: .subtitle)

        let latitude = try values.decode(CLLocationDegrees.self, forKey: .latitude)
        let longitude = try values.decode(CLLocationDegrees.self, forKey: .longitude)
        coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)

        let data = try values.decodeIfPresent(Data.self, forKey: .image)
        image = data.flatMap { UIImage(data: $0) }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encodeIfPresent(title, forKey: .title)
        try container.encodeIfPresent(subtitle, forKey: .subtitle)
        try container.encode(coordinate.latitude, forKey: .latitude)
        try container.encode(coordinate.longitude, forKey: .longitude)
        try container.encodeIfPresent(image?.pngData(), forKey: .image)
    }
}
...