Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore/Remove PovioKit dependency #30

Merged
merged 7 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
Package.resolved
68 changes: 0 additions & 68 deletions Package.resolved

This file was deleted.

14 changes: 4 additions & 10 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
// swift-tools-version: 5.8
// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "PovioKitAuth",
platforms: [
.iOS(.v13),
.macOS(.v13)
.iOS(.v13)
],
products: [
.library(name: "PovioKitAuthCore", targets: ["PovioKitAuthCore"]),
Expand All @@ -17,16 +16,13 @@ let package = Package(
.library(name: "PovioKitAuthLinkedIn", targets: ["PovioKitAuthLinkedIn"])
],
dependencies: [
.package(url: "https://github.com/poviolabs/PovioKit", .upToNextMajor(from: "3.0.0")),
.package(url: "https://github.com/google/GoogleSignIn-iOS", .upToNextMajor(from: "7.0.0")),
.package(url: "https://github.com/facebook/facebook-ios-sdk", .upToNextMajor(from: "17.0.0")),
],
targets: [
.target(
name: "PovioKitAuthCore",
dependencies: [
.product(name: "PovioKitPromise", package: "PovioKit"),
],
dependencies: [],
path: "Sources/Core",
resources: [.copy("../../Resources/PrivacyInfo.xcprivacy")]
),
Expand All @@ -51,7 +47,6 @@ let package = Package(
name: "PovioKitAuthFacebook",
dependencies: [
"PovioKitAuthCore",
.product(name: "PovioKitCore", package: "PovioKit"),
.product(name: "FacebookLogin", package: "facebook-ios-sdk")
],
path: "Sources/Facebook",
Expand All @@ -60,8 +55,7 @@ let package = Package(
.target(
name: "PovioKitAuthLinkedIn",
dependencies: [
"PovioKitAuthCore",
.product(name: "PovioKitNetworking", package: "PovioKit")
"PovioKitAuthCore"
],
path: "Sources/LinkedIn",
resources: [.copy("../../Resources/PrivacyInfo.xcprivacy")]
Expand Down
3 changes: 1 addition & 2 deletions Sources/Facebook/FacebookAuthenticator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import Foundation
import FacebookLogin
import PovioKitCore
import PovioKitAuthCore

public final class FacebookAuthenticator {
Expand Down Expand Up @@ -106,7 +105,7 @@ private extension FacebookAuthenticator {

do {
let data = try JSONSerialization.data(withJSONObject: response, options: [])
let object = try data.decode(GraphResponse.self, with: decoder)
let object = try decoder.decode(GraphResponse.self, from: data)

let authResponse = Response(
userId: object.id,
Expand Down
23 changes: 0 additions & 23 deletions Sources/LinkedIn/API/EndpointEncodable.swift

This file was deleted.

5 changes: 2 additions & 3 deletions Sources/LinkedIn/API/LinkedInAPI+Endpoints.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@
//

import Foundation
import PovioKitNetworking

extension LinkedInAPI {
enum Endpoints: EndpointEncodable {
enum Endpoints {
case accessToken
case profile
case email

var path: Path {
var path: String {
switch self {
case .accessToken:
return "accessToken"
Expand Down
96 changes: 60 additions & 36 deletions Sources/LinkedIn/API/LinkedInAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,36 @@
//

import Foundation
import PovioKitNetworking

public final class LinkedInAPI {
private let client: AlamofireNetworkClient
public struct LinkedInAPI {
private let client: HttpClient = .init()

public init(client: AlamofireNetworkClient = .init()) {
self.client = client
}
public init() {}
}

public extension LinkedInAPI {
func login(with request: LinkedInAuthRequest) async throws -> LinkedInAuthResponse {
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
let jsonData = try encoder.encode(request)
let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: [])
guard let jsonDict = jsonObject as? [String: Any] else {
throw Error.invalidRequest
}

guard var components = URLComponents(string: Endpoints.accessToken.url) else {
throw Error.invalidUrl
}

let queryItems: [URLQueryItem] = jsonDict.compactMap { key, value in
(value as? String).map { .init(name: key, value: $0) }
}

components.queryItems = queryItems
guard let url = components.url else {
throw Error.invalidUrl
}

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .custom { decoder in
Expand All @@ -27,53 +45,59 @@ public extension LinkedInAPI {
return Date().addingTimeInterval(TimeInterval(secondsRemaining))
}

let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
let response = try await client.request(
method: "POST",
url: url,
headers: ["Content-Type": "application/x-www-form-urlencoded"],
decodeTo: LinkedInAuthResponse.self,
with: decoder
)

return try await client
.request(
method: .post,
endpoint: Endpoints.accessToken,
encode: request,
parameterEncoder: .urlEncoder(encoder: encoder)
)
.validate()
.decode(LinkedInAuthResponse.self, decoder: decoder)
.asAsync
return response
}

func loadProfile(with request: LinkedInProfileRequest) async throws -> LinkedInProfileResponse {
guard let url = URL(string: Endpoints.profile.url) else { throw Error.invalidUrl }

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .iso8601

return try await client
.request(
method: .get,
endpoint: Endpoints.profile,
headers: ["Authorization": "Bearer \(request.token)"]
)
.validate()
.decode(LinkedInProfileResponse.self, decoder: decoder)
.asAsync
let response = try await client.request(
method: "GET",
url: url,
headers: ["Authorization": "Bearer \(request.token)"],
decodeTo: LinkedInProfileResponse.self,
with: decoder
)

return response
}

func loadEmail(with request: LinkedInProfileRequest) async throws -> LinkedInEmailValueResponse {
return try await client
.request(
method: .get,
endpoint: Endpoints.email,
headers: ["Authorization": "Bearer \(request.token)"])
.validate()
.decode(LinkedInEmailResponse.self)
.compactMap { $0.elements.first?.handle }
.asAsync
guard let url = URL(string: Endpoints.email.url) else { throw Error.invalidUrl }

let response = try await client.request(
method: "GET",
url: url,
headers: ["Authorization": "Bearer \(request.token)"],
decodeTo: LinkedInEmailResponse.self
)

guard let emailObject = response.elements.first?.handle else {
throw Error.invalidResponse
}

return emailObject
}
}

// MARK: - Error
public extension LinkedInAPI {
enum Error: Swift.Error {
case missingParameters
case invalidUrl
case invalidRequest
case invalidResponse
}
}
32 changes: 32 additions & 0 deletions Sources/LinkedIn/Core/HttpClient.swift
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the HttpClient part of the LinkedIn folder? Can we move it out?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's internal implementation only needed by LinkedIn target.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// HttpClient.swift
// PovioKitAuth
//
// Created by Borut Tomazin on 22/05/2024.
// Copyright © 2024 Povio Inc. All rights reserved.
//

import Foundation

struct HttpClient {
func request<D: Decodable>(
method: String,
url: URL,
headers: [String: String]?,
decodeTo decode: D.Type,
with decoder: JSONDecoder = .init()
) async throws -> D {
var urlRequest = URLRequest(url: url)
tonikocjan marked this conversation as resolved.
Show resolved Hide resolved
urlRequest.httpMethod = method
urlRequest.allHTTPHeaderFields = headers

let (data, response) = try await URLSession.shared.data(for: urlRequest)

if let httpResponse = response as? HTTPURLResponse,
!(200...299).contains(httpResponse.statusCode) {
throw NSError(domain: "HTTP Error", code: httpResponse.statusCode, userInfo: nil)
}

return try decoder.decode(decode, from: data)
}
}
18 changes: 18 additions & 0 deletions Sources/LinkedIn/Core/URL+PovioKitAuth.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// URL+PovioKitAuth.swift
// PovioKitAuth
//
// Created by Borut Tomazin on 22/05/2024.
// Copyright © 2024 Povio Inc. All rights reserved.
//

import Foundation

extension URL: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
guard let url = URL(string: value) else {
fatalError("Invalid URL string!")
}
self = url
}
}
2 changes: 0 additions & 2 deletions Sources/LinkedIn/WebView/LinkedInWebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
// Copyright © 2024 Povio Inc. All rights reserved.
//

import PovioKitCore
import SwiftUI
import WebKit

Expand Down Expand Up @@ -40,7 +39,6 @@ public struct LinkedInWebView: UIViewRepresentable {
public func updateUIView(_ uiView: UIViewType, context: Context) {
guard let webView = uiView as? WKWebView else { return }
guard let authURL = configuration.authorizationUrl(state: requestState) else {
Logger.error("Failed to geet auth url!")
dismiss()
return
}
Expand Down
Loading