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

Mqtt5 userguide #241

Open
wants to merge 93 commits into
base: iot
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
57cdc08
add title, license, and min swift version to readme
sbSteveK Feb 16, 2024
56ead4a
mqtt5 enums
sbSteveK Feb 20, 2024
01212f4
enums remaining
sbSteveK Feb 20, 2024
bc8b7a1
give enums a raw value of Int
sbSteveK Feb 20, 2024
9d31ba4
formatting
sbSteveK Feb 20, 2024
1805d2f
swift enum naming convention applied
sbSteveK Feb 20, 2024
647cb18
spelling
sbSteveK Feb 20, 2024
06a6279
add details to existing ExponentialBackoffJitterMode enum
sbSteveK Feb 20, 2024
e645c1f
Negotiated Settings Struct
sbSteveK Feb 20, 2024
e4f1b53
default is a reserved keyword
sbSteveK Feb 20, 2024
9a440e5
ClientOptions bones
sbSteveK Feb 20, 2024
dc4c9fa
ClientOptions minus callbacks
sbSteveK Feb 20, 2024
efdec3f
Spelling and TopicAliasingOptions
sbSteveK Feb 20, 2024
f53093b
var
sbSteveK Feb 21, 2024
556f172
UserProperty, PublishPacket, ConnectPacket
sbSteveK Feb 21, 2024
bde4197
dont initialize arrays
sbSteveK Feb 21, 2024
cc4af58
try typeAliasing callbacks
sbSteveK Feb 21, 2024
dfa159d
fixes
sbSteveK Feb 21, 2024
1bdf7c8
fixes
sbSteveK Feb 21, 2024
e4c5b8a
ConnackPacket
sbSteveK Feb 22, 2024
7c2590f
PubackPacket
sbSteveK Feb 22, 2024
2cbcd41
Subscription
sbSteveK Feb 22, 2024
d72d0b6
QoS fix
sbSteveK Feb 22, 2024
b9455c1
SubackPacket
sbSteveK Feb 22, 2024
5857ab5
UnsubscribePacket UnsubackPacket
sbSteveK Feb 22, 2024
79d2216
DisconnectPacket
sbSteveK Feb 22, 2024
d6a43c7
ClientOperationStatistics
sbSteveK Feb 22, 2024
7512c25
change base folder to mqtt
sbSteveK Feb 22, 2024
7c39cf3
Merge branch 'iot' into mqtt5-enums
sbSteveK Feb 22, 2024
3033b19
Changed lists to arrays to accurately describe members in descriptions
sbSteveK Feb 22, 2024
d803a23
ConnectPacket -> ConnectOptions
sbSteveK Feb 22, 2024
1609c09
Add specific Unsigned Integers
sbSteveK Feb 22, 2024
35c800a
structs -> classes
sbSteveK Feb 22, 2024
b02ce67
UserProperty, PublishPacket init()
sbSteveK Feb 22, 2024
2af0056
TopicAliasingOptions all optional
sbSteveK Feb 23, 2024
66ced3c
NegotiatedSettings
sbSteveK Feb 23, 2024
d884a24
'self' not 'this'
sbSteveK Feb 23, 2024
64be0dd
PublishPacket optionals
sbSteveK Feb 23, 2024
2775665
PubackPacket, Subscription, SubscribePacket
sbSteveK Feb 23, 2024
5735eae
SubackPacket
sbSteveK Feb 23, 2024
419d03e
Unsub related
sbSteveK Feb 23, 2024
a3cc1e0
operation statistics
sbSteveK Feb 23, 2024
33feb19
DisconnectPacket
sbSteveK Feb 26, 2024
656a3fb
ConnectOptions
sbSteveK Feb 26, 2024
b9773dc
formatting
sbSteveK Feb 26, 2024
9dc3e1e
sessionExpiryInterval optional
sbSteveK Feb 26, 2024
7e4dd9e
ConnackPacket
sbSteveK Feb 26, 2024
828d0b8
ClientOptions
sbSteveK Feb 26, 2024
420b92f
tlsContext name
sbSteveK Feb 26, 2024
d19e7d1
tlsContext
sbSteveK Feb 26, 2024
15b0813
Int to proper UInt
sbSteveK Feb 26, 2024
6079204
NegotiatedSettings let
sbSteveK Feb 26, 2024
df109b3
UserProperty let
sbSteveK Feb 26, 2024
c0af14b
let on required arguments
sbSteveK Feb 26, 2024
eb35deb
placeholder lifecycle event typealias
sbSteveK Feb 26, 2024
0441e54
PublishReceivedData
sbSteveK Feb 26, 2024
0bb89c1
LifecycleStoppedData
sbSteveK Feb 26, 2024
a25f18c
remaining callback classes
sbSteveK Feb 26, 2024
af7f359
fixes
sbSteveK Feb 26, 2024
7657a01
added errorCode to disconnection and connection failure lifecycle events
sbSteveK Feb 26, 2024
a402091
Publish payload type and string func
sbSteveK Feb 27, 2024
635652c
import Foundation
sbSteveK Feb 27, 2024
9445361
use data not payload
sbSteveK Feb 27, 2024
afaf049
addSubscription
sbSteveK Feb 27, 2024
74c82b0
subscription is not optional
sbSteveK Feb 27, 2024
fdd4768
actually, just let the customer handle it
sbSteveK Feb 27, 2024
0776fe5
Change errorCode into crtError
sbSteveK Feb 27, 2024
5e50a97
SubscribePacket overload
sbSteveK Feb 27, 2024
69c1782
remove readme changes
sbSteveK Mar 6, 2024
b5af5fa
pr changes and split Mqtt5.swift file
sbSteveK Mar 6, 2024
e42d8d2
fixes
sbSteveK Mar 6, 2024
c07412a
var -> let and convenience inits
sbSteveK Mar 7, 2024
618c10d
remove unecessary import and make func public
sbSteveK Mar 7, 2024
285ced9
Enums cleanup
sbSteveK Mar 7, 2024
e3d83a1
lint
sbSteveK Mar 7, 2024
12a3ae9
public
sbSteveK Mar 7, 2024
649d49d
lint
sbSteveK Mar 7, 2024
499cf72
initial mqtt5 userguide document wip
sbSteveK Mar 11, 2024
276034d
change options to let
sbSteveK Mar 11, 2024
d710ac6
Merge branch 'mqtt5-enums' into mqtt5-userguide
sbSteveK Mar 11, 2024
9fd6c63
test sample
sbSteveK Mar 11, 2024
407f63b
make all inits and convenience inits explicitly public
sbSteveK Mar 11, 2024
6cdef32
Merge branch 'mqtt5-enums' into mqtt5-userguide
sbSteveK Mar 11, 2024
0abf1b4
future test setup
sbSteveK Mar 13, 2024
bf0bdc3
wip of async operations
sbSteveK Mar 14, 2024
99fb881
Task handled API
sbSteveK Mar 14, 2024
ee6a4a0
API updates in doc
sbSteveK Mar 14, 2024
a45d533
doc update
sbSteveK Mar 15, 2024
d6ae583
update to subscribe() returning a Task
sbSteveK Mar 20, 2024
1762951
wip
sbSteveK Mar 20, 2024
fb0e43b
rename onPublishCallback to onPublishReceived
sbSteveK Mar 20, 2024
03f312f
Merge branch 'mqtt5-enums' into mqtt5-userguide
sbSteveK Mar 20, 2024
3d3dffe
wip
sbSteveK Mar 25, 2024
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
412 changes: 412 additions & 0 deletions Documents/MQTT5_Userguide.md

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ let awsCEventStreamExcludes = [

let awsCMqttExcludes = [
"bin",
"CODE_OF_CONDUCT.md",
"CODE_OF_CONDUCT.md"
] + excludesFromAll

packageTargets.append(contentsOf: [
Expand Down Expand Up @@ -297,6 +297,10 @@ packageTargets.append(contentsOf: [
name: "Elasticurl",
dependencies: ["AwsCommonRuntimeKit"],
path: "Source/Elasticurl"
)
),
.executableTarget(
name: "Test_Sample",
dependencies: ["AwsCommonRuntimeKit", "AwsCMqtt"],
path: "Sample")
] )
package.targets = packageTargets
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# AwsCommonRuntimeKit
The AWS CRT for Swift is currently in developer preview and is intended strictly for feedback purposes only.
The AWS CRT for Swift is currently in developer preview and is intended strictly for feedback purposes only.
Do not use this for production workloads.

## Building
Expand Down Expand Up @@ -29,4 +29,4 @@ Required Reading:
Useful Videos:
- [Safely manage pointers in Swift](https://developer.apple.com/videos/play/wwdc2020/10167/)
- [Unsafe Swift](https://developer.apple.com/videos/play/wwdc2020/10648)
- [Swift and C Interoperability](https://youtu.be/0kim9mxBOA8)
- [Swift and C Interoperability](https://youtu.be/0kim9mxBOA8)
279 changes: 279 additions & 0 deletions Sample/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
print("Sample Starting")

import AwsCommonRuntimeKit
import Combine
import Foundation

public class MqttClient {
public func start(){
// cals into native aws_mqtt5_client_start() which return success/failure
}

public func stop(disconnectPacket: DisconnectPacket? = nil) {
// cals into native aws_mqtt5_client_stop() with optional disconnect packet. returns success/failure
}

public func publish(publishPacket: PublishPacket) {
// calls into native aws_mqtt5_client_publish(). returns success/failure
}

public func subscribe(subscribePacket: SubscribePacket?) {
// calls into native aws_mqtt5_client_subscribe(). returns success/failure
}

public func unsubscribe(unsubscribePacket: UnsubscribePacket) {
// calls into native aws_mqtt5_client_unsubscribe(). returns success/failure
}

public func getStats() -> ClientOperationStatistics {
// cals into native aws_mqtt5_client_get_stats
return ClientOperationStatistics(
incompleteOperationCount: 0,
incompleteOperationSize: 0,
unackedOperationCount: 0,
unackedOperationSize: 0)
}

// This should be unecessary in Swift as all request response clients and service clients will be mqtt5 in swift.
// public func newConnection() {
// }

public init (clientOptions: MqttClientOptions) {
// calls into native aws_mqtt5_client_new() which returns a pointer to the native client or nil
}
/*

Native mqtt functions not exposed directly in swift client
aws_mqtt5_client_acquire()
aws_mqtt5_client_release()

*/
}

// for waiting/sleep
let semaphore: DispatchSemaphore = DispatchSemaphore(value: 0)

// Wait x seconds with logging
func wait (seconds: Int) {
print(" wait for \(seconds) seconds")
let timeLeft = seconds - 1
for i in (0...timeLeft).reversed() {
_ = semaphore.wait(timeout: .now() + 1)
print(" \(i) seconds left")
}
}

func waitNoCountdown(seconds: Int) {
print(" wait for \(seconds) seconds")
_ = semaphore.wait(timeout: .now() + 1)
}

func nativeSubscribe(subscribePacket: SubscribePacket, completion: @escaping (Int, SubackPacket) -> Void) -> Int {
print("[NATIVE CLIENT] SubscribePaket with topic '\(subscribePacket.subscriptions[0].topicFilter)' received for processing")

// Simulate an asynchronous task.
// This block is occuring in a background thread relative to the main thread.
DispatchQueue.global().async {
let nativeSemaphore: DispatchSemaphore = DispatchSemaphore(value: 0)

print("[NATIVE CLIENT] simulating 2 second delay for receiving a suback from broker for `\(subscribePacket.subscriptions[0].topicFilter)`")
_ = nativeSemaphore.wait(timeout: .now() + 2)

let subackPacket: SubackPacket = SubackPacket(
reasonCodes: [SubackReasonCode.grantedQos1],
userProperties: [UserProperty(name: "Topic", value: "\(subscribePacket.subscriptions[0].topicFilter)")])

print("[NATIVE CLIENT] simulating calling the swift callback with an error code and subackPacket for `\(subscribePacket.subscriptions[0].topicFilter)`")
completion(0, subackPacket)
// if (Bool.random()){
// completion(5146, subackPacket)
// } else {
// completion(0, subackPacket)
// }
}

return 0
}

func subscribeAsync(subscribePacket: SubscribePacket) async throws -> SubackPacket {
print("client.subscribeAsync() entered for `\(subscribePacket.subscriptions[0].topicFilter)`")

// withCheckedThrowingContinuation is used as a bridge between native's callback asynchrnous code and Swift's async/await model
// This func will pause until continuation.resume() is called

return try await withCheckedThrowingContinuation { continuation in
print("subscribeAsync try await withCheckedThrowingContinuation for '\(subscribePacket.subscriptions[0].topicFilter)` starting")
// The completion callback to invoke when an ack is received in native
func subscribeCompletionCallback(errorCode: Int, subackPacket: SubackPacket) {
print(" subscribeCompletionCallback called for `\(subackPacket.userProperties![0].value)`")
if errorCode == 0 {
continuation.resume(returning: subackPacket)
} else {
continuation.resume(throwing: CommonRunTimeError.crtError(CRTError(code: errorCode)))
}
}

// Translate swift packet to native packet
// We have a native callback for the operation
// We have a pointer to the swift callback
//aws_mqtt5_subscribe(nativePacket, nativeCallback)

print("subscribeAsync nativeSubscribe within withCheckedThrowingContinuation for '\(subscribePacket.subscriptions[0].topicFilter)` starting")
// represents the call to the native client
let result = nativeSubscribe(
subscribePacket: subscribePacket,
completion: subscribeCompletionCallback)

if result != 0 {
continuation.resume(throwing: CommonRunTimeError.crtError(CRTError(code: -1)))
}
}
}

func subscribe(subscribePacket: SubscribePacket) -> Task<SubackPacket, Error> {
return Task {
print("Subscribe Task for `\(subscribePacket.subscriptions[0].topicFilter)` executing")
return try await subscribeAsync(subscribePacket: subscribePacket)
}
}

/// Explicitly request a Task on an operation
func subscribeOptionalTask(subscribePacket: SubscribePacket, getAck: Bool = false) -> Task<SubackPacket, Error>? {

// If getAck is false, submit the operation to native with no callback
guard getAck else {
// Calls native subscribe() without a completion callback.
// Immediately returns nil
return nil
}

// If an ack is requested, return the Task that will
return Task {
return try await subscribeAsync(subscribePacket: subscribePacket)
}
}

func processSuback(subackPacket: SubackPacket) {
print(" =======SUBACK PACKET=======")
print(" Processing suback")
print(" Suback reasonCode: \(subackPacket.reasonCodes[0])")
if let userProperties = subackPacket.userProperties {
for property in userProperties {
print(" \(property.name) : \(property.value)")
}
}
print(" =====SUBACK PACKET END=====")
}

let subscribePacket: SubscribePacket = SubscribePacket(
topicFilter: "hello/world",
qos: QoS.atLeastOnce)

// // Ignore the returned Task
// _ = subscribe(subscribePacket: SubscribePacket(
// topicFilter: "Ignore",
// qos: QoS.atLeastOnce))

// waitNoCountdown(seconds: 1)

// let taskUnused = subscribe(subscribePacket: SubscribePacket(
// topicFilter: "Task Unused",
// qos: QoS.atLeastOnce))

// let task1 = subscribe(subscribePacket: SubscribePacket(
// topicFilter: "Within",
// qos: QoS.atLeastOnce))
// do {
// let subackPacket = try await task1.value
// processSuback(subackPacket: subackPacket)
// } catch {
// print("An error was thrown \(error)")
// }

// This passes to Native the operation, we don't care about result but the async function runs to completion
// async let _ = subscribeAsync(subscribePacket: subscribePacket)

let suback = try await subscribeAsync(subscribePacket: subscribePacket)

// results in "'async' call in a function that does not support concurrency"
// needs to be contained in an async function to be used this way
// subscribeAsync(subscribePacket: subscribePacket)


// Drops out of scope immediately without passing op to native
// Task {
// try await subscribeAsync(subscribePacket: subscribePacket)
// }

// func TestFunk() {
// Task {
// let result = try await subscribeAsync(subscribePacket: subscribePacket)
// print("RESULT \(result.reasonCodes[0])")
// }
// }
// TestFunk()





// _ = subscribe(subscribePacket: subscribePacket)


// _ = subscribe(subscribePacket: subscribePacket)

// let taskF = client.subscribe(subscribePacket: subscribePacket)
// let task = Task { try await client.subscribeAsync(subscribePacket: subscribePacket) }

// async let ack = try subscribe(subscribePacket: subscribePacket).value
// try await client.subscribeAsync(subscribePacket: subscribePacket)


// Execute the operation from within a task block
// Task.detached {
// let task1 = subscribe(subscribePacket: SubscribePacket(
// topicFilter: "Within",
// qos: QoS.atLeastOnce))
// do {
// let subackPacket = try await task1.value
// processSuback(subackPacket: subackPacket)
// } catch {
// print("An error was thrown \(error)")
// }
// }

// waitNoCountdown(seconds: 1)

// // Execute the operation and store the task and then complete it in a task block.
// let task2 = subscribe(subscribePacket: SubscribePacket(
// topicFilter: "Store and task block",
// qos: QoS.atLeastOnce))
// Task.detached {
// do {
// let subackPacket = try await task2.value
// processSuback(subackPacket: subackPacket)
// } catch {
// print("An error was thrown \(error)")
// }
// }

// waitNoCountdown(seconds: 1)
// let task3 = subscribe(subscribePacket: SubscribePacket(
// topicFilter: "Store and nothing else",
// qos: QoS.atLeastOnce))


// // Wait for the future to complete or until a timeout (e.g., 5 seconds)
// wait(seconds: 5)
// Task.detached {
// do {
// let subackTask3 = try await task3.value
// processSuback(subackPacket: subackTask3)
// } catch {
// print("An error was thrown \(error)")
// }
// }

// wait(seconds: 3)

print("Sample Ending")
Loading
Loading