Skip to content

Instantly share code, notes, and snippets.

@willwade
Last active November 3, 2023 06:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save willwade/71688bad4b5f009a29e38720221daac3 to your computer and use it in GitHub Desktop.
Save willwade/71688bad4b5f009a29e38720221daac3 to your computer and use it in GitHub Desktop.
RefTab API for Swift
import CryptoKit
import Foundation
struct EmptyResponse: Decodable {}
class ReftabClient {
let publicKey: String
let secretKey: String
let baseURL = "https://www.reftab.com/api"
init(publicKey: String, secretKey: String) {
self.publicKey = publicKey
self.secretKey = secretKey
}
private func request<T: Decodable>(method: String, endpoint: String, body: Data? = nil, completion: @escaping (Result<T, Error>) -> Void) {
guard let url = URL(string: "\(baseURL)/\(endpoint)") else {
completion(.failure(URLError(.badURL)))
return
}
var request = URLRequest(url: url)
request.httpMethod = method
let now = DateFormatter.rfc2822.string(from: Date())
var contentMD5 = ""
var contentType = ""
if let bodyData = body {
request.httpBody = bodyData
contentMD5 = Insecure.MD5.hash(data: bodyData).map { String(format: "%02hhx", $0) }.joined()
contentType = "application/json"
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
}
let signatureToSign = "\(method)\n\(contentMD5)\n\(contentType)\n\(now)\n\(baseURL)/\(endpoint)"
guard let secretKeyData = secretKey.data(using: .utf8) else {
completion(.failure(URLError(.badURL)))
return
}
let signatureData = HMAC<SHA256>.authenticationCode(for: Data(signatureToSign.utf8), using: SymmetricKey(data: secretKeyData))
let signature = Data(signatureData).map { String(format: "%02hhx", $0) }.joined()
let signatureBase64 = Data(signature.utf8).base64EncodedString()
request.setValue("RT \(publicKey):\(signatureBase64)", forHTTPHeaderField: "Authorization")
request.setValue(now, forHTTPHeaderField: "x-rt-date")
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.failure(URLError(.cannotParseResponse)))
return
}
print(String(data: data, encoding: .utf8) ?? "Invalid response data")
do {
let decodedResponse = try JSONDecoder().decode(T.self, from: data)
completion(.success(decodedResponse))
} catch {
completion(.failure(error))
}
}.resume()
}
// GET request to fetch data
func get<T: Decodable>(endpoint: String, completion: @escaping (Result<T, Error>) -> Void) {
request(method: "GET", endpoint: endpoint, completion: completion)
}
// POST request to create new data
func post<T: Decodable, U: Encodable>(endpoint: String, body: U, completion: @escaping (Result<T, Error>) -> Void) {
do {
let bodyData = try JSONEncoder().encode(body)
request(method: "POST", endpoint: endpoint, body: bodyData, completion: completion)
} catch {
completion(.failure(error))
}
}
// PUT request to update existing data
func put<T: Decodable, U: Encodable>(endpoint: String, id: String, body: U, completion: @escaping (Result<T, Error>) -> Void) {
do {
let bodyData = try JSONEncoder().encode(body)
request(method: "PUT", endpoint: "\(endpoint)/\(id)", body: bodyData, completion: completion)
} catch {
completion(.failure(error))
}
}
// DELETE request to remove data
func delete<T: Decodable>(endpoint: String, id: String, completion: @escaping (Result<T, Error>) -> Void) {
request(method: "DELETE", endpoint: "\(endpoint)/\(id)", completion: completion)
}
}
extension DateFormatter {
static let rfc2822: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter
}()
}
struct Loan: Codable {
let lid: Int
let lgid: Int?
let accid: Int?
let aid: String?
let licid: Int?
let kid: Int?
let categoryName: String?
let clientName: String?
let locationName: String?
let clid: Int?
let due: String?
let `in`: String?
let out: String?
let details: [String: String]?
let notes: String?
let title: String?
let status: String?
let signature: String?
let lnid: Int?
let loan_uid: Int?
let loanee: String?
let loaneeName: String?
let loanerEmail: String?
let returnerEmail: String?
enum CodingKeys: String, CodingKey {
case lid, lgid, accid, aid, licid, kid, categoryName, clientName, locationName, clid, due, `in`, out, details, notes, title, status, signature, lnid, loan_uid, loanee, loaneeName, loanerEmail, returnerEmail
}
}
struct Loanee: Codable {
let id: Int?
let name: String
let email: String
let title: String
let employeeId: String
let details: [String: String]?
enum CodingKeys: String, CodingKey {
case id = "lnid"
case name, email, title, employeeId, details
}
}
struct NewLoan: Codable {
var accids: [Int]? = nil
var aids: [String]? = nil
var licids: [Int]? = nil
var kids: [Int]? = nil
var due: String? = nil
var details: [String: String]? = nil
var notes: String? = nil
var signature: String? = nil
var lnid: Int
var loan_uid: Int? = nil
var lgid: Int? = nil
}
struct Asset: Codable {
let title: String
let aid: String
let clid: Int?
let cid: Int?
let notes: String?
let upid: Int?
let loan_period: Int?
let statid: Int?
let details: [String: String]?
let catName: String?
let locationName: String?
let lid: Int?
let loanee: String?
let loaneeName: String?
let lastScanned: String?
let permission: String?
let depreciation: Depreciation?
let loan: LoanInfo?
let image: ImageInfo?
let status: StatusInfo?
struct Depreciation: Codable {
// Define properties according to your depreciation object
let error: String?
}
struct LoanInfo: Codable {
// Define properties according to your loan object
let check_out: String?
let due: String?
let lid: Int?
let loanee: String?
let loaneeName: String?
}
struct ImageInfo: Codable {
// Define properties according to your image object
let filename: String?
let full: String?
let thumbnail: String?
}
struct StatusInfo: Codable {
// Define properties according to your status object
let name: String?
let loanability: Int?
let defaultVisibility: Int?
let color: String?
}
}
struct Accessory: Codable {
let title: String
let accid: Int
let type: String
let email: String?
let locationName: String?
let vendor: String?
let barcode: String?
let orderNumber: String?
let notes: String?
let available: Int?
let portalVisible: Int?
let quantity: Int?
let minQuantity: Int?
let used: Int?
let clid: Int?
let purchaseDate: String?
let details: [String: String]?
enum CodingKeys: String, CodingKey {
case title
case accid
case type
case email
case locationName
case vendor
case barcode
case orderNumber
case notes
case available
case portalVisible
case quantity
case minQuantity
case used
case clid
case purchaseDate
case details
}
}
struct Location: Codable {
let name: String
let address: String
let phone: String
let email: String
let mainContact: String
let notes: String?
let parent: Int?
let clid: Int
let upid: Int?
let assetCount: Int
let licenseCount: Int
let accessoryCount: Int
let expiredLicenseCount: Int
let minimumAccessoryCount: Int
let categories: [String: Int]
let children: [ChildLocation]
}
struct ChildLocation: Codable {
let name: String
let address: String
let phone: String
let email: String
let mainContact: String
let notes: String?
let parent: Int
let clid: Int
let upid: Int?
let assetCount: Int
let licenseCount: Int
let accessoryCount: Int
let expiredLicenseCount: Int
let minimumAccessoryCount: Int
let categories: [String: Int]
}
struct Reservation: Codable {
let resid: Int
let lgid: Int?
let accid: Int?
let aid: String?
let licid: Int?
let kid: Int?
let categoryName: String?
let clientName: String?
let locationName: String?
let clid: Int?
let end: String?
let start: String
let notes: String?
let title: String
let status: String
let lnid: Int?
let loan_uid: Int?
let loaneeEmail: String?
let loaneeName: String?
let email: String
}
struct NewReservation: Codable {
var accid: [Int]?
var aid: String?
var licid: [Int]?
var kid: Int?
var start: String
var end: String?
var notes: String?
var lnid: Int?
var loan_uid: Int?
var lgid: Int?
}
struct Subuser: Codable {
let subuserId: Int
let name: String
let email: String
let role: String
let lastLogin: String?
let details: [String: String]?
}
struct AccessoryTransaction: Codable {
let transactionId: Int
let accid: Int
let type: String
let quantity: Int
let date: String
let notes: String?
let userName: String?
let userId: Int?
}
struct AccessoryTransactionTypes: Codable {
let types: [String]
}
// Assuming you have a struct that matches the JSON response for assets
struct AssetsResponse: Codable {
var assets: [Asset]
}
// Example usage of ReftabClient
let client = ReftabClient(publicKey: "yourPublicKey", secretKey: "yourSecretKey")
// Fetch assets
client.get(endpoint: "assets") { (result: Result<AssetsResponse, Error>) in
DispatchQueue.main.async {
switch result {
case .success(let assetsResponse):
// Handle the successful response
for asset in assetsResponse.assets {
print("Asset: \(asset.title)")
}
case .failure(let error):
// Handle any errors
print("Error fetching assets: \(error.localizedDescription)")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment