URL+id+iban.swift (3755B)
1 /* 2 * This file is part of GNU Taler, ©2022-25 Taler Systems S.A. 3 * See LICENSE.md 4 */ 5 /** 6 * @author Marc Stibane 7 */ 8 import SwiftUI 9 10 extension URL: Identifiable { 11 public var id: URL {self} 12 } 13 14 extension URL { 15 init(_ string: StaticString) { 16 self.init(string: "\(string)")! 17 } 18 19 var trimmedString: String { 20 absoluteString.trimmingCharacters(in: .whitespacesAndNewlines) 21 } 22 23 var cyclos: String? { 24 /// "payto://cyclos/" host ["/" fpath] "/" account-id [ "?" opts ] 25 if scheme == "payto", let host = host { 26 let path = path.dropFirst(1) 27 if host == "cyclos" { 28 return String(path) 29 } else if host.hasPrefix("cyclos") { 30 let cyclos = host.dropFirst(6) 31 if path.hasPrefix("/") { 32 return String(cyclos + path) 33 } 34 return String(cyclos + "/" + path) 35 } 36 } 37 return nil 38 } 39 40 var iban: String? { 41 /// https://datatracker.ietf.org/doc/rfc8905/ 42 /// payto://iban/DE75512108001245126199?amount=EUR:200.0&message=hello 43 if scheme == "payto" && host == "iban" { 44 return lastPathComponent 45 } 46 return nil 47 } 48 49 var xTaler: String? { 50 /// https://datatracker.ietf.org/doc/rfc8905/ 51 /// payto://x-taler-bank/BANK-HOST/ACCOUNT?receiver-name=… 52 /// The bank host is part of the target, so return "BANK-HOST/ACCOUNT" — 53 /// returning only lastPathComponent silently dropped the bank and made 54 /// the account unaddressable when it was written back out. 55 if scheme == "payto" && host == "x-taler-bank" { 56 let target = path.hasPrefix("/") ? String(path.dropFirst()) : path 57 return target.isEmpty ? nil : target 58 } 59 return nil 60 } 61 62 /// SwifterSwift: Dictionary of the URL's query parameters. 63 var queryParameters: [String:String]? { 64 guard let components = URLComponents(url: self, resolvingAgainstBaseURL: false), 65 let queryItems = components.queryItems else { return nil } 66 67 var items: [String:String] = [:] 68 69 for queryItem in queryItems { 70 items[queryItem.name] = queryItem.value 71 } 72 return items 73 } 74 75 static var docDirUrl: URL? { // accessible by FileSharing 76 if #available(iOS 16.4, *) { 77 return URL.documentsDirectory 78 } else { 79 let docDirNr: FileManager.SearchPathDirectory = .documentDirectory 80 if let documentsDir = FileManager.default.urls(for: docDirNr, in: .userDomainMask).first { 81 return documentsDir 82 } 83 } 84 return nil 85 } 86 87 static var appSuppUrl: URL? { 88 if #available(iOS 16.4, *) { 89 return URL.applicationSupportDirectory 90 } else { 91 let appDirNr: FileManager.SearchPathDirectory = .applicationSupportDirectory 92 if let appSupportDir = FileManager.default.urls(for: appDirNr, in: .userDomainMask).first { 93 return appSupportDir 94 } 95 } 96 return nil 97 } 98 99 func path(withSlash: Bool) -> String { 100 let path: String 101 if #available(iOS 16.4, *) { 102 path = self.path(percentEncoded: false) 103 } else { 104 path = self.path 105 } 106 if let char = path.last { 107 let slash: String.Element = "/" 108 109 if withSlash { 110 if char != slash { 111 return path + String(slash) 112 } 113 } else { 114 if char == slash { 115 return String(path.dropLast()) 116 } 117 } 118 } 119 return path 120 } 121 }