PQCommonMethodUtil.swift 35 KB

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