From 0707667a5511c9ff738429501d8b964cfa684ee0 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Mon, 13 Nov 2023 12:33:21 -0400 Subject: [PATCH] ADD: RON currency for widget --- ios/BlueWallet.xcodeproj/project.pbxproj | 14 +- ios/Widgets/Shared/WidgetAPI.swift | 181 ++++++++++++--------- ios/Widgets/Shared/XMLParserDelegate.swift | 29 ++++ 3 files changed, 147 insertions(+), 77 deletions(-) create mode 100644 ios/Widgets/Shared/XMLParserDelegate.swift diff --git a/ios/BlueWallet.xcodeproj/project.pbxproj b/ios/BlueWallet.xcodeproj/project.pbxproj index e2446b3e4..45fe913a0 100644 --- a/ios/BlueWallet.xcodeproj/project.pbxproj +++ b/ios/BlueWallet.xcodeproj/project.pbxproj @@ -68,6 +68,9 @@ B43D037B225847C500FBAA95 /* TransactionTableRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43D0375225847C500FBAA95 /* TransactionTableRow.swift */; }; B43D037C225847C500FBAA95 /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43D0376225847C500FBAA95 /* Wallet.swift */; }; B43D037D225847C500FBAA95 /* WalletInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43D0377225847C500FBAA95 /* WalletInformation.swift */; }; + B451DAF22B027FD3008B561D /* XMLParserDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B451DAF12B027FD3008B561D /* XMLParserDelegate.swift */; }; + B451DAF32B027FD3008B561D /* XMLParserDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B451DAF12B027FD3008B561D /* XMLParserDelegate.swift */; }; + B451DAF42B027FD3008B561D /* XMLParserDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B451DAF12B027FD3008B561D /* XMLParserDelegate.swift */; }; B461B852299599F800E431AA /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = B461B851299599F800E431AA /* AppDelegate.mm */; }; B4EE583C226703320003363C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B40D4E35225841ED00428FCC /* Assets.xcassets */; }; E5D4794B26781FC0007838C1 /* fiatUnits.json in Resources */ = {isa = PBXBuildFile; fileRef = 6DD410AD266CAF1F0087DE03 /* fiatUnits.json */; }; @@ -283,6 +286,7 @@ B43D0376225847C500FBAA95 /* Wallet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = ""; }; B43D0377225847C500FBAA95 /* WalletInformation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletInformation.swift; sourceTree = ""; }; B43D046E22584C1B00FBAA95 /* libRNWatch.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRNWatch.a; sourceTree = BUILT_PRODUCTS_DIR; }; + B451DAF12B027FD3008B561D /* XMLParserDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMLParserDelegate.swift; sourceTree = ""; }; B459EE96941AE09BCB547DC0 /* Pods-BlueWallet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BlueWallet.release.xcconfig"; path = "Pods/Target Support Files/Pods-BlueWallet/Pods-BlueWallet.release.xcconfig"; sourceTree = ""; }; B461B850299599F800E431AA /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = BlueWallet/AppDelegate.h; sourceTree = ""; }; B461B851299599F800E431AA /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = BlueWallet/AppDelegate.mm; sourceTree = ""; }; @@ -514,6 +518,7 @@ 6D4AF18325D215D1009DD853 /* UserDefaultsExtension.swift */, 6D4AF18225D215D0009DD853 /* BlueWalletWatch-Bridging-Header.h */, B40FC3F829CCD1AC0007EBAC /* SwiftTCPClient.swift */, + B451DAF12B027FD3008B561D /* XMLParserDelegate.swift */, ); path = Shared; sourceTree = ""; @@ -827,7 +832,7 @@ ); mainGroup = 83CBB9F61A601CBA00E9B192; packageReferences = ( - 6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode.git" */, + 6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */, ); productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; projectDirPath = ""; @@ -1067,6 +1072,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B451DAF22B027FD3008B561D /* XMLParserDelegate.swift in Sources */, 6D32C5C62596CE3A008C077C /* EventEmitter.m in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, B461B852299599F800E431AA /* AppDelegate.mm in Sources */, @@ -1089,6 +1095,7 @@ 6DD410BB266CAF5C0087DE03 /* MarketView.swift in Sources */, 6DD410B5266CAF5C0087DE03 /* WidgetDataStore.swift in Sources */, 6DD410C0266CB1460087DE03 /* MarketWidget.swift in Sources */, + B451DAF42B027FD3008B561D /* XMLParserDelegate.swift in Sources */, 6DD410BA266CAF5C0087DE03 /* FiatUnit.swift in Sources */, 6DD410AF266CAF5C0087DE03 /* WalletInformationAndMarketWidget.swift in Sources */, 6DD410BF266CB13D0087DE03 /* Models.swift in Sources */, @@ -1124,6 +1131,7 @@ B40D4E632258425500428FCC /* ReceiveInterfaceController.swift in Sources */, B43D0378225847C500FBAA95 /* WalletGradient.swift in Sources */, 6D4AF18425D215D1009DD853 /* UserDefaultsExtension.swift in Sources */, + B451DAF32B027FD3008B561D /* XMLParserDelegate.swift in Sources */, B40D4E5E2258425500428FCC /* NumericKeypadInterfaceController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1830,7 +1838,7 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode.git" */ = { + 6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/EFPrefix/EFQRCode.git"; requirement = { @@ -1843,7 +1851,7 @@ /* Begin XCSwiftPackageProductDependency section */ 6DFC806F24EA0B6C007B8700 /* EFQRCode */ = { isa = XCSwiftPackageProductDependency; - package = 6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode.git" */; + package = 6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */; productName = EFQRCode; }; /* End XCSwiftPackageProductDependency section */ diff --git a/ios/Widgets/Shared/WidgetAPI.swift b/ios/Widgets/Shared/WidgetAPI.swift index c97ff4ff7..1e11e28a1 100644 --- a/ios/Widgets/Shared/WidgetAPI.swift +++ b/ios/Widgets/Shared/WidgetAPI.swift @@ -39,89 +39,120 @@ class WidgetAPI { urlString = "https://www.bitstamp.net/api/v2/ticker/btc\(endPointKey.lowercased())" case "Coinbase": urlString = "https://api.coinbase.com/v2/prices/BTC-\(endPointKey.uppercased())/buy" - case "CoinGecko": + case "CoinGecko": urlString = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=\(endPointKey.lowercased())" + case "BNR": + urlString = "https://www.bnr.ro/nbrfxrates.xml" default: urlString = "https://api.coindesk.com/v1/bpi/currentprice/\(endPointKey).json" } guard let url = URL(string:urlString) else { return } - URLSession.shared.dataTask(with: url) { (data, response, error) in - guard let dataResponse = data, - let json = (try? JSONSerialization.jsonObject(with: dataResponse, options: .mutableContainers) as? Dictionary), - error == nil - else { - print(error?.localizedDescription ?? "Response Error") - completion(nil, error) - return - } + if source == "BNR" { + URLSession.shared.dataTask(with: url) { (data, response, error) in + if let error = error { + print("Error fetching data: \(error.localizedDescription)") + completion(nil, error) + return + } - var latestRateDataStore: WidgetDataStore? - switch source { - case "Yadio": - guard let rateDict = json[endPointKey] as? [String: Any], - let rateDouble = rateDict["price"] as? Double, - let lastUpdated = json["timestamp"] as? Int - else { break } - let unix = Double(lastUpdated / 1_000) - let lastUpdatedString = ISO8601DateFormatter().string(from: Date(timeIntervalSince1970: unix)) - latestRateDataStore = WidgetDataStore(rate: String(rateDouble), lastUpdate: lastUpdatedString, rateDouble: rateDouble) - case "CoinGecko": - guard let rateDict = json["bitcoin"] as? [String: Any], - let rateDouble = rateDict[endPointKey.lowercased()] as? Double - else { break } - let lastUpdatedString = ISO8601DateFormatter().string(from: Date()) - latestRateDataStore = WidgetDataStore(rate: String(rateDouble), lastUpdate: lastUpdatedString, rateDouble: rateDouble) - case "YadioConvert": - guard let rateDict = json as? [String: Any], - let rateDouble = rateDict["rate"] as? Double, - let lastUpdated = json["timestamp"] as? Int - else { break } - let unix = Double(lastUpdated / 1_000) - let lastUpdatedString = ISO8601DateFormatter().string(from: Date(timeIntervalSince1970: unix)) - latestRateDataStore = WidgetDataStore(rate: String(rateDouble), lastUpdate: lastUpdatedString, rateDouble: rateDouble) - case "Exir": - guard let rateDouble = json["last"] as? Double else { break } - let rateString = String(rateDouble) - let lastUpdatedString = ISO8601DateFormatter().string(from: Date()) - latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble) - case "Bitstamp": - guard let rateString = json["last"] as? String, let rateDouble = Double(rateString) else { break } - let lastUpdatedString = ISO8601DateFormatter().string(from: Date()) - latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble) - case "wazirx": - guard let tickerDict = json["ticker"] as? [String: Any], - let rateString = tickerDict["buy"] as? String, - let rateDouble = Double(rateString) - else { break } - let lastUpdatedString = ISO8601DateFormatter().string(from: Date()) - latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble) - case "Coinbase": - guard let data = json["data"] as? Dictionary, - let rateString = data["amount"] as? String, - let rateDouble = Double(rateString) - else { break } - let lastUpdatedString = ISO8601DateFormatter().string(from: Date()) - latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble) - default: - guard let bpi = json["bpi"] as? Dictionary, - let preferredCurrency = bpi[endPointKey] as? Dictionary, - let rateString = preferredCurrency["rate"] as? String, - let rateDouble = preferredCurrency["rate_float"] as? Double, - let time = json["time"] as? Dictionary, - let lastUpdatedString = time["updatedISO"] as? String - else { break } - latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble) - } + guard let data = data else { + print("No data received") + completion(nil, nil) + return + } - if (latestRateDataStore == nil) { - completion(nil, CurrencyError()) - return - } - - completion(latestRateDataStore, nil) - }.resume() + // Parse XML data + let parser = XMLParser(data: data) + let delegate = BNRXMLParserDelegate() + parser.delegate = delegate + if parser.parse(), let rate = delegate.usdRate { + let lastUpdatedString = ISO8601DateFormatter().string(from: Date()) + let latestRateDataStore = WidgetDataStore(rate: String(rate), lastUpdate: lastUpdatedString, rateDouble: rate) + completion(latestRateDataStore, nil) + } else { + print("Error parsing XML") + completion(nil, CurrencyError()) + } + }.resume() + } else { + URLSession.shared.dataTask(with: url) { (data, response, error) in + guard let dataResponse = data, + let json = (try? JSONSerialization.jsonObject(with: dataResponse, options: .mutableContainers) as? Dictionary), + error == nil + else { + print(error?.localizedDescription ?? "Response Error") + completion(nil, error) + return + } + + var latestRateDataStore: WidgetDataStore? + switch source { + case "Yadio": + guard let rateDict = json[endPointKey] as? [String: Any], + let rateDouble = rateDict["price"] as? Double, + let lastUpdated = json["timestamp"] as? Int + else { break } + let unix = Double(lastUpdated / 1_000) + let lastUpdatedString = ISO8601DateFormatter().string(from: Date(timeIntervalSince1970: unix)) + latestRateDataStore = WidgetDataStore(rate: String(rateDouble), lastUpdate: lastUpdatedString, rateDouble: rateDouble) + case "CoinGecko": + guard let rateDict = json["bitcoin"] as? [String: Any], + let rateDouble = rateDict[endPointKey.lowercased()] as? Double + else { break } + let lastUpdatedString = ISO8601DateFormatter().string(from: Date()) + latestRateDataStore = WidgetDataStore(rate: String(rateDouble), lastUpdate: lastUpdatedString, rateDouble: rateDouble) + case "YadioConvert": + guard let rateDict = json as? [String: Any], + let rateDouble = rateDict["rate"] as? Double, + let lastUpdated = json["timestamp"] as? Int + else { break } + let unix = Double(lastUpdated / 1_000) + let lastUpdatedString = ISO8601DateFormatter().string(from: Date(timeIntervalSince1970: unix)) + latestRateDataStore = WidgetDataStore(rate: String(rateDouble), lastUpdate: lastUpdatedString, rateDouble: rateDouble) + case "Exir": + guard let rateDouble = json["last"] as? Double else { break } + let rateString = String(rateDouble) + let lastUpdatedString = ISO8601DateFormatter().string(from: Date()) + latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble) + case "Bitstamp": + guard let rateString = json["last"] as? String, let rateDouble = Double(rateString) else { break } + let lastUpdatedString = ISO8601DateFormatter().string(from: Date()) + latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble) + case "wazirx": + guard let tickerDict = json["ticker"] as? [String: Any], + let rateString = tickerDict["buy"] as? String, + let rateDouble = Double(rateString) + else { break } + let lastUpdatedString = ISO8601DateFormatter().string(from: Date()) + latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble) + case "Coinbase": + guard let data = json["data"] as? Dictionary, + let rateString = data["amount"] as? String, + let rateDouble = Double(rateString) + else { break } + let lastUpdatedString = ISO8601DateFormatter().string(from: Date()) + latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble) + default: + guard let bpi = json["bpi"] as? Dictionary, + let preferredCurrency = bpi[endPointKey] as? Dictionary, + let rateString = preferredCurrency["rate"] as? String, + let rateDouble = preferredCurrency["rate_float"] as? Double, + let time = json["time"] as? Dictionary, + let lastUpdatedString = time["updatedISO"] as? String + else { break } + latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble) + } + + if (latestRateDataStore == nil) { + completion(nil, CurrencyError()) + return + } + + completion(latestRateDataStore, nil) + }.resume() + } } static func getUserPreferredCurrency() -> String { @@ -163,3 +194,5 @@ class WidgetAPI { } } + + diff --git a/ios/Widgets/Shared/XMLParserDelegate.swift b/ios/Widgets/Shared/XMLParserDelegate.swift new file mode 100644 index 000000000..d2c09e45d --- /dev/null +++ b/ios/Widgets/Shared/XMLParserDelegate.swift @@ -0,0 +1,29 @@ +// +// XMLParserDelegate.swift +// BlueWallet +// +// Created by Marcos Rodriguez on 11/13/23. +// Copyright © 2023 BlueWallet. All rights reserved. +// + +import Foundation + +class BNRXMLParserDelegate: NSObject, XMLParserDelegate { + var usdRate: Double? + var currentElement = "" + var foundRate = false + + func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) { + currentElement = elementName + if elementName == "Rate" && attributeDict["currency"] == "USD" { + foundRate = true + } + } + + func parser(_ parser: XMLParser, foundCharacters string: String) { + if foundRate { + usdRate = Double(string) + foundRate = false + } + } +}