From bb984d33cf5e36c46e66c182fa3d4c8c79e472f9 Mon Sep 17 00:00:00 2001 From: Egzon Arifi Date: Fri, 15 Nov 2024 15:03:40 +0100 Subject: [PATCH] Feature/Introduce PersonNameComponents for Google, Facebook, LinkedIn sign in (#33) Co-authored-by: Yll Fejziu --- .github/workflows/Tests.yml | 8 ++-- Sources/Apple/AppleAuthenticator+Models.swift | 8 +--- .../Core/PersonNameComponents+Extension.swift | 42 +++++++++++++++++++ .../FacebookAuthenticator+Models.swift | 15 +++---- Sources/Facebook/FacebookAuthenticator.swift | 5 ++- .../Google/GoogleAuthenticator+Models.swift | 8 +++- Sources/Google/GoogleAuthenticator.swift | 5 ++- .../LinkedInAuthenticator+Models.swift | 7 +++- Sources/LinkedIn/LinkedInAuthenticator.swift | 6 ++- 9 files changed, 78 insertions(+), 26 deletions(-) create mode 100644 Sources/Core/PersonNameComponents+Extension.swift diff --git a/.github/workflows/Tests.yml b/.github/workflows/Tests.yml index 9627414..326c034 100644 --- a/.github/workflows/Tests.yml +++ b/.github/workflows/Tests.yml @@ -7,7 +7,7 @@ on: jobs: Tests: - runs-on: macos-14-xlarge + runs-on: macos-15-xlarge steps: - name: Cancel previous jobs uses: styfle/cancel-workflow-action@0.11.0 @@ -21,8 +21,8 @@ jobs: xcode-version: latest-stable - name: Build project - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild build-for-testing -destination 'name=iPhone 14 Pro' -scheme 'PovioKitAuth-Package' | xcpretty + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild build-for-testing -destination 'name=iPhone 16 Pro,OS=18.0' -scheme 'PovioKitAuth-Package' | xcpretty - name: Run tests - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild test-without-building -destination 'name=iPhone 14 Pro' -scheme 'PovioKitAuth-Package' | xcpretty - + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild test-without-building -destination 'name=iPhone 16 Pro,OS=18.0' -scheme 'PovioKitAuth-Package' | xcpretty + diff --git a/Sources/Apple/AppleAuthenticator+Models.swift b/Sources/Apple/AppleAuthenticator+Models.swift index 935eb49..d09335d 100644 --- a/Sources/Apple/AppleAuthenticator+Models.swift +++ b/Sources/Apple/AppleAuthenticator+Models.swift @@ -25,13 +25,7 @@ public extension AppleAuthenticator { /// User full name represented by `givenName` and `familyName` public var name: String? { - guard let givenName = nameComponents?.givenName else { - return nameComponents?.familyName - } - guard let familyName = nameComponents?.familyName else { - return givenName - } - return "\(givenName) \(familyName)" + nameComponents?.name } } diff --git a/Sources/Core/PersonNameComponents+Extension.swift b/Sources/Core/PersonNameComponents+Extension.swift new file mode 100644 index 0000000..46c70b7 --- /dev/null +++ b/Sources/Core/PersonNameComponents+Extension.swift @@ -0,0 +1,42 @@ +// +// PersonNameComponents+Extension.swift +// PovioKitAuth +// +// Created by Egzon Arifi on 09/11/2024. +// + +import AuthenticationServices +import Foundation + +public extension PersonNameComponents { + var name: String? { + guard let givenName else { + return familyName + } + guard let familyName else { + return givenName + } + return "\(givenName) \(familyName)" + } +} + +public extension PersonNameComponents { + init( + namePrefix: String? = .none, + middleName: String? = .none, + givenName: String? = .none, + familyName: String? = .none, + nameSuffix: String? = .none, + nickname: String? = .none, + phoneticRepresentation: PersonNameComponents? = .none + ) { + self.init() + self.namePrefix = namePrefix + self.familyName = familyName + self.middleName = middleName + self.givenName = givenName + self.nameSuffix = nameSuffix + self.nickname = nickname + self.phoneticRepresentation = phoneticRepresentation + } +} diff --git a/Sources/Facebook/FacebookAuthenticator+Models.swift b/Sources/Facebook/FacebookAuthenticator+Models.swift index ca060d2..9996584 100644 --- a/Sources/Facebook/FacebookAuthenticator+Models.swift +++ b/Sources/Facebook/FacebookAuthenticator+Models.swift @@ -12,9 +12,14 @@ public extension FacebookAuthenticator { struct Response { public let userId: String public let token: String - public let name: String? + public let nameComponents: PersonNameComponents? public let email: String? public let expiresAt: Date + + /// User full name represented by `givenName` and `familyName` + public var name: String? { + nameComponents?.name + } } struct GraphResponse: Decodable { @@ -36,11 +41,3 @@ public extension FacebookAuthenticator { } } } - -public extension FacebookAuthenticator.GraphResponse { - var displayName: String { - [firstName, lastName] - .compactMap { $0 } - .joined(separator: " ") - } -} diff --git a/Sources/Facebook/FacebookAuthenticator.swift b/Sources/Facebook/FacebookAuthenticator.swift index 90bd25a..f133815 100644 --- a/Sources/Facebook/FacebookAuthenticator.swift +++ b/Sources/Facebook/FacebookAuthenticator.swift @@ -110,7 +110,10 @@ private extension FacebookAuthenticator { let authResponse = Response( userId: object.id, token: token.tokenString, - name: object.displayName, + nameComponents: PersonNameComponents( + givenName: object.firstName, + familyName: object.lastName + ), email: object.email, expiresAt: token.expirationDate ) diff --git a/Sources/Google/GoogleAuthenticator+Models.swift b/Sources/Google/GoogleAuthenticator+Models.swift index a0906f6..7f08a41 100644 --- a/Sources/Google/GoogleAuthenticator+Models.swift +++ b/Sources/Google/GoogleAuthenticator+Models.swift @@ -7,6 +7,7 @@ // import Foundation +import PovioKitAuthCore public extension GoogleAuthenticator { struct Response { @@ -14,8 +15,13 @@ public extension GoogleAuthenticator { public let idToken: String? public let accessToken: String public let refreshToken: String - public let name: String? + public let nameComponents: PersonNameComponents? public let email: String? public let expiresAt: Date? + + /// User full name represented by `givenName` and `familyName` + public var name: String? { + nameComponents?.name + } } } diff --git a/Sources/Google/GoogleAuthenticator.swift b/Sources/Google/GoogleAuthenticator.swift index 6e7f88d..367eb46 100644 --- a/Sources/Google/GoogleAuthenticator.swift +++ b/Sources/Google/GoogleAuthenticator.swift @@ -107,7 +107,10 @@ private extension GIDGoogleUser { idToken: idToken?.tokenString, accessToken: accessToken.tokenString, refreshToken: refreshToken.tokenString, - name: profile?.name, + nameComponents: PersonNameComponents( + givenName: profile?.givenName, + familyName: profile?.familyName + ), email: profile?.email, expiresAt: accessToken.expirationDate ) diff --git a/Sources/LinkedIn/LinkedInAuthenticator+Models.swift b/Sources/LinkedIn/LinkedInAuthenticator+Models.swift index 73441cf..e835783 100644 --- a/Sources/LinkedIn/LinkedInAuthenticator+Models.swift +++ b/Sources/LinkedIn/LinkedInAuthenticator+Models.swift @@ -86,8 +86,13 @@ public extension LinkedInAuthenticator { struct Response { public let userId: String public let token: String - public let name: String + public let nameComponents: PersonNameComponents public let email: String public let expiresAt: Date + + /// User full name represented by `givenName` and `familyName` + public var name: String? { + nameComponents.name + } } } diff --git a/Sources/LinkedIn/LinkedInAuthenticator.swift b/Sources/LinkedIn/LinkedInAuthenticator.swift index bc91c4d..e2a8aa1 100644 --- a/Sources/LinkedIn/LinkedInAuthenticator.swift +++ b/Sources/LinkedIn/LinkedInAuthenticator.swift @@ -39,11 +39,13 @@ extension LinkedInAuthenticator: Authenticator { storage.set(true, forKey: storageIsAuthenticatedKey) - let name = "\(profileResponse.localizedFirstName) \(profileResponse.localizedLastName)" return Response( userId: profileResponse.id, token: authResponse.accessToken, - name: name, + nameComponents: PersonNameComponents( + givenName: profileResponse.localizedFirstName, + familyName: profileResponse.localizedLastName + ), email: emailResponse.emailAddress, expiresAt: authResponse.expiresIn )