Skip to content

Commit e0198e0

Browse files
authored
Add proxy config, cleanup enums (#137)
* Add proxy config, cleanup enums * remove lazy var jsonEncoder()
1 parent 53a2d2a commit e0198e0

File tree

6 files changed

+157
-51
lines changed

6 files changed

+157
-51
lines changed

Sources/APNSwift/APNSClient.swift

+65-23
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ import Foundation
1717
import Logging
1818
import NIOCore
1919
import NIOFoundationCompat
20+
import NIOSSL
2021

2122
public final class APNSClient {
23+
internal static let loggingDisabled = Logger(
24+
label: "APNS-do-not-log", factory: { _ in SwiftLogNoOpLogHandler() })
2225

2326
private let configuration: APNSConfiguration
2427
private let bearerTokenFactory: APNSBearerTokenFactory
@@ -27,10 +30,6 @@ public final class APNSClient {
2730
internal let jsonEncoder = JSONEncoder()
2831
internal let jsonDecoder = JSONDecoder()
2932

30-
private var logger: Logger? {
31-
configuration.logger
32-
}
33-
3433
/// APNSClient manages the connection and sending of push notifications to Apple's servers
3534
///
3635
/// - Parameter configuration: `APNSConfiguration` contains various values the client will need.
@@ -42,9 +41,18 @@ public final class APNSClient {
4241
authenticationConfig: configuration.authenticationConfig,
4342
logger: configuration.logger
4443
)
44+
45+
let httpClientConfiguration = HTTPClient.Configuration(
46+
proxy: configuration.proxy?.base
47+
)
48+
4549
self.httpClient = HTTPClient(
46-
eventLoopGroupProvider: configuration.eventLoopGroupProvider.httpClientValue
50+
eventLoopGroupProvider: configuration.eventLoopGroupProvider.httpClientValue,
51+
configuration: httpClientConfiguration,
52+
backgroundActivityLogger: configuration.logger ?? APNSClient.loggingDisabled
4753
)
54+
55+
jsonEncoder.outputFormatting = .withoutEscapingSlashes
4856
}
4957

5058
/// Shuts down the connections
@@ -74,10 +82,12 @@ public final class APNSClient {
7482
priority: Int?,
7583
collapseIdentifier: String?,
7684
topic: String?,
77-
apnsID: UUID?
85+
apnsID: UUID?,
86+
logger: Logger? = nil
7887
) async throws {
79-
8088
let topic = topic ?? configuration.topic
89+
let logger = logger ?? configuration.logger
90+
8191
let urlBase: String =
8292
environment?.url.absoluteString ?? configuration.environment.url.absoluteString
8393

@@ -87,7 +97,7 @@ public final class APNSClient {
8797
request.headers.add(name: "user-agent", value: "APNS/swift-nio")
8898
request.headers.add(name: "content-length", value: "\(payload.readableBytes)")
8999
request.headers.add(name: "apns-topic", value: topic)
90-
request.headers.add(name: "apns-push-type", value: pushType.rawValue)
100+
request.headers.add(name: "apns-push-type", value: pushType.base.rawValue)
91101
request.headers.add(name: "host", value: urlBase)
92102

93103
if let priority = priority {
@@ -118,7 +128,9 @@ public final class APNSClient {
118128
request,
119129
timeout: configuration.timeout ?? .seconds(30)
120130
)
131+
121132
logger?.debug("APNS request - finished - \(response.status)")
133+
122134
if response.status != .ok {
123135
let body = try await response.body.collect(upTo: 1024 * 1024)
124136

@@ -130,13 +142,28 @@ public final class APNSClient {
130142
}
131143

132144
extension APNSClient {
133-
public enum PushType: String {
134-
case alert
135-
case background
136-
case mdm
137-
case voip
138-
case fileprovider
139-
case complication
145+
public struct PushType: Hashable, Sendable {
146+
internal enum Base: String {
147+
case alert
148+
case background
149+
case mdm
150+
case voip
151+
case fileprovider
152+
case complication
153+
}
154+
155+
internal var base: Base
156+
157+
init(_ base: Base) {
158+
self.base = base
159+
}
160+
161+
public static let alert = PushType(.alert)
162+
public static let background = PushType(.background)
163+
public static let mdm = PushType(.mdm)
164+
public static let voip = PushType(.voip)
165+
public static let fileprovider = PushType(.fileprovider)
166+
public static let complication = PushType(.complication)
140167
}
141168
}
142169

@@ -172,7 +199,8 @@ extension APNSClient {
172199
priority: Int? = nil,
173200
collapseIdentifier: String? = nil,
174201
topic: String? = nil,
175-
apnsID: UUID? = nil
202+
apnsID: UUID? = nil,
203+
logger: Logger? = nil
176204
) async throws {
177205
try await self.send(
178206
APNSPayload(alert: alert),
@@ -184,7 +212,8 @@ extension APNSClient {
184212
priority: priority,
185213
collapseIdentifier: collapseIdentifier,
186214
topic: topic,
187-
apnsID: apnsID
215+
apnsID: apnsID,
216+
logger: logger
188217
)
189218
}
190219

@@ -218,7 +247,8 @@ extension APNSClient {
218247
priority: Int? = nil,
219248
collapseIdentifier: String? = nil,
220249
topic: String? = nil,
221-
apnsID: UUID? = nil
250+
apnsID: UUID? = nil,
251+
logger: Logger? = nil
222252
) async throws {
223253
struct BasicNotification: APNSNotification {
224254
let aps: APNSPayload
@@ -233,7 +263,8 @@ extension APNSClient {
233263
priority: priority,
234264
collapseIdentifier: collapseIdentifier,
235265
topic: topic,
236-
apnsID: apnsID
266+
apnsID: apnsID,
267+
logger: logger
237268
)
238269
}
239270

@@ -267,14 +298,22 @@ extension APNSClient {
267298
priority: Int? = nil,
268299
collapseIdentifier: String? = nil,
269300
topic: String? = nil,
270-
apnsID: UUID? = nil
301+
apnsID: UUID? = nil,
302+
logger: Logger? = nil
271303
) async throws where Notification: APNSNotification {
272304
let data: Data
273305
if let encoder = encoder {
274306
data = try encoder.encode(notification)
275307
} else {
276308
data = try jsonEncoder.encode(notification)
277309
}
310+
311+
let loggerToUse = logger ?? configuration.logger
312+
if let loggerLevel = loggerToUse?.logLevel, loggerLevel >= .debug {
313+
let payloadString = String(data: data, encoding: .utf8) ?? ""
314+
loggerToUse?.debug("APNS Payload: \(payloadString)")
315+
}
316+
278317
try await self.send(
279318
raw: data,
280319
pushType: pushType,
@@ -284,7 +323,8 @@ extension APNSClient {
284323
priority: priority,
285324
collapseIdentifier: collapseIdentifier,
286325
topic: topic,
287-
apnsID: apnsID
326+
apnsID: apnsID,
327+
logger: loggerToUse
288328
)
289329
}
290330

@@ -299,7 +339,8 @@ extension APNSClient {
299339
priority: Int?,
300340
collapseIdentifier: String?,
301341
topic: String?,
302-
apnsID: UUID? = nil
342+
apnsID: UUID? = nil,
343+
logger: Logger? = nil
303344
) async throws
304345
where Bytes: Collection, Bytes.Element == UInt8 {
305346
var buffer = ByteBufferAllocator().buffer(capacity: payload.count)
@@ -313,7 +354,8 @@ extension APNSClient {
313354
priority: priority,
314355
collapseIdentifier: collapseIdentifier,
315356
topic: topic,
316-
apnsID: apnsID
357+
apnsID: apnsID,
358+
logger: logger
317359
)
318360
}
319361
}

Sources/APNSwift/APNSConfiguration.swift

+53-11
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public struct APNSConfiguration {
5151
/// Optional timeout time if the connection does not receive a response.
5252
internal let timeout: TimeAmount?
5353
internal let eventLoopGroupProvider: EventLoopGroupProvider
54+
internal let proxy: Proxy?
5455

5556
/// `APNSConfiguration` provides the values for APNSClient to use when sending pushes
5657
/// - Parameters:
@@ -66,14 +67,16 @@ public struct APNSConfiguration {
6667
environment: APNSConfiguration.Environment,
6768
eventLoopGroupProvider: EventLoopGroupProvider,
6869
logger: Logger? = nil,
69-
timeout: TimeAmount? = nil
70+
timeout: TimeAmount? = nil,
71+
proxy: Proxy? = nil
7072
) {
7173
self.topic = topic
7274
self.authenticationConfig = authenticationConfig
7375
self.environment = environment
7476
self.eventLoopGroupProvider = eventLoopGroupProvider
7577
self.timeout = timeout
7678
self.logger = logger
79+
self.proxy = proxy
7780
}
7881
}
7982

@@ -94,19 +97,58 @@ extension APNSConfiguration {
9497
}
9598

9699
/// Specifies how `EventLoopGroup` will be created and establishes lifecycle ownership.
97-
public enum EventLoopGroupProvider {
98-
/// `EventLoopGroup` will be provided by the user. Owner of this group is responsible for its lifecycle.
99-
case shared(EventLoopGroup)
100+
public struct EventLoopGroupProvider {
101+
var httpClientValue: HTTPClient.EventLoopGroupProvider
102+
103+
init(_ httpClientValue: HTTPClient.EventLoopGroupProvider) {
104+
self.httpClientValue = httpClientValue
105+
}
106+
100107
/// `EventLoopGroup` will be created by the client. When `syncShutdown` is called, created `EventLoopGroup` will be shut down as well.
101-
case createNew
108+
public static let createNew = EventLoopGroupProvider(.createNew)
102109

103-
internal var httpClientValue: HTTPClient.EventLoopGroupProvider {
104-
switch self {
105-
case .createNew:
106-
return .createNew
107-
case .shared(let group):
108-
return .shared(group)
110+
/// `EventLoopGroup` will be provided by the user. Owner of this group is responsible for its lifecycle.
111+
public static func shared(_ eventLoopGroup: EventLoopGroup) -> EventLoopGroupProvider {
112+
.init(.shared(eventLoopGroup))
113+
}
114+
}
115+
}
116+
117+
extension APNSConfiguration {
118+
public struct Proxy {
119+
public struct HTTPAuthorization {
120+
var base: HTTPClient.Authorization
121+
122+
init(_ base: HTTPClient.Authorization) {
123+
self.base = base
109124
}
125+
126+
public static func basic(username: String, password: String) -> HTTPClient.Authorization
127+
{
128+
.basic(username: username, password: password)
129+
}
130+
public static func basic(credentials: String) -> HTTPClient.Authorization {
131+
.basic(credentials: credentials)
132+
}
133+
public static func bearer(tokens: String) -> HTTPClient.Authorization {
134+
.bearer(tokens: tokens)
135+
}
136+
}
137+
138+
var base: HTTPClient.Configuration.Proxy
139+
140+
public static func server(host: String, port: Int) -> Proxy {
141+
return .server(host: host, port: port)
142+
}
143+
144+
public static func http(host: String, port: Int, authorization: HTTPAuthorization? = nil)
145+
-> Proxy
146+
{
147+
return .http(host: host, port: port, authorization: authorization)
148+
}
149+
150+
public static func socks(host: String, port: Int = 1080) -> Proxy {
151+
return .socks(host: host, port: port)
110152
}
111153
}
112154
}

Sources/APNSwift/APNSPayload.swift

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public struct APNSPayload: Codable {
5757
self.alert = alert
5858
self.badge = badge
5959
self.sound = sound
60+
6061
if let hasContentAvailable = hasContentAvailable {
6162
self.contentAvailable = hasContentAvailable ? 1 : 0
6263
} else {

Sources/APNSwift/APNSSound.swift

+24-9
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,33 @@ public struct APNSSoundDictionary: Codable, Equatable {
3737
self.volume = volume
3838
}
3939
}
40-
/// An enum to define how to use sound.
41-
/// - Parameter string: use this for a normal alert sound
42-
/// - Parameter critical: use for a critical alert type
43-
public enum APNSSoundType: Codable, Equatable {
44-
case normal(String)
45-
case critical(APNSSoundDictionary)
40+
41+
public struct APNSSoundType: Codable, Equatable {
42+
internal enum Base: Codable, Equatable {
43+
case normal(String)
44+
case critical(APNSSoundDictionary)
45+
}
46+
internal var base: Base
47+
48+
public static let none: APNSSoundType? = nil
49+
50+
init(_ base: Base) {
51+
self.base = base
52+
}
53+
54+
public static func makeNormalSound(soundFileName: String) -> APNSSoundType {
55+
return APNSSoundType(.normal(soundFileName))
56+
}
57+
58+
public static func makeDictionarySound(soundDictionary: APNSSoundDictionary) -> APNSSoundType {
59+
return APNSSoundType(.critical(soundDictionary))
60+
}
4661
}
4762

4863
extension APNSSoundType {
4964
public func encode(to encoder: Encoder) throws {
5065
var container = encoder.singleValueContainer()
51-
switch self {
66+
switch self.base {
5267
case .normal(let string):
5368
try container.encode(string)
5469
case .critical(let dict):
@@ -59,9 +74,9 @@ extension APNSSoundType {
5974
public init(from decoder: Decoder) throws {
6075
let container = try decoder.singleValueContainer()
6176
if let critical = try? container.decode(APNSSoundDictionary.self) {
62-
self = .critical(critical)
77+
self = .init(.critical(critical))
6378
} else {
64-
self = .normal(try container.decode(String.self))
79+
self = .init(.normal(try container.decode(String.self)))
6580
}
6681
}
6782
}

0 commit comments

Comments
 (0)