PQCommonMethodUtil.swift 34 KB

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