Add first client
This commit is contained in:
parent
3568943179
commit
1274092b0f
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,3 +7,4 @@ DerivedData/
|
||||
.swiftpm/config/registries.json
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||
.netrc
|
||||
Package.resolved
|
||||
|
@ -1,28 +1,28 @@
|
||||
// swift-tools-version: 5.6
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "Push-iOS",
|
||||
platforms: [
|
||||
.macOS(.v12),
|
||||
.iOS(.v15),
|
||||
],
|
||||
products: [
|
||||
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||
.library(
|
||||
name: "Push-iOS",
|
||||
targets: ["Push-iOS"]),
|
||||
name: "Push",
|
||||
targets: ["Push"]),
|
||||
],
|
||||
dependencies: [
|
||||
// Dependencies declare other packages that this package depends on.
|
||||
// .package(url: /* package url */, from: "1.0.0"),
|
||||
.package(url: "https://christophhagen.de/git/ch/Push-API.git", branch: "main"),
|
||||
.package(url: "https://github.com/apple/swift-crypto.git", "1.0.0" ..< "3.0.0")
|
||||
],
|
||||
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: "Push-iOS",
|
||||
dependencies: []),
|
||||
.testTarget(
|
||||
name: "Push-iOSTests",
|
||||
dependencies: ["Push-iOS"]),
|
||||
name: "Push",
|
||||
dependencies: [
|
||||
.product(name: "PushAPI", package: "Push-API"),
|
||||
.product(name: "Crypto", package: "swift-crypto")
|
||||
]),
|
||||
]
|
||||
)
|
||||
|
@ -1,6 +0,0 @@
|
||||
public struct Push_iOS {
|
||||
public private(set) var text = "Hello, World!"
|
||||
|
||||
public init() {
|
||||
}
|
||||
}
|
162
Sources/Push/PushClient.swift
Normal file
162
Sources/Push/PushClient.swift
Normal file
@ -0,0 +1,162 @@
|
||||
import Foundation
|
||||
import PushAPI
|
||||
import SwiftUI
|
||||
|
||||
#if canImport(CryptoKit)
|
||||
import CryptoKit
|
||||
#else
|
||||
import Crypto
|
||||
#endif
|
||||
|
||||
/**
|
||||
A client to interact with a push server.
|
||||
*/
|
||||
public final class PushClient {
|
||||
|
||||
/**
|
||||
The bas url to reach the push server
|
||||
*/
|
||||
public let server: URL
|
||||
|
||||
/**
|
||||
The application id of the service for which this client is used.
|
||||
*/
|
||||
public let application: ApplicationId
|
||||
|
||||
private static let encoder = JSONEncoder()
|
||||
private static let decoder = JSONDecoder()
|
||||
|
||||
/**
|
||||
Create a new client.
|
||||
- Parameter server: The base url of the push server.
|
||||
- Parameter application: The id of the application
|
||||
*/
|
||||
public init(server: URL, application: ApplicationId) {
|
||||
self.server = server
|
||||
self.application = application
|
||||
}
|
||||
|
||||
/**
|
||||
Register the device with the push server.
|
||||
- Parameter token: The APNs token of the device
|
||||
- Parameter name: The optional device name for easier identification
|
||||
- Returns: The new authentication token associated with the device
|
||||
- Note: Depending on the application, the device must be approved by an administrator.
|
||||
*/
|
||||
public func register(token: PushToken, name: String? = nil) async -> AuthenticationToken? {
|
||||
let device = DeviceRegistration(
|
||||
pushToken: token,
|
||||
application: application,
|
||||
name: name ?? "")
|
||||
return await post(.registerNewDevice, body: device)
|
||||
}
|
||||
|
||||
/**
|
||||
Get the list of all devices in the same application.
|
||||
- Parameter pushToken: The APNs token of the registered device.
|
||||
- Parameter authToken: The authentication token of the device.
|
||||
- Returns: The list of registered and approved devices in the application
|
||||
*/
|
||||
public func getDeviceList(pushToken: PushToken, authToken: AuthenticationToken) async -> [DeviceRegistration]? {
|
||||
let device = DeviceAuthentication(pushToken: pushToken, authentication: authToken)
|
||||
guard let data = await post(.listDevicesInApplication, body: device) else {
|
||||
return nil
|
||||
}
|
||||
do {
|
||||
return try PushClient.decoder.decode([DeviceRegistration].self, from: data)
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Get a list of unapproved devices for an administrator key.
|
||||
- Parameter masterKey: The master key of the administrator
|
||||
- Returns: A list of registered devices which need to be approved or rejected.
|
||||
*/
|
||||
public func getUnapprovedDevices(masterKey: String) async -> [DeviceRegistration]? {
|
||||
let hash = hash(masterKey)
|
||||
guard let data = await post(.listUnapprovedDevices, bodyData: hash) else {
|
||||
return nil
|
||||
}
|
||||
do {
|
||||
return try PushClient.decoder.decode([DeviceRegistration].self, from: data)
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Approve a device using the administrator key.
|
||||
- Parameter pushToken: The push token of the approved device.
|
||||
- Parameter masterKey: The administrator master key.
|
||||
- Returns: `true`, if the device has been approved
|
||||
*/
|
||||
public func approve(pushToken: PushToken, with masterKey: String) async -> Bool {
|
||||
let hash = hash(masterKey)
|
||||
let device = DeviceDecision(pushToken: pushToken, masterKeyHash: hash)
|
||||
return await post(.approveDevice, body: device) != nil
|
||||
}
|
||||
|
||||
/**
|
||||
Reject a device using the administrator key.
|
||||
- Parameter pushToken: The push token of the rejected device.
|
||||
- Parameter masterKey: The administrator master key.
|
||||
- Returns: `true`, if the device has been rejected
|
||||
*/
|
||||
public func reject(pushToken: PushToken, with masterKey: String) async -> Bool {
|
||||
let hash = hash(masterKey)
|
||||
let device = DeviceDecision(pushToken: pushToken, masterKeyHash: hash)
|
||||
return await post(.rejectDevice, body: device) != nil
|
||||
}
|
||||
|
||||
/**
|
||||
Check if the device has been approved.
|
||||
- Parameter token: The APNs token of the device.
|
||||
- Parameter authToken: The authentication token of the device.
|
||||
- Returns: `true`, if the device is approved, `false` on error
|
||||
*/
|
||||
public func isConfirmed(token: PushToken, authentication: AuthenticationToken) async -> Bool {
|
||||
let device = DeviceAuthentication(pushToken: token, authentication: authentication)
|
||||
return await post(.isDeviceApproved, body: device) != nil
|
||||
}
|
||||
|
||||
/**
|
||||
Send a push notification.
|
||||
- Parameter message: The push message to send
|
||||
- Returns: `true` if the message was sent
|
||||
*/
|
||||
public func send(push message: AuthenticatedPushMessage) async -> Bool {
|
||||
await post(.sendPushNotification, body: message) != nil
|
||||
}
|
||||
|
||||
private func post<T>(_ route: Route, body: T) async -> Data? where T: Encodable {
|
||||
let bodyData = try! PushClient.encoder.encode(body)
|
||||
return await post(route, bodyData: bodyData)
|
||||
}
|
||||
|
||||
private func post(_ route: Route, bodyData: Data) async -> Data? {
|
||||
var request = URLRequest(url: server.appendingPathComponent(route.rawValue))
|
||||
request.httpBody = bodyData
|
||||
request.httpMethod = "POST"
|
||||
do {
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
guard let httpResponse = response as? HTTPURLResponse else {
|
||||
return nil
|
||||
}
|
||||
guard httpResponse.statusCode == 200 else {
|
||||
print("Failed with code: \(httpResponse.statusCode)")
|
||||
return nil
|
||||
}
|
||||
return data
|
||||
} catch {
|
||||
print("Failed with error: \(error)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func hash(_ masterKey: String) -> Data {
|
||||
Data(SHA256.hash(data: masterKey.data(using: .utf8)!))
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user