Skip to content

Commit 34d8d6b

Browse files
committed
feat: implement custom decoder for nullable arrays
1 parent 89da579 commit 34d8d6b

File tree

4 files changed

+102
-8
lines changed

4 files changed

+102
-8
lines changed

Sources/VRCKit/Models/FriendModel.swift

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import Foundation
99

1010
extension Friend: ProfileElementRepresentable, LocationRepresentable {}
1111

12-
public struct Friend: Codable {
12+
public struct Friend {
1313
public let bio: String?
14-
@NullableArray public var bioLinks: [URL]
14+
public var bioLinks: [String]
1515
public let currentAvatarImageUrl: String?
1616
public let currentAvatarThumbnailImageUrl: String?
1717
public let displayName: String
@@ -26,6 +26,32 @@ public struct Friend: Codable {
2626
public let userIcon: String?
2727
public let location: String
2828
public let friendKey: String
29+
30+
}
31+
32+
extension Friend: Codable {
33+
public init(from decoder: any Decoder) throws {
34+
let container = try decoder.container(keyedBy: CodingKeys.self)
35+
self.bio = try container.decodeIfPresent(String.self, forKey: .bio)
36+
self.bioLinks = try container.decodeIfPresent([String].self, forKey: .bioLinks) ?? []
37+
self.currentAvatarImageUrl = try container.decodeIfPresent(String.self, forKey: .currentAvatarImageUrl)
38+
self.currentAvatarThumbnailImageUrl = try container.decodeIfPresent(
39+
String.self,
40+
forKey: .currentAvatarThumbnailImageUrl
41+
)
42+
self.displayName = try container.decode(String.self, forKey: .displayName)
43+
self.id = try container.decode(String.self, forKey: .id)
44+
self.isFriend = try container.decode(Bool.self, forKey: .isFriend)
45+
self.lastLogin = try container.decode(Date.self, forKey: .lastLogin)
46+
self.lastPlatform = try container.decode(String.self, forKey: .lastPlatform)
47+
self.profilePicOverride = try container.decodeIfPresent(String.self, forKey: .profilePicOverride)
48+
self.status = try container.decode(UserStatus.self, forKey: .status)
49+
self.statusDescription = try container.decode(String.self, forKey: .statusDescription)
50+
self.tags = try container.decode([Tag].self, forKey: .tags)
51+
self.userIcon = try container.decodeIfPresent(String.self, forKey: .userIcon)
52+
self.location = try container.decode(String.self, forKey: .location)
53+
self.friendKey = try container.decode(String.self, forKey: .friendKey)
54+
}
2955
}
3056

3157
extension FriendsLocation: LocationRepresentable {}

Sources/VRCKit/Models/UserDetailModel.swift

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ extension UserDetail: ProfileDetailRepresentable, LocationRepresentable {}
1111

1212
public typealias Tag = String
1313

14-
public struct UserDetail: Codable {
14+
public struct UserDetail {
1515
public var bio: String?
16-
@NullableArray public var bioLinks: [URL]
16+
public var bioLinks: [String]
1717
public let currentAvatarImageUrl: String?
1818
public let currentAvatarThumbnailImageUrl: String?
1919
public let displayName: String
@@ -34,6 +34,35 @@ public struct UserDetail: Codable {
3434
public let lastActivity: Date
3535
}
3636

37+
extension UserDetail: Codable {
38+
public init(from decoder: any Decoder) throws {
39+
let container = try decoder.container(keyedBy: CodingKeys.self)
40+
bio = try container.decodeIfPresent(String.self, forKey: .bio)
41+
bioLinks = try container.decodeIfPresent([String].self, forKey: .bioLinks) ?? []
42+
currentAvatarImageUrl = try container.decodeIfPresent(String.self, forKey: .currentAvatarImageUrl)
43+
currentAvatarThumbnailImageUrl = try container.decodeIfPresent(
44+
String.self,
45+
forKey: .currentAvatarThumbnailImageUrl
46+
)
47+
displayName = try container.decode(String.self, forKey: .displayName)
48+
id = try container.decode(String.self, forKey: .id)
49+
isFriend = try container.decode(Bool.self, forKey: .isFriend)
50+
lastLogin = try container.decode(Date.self, forKey: .lastLogin)
51+
lastPlatform = try container.decode(String.self, forKey: .lastPlatform)
52+
profilePicOverride = try container.decodeIfPresent(String.self, forKey: .profilePicOverride)
53+
state = try container.decode(User.State.self, forKey: .state)
54+
status = try container.decode(UserStatus.self, forKey: .status)
55+
statusDescription = try container.decode(String.self, forKey: .statusDescription)
56+
tags = try container.decode([Tag].self, forKey: .tags)
57+
userIcon = try container.decodeIfPresent(String.self, forKey: .userIcon)
58+
location = try container.decode(String.self, forKey: .location)
59+
friendKey = try container.decode(String.self, forKey: .friendKey)
60+
dateJoined = try container.decode(String.self, forKey: .dateJoined)
61+
note = try container.decode(String.self, forKey: .note)
62+
lastActivity = try container.decode(Date.self, forKey: .lastActivity)
63+
}
64+
}
65+
3766
public struct EditableUserInfo: Codable, Hashable {
3867
public var bio: String
3968
public var bioLinks: [URL]
@@ -43,7 +72,7 @@ public struct EditableUserInfo: Codable, Hashable {
4372

4473
public init(detail: any ProfileDetailRepresentable) {
4574
self.bio = detail.bio ?? ""
46-
self.bioLinks = detail.bioLinks
75+
self.bioLinks = []
4776
self.status = detail.status
4877
self.statusDescription = detail.statusDescription
4978
self.tags = detail.tags

Sources/VRCKit/Models/UserModel.swift

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77

88
import Foundation
99

10-
public struct User: Codable, ProfileDetailRepresentable {
10+
public struct User: ProfileDetailRepresentable {
1111
public let activeFriends: [String]
1212
public let allowAvatarCopying: Bool
1313
public let bio: String?
14-
@NullableArray public var bioLinks: [URL]
14+
public var bioLinks: [String]
1515
public let currentAvatar: String
1616
public let currentAvatarAssetUrl: String
1717
public let currentAvatarImageUrl: String?
@@ -54,6 +54,45 @@ public struct User: Codable, ProfileDetailRepresentable {
5454
}
5555
}
5656

57+
extension User: Codable {
58+
public init(from decoder: any Decoder) throws {
59+
let container = try decoder.container(keyedBy: CodingKeys.self)
60+
self.activeFriends = try container.decode([String].self, forKey: .activeFriends)
61+
self.allowAvatarCopying = try container.decode(Bool.self, forKey: .allowAvatarCopying)
62+
self.bio = try container.decodeIfPresent(String.self, forKey: .bio)
63+
self.bioLinks = try container.decodeIfPresent([String].self, forKey: .bioLinks) ?? []
64+
self.currentAvatar = try container.decode(String.self, forKey: .currentAvatar)
65+
self.currentAvatarAssetUrl = try container.decode(String.self, forKey: .currentAvatarAssetUrl)
66+
self.currentAvatarImageUrl = try container.decodeIfPresent(String.self, forKey: .currentAvatarImageUrl)
67+
self.currentAvatarThumbnailImageUrl = try container.decodeIfPresent(
68+
String.self,
69+
forKey: .currentAvatarThumbnailImageUrl
70+
)
71+
self.dateJoined = try container.decode(String.self, forKey: .dateJoined)
72+
self.displayName = try container.decode(String.self, forKey: .displayName)
73+
self.friendKey = try container.decode(String.self, forKey: .friendKey)
74+
self.friends = try container.decode([String].self, forKey: .friends)
75+
self.homeLocation = try container.decode(String.self, forKey: .homeLocation)
76+
self.id = try container.decode(String.self, forKey: .id)
77+
self.isFriend = try container.decode(Bool.self, forKey: .isFriend)
78+
self.lastActivity = try container.decode(Date.self, forKey: .lastActivity)
79+
self.lastLogin = try container.decode(Date.self, forKey: .lastLogin)
80+
self.lastPlatform = try container.decode(String.self, forKey: .lastPlatform)
81+
self.offlineFriends = try container.decode([String].self, forKey: .offlineFriends)
82+
self.onlineFriends = try container.decode([String].self, forKey: .onlineFriends)
83+
self.pastDisplayNames = try container.decode([User.DisplayName].self, forKey: .pastDisplayNames)
84+
self.profilePicOverride = try container.decodeIfPresent(String.self, forKey: .profilePicOverride)
85+
self.state = try container.decode(User.State.self, forKey: .state)
86+
self.status = try container.decode(UserStatus.self, forKey: .status)
87+
self.statusDescription = try container.decode(String.self, forKey: .statusDescription)
88+
self.tags = try container.decode([Tag].self, forKey: .tags)
89+
self.twoFactorAuthEnabled = try container.decode(Bool.self, forKey: .twoFactorAuthEnabled)
90+
self.userIcon = try container.decodeIfPresent(String.self, forKey: .userIcon)
91+
self.userLanguage = try container.decodeIfPresent(String.self, forKey: .userLanguage)
92+
self.userLanguageCode = try container.decodeIfPresent(String.self, forKey: .userLanguageCode)
93+
}
94+
}
95+
5796
public struct UpdatedUser: Codable {
5897
public let bio: String?
5998
public let statusDescription: String?

Sources/VRCKit/Protocols/ProfileProtocols.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Foundation
1111
/// This protocol can be adopted by structures such as User, Friend, and UserDetail.
1212
public protocol ProfileElementRepresentable: Hashable, Identifiable {
1313
var bio: String? { get }
14-
var bioLinks: [URL] { get }
14+
var bioLinks: [String] { get }
1515
var currentAvatarImageUrl: String? { get }
1616
var currentAvatarThumbnailImageUrl: String? { get }
1717
var displayName: String { get }

0 commit comments

Comments
 (0)