Last active
November 3, 2023 06:50
-
-
Save willwade/71688bad4b5f009a29e38720221daac3 to your computer and use it in GitHub Desktop.
RefTab API for Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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