From 02fda6a36b6fb37b65a7c1786b01bcf82b51e3de Mon Sep 17 00:00:00 2001 From: makinosp Date: Tue, 10 Sep 2024 22:07:58 +0900 Subject: [PATCH 01/16] feat: add validation in verify2FA --- Sources/VRCKit/Services/AuthenticationService.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/VRCKit/Services/AuthenticationService.swift b/Sources/VRCKit/Services/AuthenticationService.swift index e2c4b6a..398b2db 100644 --- a/Sources/VRCKit/Services/AuthenticationService.swift +++ b/Sources/VRCKit/Services/AuthenticationService.swift @@ -37,6 +37,7 @@ public class AuthenticationService: APIService, AuthenticationServiceProtocol { /// Verify 2FA With TOTP or Email OTP public func verify2FA(verifyType: VerifyType, code: String) async throws -> Bool { + guard code.count == 6 else { throw VRCKitError.invalidRequest("Code must be 6 digits") } let path = "\(authPath)/twofactorauth/\(verifyType.rawValue.lowercased())/verify" let requestData = try Serializer.shared.encode(VerifyRequest(code: code)) let response = try await client.request( From 5eb1db05cef2b2a27bbf4011c0902393475d4a84 Mon Sep 17 00:00:00 2001 From: makinosp Date: Thu, 12 Sep 2024 21:36:54 +0900 Subject: [PATCH 02/16] refact: refactoring code --- Sources/VRCKit/APIClient.swift | 2 +- Sources/VRCKit/Models/UserModel.swift | 27 ++++++++++----------------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/Sources/VRCKit/APIClient.swift b/Sources/VRCKit/APIClient.swift index 5297dff..612bee2 100644 --- a/Sources/VRCKit/APIClient.swift +++ b/Sources/VRCKit/APIClient.swift @@ -44,7 +44,7 @@ public final class APIClient { /// - password: The password associated with the username. /// - Returns: A Basic Authentication token string. /// - Throws: `VRCKitError.unexpectedError` if the username and password cannot be converted to UTF-8 data. - func encodeAuthorization(_ username: String, _ password: String) throws -> String { + private func encodeAuthorization(_ username: String, _ password: String) throws -> String { let authString = "\(username):\(password)" guard let payload = authString.data(using: .utf8) else { throw VRCKitError.unexpected diff --git a/Sources/VRCKit/Models/UserModel.swift b/Sources/VRCKit/Models/UserModel.swift index c2a5f57..c7df732 100644 --- a/Sources/VRCKit/Models/UserModel.swift +++ b/Sources/VRCKit/Models/UserModel.swift @@ -38,25 +38,12 @@ public struct User: ProfileDetailRepresentable { public let userLanguage: String? public let userLanguageCode: String? public let presence: Presence -} - -public extension User { - var platform: UserPlatform { - presence.platform - } - var url: URL? { - URL(string: [Const.homeBaseUrl, "user", id].joined(separator: "/")) - } -} -public extension User { - struct DisplayName: Codable, Hashable { + public struct DisplayName: Codable, Hashable { public let displayName: String public let updatedAt: Date } -} -public extension User { enum State: String, Codable { /// User is online in VRChat case online @@ -65,10 +52,8 @@ public extension User { /// User is offline case offline } -} -public extension User { - struct Presence: Codable, Hashable { + public struct Presence: Codable, Hashable { public let groups: [String] public let id: String public let instance: String @@ -81,6 +66,14 @@ public extension User { } } +public extension User { + var platform: UserPlatform { + presence.platform + } + var url: URL? { + URL(string: [Const.homeBaseUrl, "user", id].joined(separator: "/")) + } +} extension User.Presence { init() { groups = [] From 5aacb394d07809ca69875baa0f58f7a93e86f575 Mon Sep 17 00:00:00 2001 From: makinosp Date: Thu, 12 Sep 2024 21:37:14 +0900 Subject: [PATCH 03/16] feat: conform TrustRank to Equatable --- Sources/VRCKit/Utils/TrustRank.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/VRCKit/Utils/TrustRank.swift b/Sources/VRCKit/Utils/TrustRank.swift index 88c45e1..2b0cae3 100644 --- a/Sources/VRCKit/Utils/TrustRank.swift +++ b/Sources/VRCKit/Utils/TrustRank.swift @@ -5,7 +5,7 @@ // Created by makinosp on 2024/08/04. // -public enum TrustRank { +public enum TrustRank: Equatable { case trusted, known, user, newUser, visitor, unknown } From da6af4d2410ccb857727a7d993c126caf3d0866c Mon Sep 17 00:00:00 2001 From: makinosp Date: Thu, 12 Sep 2024 21:37:56 +0900 Subject: [PATCH 04/16] refact: filename of TrustRank --- .../{Utils/TrustRank.swift => Models/TrustRankModel.swift} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Sources/VRCKit/{Utils/TrustRank.swift => Models/TrustRankModel.swift} (97%) diff --git a/Sources/VRCKit/Utils/TrustRank.swift b/Sources/VRCKit/Models/TrustRankModel.swift similarity index 97% rename from Sources/VRCKit/Utils/TrustRank.swift rename to Sources/VRCKit/Models/TrustRankModel.swift index 2b0cae3..50cefd6 100644 --- a/Sources/VRCKit/Utils/TrustRank.swift +++ b/Sources/VRCKit/Models/TrustRankModel.swift @@ -1,5 +1,5 @@ // -// TrustRank.swift +// TrustRankModel.swift // VRCKit // // Created by makinosp on 2024/08/04. From e25d5b0775b1d24215de5a76187f8971f1d6125a Mon Sep 17 00:00:00 2001 From: makinosp Date: Thu, 12 Sep 2024 21:40:38 +0900 Subject: [PATCH 05/16] fix: access control of User.State --- Sources/VRCKit/Models/UserModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/VRCKit/Models/UserModel.swift b/Sources/VRCKit/Models/UserModel.swift index c7df732..7a88f57 100644 --- a/Sources/VRCKit/Models/UserModel.swift +++ b/Sources/VRCKit/Models/UserModel.swift @@ -44,7 +44,7 @@ public struct User: ProfileDetailRepresentable { public let updatedAt: Date } - enum State: String, Codable { + public enum State: String, Codable { /// User is online in VRChat case online /// User is online, but not in VRChat From ab7d074ad54b515251f30f87876fcde1d331e553 Mon Sep 17 00:00:00 2001 From: kiripoipoi Date: Sat, 14 Sep 2024 11:46:27 +0900 Subject: [PATCH 06/16] feat: add favoriteGroup parameter --- Sources/VRCKit/Models/WorldModel.swift | 3 ++- Sources/VRCKit/PreviewServices/PreviewDataProvider.swift | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/VRCKit/Models/WorldModel.swift b/Sources/VRCKit/Models/WorldModel.swift index eba49f0..2f48d04 100644 --- a/Sources/VRCKit/Models/WorldModel.swift +++ b/Sources/VRCKit/Models/WorldModel.swift @@ -10,7 +10,7 @@ import Foundation public struct World: Codable, Identifiable, Hashable { public let id: String public let name: String - public let description: String + public let description: String? public let featured: Bool public let authorId: String public let authorName: String @@ -30,6 +30,7 @@ public struct World: Codable, Identifiable, Hashable { public let visits: Int public let popularity: Int public let heat: Int + public let favoriteGroup: String? // MARK: Only world info params // public let publicOccupants: Int // public let privateOccupants: Int diff --git a/Sources/VRCKit/PreviewServices/PreviewDataProvider.swift b/Sources/VRCKit/PreviewServices/PreviewDataProvider.swift index 891c93c..e6b29d5 100644 --- a/Sources/VRCKit/PreviewServices/PreviewDataProvider.swift +++ b/Sources/VRCKit/PreviewServices/PreviewDataProvider.swift @@ -227,6 +227,7 @@ public final class PreviewDataProvider { visits: 1, popularity: 1, heat: 1, + favoriteGroup: "", version: 1 ) } From 59513dd7c314eb5b918ba7d72ffdd26892cdba30 Mon Sep 17 00:00:00 2001 From: makinosp Date: Sun, 15 Sep 2024 13:57:16 +0900 Subject: [PATCH 07/16] fix: encoding user tag --- Sources/VRCKit/Models/UserTagModel.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/VRCKit/Models/UserTagModel.swift b/Sources/VRCKit/Models/UserTagModel.swift index 85a4b89..60214722 100644 --- a/Sources/VRCKit/Models/UserTagModel.swift +++ b/Sources/VRCKit/Models/UserTagModel.swift @@ -49,6 +49,8 @@ extension UserTags: Encodable { public func encode(to encoder: any Encoder) throws { var container = encoder.unkeyedContainer() let tags = languageTags.map(\.rawValue) - try container.encode(tags) + for tag in tags { + try container.encode(tag) + } } } From c5e0c88a1234c6f720a46e24b8aef2f077f21c78 Mon Sep 17 00:00:00 2001 From: makinosp Date: Sun, 15 Sep 2024 14:27:55 +0900 Subject: [PATCH 08/16] update: remove unnecessary dependency --- Package.resolved | 14 -------------- Package.swift | 18 ------------------ 2 files changed, 32 deletions(-) delete mode 100644 Package.resolved diff --git a/Package.resolved b/Package.resolved deleted file mode 100644 index c5f5e15..0000000 --- a/Package.resolved +++ /dev/null @@ -1,14 +0,0 @@ -{ - "pins" : [ - { - "identity" : "swiftlintplugins", - "kind" : "remoteSourceControl", - "location" : "https://github.com/SimplyDanny/SwiftLintPlugins", - "state" : { - "revision" : "6c3d6c32a37224179dc290f21e03d1238f3d963b", - "version" : "0.56.2" - } - } - ], - "version" : 2 -} diff --git a/Package.swift b/Package.swift index 1eac926..12e153d 100644 --- a/Package.swift +++ b/Package.swift @@ -12,23 +12,14 @@ let package = Package( .watchOS(.v6) ], products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "VRCKit", targets: ["VRCKit"] ) ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), - .package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", from: "0.55.1") - ], targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "VRCKit", - dependencies: [], path: "Sources" ), .testTarget( @@ -37,12 +28,3 @@ let package = Package( ) ] ) - -package.targets.forEach { - $0.plugins = [ - .plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLintPlugins") - ] - $0.swiftSettings = [ - .enableUpcomingFeature("ForwardTrailingClosures") // SE-0286 - ] -} From ad7270ba40e85adcf199ae7deebd5cb527974d5a Mon Sep 17 00:00:00 2001 From: makinosp Date: Sun, 15 Sep 2024 14:32:27 +0900 Subject: [PATCH 09/16] feat: add bad gateway error in VRCKitError --- Sources/VRCKit/Errors.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/VRCKit/Errors.swift b/Sources/VRCKit/Errors.swift index 2214867..66894ec 100644 --- a/Sources/VRCKit/Errors.swift +++ b/Sources/VRCKit/Errors.swift @@ -15,6 +15,9 @@ public enum VRCKitError: Error, LocalizedError, Equatable { /// Represents an error from the API with details. case apiError(_ details: String) + /// Represents a bad gatewaty error. + case badGateway + /// Represents an error indicating that the client has been deallocated. case clientDeallocated @@ -37,6 +40,7 @@ public enum VRCKitError: Error, LocalizedError, Equatable { public var errorDescription: String? { switch self { case .apiError: "API Error" + case .badGateway: "Bad Gateway" case .clientDeallocated: "Client Deallocated" case .invalidResponse: "Invalid Response" case .invalidRequest: "Invalid Request" From 595a75eb4af7f48cc4aaaa886758d78f1bea24a4 Mon Sep 17 00:00:00 2001 From: makinosp Date: Sun, 15 Sep 2024 14:48:36 +0900 Subject: [PATCH 10/16] refact: remove unnecessary code --- Sources/VRCKit/Models/EditableUserModel.swift | 5 ----- Sources/VRCKit/Models/WorldModel.swift | 3 --- Sources/VRCKit/Protocols/WorldServiceProtocol.swift | 1 - 3 files changed, 9 deletions(-) diff --git a/Sources/VRCKit/Models/EditableUserModel.swift b/Sources/VRCKit/Models/EditableUserModel.swift index ca61e9f..952cdce 100644 --- a/Sources/VRCKit/Models/EditableUserModel.swift +++ b/Sources/VRCKit/Models/EditableUserModel.swift @@ -24,8 +24,3 @@ public extension EditableUserInfo { tags = detail.tags } } - -public struct UpdatedUser: Codable { - public let bio: String? - public let statusDescription: String? -} diff --git a/Sources/VRCKit/Models/WorldModel.swift b/Sources/VRCKit/Models/WorldModel.swift index 2f48d04..bd2fa32 100644 --- a/Sources/VRCKit/Models/WorldModel.swift +++ b/Sources/VRCKit/Models/WorldModel.swift @@ -31,9 +31,6 @@ public struct World: Codable, Identifiable, Hashable { public let popularity: Int public let heat: Int public let favoriteGroup: String? - // MARK: Only world info params - // public let publicOccupants: Int - // public let privateOccupants: Int public let version: Int? public enum ReleaseStatus: String, Codable { diff --git a/Sources/VRCKit/Protocols/WorldServiceProtocol.swift b/Sources/VRCKit/Protocols/WorldServiceProtocol.swift index 5ee519c..5b59158 100644 --- a/Sources/VRCKit/Protocols/WorldServiceProtocol.swift +++ b/Sources/VRCKit/Protocols/WorldServiceProtocol.swift @@ -5,7 +5,6 @@ // Created by kiripoipoi on 2024/09/07. // -// WorldServiceProtocol.swift public protocol WorldServiceProtocol { func fetchWorld(worldId: String) async throws -> World func fetchFavoritedWorlds() async throws -> [World] From 8a41adc2b663853c33d9c2d7c2000d0f6b499ecb Mon Sep 17 00:00:00 2001 From: makinosp Date: Sun, 15 Sep 2024 14:48:48 +0900 Subject: [PATCH 11/16] refact: lint --- Sources/VRCKit/Models/UserModel.swift | 1 + Sources/VRCKit/Utils/OptionalISO8601Date.swift | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/VRCKit/Models/UserModel.swift b/Sources/VRCKit/Models/UserModel.swift index 7a88f57..f4dc629 100644 --- a/Sources/VRCKit/Models/UserModel.swift +++ b/Sources/VRCKit/Models/UserModel.swift @@ -74,6 +74,7 @@ public extension User { URL(string: [Const.homeBaseUrl, "user", id].joined(separator: "/")) } } + extension User.Presence { init() { groups = [] diff --git a/Sources/VRCKit/Utils/OptionalISO8601Date.swift b/Sources/VRCKit/Utils/OptionalISO8601Date.swift index c99ed6f..ee93369 100644 --- a/Sources/VRCKit/Utils/OptionalISO8601Date.swift +++ b/Sources/VRCKit/Utils/OptionalISO8601Date.swift @@ -48,11 +48,13 @@ extension OptionalISO8601Date: Encodable { } } -extension OptionalISO8601Date: Hashable { +extension OptionalISO8601Date: Equatable { public static func == (lhs: OptionalISO8601Date, rhs: OptionalISO8601Date) -> Bool { lhs.date == rhs.date } +} +extension OptionalISO8601Date: Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(date) } From fa46042466651f842369d1b7be3266cfe9989cd0 Mon Sep 17 00:00:00 2001 From: makinosp Date: Sun, 15 Sep 2024 11:49:15 +0000 Subject: [PATCH 12/16] update: add docker-compose.yml --- docker-compose.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..28f1929 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +services: + build: + image: swift:5.10.1 + command: bash -c "swift build -c release" + volumes: + - ${PWD}:${PWD} + working_dir: ${PWD} + lint: + image: ghcr.io/realm/swiftlint:latest + volumes: + - ${PWD}:${PWD} + working_dir: ${PWD} From fb7523fbc37143a3f4bd6e3724fe1b4b14b63e81 Mon Sep 17 00:00:00 2001 From: makinosp Date: Sun, 15 Sep 2024 11:49:15 +0000 Subject: [PATCH 13/16] feat: requesting with FoundationNetworking, compiler statement for linux --- Sources/VRCKit/APIClient.swift | 32 ++++++++++++++++++++++++ Sources/VRCKit/Utils/CookieManager.swift | 3 +++ 2 files changed, 35 insertions(+) diff --git a/Sources/VRCKit/APIClient.swift b/Sources/VRCKit/APIClient.swift index 612bee2..199d122 100644 --- a/Sources/VRCKit/APIClient.swift +++ b/Sources/VRCKit/APIClient.swift @@ -6,6 +6,9 @@ // import Foundation +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif public final class APIClient { typealias HTTPResponse = (data: Data, response: HTTPURLResponse) @@ -95,12 +98,41 @@ public final class APIClient { request.httpBody = body } + #if canImport(FoundationNetworking) + return try await requestWithFoundationNetworking(request) + #else + return try await request(request) + #endif + } + + #if canImport(FoundationNetworking) + private func requestWithFoundationNetworking(_ request: URLRequest) async throws -> HTTPResponse { + var requestError: Error? = nil + let httpResponse = await withCheckedContinuation { continuation in + URLSession.shared.dataTask(with: request) { data, urlResponse, error in + guard let data = data, let reponse = urlResponse as? HTTPURLResponse else { + requestError = error + return + } + continuation.resume(returning: (data, reponse)) + } + .resume() + } + if let requestError = requestError { + throw requestError + } + return httpResponse + } + + #else + private func request(_ request: URLRequest) async throws -> HTTPResponse { let (data, response) = try await URLSession.shared.data(for: request) guard let response = response as? HTTPURLResponse else { throw VRCKitError.invalidResponse } return (data, response) } + #endif } extension APIClient.Method: CustomStringConvertible { diff --git a/Sources/VRCKit/Utils/CookieManager.swift b/Sources/VRCKit/Utils/CookieManager.swift index a089a23..e134886 100644 --- a/Sources/VRCKit/Utils/CookieManager.swift +++ b/Sources/VRCKit/Utils/CookieManager.swift @@ -6,6 +6,9 @@ // import Foundation +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif public final class CookieManager { private var domainURL: String? From 247b8bc25be45ebf9d9a1d5ccbb8ba9746567bed Mon Sep 17 00:00:00 2001 From: makinosp Date: Sat, 21 Sep 2024 11:58:10 +0900 Subject: [PATCH 14/16] refact: add defaultLocalization --- Package.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Package.swift b/Package.swift index 12e153d..642b52a 100644 --- a/Package.swift +++ b/Package.swift @@ -5,6 +5,7 @@ import PackageDescription let package = Package( name: "VRCKit", + defaultLocalization: "en", platforms: [ .macOS(.v12), .iOS(.v15), From e6d6d9fbbceb1fdf08fb98365425a5d4d03df137 Mon Sep 17 00:00:00 2001 From: makinosp Date: Sat, 21 Sep 2024 11:58:21 +0900 Subject: [PATCH 15/16] refact: refactoring --- Sources/VRCKit/Services/WorldService.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/VRCKit/Services/WorldService.swift b/Sources/VRCKit/Services/WorldService.swift index 1349c88..9a93c90 100644 --- a/Sources/VRCKit/Services/WorldService.swift +++ b/Sources/VRCKit/Services/WorldService.swift @@ -14,8 +14,7 @@ public class WorldService: APIService, WorldServiceProtocol { } public func fetchFavoritedWorlds() async throws -> [World] { - let path = "worlds/favorites" - let response = try await client.request(path: path, method: .get) + let response = try await client.request(path: "\(path)/favorites", method: .get) let favoriteWorldWrapper: FavoriteWorldWrapper = try Serializer.shared.decode(response.data) return favoriteWorldWrapper.worlds } From 3d782206e23e55d3a594df2b240b9a5e765eb52b Mon Sep 17 00:00:00 2001 From: makinosp Date: Sat, 21 Sep 2024 12:01:55 +0900 Subject: [PATCH 16/16] refact: fix function name --- Sources/VRCKit/APIClient.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/VRCKit/APIClient.swift b/Sources/VRCKit/APIClient.swift index 199d122..c3b4f1e 100644 --- a/Sources/VRCKit/APIClient.swift +++ b/Sources/VRCKit/APIClient.swift @@ -101,13 +101,13 @@ public final class APIClient { #if canImport(FoundationNetworking) return try await requestWithFoundationNetworking(request) #else - return try await request(request) + return try await requestWithFoundation(request) #endif } #if canImport(FoundationNetworking) private func requestWithFoundationNetworking(_ request: URLRequest) async throws -> HTTPResponse { - var requestError: Error? = nil + var requestError: Error? let httpResponse = await withCheckedContinuation { continuation in URLSession.shared.dataTask(with: request) { data, urlResponse, error in guard let data = data, let reponse = urlResponse as? HTTPURLResponse else { @@ -125,7 +125,7 @@ public final class APIClient { } #else - private func request(_ request: URLRequest) async throws -> HTTPResponse { + private func requestWithFoundation(_ request: URLRequest) async throws -> HTTPResponse { let (data, response) = try await URLSession.shared.data(for: request) guard let response = response as? HTTPURLResponse else { throw VRCKitError.invalidResponse