在不知道类型的情况下初始化swift变量



我正在为我的应用程序构建一个通知系统,我找到了一种将多种类型传递给我的模型的方法。

我需要在这个模型中定义一个目标、第二个目标和第三个目标。根据通知类型,这些目标是不同的类型。它看起来像这样:

class UserNotification<T1, T2, T3>: Codable {
let notifyType: String
let target: T1
let secondTarget: T2
let thirdTarget: T3
enum CodingKeys: String, CodingKey {
case notifyType = "notify_type"
case target
case secondTarget = "second_target"
case thirdTarget = "third_target"
}

它工作得很好。问题是我需要在我的视图中定义一个notification变量,这样我就可以从后台传递通知。这就是我被卡住的地方。如何定义变量,以便根据notifyType值进行类型转换?

class NotificationBox: UITableViewCell, CellElement {
private var notification: UserNotification<?, ?, ?> // This is where I'm stuck
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}

func setResource(resource: Codable) {
var notification = resource as! UserNotification<Any, Any, Any>
switch(notification.notifyType) {
case "get_badge":
self.notification = notification as! UserNotification<Any, Badge, Any>
case "group_reply":
self.notification = notification as! UserNotification<Message, Debate, Message>
case "level_unlock":
self.notification = notification as! UserNotification<User, Any, Any>
case "get_vote":
self.notification = notification as! UserNotification<Any, Debate, Any>
case "group_follow_argument":
self.notification = notification as! UserNotification<Message, Debate, Message>
case "user_follow_level_unlock":
self.notification = notification as! UserNotification<Any, Any, Any>
case "followed":
self.notification = notification as! UserNotification<Any, Any, Any>
case "group_invitation_new":
self.notification = notification as! UserNotification<Any, Any, Any>
default:
self.notification = notification as! UserNotification<Any, Any, Any>
}
configure()
}

除了变量定义外,一切都很好。我不知道怎么做。我试着把它定义为:

private var notification: UserNotification<Any, Any, Any>

但它给了我一个错误:

Cannot assign value of type 'UserNotification<Any, Badge, Any>' to type 'UserNotification<Any, Any, Any>'

您应该使用带有关联值的枚举。正如Schottky在他们的回答中所说,您将需要一个自定义的Codable实现。这里有一个例子,假设UserBadgeMessageDebate都是Codable,只实现其中的4种情况。

enum UserNotification: Codable {
case getBadge(Badge)
case groupReply(Message, Debate, Message)
case levelUnlock(User)
case userFollowLevelUnlock

var notifyType: String {
switch self {
case .getBadge:
return "get_badge"
case .groupReply:
return "group_reply"
case .levelUnlock:
return "level_unlock"
case .userFollowLevelUnlock:
return "user_follow_level_unlock"
}
}

enum CodingKeys: String, CodingKey {
/*
When decoding/encoding, use convertFromSnakeCase/convertToSnakeCase to convert the key names to/from snake case
No need to hard code them here
e.g.
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
*/
case notifyType
case target
case secondTarget
case thirdTarget
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(notifyType, forKey: .notifyType)
switch self {
case .getBadge(let badge):
// you can also encode the unused targets here if your backend needs them
try container.encode(badge, forKey: .secondTarget)
case .groupReply(let msg1, let debate, let msg2):
try container.encode(msg1, forKey: .target)
try container.encode(debate, forKey: .secondTarget)
try container.encode(msg2, forKey: .thirdTarget)
case .levelUnlock(let user):
try container.encode(user, forKey: .target)
case .userFollowLevelUnlock:
break // nothing more to encode in this case
}
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
switch try container.decode(String.self, forKey: .notifyType) {
case "get_badge":
self = .getBadge(
try container.decode(Badge.self, forKey: .secondTarget)
)
case "group_reply":
self = .groupReply(
try container.decode(Message.self, forKey: .target),
try container.decode(Debate.self, forKey: .secondTarget),
try container.decode(Message.self, forKey: .thirdTarget)
)
case "level_unlock":
self = .levelUnlock(
try container.decode(User.self, forKey: .target)
)
case "user_follow_level_unlock":
self = .userFollowLevelUnlock
default:
throw DecodingError.dataCorruptedError(forKey: .notifyType, in: container, debugDescription: "Unknown notifyType")
}
}
}

这样,notification属性就可以是:

private var notification: UserNotification

而CCD_ 10是琐碎的。

回应评论:我认为更好的方法是使用带有关联数据的枚举;像这样的东西:

enum Notification {
case badge(Badge)
case groupReply(message: Message, debate: Debate, reply: Message)
case followed
// ...
}

如果你想知道你有哪个通知(并获得相关数据(,你可以这样做:

switch notification {
case .badge(let badge):
// badge is now a let of type Badge
case .groupReply(let message, _, let reply):
// you typically ignore unrelated stuff with _
case .followed:
}

这有一个缺点,即目前您必须提供自己的Codable一致性。不过,swift的下一次更新已经实现了这一功能。

最新更新