taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

quickjs.swift (5417B)


      1 /*
      2  * This file is part of GNU Taler, ©2022-26 Taler Systems S.A.
      3  * See LICENSE.md
      4  */
      5 /**
      6  * @author Marc Stibane
      7  */
      8 import Foundation
      9 import os.log
     10 
     11 import FTalerWalletcore
     12 
     13 public protocol QuickjsMessageHandler: AnyObject {
     14     func handleMessage(message: String)
     15     func handleLog(message: String)
     16 }
     17 // MARK: -
     18 func notification_callback(userdata: Optional<UnsafeMutableRawPointer>,
     19                            payload: Optional<UnsafePointer<Int8>>) {
     20     let quickjs = Unmanaged<Quickjs>.fromOpaque(userdata!).takeUnretainedValue()
     21     let string = String(cString: payload!)
     22     quickjs.internalOnNotify(payload: string)
     23 }
     24 
     25 func logging_callback(userdata: Optional<UnsafeMutableRawPointer>,
     26                           level: TALER_WALLET_LogLevel,
     27                             tag: Optional<UnsafePointer<Int8>>,
     28                         message: Optional<UnsafePointer<Int8>>) {
     29     let quickjs = Unmanaged<Quickjs>.fromOpaque(userdata!).takeUnretainedValue()
     30     let logger = quickjs.logger
     31     let swiftTag = String(cString: tag!)
     32     let swiftMessage = String(cString: message!)
     33 
     34     switch level {
     35         case TALER_WALLET_LOG_ERROR:
     36             logger.error("\(swiftTag, privacy: .public)  \(swiftMessage, privacy: .public)")
     37         case TALER_WALLET_LOG_WARN:
     38             logger.warning("\(swiftTag, privacy: .public)  \(swiftMessage, privacy: .public)")
     39         case TALER_WALLET_LOG_MESSAGE:
     40             logger.notice("\(swiftTag, privacy: .public)  \(swiftMessage, privacy: .public)")
     41         case TALER_WALLET_LOG_INFO:
     42             logger.info("\(swiftTag, privacy: .public)  \(swiftMessage, privacy: .public)")
     43         case TALER_WALLET_LOG_TRACE:
     44             logger.trace("\(swiftTag, privacy: .public)  \(swiftMessage, privacy: .public)")
     45         default: break
     46     }
     47     quickjs.internalOnLog(message: swiftMessage)
     48 }
     49 // MARK: -
     50 public class Quickjs {      // acts as singleton, since only one instance ever exists
     51     var talerWalletInstance: OpaquePointer!
     52     public weak var messageHandler: QuickjsMessageHandler?
     53     var logger: Logger
     54 
     55     @Atomic(default: 0)                                                         // boilerPlate around NSLock…
     56     private var lastRequestID: Int32                                            // …to safeguard increments
     57     private var requests: [Int32: QuickDataTask] = [:]
     58 
     59     private lazy var urlSession: URLSession = {
     60         return URLSession(configuration: .ephemeral)    // we don't want the filesystem cache - keep it in RAM
     61     }()
     62 
     63     public init() {
     64         self.logger = Logger(subsystem: "net.taler.gnu", category: "QuickJS")
     65         self.talerWalletInstance = TALER_WALLET_create()
     66         TALER_WALLET_set_message_handler(talerWalletInstance,
     67                                          notification_callback,
     68                                          Unmanaged.passUnretained(self).toOpaque())
     69         TALER_WALLET_set_log_handler(talerWalletInstance,
     70                                      logging_callback,
     71                                      Unmanaged.passUnretained(self).toOpaque())
     72         let http_impl = TALER_pack_http_client_implementation(request_create, request_cancel,
     73                                                               Unmanaged.passUnretained(self).toOpaque())
     74         // http_impl got malloc'd, and could possibly be free'd when the app terminates
     75         TALER_set_http_client_implementation(talerWalletInstance, http_impl)
     76         // - but we never free anything on termination, thus we don't save http_impl here
     77         TALER_WALLET_run(talerWalletInstance)
     78     }
     79 
     80     func reqCreate(_ request: URLRequest,
     81                 _ responseCb: JSHttpResponseCb?,
     82              _ responseCbCls: Optional<UnsafeMutableRawPointer>) -> Int32 {
     83         let requestID = $lastRequestID.atomicIncrement()                        // ensure requestID is unique
     84         let quickDataTask = QuickDataTask(urlSession: urlSession,
     85                                              request: request,
     86                                            requestID: requestID,
     87                                             requests: &requests,
     88                                           responseCb: responseCb,
     89                                        responseCbCls: responseCbCls)
     90         quickDataTask.run()
     91         requests[requestID] = quickDataTask
     92         return requestID
     93     }
     94 
     95     func reqCancel(_ requestID: Int32) -> Int32 {
     96         if let quickDataTask = requests[requestID] {
     97             if let dataTask = quickDataTask.dataTask {
     98                 dataTask.cancel()
     99             }
    100         }
    101         requests[requestID] = nil
    102         return 0
    103     }
    104 
    105     deinit {
    106         // No need to call TALER_WALLET_destroy - memory gets purged anyway
    107     }
    108 
    109     func internalOnNotify(payload: String) {
    110         if let handler = messageHandler {
    111             handler.handleMessage(message: payload)
    112         }
    113     }
    114 
    115     public func internalOnLog(message: String) {
    116         if let handler = messageHandler {
    117             handler.handleLog(message: message)
    118         }
    119     }
    120 
    121 //    public func notifyNative() {
    122 //        __notifyNative(instance)
    123 //    }
    124 
    125 //    public func evalNodeCode(source: String) {
    126 //        scheduleNodeThreadAsync {
    127 //            __makeCallbackNative(self.instance, source.cString(using: .utf8))
    128 //        }
    129 //    }
    130 
    131     public func sendMessage(message: String) {
    132         TALER_WALLET_send_request(talerWalletInstance, message)
    133     }
    134 }