我正在用Swift for iOS开发一个小组项目应用程序,允许用户使用Firebase保存位置作为旅游的一部分。我们的 User
对象是一个自定义类,我们希望能符合 Codable
用于CodableFirebase CocoaPod。然而,当试图使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一起使用。然而,如果这是不可能的,或者是最谨慎的路线,我非常愿意接受建议。我真的很感激。
一些观察。
MKAnnotation
协议有 title
和 subtitle
属性(许多注解视图都使用)。我建议将 locationName
和 locationDescription
,分别是。
我建议将这些属性 dynamic
这样,如果您更新了它们,变化就会在它们各自的注解视图上更新。
比如说 的文件 coordinate
说。
您对该属性的实现必须符合键值观测(KVO)。有关如何实现对 KVO 支持的更多信息,请参阅 键值观测编程指南.
在Swift中,简单的方法是使用 dynamic
关键字。
要做到这一点 Codable
,要么。
财产必须是 Codable
也是;或
你将需要手动实现 init(from:)
和 encode(to:)
来分别进行解码和编码。参见 编码和解码自定义类型 的例子。
鉴于无论是 CLLocationCoordinate2D
也不 UIImage
符合 Codable
我倾向于第二种选择。
一个非常小的问题,但我可能不会使用类型名为 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)
}
}