Skip to content
Open
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
119 changes: 100 additions & 19 deletions Sources/BluetoothGATT/ATTError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,94 +14,155 @@ import Foundation
///
/// These error constants are based on the Bluetooth ATT error codes, defined in the Bluetooth 4.0 specification.
/// For more information about these errors, see the Bluetooth 4.0 specification, Volume 3, Part F, Section 3.4.1.1.
@frozen
public enum ATTError: UInt8, Error {

public struct ATTError: RawRepresentable, Equatable, Hashable, Error {
public typealias RawValue = UInt8

enum Code: UInt8 {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you remove the Code enum and change that to static var cases.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

To confirm, do you want the numeric codes represented as static var cases, or the actual error cases changed to static var?

// Existing PR state:
public static let invalidHandle = ATTError(code: .invalidHandle)

// Potential change:
public static var invalidHandle = ATTError(code: 0x01)! // ! is known safe in this case

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@colemancda Happy to make changes here, just looking for some clarification

case invalidHandle = 0x01
case readNotPermitted = 0x02
case writeNotPermitted = 0x03
case invalidPDU = 0x04
case insufficientAuthentication = 0x05
case requestNotSupported = 0x06
case invalidOffset = 0x07
case insufficientAuthorization = 0x08
case prepareQueueFull = 0x09
case attributeNotFound = 0x0A
case attributeNotLong = 0x0B
case insufficientEncryptionKeySize = 0x0C
case invalidAttributeValueLength = 0x0D
case unlikelyError = 0x0E
case insufficientEncryption = 0x0F
case unsupportedGroupType = 0x10
case insufficientResources = 0x11

case writeRequestRejected = 0xFC
case cccdImproperlyConfigured = 0xFD
case procedureAlreadyInProgress = 0xFE
case outOfRange = 0xFF
}

public var rawValue: UInt8

public init?(rawValue: UInt8) {
if rawValue == 0 { return nil } // This is the success case
self.rawValue = rawValue
}

init(code: Code) {
self.rawValue = code.rawValue
}


/// Invalid Handle
///
/// The attribute handle given was not valid on this server.
case invalidHandle = 0x01
public static let invalidHandle = ATTError(code: .invalidHandle)

/// Read Not Permitted
///
/// The attribute cannot be read.
case readNotPermitted = 0x02
public static let readNotPermitted = ATTError(code: .readNotPermitted)

/// Write Not Permitted
///
/// The attribute cannot be written.
case writeNotPermitted = 0x03
public static let writeNotPermitted = ATTError(code: .writeNotPermitted)

/// Invalid PDU
///
/// The attribute PDU was invalid.
case invalidPDU = 0x04
public static let invalidPDU = ATTError(code: .invalidPDU)

/// Insufficient Authentication
///
/// The attribute requires authentication before it can be read or written.
case insufficientAuthentication = 0x05
public static let insufficientAuthentication = ATTError(code: .insufficientAuthentication)

/// Request Not Supported
///
/// Attribute server does not support the request received from the client.
case requestNotSupported = 0x06
public static let requestNotSupported = ATTError(code: .requestNotSupported)

/// Invalid Offset
///
/// Offset specified was past the end of the attribute.
case invalidOffset = 0x07
public static let invalidOffset = ATTError(code: .invalidOffset)

/// Insufficient Authorization
///
/// The attribute requires authorization before it can be read or written.
case insufficientAuthorization = 0x08
public static let insufficientAuthorization = ATTError(code: .insufficientAuthorization)

/// Prepare Queue Full
///
/// Too many prepare writes have been queued.
case prepareQueueFull = 0x09
public static let prepareQueueFull = ATTError(code: .prepareQueueFull)

/// Attribute Not Found
///
/// No attribute found within the given attribute handle range.
case attributeNotFound = 0x0A
public static let attributeNotFound = ATTError(code: .attributeNotFound)

/// Attribute Not Long
///
/// The attribute cannot be read or written using the *Read Blob Request*.
case attributeNotLong = 0x0B
public static let attributeNotLong = ATTError(code: .attributeNotLong)

/// Insufficient Encryption Key Size
///
/// The *Encryption Key Size* used for encrypting this link is insufficient.
case insufficientEncryptionKeySize = 0x0C
public static let insufficientEncryptionKeySize = ATTError(code: .insufficientEncryptionKeySize)

/// Invalid Attribute Value Length
///
/// The attribute value length is invalid for the operation.
case invalidAttributeValueLength = 0x0D
public static let invalidAttributeValueLength = ATTError(code: .invalidAttributeValueLength)

/// Unlikely Error
///
/// The attribute request that was requested has encountered an error that was unlikely,
/// and therefore could not be completed as requested.
case unlikelyError = 0x0E
public static let unlikelyError = ATTError(code: .unlikelyError)

/// Insufficient Encryption
///
/// The attribute requires encryption before it can be read or written.
case insufficientEncryption = 0x0F
public static let insufficientEncryption = ATTError(code: .insufficientEncryption)

/// Unsupported Group Type
///
/// The attribute type is not a supported grouping attribute as defined by a higher layer specification.
case unsupportedGroupType = 0x10
public static let unsupportedGroupType = ATTError(code: .unsupportedGroupType)

/// Insufficient Resources
///
/// Insufficient Resources to complete the request.
case insufficientResources = 0x11
public static let insufficientResources = ATTError(code: .insufficientResources)


// Common Profile and Service Error Code Descriptions

/// Write Request Rejected
///
/// The requested write operation cannot be fulfilled for reasons other than permissions.
public static let writeRequestRejected = ATTError(code: .writeRequestRejected)

/// Client Characteristic Configuration Descriptor Improperly Configured
///
/// Client Characteristic Configuration descriptor is not configured according to the requirements of the profile or service.
public static let cccdImproperlyConfigured = ATTError(code: .cccdImproperlyConfigured)

/// Procedure Already in Progress
///
/// The profile or service request cannot be serviced because an operation that has been previously triggered is still in progress.
public static let procedureAlreadyInProgress = ATTError(code: .procedureAlreadyInProgress)

/// Out of Range
///
/// The attribute value is out of range as defined by a profile or service specification.
public static let outOfRange = ATTError(code: .outOfRange)
}

// MARK: - CustomStringConvertible
Expand Down Expand Up @@ -154,6 +215,16 @@ public extension ATTError {
return "Unsupported Group Type"
case .insufficientResources:
return "Insufficient Resources"
case .writeRequestRejected:
return "Write Request Rejected"
case .cccdImproperlyConfigured:
return "Client Characteristic Configuration Descriptor Improperly Configured"
case .procedureAlreadyInProgress:
return "Procedure Already in Progress"
case .outOfRange:
return "Out of Range"
default:
return "ATTError 0x\(String(self.rawValue, radix: 16))"
}
}

Expand Down Expand Up @@ -195,6 +266,16 @@ public extension ATTError {
return "The attribute type is not a supported grouping attribute as defined by a higher layer specification."
case .insufficientResources:
return "Insufficient Resources to complete the request."
case .writeRequestRejected:
return "The requested write operation could not be completed."
case .cccdImproperlyConfigured:
return "The Client Characteristic Configuration Descriptor is not configured correctly."
case .procedureAlreadyInProgress:
return "An operation is already in progress."
case .outOfRange:
return "The attribute value is out of range."
default:
return "ATTError error code \(String(self.rawValue, radix: 16))"
}
}
#endif
Expand Down
3 changes: 1 addition & 2 deletions Tests/BluetoothTests/AttributeProtocolTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ import Foundation
#expect(ATTError.invalidHandle.errorDescription == "The attribute handle given was not valid on this server.")
#expect(ATTError.invalidHandle.description == ATTError.invalidHandle.name)

let errors = (1 ... .max).compactMap { ATTError(rawValue: $0) }
#expect(errors.count == 0x11)
let errors = (1 ... 0x11).compactMap { ATTError(rawValue: $0) }

for error in errors {

Expand Down