PQCommonMethodUtil.swift 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944
  1. //
  2. // PQCommonMethodUtil.swift
  3. // PQSpeed
  4. //
  5. // Created by lieyunye on 2020/5/29.
  6. // Copyright © 2020 BytesFlow. All rights reserved.
  7. //
  8. import AdSupport
  9. import Alamofire
  10. import Foundation
  11. import KeychainAccess
  12. import Kingfisher
  13. import KingfisherWebP
  14. import Photos
  15. import Toast_Swift
  16. // import RealmSwift
  17. import UIKit
  18. /// Home文件地址
  19. public let homeDirectory = NSHomeDirectory()
  20. /// docdocumens文件地址
  21. public let documensDirectory = homeDirectory + "/Documents"
  22. /// library文件地址
  23. public let libraryDirectory = homeDirectory + "/Library"
  24. /// 本地存储资源地址
  25. public let resourceDirectory = documensDirectory + "/Resource"
  26. /// 播放视频缓冲本地沙河目录
  27. public let videoCacheDirectory = resourceDirectory + "/VideoCache"
  28. /// 相册视频导出到本地沙河目录
  29. public let photoLibraryDirectory = resourceDirectory + "/PhotoLibrary/"
  30. /// 背景音乐导出到本地沙河目录
  31. public let bgMusicDirectory = resourceDirectory + "/BGMusic/"
  32. /// 网络视频素材下载到本地沙河目录
  33. public let downloadDirectory = resourceDirectory + "/Download/"
  34. /// 网络图片、GIF 素材下载到本地沙河目录
  35. public let downloadImagesDirectory = resourceDirectory + "/DownloadImages/"
  36. /// 临时缓存本地沙河目录地址
  37. public let tempDirectory = resourceDirectory + "/Temp/"
  38. /// 导出声音的本地沙盒目录v
  39. public let exportAudiosDirectory = resourceDirectory + "/ExportAudios/"
  40. /// 录制视频生成目录
  41. public let recordVideosDirectory = resourceDirectory + "/RecordVideos/"
  42. /// 导出合成视频的本地沙盒目录
  43. public let exportVideosDirectory = resourceDirectory + "/ExportVideos/"
  44. // 版本构建号
  45. public let versionCode = "\(Bundle.main.infoDictionary?["CFBundleVersion"] ?? "1")"
  46. // 版本号
  47. public let versionName = "\(Bundle.main.infoDictionary?["CFBundleShortVersionString"] ?? "1.0.0")"
  48. /// 创建目录文件
  49. /// - Returns: <#description#>
  50. public func createDirectory(path: String) {
  51. let fileManager = FileManager.default
  52. if !fileManager.fileExists(atPath: path) {
  53. try? fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
  54. }
  55. }
  56. /// 判断文件夹是否存在
  57. /// - Parameter dicPath:文件夹 目录
  58. public func directoryIsExists(dicPath: String) -> Bool {
  59. BFLog(message: " dir path is: \(dicPath)")
  60. var directoryExists = ObjCBool(false)
  61. let fileExists = FileManager.default.fileExists(atPath: dicPath, isDirectory: &directoryExists)
  62. return fileExists && directoryExists.boolValue
  63. }
  64. /// 判断文件是否存在
  65. /// - Parameter filepath: 文件目录
  66. public func fileIsExists(filePath: String) -> Bool {
  67. BFLog(message: "file path is: \(filePath)")
  68. let fileExists = FileManager.default.fileExists(atPath: filePath)
  69. return fileExists
  70. }
  71. /// 创建沙河文件地址
  72. /// - Parameter url: 原地址
  73. /// - Returns: <#description#>
  74. public func createFilePath(url: String) -> Bool {
  75. let fileManager = FileManager.default
  76. if !fileManager.fileExists(atPath: url) {
  77. let isFinished = fileManager.createFile(atPath: url, contents: nil, attributes: nil)
  78. return isFinished
  79. }
  80. return true
  81. }
  82. public func bf_getRootViewController() -> UIViewController? {
  83. guard let window = UIApplication.shared.delegate?.window else {
  84. return nil
  85. }
  86. return window!.rootViewController
  87. }
  88. public func bf_getCurrentViewController() -> UIViewController? {
  89. var currentVC = bf_getRootViewController()
  90. if currentVC == nil {
  91. return nil
  92. }
  93. let runloopFind = true
  94. while runloopFind {
  95. if let vc = currentVC!.presentedViewController {
  96. currentVC = vc
  97. } else {
  98. if currentVC is UINavigationController {
  99. currentVC = (currentVC as! UINavigationController).visibleViewController
  100. } else if currentVC is UITabBarController {
  101. currentVC = (currentVC as! UITabBarController).selectedViewController
  102. } else {
  103. break
  104. }
  105. }
  106. }
  107. return currentVC
  108. }
  109. /** 打印
  110. type = 1 : 胡志强
  111. type = 2 :王成
  112. type = 3 : 文伟伟
  113. */
  114. public func BFLog<T>(_ type: Int = 0, _ file: String = #file, _ line: Int = #line, message: T) {
  115. let filePath = (file as NSString).lastPathComponent;
  116. let dateFmt = DateFormatter()
  117. dateFmt.dateFormat = "HH:mm:ss:SSSS"
  118. let msg = "\(filePath) (L:\(line)) \(message)"
  119. // if type == 0{
  120. //// BuglyLog.level(.warn, logs: msg)
  121. print("\(dateFmt.string(from: Date())) \(msg)")
  122. // }
  123. #if DEBUG
  124. if type == 1 {
  125. print("hhz-\(dateFmt.string(from: Date())) \(msg)");
  126. }else if type == 2 {
  127. print("ak-\(dateFmt.string(from: Date())) \(msg)");
  128. }else if type == 3 {
  129. print("ww-\(dateFmt.string(from: Date())) \(msg)");
  130. }
  131. //
  132. #endif
  133. }
  134. // MARK: 获取公共参数
  135. public func commonParams() -> [String: Any] {
  136. let model = UIDevice.current.model
  137. let systemName = UIDevice.current.systemName
  138. let systemVersion = UIDevice.current.systemVersion
  139. let localizedModel = UIDevice.current.localizedModel
  140. let machineInfo: [String: Any] = [
  141. "model": model, "system": systemName + " " + systemVersion, "brand": localizedModel, "platform": "iOS", "networkType": networkStatus(), "clientIp": ipAddress(),
  142. ]
  143. var commParams: [String: Any] = [
  144. "appVersionCode": versionCode,
  145. "versionCode": versionCode,
  146. "system": systemName + " " + systemVersion,
  147. "systemVersion": systemName + " " + systemVersion,
  148. "appType": BFConfig.shared.appType,
  149. "appId": BFConfig.shared.appId,
  150. "machineCode": getMachineCode(),
  151. "networkType": networkStatus(),
  152. "ipAddress": ipAddress(),
  153. "clientTimestamp": Int64(Date().timeIntervalSince1970 * 1000),
  154. "platform": "iOS",
  155. "versionName": versionName,
  156. "mid": getMachineCode(),
  157. "machineInfo": dictionaryToJsonString(machineInfo) ?? "",
  158. "requestId": getUniqueId(desc: "requestId"),
  159. "idfa": ASIdentifierManager.shared().advertisingIdentifier.uuidString,
  160. "idfv": UIDevice.current.identifierForVendor?.uuidString ?? "",
  161. "sessionId": BFConfig.shared.sessionId,
  162. "subSessionId": BFConfig.shared.subSessionId ?? BFConfig.shared.sessionId,
  163. ]
  164. if BFConfig.shared.token != nil, (BFConfig.shared.token?.count ?? 0) > 0 {
  165. commParams["token"] = BFConfig.shared.token ?? ""
  166. }
  167. if BFConfig.shared.loginUid != nil, (BFConfig.shared.loginUid?.count ?? 0) > 0 {
  168. commParams["loginUid"] = BFConfig.shared.loginUid ?? ""
  169. commParams["uid"] = BFConfig.shared.loginUid ?? ""
  170. }
  171. if BFConfig.shared.deviceToken != nil, (BFConfig.shared.deviceToken?.count ?? 0) > 0 {
  172. commParams["deviceToken"] = BFConfig.shared.deviceToken ?? ""
  173. }
  174. return commParams
  175. }
  176. /// 获取网络状态
  177. /// - Returns: <#description#>
  178. public func networkStatus() -> String {
  179. let status = NetworkReachabilityManager(host: "www.baidu.com")?.status
  180. var statusStr: String!
  181. switch status {
  182. case .unknown:
  183. statusStr = "NETWORK_UNKNOWN"
  184. case .notReachable:
  185. statusStr = "NETWORK_NO"
  186. case .reachable(.cellular):
  187. statusStr = "4G/5G"
  188. case .reachable(.ethernetOrWiFi):
  189. statusStr = "Wi-Fi"
  190. default:
  191. statusStr = "NETWORK_UNKNOWN"
  192. }
  193. return statusStr
  194. }
  195. /// 判断是否有网
  196. /// - Returns: <#description#>
  197. public func isNetConnected() -> Bool {
  198. return NetworkReachabilityManager(host: "www.baidu.com")?.status == .reachable(.cellular) || NetworkReachabilityManager(host: "www.baidu.com")?.status == .reachable(.ethernetOrWiFi)
  199. }
  200. /// 获取ip地址
  201. /// - Returns: <#description#>
  202. public func ipAddress() -> String {
  203. var addresses = [String]()
  204. var ifaddr: UnsafeMutablePointer<ifaddrs>?
  205. if getifaddrs(&ifaddr) == 0 {
  206. var ptr = ifaddr
  207. while ptr != nil {
  208. let flags = Int32(ptr!.pointee.ifa_flags)
  209. var addr = ptr!.pointee.ifa_addr.pointee
  210. if (flags & (IFF_UP | IFF_RUNNING | IFF_LOOPBACK)) == (IFF_UP | IFF_RUNNING) {
  211. if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {
  212. var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
  213. if getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0 {
  214. if let address = String(validatingUTF8: hostname) {
  215. addresses.append(address)
  216. }
  217. }
  218. }
  219. }
  220. ptr = ptr!.pointee.ifa_next
  221. }
  222. freeifaddrs(ifaddr)
  223. }
  224. return addresses.first ?? "0.0.0.0"
  225. }
  226. /// 生成唯一ID / 分享跟冷启动
  227. /// - Parameter desc: <#desc description#>
  228. /// - Returns: <#description#>
  229. public func getUniqueId(desc: String) -> String {
  230. let timeStr: String = "\(Date().timeIntervalSince1970)"
  231. let uuid: String = getMachineCode()
  232. let code: String = "\(arc4random_uniform(1_000_000_000))"
  233. let uniqueId = (timeStr + desc + uuid + code).md5.md5
  234. BFLog(message: "生成唯一码:desc = \(desc),timeStr = \(timeStr),uuid = \(uuid),code = \(code),uniqueId = \(uniqueId)")
  235. return uniqueId
  236. }
  237. // MARK: 字典转字符串
  238. public func dictionaryToJsonString(_ dic: [String: Any]) -> String? {
  239. BFLog(message: "dictionaryToJsonString = \(dic)")
  240. if !JSONSerialization.isValidJSONObject(dic) {
  241. return ""
  242. }
  243. guard let data = try? JSONSerialization.data(withJSONObject: dic, options: []) else {
  244. return ""
  245. }
  246. BFLog(message: "dictionaryToJsonString - data = \(data)")
  247. let str = String(data: data, encoding: String.Encoding.utf8)
  248. BFLog(message: "dictionaryToJsonString - str = \(String(describing: str))")
  249. return str
  250. }
  251. // MARK: 字符串转字典
  252. public func jsonStringToDictionary(_ str: String) -> [String: Any]? {
  253. if str.count <= 0 {
  254. return [:]
  255. }
  256. let data = str.data(using: String.Encoding.utf8)
  257. if data == nil || (data?.count ?? 0) <= 0 {
  258. return [:]
  259. }
  260. if let dict = try? JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any] {
  261. return dict
  262. }
  263. return [:]
  264. }
  265. // MARK: 字符串转数组
  266. public func jsonStringToArray(_ str: String) -> [[String: String]]? {
  267. let data = str.data(using: String.Encoding.utf8)
  268. if let array = try? JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? [[String: String]] {
  269. return array
  270. }
  271. return nil
  272. }
  273. /// 数组转为string
  274. /// - Parameter array: <#array description#>
  275. /// - Returns: <#description#>
  276. public func arrayToJsonString(_ array: [Any]) -> String {
  277. if !JSONSerialization.isValidJSONObject(array) {
  278. BFLog(message: "无法解析String")
  279. return ""
  280. }
  281. let data: NSData! = try? JSONSerialization.data(withJSONObject: array, options: []) as NSData?
  282. let JSONString = NSString(data: data as Data, encoding: String.Encoding.utf8.rawValue)
  283. return JSONString! as String
  284. }
  285. /// jsonString转为数组
  286. /// - Parameter jsonString: <#jsonString description#>
  287. /// - Returns: <#description#>
  288. public func jsonStringToArray(jsonString: String) -> [Any]? {
  289. let data = jsonString.data(using: String.Encoding.utf8)
  290. if data == nil {
  291. return nil
  292. }
  293. if let array = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [Any] {
  294. return array
  295. }
  296. return nil
  297. }
  298. /// 计算字符串大小
  299. /// - Parameters:
  300. /// - text: <#text description#>
  301. /// - font: <#font description#>
  302. /// - size: <#size description#>
  303. /// - Returns: <#description#>
  304. public func sizeWithText(attributedText: NSMutableAttributedString? = nil, text: String, font: UIFont, size: CGSize) -> CGSize {
  305. let option = NSStringDrawingOptions.usesLineFragmentOrigin
  306. if attributedText != nil {
  307. let rect: CGRect = attributedText?.boundingRect(with: size, options: option, context: nil) ?? CGRect(origin: CGPoint.zero, size: size)
  308. return rect.size
  309. } else {
  310. let attributes = [NSAttributedString.Key.font: font]
  311. let rect: CGRect = text.boundingRect(with: size, options: option, attributes: attributes, context: nil)
  312. return rect.size
  313. }
  314. }
  315. /// 根据行数计算字符串大小
  316. /// - Parameters:
  317. /// - text: <#text description#>
  318. /// - numberOfLines: <#numberOfLines description#>
  319. /// - font: <#font description#>
  320. /// - maxSize: <#maxSize description#>
  321. /// - Returns: <#description#>
  322. public func sizeTextFits(attributedText: NSMutableAttributedString?, text: String?, numberOfLines: Int, font: UIFont, maxSize: CGSize) -> CGSize {
  323. var newSize: CGSize = CGSize(width: 0, height: 0)
  324. let label = UILabel(frame: CGRect.zero)
  325. label.font = font
  326. label.numberOfLines = numberOfLines
  327. if attributedText != nil {
  328. label.attributedText = attributedText
  329. } else {
  330. label.text = text
  331. }
  332. newSize = label.sizeThatFits(maxSize)
  333. return newSize
  334. }
  335. public func textNumberOfLines(text: String, font: UIFont, maxSize _: CGSize) -> Int {
  336. let label = UILabel(frame: CGRect.zero)
  337. label.font = font
  338. label.numberOfLines = 0
  339. label.text = text
  340. return label.numberOfLines
  341. }
  342. /// 生成渐变色
  343. /// - Parameters:
  344. /// - size: <#size description#>
  345. /// - endPoint: <#endPoint description#>
  346. /// - startColor: <#startColor description#>
  347. /// - endColor: <#endColor description#>
  348. /// - Returns: <#description#>
  349. public func gradientColor(size: CGSize, endPoint: CGPoint, startColor: UIColor, endColor: UIColor) -> UIColor {
  350. let gradientLayer = CAGradientLayer()
  351. gradientLayer.frame = CGRect(origin: CGPoint(), size: size)
  352. gradientLayer.startPoint = CGPoint.zero
  353. gradientLayer.endPoint = endPoint
  354. gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
  355. UIGraphicsBeginImageContext(size)
  356. gradientLayer.render(in: UIGraphicsGetCurrentContext()!)
  357. let image = UIGraphicsGetImageFromCurrentImageContext()!
  358. return UIColor(patternImage: image)
  359. }
  360. /// 获取设备ID
  361. /// - Returns: <#description#>
  362. public func getMachineCode() -> String {
  363. let userInfo: [String: Any]? = jsonStringToDictionary(UserDefaults.standard.string(forKey: cUserInfoStorageKey) ?? "")
  364. if userInfo != nil && ((userInfo?.keys.contains("isVirtualUser") ?? false) && !(userInfo?["isVirtualUser"] is NSNull) && ((userInfo?["isVirtualUser"] as? Bool) ?? false)) && ((userInfo?.keys.contains("mid") ?? false) && !(userInfo?["mid"] is NSNull)) {
  365. BFLog(message: "虚拟账号mid:\("\(userInfo?["mid"] ?? "")")")
  366. return "\(userInfo?["mid"] ?? "")"
  367. }
  368. let keychain = Keychain(service: "com.piaoquan.pqspeed")
  369. var uuid: String = keychain["machineCode"] ?? ""
  370. if uuid.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
  371. uuid = NSUUID().uuidString
  372. keychain["machineCode"] = uuid
  373. }
  374. BFLog(message: "正式账号mid:\(uuid)")
  375. return uuid
  376. }
  377. /// 显示加载中视图
  378. /// - Parameters:
  379. /// - superView: <#superView description#>
  380. /// - msg: <#msg description#>
  381. /// - Returns: <#description#>
  382. public func cShowHUB(superView: UIView?, msg: String?) {
  383. var sty: ToastStyle = ToastManager.shared.style
  384. ToastManager.shared.isQueueEnabled = true
  385. sty.messageAlignment = .center
  386. DispatchQueue.main.async {
  387. if superView == nil {
  388. if msg == nil {
  389. UIApplication.shared.keyWindow?.makeToastActivity(.center)
  390. } else {
  391. UIApplication.shared.keyWindow?.makeToast(msg, duration: 3.0, position: .center, style: sty)
  392. }
  393. } else {
  394. if msg == nil {
  395. superView!.makeToastActivity(.center)
  396. } else {
  397. superView!.makeToast(msg, duration: 3.0, position: .center, style: sty)
  398. }
  399. }
  400. }
  401. }
  402. /// 隐藏加载中视图
  403. /// - Parameter superView: <#superView description#>
  404. /// - Returns: <#description#>
  405. public func cHiddenHUB(superView: UIView?) {
  406. DispatchQueue.main.async {
  407. if superView == nil {
  408. UIApplication.shared.keyWindow?.hideAllToasts()
  409. UIApplication.shared.keyWindow?.hideToastActivity()
  410. } else {
  411. superView!.hideAllToasts()
  412. superView?.hideToastActivity()
  413. }
  414. }
  415. }
  416. /// 获取存储值
  417. /// - Parameter key: key description
  418. /// - Returns: description
  419. public func getUserDefaults(key: String) -> Any? {
  420. return UserDefaults.standard.object(forKey: key)
  421. }
  422. /// 存储数据
  423. /// - Parameters:
  424. /// - key: key description
  425. /// - value: value description
  426. /// - Returns: description
  427. public func saveUserDefaults(key: String, value: String) {
  428. UserDefaults.standard.set(value, forKey: key)
  429. UserDefaults.standard.synchronize()
  430. }
  431. /// 存储数据带版本号
  432. /// - Parameters:
  433. /// - key: <#key description#>
  434. /// - value: <#value description#>
  435. public func saveUserDefaultsToJson(key: String, value: Any) {
  436. UserDefaults.standard.set(dictionaryToJsonString([key: value, "appVersionCode": versionCode, "versionName": versionName]), forKey: key)
  437. UserDefaults.standard.synchronize()
  438. }
  439. /// 获取数据带版本号
  440. /// - Parameter key: <#key description#>
  441. /// - Returns: <#description#>
  442. public func getUserDefaultsForJson(key: String) -> Any? {
  443. let jsonStr = UserDefaults.standard.object(forKey: key)
  444. if jsonStr != nil {
  445. return jsonStringToDictionary(jsonStr as! String)?[key]
  446. }
  447. return UserDefaults.standard.object(forKey: key)
  448. }
  449. /// 清空数据
  450. /// - Parameters:
  451. /// - key: key description
  452. /// - value: value description
  453. /// - Returns: description
  454. public func removeUserDefaults(key: String) {
  455. UserDefaults.standard.removeObject(forKey: key)
  456. UserDefaults.standard.synchronize()
  457. }
  458. /// 存储数据
  459. /// - Parameters:
  460. /// - key: key description
  461. /// - value: value description
  462. /// - Returns: description
  463. public func saveUserDefaults(key: String, value: Any) {
  464. UserDefaults.standard.set(value, forKey: key)
  465. UserDefaults.standard.synchronize()
  466. }
  467. /// 保存自定义model as NSArray 当 OBJ 是数组时不能使用 Array 要使用 NSArray
  468. /// - Parameter object: <#object description#>
  469. /// - Parameter key: <#key description#>
  470. public func saveCustomObject(customObject object: NSCoding, key: String) {
  471. let encodedObject = NSKeyedArchiver.archivedData(withRootObject: object)
  472. UserDefaults.standard.set(encodedObject, forKey: key)
  473. UserDefaults.standard.synchronize()
  474. BFLog(message: "保存自定义类成功 key is \(key) \(encodedObject.count)")
  475. }
  476. /// 取自定义model
  477. /// - Parameter key: <#key description#>
  478. public func getCustomObject(forKey key: String) -> AnyObject? {
  479. let decodedObject = UserDefaults.standard.object(forKey: key) as? Data
  480. if decodedObject == nil {
  481. BFLog(message: "key is \(key) decodedObject is nil")
  482. }
  483. if let decoded = decodedObject {
  484. let object = NSKeyedUnarchiver.unarchiveObject(with: decoded as Data)
  485. return object as AnyObject?
  486. }
  487. return nil
  488. }
  489. /// 添加通知
  490. /// - Parameters:
  491. /// - observer: <#observer description#>
  492. /// - aSelectorName: <#aSelectorName description#>
  493. /// - aName: <#aName description#>
  494. /// - anObject: <#anObject description#>
  495. /// - Returns: <#description#>
  496. public func addNotification(_ observer: Any, selector aSelectorName: Selector, name aName: String, object anObject: Any?) {
  497. PQNotification.addObserver(observer, selector: aSelectorName, name: NSNotification.Name(rawValue: aName), object: anObject)
  498. }
  499. /// 发送通知
  500. /// - Parameter aName: <#aName description#>
  501. /// - Returns: <#description#>
  502. public func postNotification(name aName: String, userInfo: [AnyHashable: Any]? = nil) {
  503. PQNotification.post(name: NSNotification.Name(aName), object: nil, userInfo: userInfo)
  504. }
  505. /// 获取是否打开推送
  506. /// - Parameter completeHander: <#completeHander description#>
  507. /// - Returns: <#description#>
  508. public func pushNotificationIsOpen(completeHander: ((_ isOpen: Bool) -> Void)?) {
  509. if #available(iOS 10.0, *) {
  510. UNUserNotificationCenter.current().getNotificationSettings { setttings in
  511. completeHander!(setttings.authorizationStatus == .authorized)
  512. }
  513. } else {
  514. completeHander!(UIApplication.shared.currentUserNotificationSettings?.types.contains(UIUserNotificationType.alert) ?? false)
  515. }
  516. }
  517. /// 发送上传本地推送
  518. /// - Parameter isSuccess: 是否上传成功
  519. /// - Returns: <#description#>
  520. public func sendUploadNotification(isSuccess: Bool) {
  521. let title: String = isSuccess ? "上传完成了!" : "上传失败了!"
  522. let body: String = isSuccess ? "请点击发布,完成上传。否则,您的视频可能丢失" : "快来看看怎么了?"
  523. sendLocalNotification(title: title, body: body)
  524. }
  525. /// 发送本地推送
  526. /// - Parameters:
  527. /// - title: 标题
  528. /// - body: 内容
  529. /// - Returns: <#description#>
  530. public func sendLocalNotification(title: String, body: String) {
  531. // 设置推送内容
  532. if #available(iOS 10.0, *) {
  533. let content = UNMutableNotificationContent()
  534. content.title = title
  535. content.body = body
  536. content.badge = 1
  537. // 设置通知触发器
  538. let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
  539. // 设置请求标识符
  540. let requestIdentifier = getUniqueId(desc: "notification\(title)")
  541. // 设置一个通知请求
  542. let request = UNNotificationRequest(identifier: requestIdentifier,
  543. content: content, trigger: trigger)
  544. // 将通知请求添加到发送中心
  545. UNUserNotificationCenter.current().add(request) { error in
  546. if error == nil {
  547. print("Time Interval Notification scheduled: \(requestIdentifier)")
  548. }
  549. }
  550. } else {
  551. // Fallback on earlier versions
  552. let notification = UILocalNotification()
  553. notification.alertBody = body
  554. notification.alertTitle = title
  555. notification.applicationIconBadgeNumber = 1
  556. notification.fireDate = Date(timeIntervalSinceNow: 0)
  557. UIApplication.shared.scheduledLocalNotifications = [notification]
  558. }
  559. }
  560. /// 打开应用设置
  561. public func openAppSetting() {
  562. if UIApplication.shared.canOpenURL(URL(string: UIApplication.openSettingsURLString)!) {
  563. UIApplication.shared.openURL(URL(string: UIApplication.openSettingsURLString)!)
  564. }
  565. }
  566. /// dns解析
  567. /// - Parameter hostUrl: speed.piaoquantv.com /
  568. /// - Returns: <#description#>
  569. public func parseDNS(hostUrl: String) -> [String: Any]? {
  570. let host: CFHost? = CFHostCreateWithName(nil, hostUrl as CFString).takeRetainedValue()
  571. let start = CFAbsoluteTimeGetCurrent()
  572. var success: DarwinBoolean = false
  573. var addressList: [String] = Array<String>.init()
  574. var addresses: NSArray?
  575. if CFHostStartInfoResolution(host!, .addresses, nil) {
  576. addresses = (CFHostGetAddressing(host!, &success)?.takeUnretainedValue())
  577. }
  578. if success == true {
  579. for case let theAddress as NSData in addresses! {
  580. var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
  581. if getnameinfo(theAddress.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(theAddress.length),
  582. &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0
  583. {
  584. let numAddress = String(cString: hostname)
  585. addressList.append("\(hostUrl)/\(numAddress)")
  586. }
  587. }
  588. }
  589. let end = CFAbsoluteTimeGetCurrent()
  590. let duration = end - start
  591. BFLog(message: "duration = \(duration)")
  592. BFLog(message: "addressList = \(addressList)")
  593. if addressList.count > 0 {
  594. return ["dnsResult": arrayToJsonString(addressList), "duration": duration * 1000, "hostName": hostUrl, "networkType": networkStatus()]
  595. } else {
  596. return nil
  597. }
  598. }
  599. /// 获取当前日期
  600. /// - Returns: <#description#>
  601. public func systemCurrentDate() -> String {
  602. let dateFormatter = DateFormatter()
  603. dateFormatter.dateFormat = "YYYY-MM-dd"
  604. return dateFormatter.string(from: Date())
  605. }
  606. /// 时间戳转日期
  607. /// - Parameter timeInterval: <#timeInterval description#>
  608. /// - Returns: <#description#>
  609. public func timeIntervalToDateString(timeInterval: TimeInterval) -> String {
  610. let date = Date(timeIntervalSince1970: timeInterval)
  611. let dateFormatter = DateFormatter()
  612. dateFormatter.dateFormat = "yyyy年MM月dd日"
  613. return dateFormatter.string(from: date)
  614. }
  615. public func updateTimeToCurrenTime(timeInterval: TimeInterval) -> String {
  616. // 获取当前的时间戳
  617. let currentTime = Date().timeIntervalSince1970
  618. // print(currentTime, timeInterval, "sdsss")
  619. // 时间戳为毫秒级要 / 1000, 秒就不用除1000,参数带没带000
  620. // let timeSta:TimeInterval = TimeInterval(timeInterval / 1000)
  621. // 时间差
  622. let reduceTime: TimeInterval = currentTime - timeInterval
  623. // 时间差小于60秒
  624. if reduceTime < 60 {
  625. return "刚刚"
  626. }
  627. // 时间差大于一分钟小于60分钟内
  628. let mins = Int(reduceTime / 60)
  629. if mins < 60 {
  630. return "\(mins)分钟前"
  631. }
  632. let hours = Int(reduceTime / 3600)
  633. if hours < 24 {
  634. return "\(hours)小时前"
  635. }
  636. // let days = Int(reduceTime / 3600 / 24)
  637. // if days < 30 {
  638. // return "\(days)天前"
  639. // }
  640. // 不满足上述条件---或者是未来日期-----直接返回日期
  641. let date = NSDate(timeIntervalSince1970: timeInterval)
  642. let dfmatter = DateFormatter()
  643. // yyyy-MM-dd HH:mm:ss
  644. dfmatter.dateFormat = "yyyy年M月d日 HH:mm"
  645. var dfmatterStr = dfmatter.string(from: date as Date)
  646. let currentDF = DateFormatter()
  647. // yyyy-MM-dd HH:mm:ss
  648. currentDF.dateFormat = "yyyy"
  649. let currentDFStr = currentDF.string(from: Date())
  650. if dfmatterStr.hasPrefix(currentDFStr) {
  651. dfmatterStr.removeFirst(currentDFStr.count + 1)
  652. }
  653. return dfmatterStr
  654. }
  655. /// 判断字符串或者字典是否为空
  656. /// - Parameter object: <#object description#>
  657. /// - Returns: <#description#>
  658. public func isEmpty(object: Any?) -> Bool {
  659. if object == nil {
  660. return true
  661. }
  662. if object is String {
  663. return (object as! String).count <= 0
  664. }
  665. if object is [String: Any] {
  666. return (object as! [String: Any]).keys.count <= 0
  667. }
  668. return false
  669. }
  670. public func isEmptyObject(object: Any?) -> Bool {
  671. if object == nil {
  672. return true
  673. }
  674. if object is String {
  675. return object == nil || ((object as? String)?.count ?? 0) <= 0
  676. }
  677. if object is [String: Any] {
  678. return object == nil || ((object as? [String: Any])?.keys.count ?? 0) <= 0
  679. }
  680. // if object is List<Object> {
  681. // return object == nil || ((object as? List<Object>)?.count ?? 0) <= 0
  682. // }
  683. return false
  684. }
  685. /// <#Description#>
  686. /// - Parameter string: <#string description#>
  687. /// - Returns: <#description#>
  688. public func isIncludeChineseIn(string: String) -> Bool {
  689. for (_, value) in string.enumerated() {
  690. if value >= "\u{4E00}", value <= "\u{9FA5}" {
  691. return true
  692. }
  693. }
  694. return false
  695. }
  696. /// 获取文件内容的MD5
  697. /// - Parameters:
  698. /// - path: 地址
  699. /// - data: data
  700. /// - Returns: <#description#>
  701. public func contentMD5(path: String? = nil, data _: Data? = nil) -> String? {
  702. if path == nil || (path?.count ?? 0) <= 0 || !FileManager.default.fileExists(atPath: path ?? "") {
  703. BFLog(message: "生成内容md5值:地址错误或者不存在\(String(describing: path))")
  704. return ""
  705. }
  706. let att = try? FileManager.default.attributesOfItem(atPath: path ?? "")
  707. let size = Int64(att?[FileAttributeKey.size] as! UInt64)
  708. if size <= 0 {
  709. BFLog(message: "生成内容md5值:文件大小为0\(size)")
  710. return ""
  711. }
  712. let hash: String = PQBridgeObject.base64Md5(forFilePath: path ?? "")
  713. BFLog(message: "生成内容md5值:contentMD5 = \(hash)")
  714. return hash
  715. }
  716. /// 检测URL
  717. /// - Parameter url: <#url description#>
  718. /// - Returns: <#description#>
  719. public func isValidURL(url: String?) -> Bool {
  720. if url == nil || (url?.count ?? 0) <= 4 || (!(url?.hasPrefix("http") ?? false) && !(url?.hasPrefix("https") ?? false)) {
  721. return false
  722. }
  723. return true
  724. }
  725. /// 相册数据按创建时间排序
  726. public var creaFetchOptions: PHFetchOptions = {
  727. let fetchOptions = PHFetchOptions()
  728. fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
  729. return fetchOptions
  730. }()
  731. /// 相册数据按修改时间排序
  732. public var modiFetchOptions: PHFetchOptions = {
  733. let fetchOptions = PHFetchOptions()
  734. fetchOptions.sortDescriptors = [NSSortDescriptor(key: "modificationDate", ascending: false)]
  735. return fetchOptions
  736. }()
  737. /// 获取本地素材
  738. public var avAssertOptions: [String: Any]? = {
  739. [AVURLAssetPreferPreciseDurationAndTimingKey: NSNumber(value: true)]
  740. }()
  741. /// 播放动画图
  742. public var playGifImages: [UIImage] = {
  743. var gifImages = Array<UIImage>.init()
  744. for i in 0...44 {
  745. gifImages.append(UIImage(named: "\(i).png")!)
  746. }
  747. return gifImages
  748. }()
  749. /// 压缩图片
  750. /// - Parameter image: <#image description#>
  751. /// -
  752. /// - Returns: <#description#>
  753. public func zipImage(image: UIImage?, size: Int) -> Data? {
  754. var data = image?.pngData()
  755. var dataKBytes = Int(data?.count ?? 0) / 1000
  756. var maxQuality = 0.9
  757. while dataKBytes > size, maxQuality > 0.01 {
  758. maxQuality = maxQuality - 0.01
  759. data = image?.jpegData(compressionQuality: CGFloat(maxQuality))
  760. dataKBytes = (data?.count ?? 0) / 1000
  761. }
  762. return data
  763. }
  764. /// 压缩图片到指定大小
  765. /// - Parameters:
  766. /// - image: <#image description#>
  767. /// - maxLength: <#maxLength description#>
  768. /// - cyles: <#cyles description#>
  769. /// - Returns: <#description#>
  770. public func zipImageQuality(image: UIImage, maxLength: NSInteger, cyles: Int = 6) -> Data {
  771. var compression: CGFloat = 1
  772. var data = image.jpegData(compressionQuality: compression)!
  773. if data.count < maxLength {
  774. return data
  775. }
  776. var max: CGFloat = 1
  777. var min: CGFloat = 0
  778. var bestData: Data = data
  779. for _ in 0..<cyles {
  780. compression = (max + min) / 2
  781. data = image.jpegData(compressionQuality: compression)!
  782. if Double(data.count) < Double(maxLength) * 0.9 {
  783. min = compression
  784. bestData = data
  785. } else if data.count > maxLength {
  786. max = compression
  787. } else {
  788. bestData = data
  789. break
  790. }
  791. }
  792. return bestData
  793. }
  794. public func resetImgSize(sourceImage: UIImage, maxImageLenght: CGFloat, maxSizeKB: CGFloat) -> Data {
  795. var maxSize = maxSizeKB
  796. var maxImageSize = maxImageLenght
  797. if maxSize <= 0.0 {
  798. maxSize = 1024.0
  799. }
  800. if maxImageSize <= 0.0 {
  801. maxImageSize = 1024.0
  802. }
  803. // 先调整分辨率
  804. var newSize = CGSize(width: sourceImage.size.width, height: sourceImage.size.height)
  805. let tempHeight = newSize.height / maxImageSize
  806. let tempWidth = newSize.width / maxImageSize
  807. if tempWidth > 1.0, tempWidth > tempHeight {
  808. newSize = CGSize(width: sourceImage.size.width / tempWidth, height: sourceImage.size.height / tempWidth)
  809. } else if tempHeight > 1.0, tempWidth < tempHeight {
  810. newSize = CGSize(width: sourceImage.size.width / tempHeight, height: sourceImage.size.height / tempHeight)
  811. }
  812. UIGraphicsBeginImageContext(newSize)
  813. sourceImage.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
  814. let newImage = UIGraphicsGetImageFromCurrentImageContext()
  815. UIGraphicsEndImageContext()
  816. var imageData = newImage!.jpegData(compressionQuality: 1.0)
  817. var sizeOriginKB: CGFloat = CGFloat((imageData?.count)!) / 1024.0
  818. // 调整大小
  819. var resizeRate = 0.9
  820. while sizeOriginKB > maxSize, resizeRate > 0.1 {
  821. imageData = newImage!.jpegData(compressionQuality: CGFloat(resizeRate))
  822. sizeOriginKB = CGFloat((imageData?.count)!) / 1024.0
  823. resizeRate -= 0.1
  824. }
  825. return imageData!
  826. }
  827. /// 获取开屏广告图
  828. /// - Returns: <#description#>
  829. public func getLaunchImage() -> UIImage {
  830. var lauchImg: UIImage!
  831. var viewOrientation: String!
  832. let viewSize = UIScreen.main.bounds.size
  833. let orientation = UIApplication.shared.statusBarOrientation
  834. if orientation == .landscapeLeft || orientation == .landscapeRight {
  835. viewOrientation = "Landscape"
  836. } else {
  837. viewOrientation = "Portrait"
  838. }
  839. let imgsInfoArray = Bundle.main.infoDictionary!["UILaunchImages"]
  840. for dict: [String: String] in imgsInfoArray as! Array {
  841. let imageSize = NSCoder.cgSize(for: dict["UILaunchImageSize"]!)
  842. if __CGSizeEqualToSize(imageSize, viewSize), viewOrientation == dict["UILaunchImageOrientation"]! as String {
  843. lauchImg = UIImage(named: dict["UILaunchImageName"]!)
  844. }
  845. }
  846. return lauchImg
  847. }
  848. public func currentBundlePath() -> Bundle? {
  849. // var associateBundleURL = Bundle.main.url(forResource: "Frameworks", withExtension: nil)
  850. // associateBundleURL = associateBundleURL?.appendingPathComponent("BFFramework")
  851. // associateBundleURL = associateBundleURL?.appendingPathExtension("framework")
  852. //
  853. // if associateBundleURL == nil {
  854. // print("获取bundle失败")
  855. // return nil
  856. // }
  857. // let associateBunle = Bundle(url: associateBundleURL!)
  858. let associateBundleURL = Bundle.main.url(forResource: "BFCommonKit_Resources", withExtension: "bundle")
  859. if associateBundleURL == nil {
  860. return nil
  861. }
  862. return Bundle(url: associateBundleURL!)
  863. }
  864. public func synced(_ lock: Any, closure: () -> Void) {
  865. objc_sync_enter(lock)
  866. closure()
  867. objc_sync_exit(lock)
  868. }