diff --git a/DevLog.xcodeproj/project.pbxproj b/DevLog.xcodeproj/project.pbxproj index d2bdc23d..e94976e3 100644 --- a/DevLog.xcodeproj/project.pbxproj +++ b/DevLog.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ DFABA3B02E23526500FEFBDB /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = DFABA3AF2E23526500FEFBDB /* FirebaseFirestore */; }; DFABA3B22E23526500FEFBDB /* FirebaseFunctions in Frameworks */ = {isa = PBXBuildFile; productRef = DFABA3B12E23526500FEFBDB /* FirebaseFunctions */; }; DFABA3B42E23526500FEFBDB /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = DFABA3B32E23526500FEFBDB /* FirebaseMessaging */; }; + DFD3B68D2F8F1FB8001DA7CD /* Nexa in Frameworks */ = {isa = PBXBuildFile; productRef = DFD3B68C2F8F1FB8001DA7CD /* Nexa */; }; DFD645402EC827A10073E133 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = DFD6453F2EC827A10073E133 /* .gitignore */; }; DFD74E2F2E423EA700613803 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = DFD74E2E2E423EA700613803 /* README.md */; }; DFF2DACE2EDC02AD00778738 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = DFF2DACD2EDC02AD00778738 /* OrderedCollections */; }; @@ -76,6 +77,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + DFD3B68D2F8F1FB8001DA7CD /* Nexa in Frameworks */, DFABA3B42E23526500FEFBDB /* FirebaseMessaging in Frameworks */, DFABA3A92E2351EE00FEFBDB /* GoogleSignInSwift in Frameworks */, DFABA3B22E23526500FEFBDB /* FirebaseFunctions in Frameworks */, @@ -172,6 +174,7 @@ DFABA3B12E23526500FEFBDB /* FirebaseFunctions */, DFABA3B32E23526500FEFBDB /* FirebaseMessaging */, DFF2DACD2EDC02AD00778738 /* OrderedCollections */, + DFD3B68C2F8F1FB8001DA7CD /* Nexa */, ); productName = SwiftUI_DevLog; productReference = DFD48B002DC4D6E2005905C5 /* DevLog.app */; @@ -211,6 +214,7 @@ DFABA3AA2E23526500FEFBDB /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, DF66A07B2EA52E970098E643 /* XCRemoteSwiftPackageReference "SwiftLint" */, DFF2DACC2EDC02AD00778738 /* XCRemoteSwiftPackageReference "swift-collections" */, + DFD3B68B2F8F1FB8001DA7CD /* XCRemoteSwiftPackageReference "Nexa" */, ); preferredProjectObjectVersion = 77; productRefGroup = DFD48B012DC4D6E2005905C5 /* Products */; @@ -610,6 +614,14 @@ minimumVersion = 11.15.0; }; }; + DFD3B68B2F8F1FB8001DA7CD /* XCRemoteSwiftPackageReference "Nexa" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/opficdev/Nexa"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.1.0; + }; + }; DFF2DACC2EDC02AD00778738 /* XCRemoteSwiftPackageReference "swift-collections" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/apple/swift-collections.git"; @@ -666,6 +678,11 @@ package = DFABA3AA2E23526500FEFBDB /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; productName = FirebaseMessaging; }; + DFD3B68C2F8F1FB8001DA7CD /* Nexa */ = { + isa = XCSwiftPackageProductDependency; + package = DFD3B68B2F8F1FB8001DA7CD /* XCRemoteSwiftPackageReference "Nexa" */; + productName = Nexa; + }; DFF2DACD2EDC02AD00778738 /* OrderedCollections */ = { isa = XCSwiftPackageProductDependency; package = DFF2DACC2EDC02AD00778738 /* XCRemoteSwiftPackageReference "swift-collections" */; diff --git a/DevLog.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DevLog.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 75e09a49..fe577f2a 100644 --- a/DevLog.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DevLog.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "e440a72cfb4192fa35c539dad59247f988a89b2436e7dfc795518078100559a4", + "originHash" : "6e83e8a97f59e7d95bbfcd1dc82daad8306c7a0f7df1e8bb7966a26baf0e4a2b", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -163,6 +163,15 @@ "version" : "6.0.1" } }, + { + "identity" : "nexa", + "kind" : "remoteSourceControl", + "location" : "https://github.com/opficdev/Nexa", + "state" : { + "revision" : "a2f3ca9862eb24ffc28bdeb6d67293b4e8ddf3a5", + "version" : "1.1.0" + } + }, { "identity" : "promises", "kind" : "remoteSourceControl", diff --git a/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift b/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift index 991e5974..5989ff4d 100644 --- a/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift +++ b/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift @@ -11,6 +11,7 @@ import FirebaseAuth import FirebaseFirestore import FirebaseFunctions import FirebaseMessaging +import Nexa final class GithubAuthenticationService: NSObject, AuthenticationService { private enum FunctionName: String { @@ -18,6 +19,11 @@ final class GithubAuthenticationService: NSObject, AuthenticationService { case revokeGithubAccessToken } + private enum GitHubAPI { + static let baseURL = URL(string: "https://api.github.com")! + static let acceptHeader = "application/vnd.github.v3+json" + } + private let store = Firestore.firestore() private let functions = Functions.functions(region: "asia-northeast3") private let messaging = Messaging.messaging() @@ -25,6 +31,12 @@ final class GithubAuthenticationService: NSObject, AuthenticationService { private let providerID = AuthProviderID.gitHub private let provider = TopViewControllerProvider() private let logger = Logger(category: "GithubAuthService") + private let gitHubApiClient = NXAPIClient( + configuration: NXClientConfiguration( + baseURL: GitHubAPI.baseURL, + headers: ["Accept": GitHubAPI.acceptHeader] + ) + ) func signIn() async throws -> AuthDataResponse { logger.info("Starting GitHub sign in") @@ -243,24 +255,11 @@ final class GithubAuthenticationService: NSObject, AuthenticationService { // GitHub API로 사용자 프로필 정보 가져오기 private func requestUserProfile(accessToken: String) async throws -> GitHubUser { - guard let url = URL(string: "https://api.github.com/user") else { - throw URLError(.badURL) - } - - var request = URLRequest(url: url) - request.httpMethod = "GET" - request.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") - request.addValue("application/vnd.github.v3+json", forHTTPHeaderField: "Accept") - - let (data, response) = try await URLSession.shared.data(for: request) - - guard let httpResponse = response as? HTTPURLResponse, - httpResponse.statusCode == 200 else { - throw URLError(.badServerResponse) - } - - let decoder = JSONDecoder() - let gitHubUser = try decoder.decode(GitHubUser.self, from: data) + let gitHubUser = try await gitHubApiClient + .get("/user", as: GitHubUser.self) + .header("Authorization", "Bearer \(accessToken)") + .validate(.statusCodes([200])) + .send() if gitHubUser.email != nil { return gitHubUser @@ -276,24 +275,11 @@ final class GithubAuthenticationService: NSObject, AuthenticationService { } private func requestPrimaryVerifiedEmail(accessToken: String) async throws -> String? { - guard let url = URL(string: "https://api.github.com/user/emails") else { - throw URLError(.badURL) - } - - var request = URLRequest(url: url) - request.httpMethod = "GET" - request.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") - request.addValue("application/vnd.github.v3+json", forHTTPHeaderField: "Accept") - - let (data, response) = try await URLSession.shared.data(for: request) - - guard let httpResponse = response as? HTTPURLResponse, - httpResponse.statusCode == 200 else { - throw URLError(.badServerResponse) - } - - let decoder = JSONDecoder() - let gitHubEmails = try decoder.decode([GitHubEmail].self, from: data) + let gitHubEmails = try await gitHubApiClient + .get("/user/emails", as: [GitHubEmail].self) + .header("Authorization", "Bearer \(accessToken)") + .validate(.statusCodes([200])) + .send() if let primaryVerifiedEmail = gitHubEmails.first(where: { $0.primary && $0.verified }) { return primaryVerifiedEmail.email