// // PQCommonMethodUtil.swift // PQSpeed // // Created by lieyunye on 2020/5/29. // Copyright © 2020 BytesFlow. All rights reserved. // import AdSupport import Alamofire import Foundation import KeychainAccess import Kingfisher import KingfisherWebP import Photos import RealmSwift import Toast_Swift import NXFramework_Swift /// Home文件地址 public let homeDirectory = NSHomeDirectory() /// docdocumens文件地址 public let documensDirectory = homeDirectory + "/Documents" /// library文件地址 public let libraryDirectory = homeDirectory + "/Library" /// 本地存储资源地址 public let resourceDirectory = documensDirectory + "/Resource" /// 播放视频缓冲本地沙河目录 public let videoCacheDirectory = resourceDirectory + "/VideoCache" /// 相册视频导出到本地沙河目录 public let photoLibraryDirectory = resourceDirectory + "/PhotoLibrary/" /// 背景音乐导出到本地沙河目录 public let bgMusicDirectory = resourceDirectory + "/BGMusic/" /// 网络视频素材下载到本地沙河目录 public let downloadDirectory = resourceDirectory + "/Download/" /// 网络图片、GIF 素材下载到本地沙河目录 public let downloadImagesDirectory = resourceDirectory + "/DownloadImages/" /// 临时缓存本地沙河目录地址 public let tempDirectory = resourceDirectory + "/Temp/" /// 导出声音的本地沙盒目录v public let exportAudiosDirectory = resourceDirectory + "/ExportAudios/" /// 导出合成视频的本地沙盒目录 public let exportVideosDirectory = resourceDirectory + "/ExportVideos/" // 版本构建号 public let versionCode = "\(Bundle.main.infoDictionary?["CFBundleVersion"] ?? "1")" // 版本号 public let versionName = "\(Bundle.main.infoDictionary?["CFBundleShortVersionString"] ?? "1.0.0")" /// 创建目录文件 /// - Returns: <#description#> public func createDirectory(path: String) { let fileManager = FileManager.default if !fileManager.fileExists(atPath: path) { try? fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil) } } /// 判断文件夹是否存在 /// - Parameter dicPath:文件夹 目录 public func directoryIsExists(dicPath: String) -> Bool { BFLog(message: " dir path is: \(dicPath)") var directoryExists = ObjCBool(false) let fileExists = FileManager.default.fileExists(atPath: dicPath, isDirectory: &directoryExists) return fileExists && directoryExists.boolValue } /// 判断文件是否存在 /// - Parameter filepath: 文件目录 public func fileIsExists(filePath: String) -> Bool { BFLog(message: "file path is: \(filePath)") let fileExists = FileManager.default.fileExists(atPath: filePath) return fileExists } /// 创建沙河文件地址 /// - Parameter url: 原地址 /// - Returns: <#description#> public func createFilePath(url: String) -> Bool { let fileManager = FileManager.default if !fileManager.fileExists(atPath: url) { let isFinished = fileManager.createFile(atPath: url, contents: nil, attributes: nil) return isFinished } return true } public func cIPHONE_X() -> Bool { guard #available(iOS 11.0, *) else { return false } let isX = (UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0) > 0 return isX } /// 给按钮/imageView加载网络图片 /// /// - Parameters: /// - url: 网络url /// - mainView: 需要加载的视图 public func netImage(url: String, mainView: Any, placeholder: UIImage = UIImage.init().BF_Image(named: "placehold_image")) { if mainView is UIImageView { (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 }) { _, _, _, _ in } } else if mainView is UIButton { (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 }) { _, _, _, _ in } } } /** 获取Kingfisher缓存的图片的data */ public func kf_imageCacheData(originUrl: String) -> Data? { let diskCachePath = ImageCache.default.cachePath(forKey: originUrl) let data = try? Data(contentsOf: URL(fileURLWithPath: diskCachePath)) return data } /** 获取Kingfisher缓存的图片 */ public func kf_imageCacheImage(originUrl: String) -> UIImage? { return ImageCache.default.retrieveImageInDiskCache(forKey: originUrl, options: [.cacheOriginalImage]) } /** 打印 */ public func BFLog(message: T) { // let logger = NXLogger.shared // // logger.level = .info // logger.ouput = .debuggerConsole // // logger.d(message as? String ?? "") } // MARK: 获取公共参数 public func commonParams() -> [String: Any] { let model = UIDevice.current.model let systemName = UIDevice.current.systemName let systemVersion = UIDevice.current.systemVersion let localizedModel = UIDevice.current.localizedModel let machineInfo: [String: Any] = [ "model": model, "system": systemName + " " + systemVersion, "brand": localizedModel, "platform": "iOS", "networkType": networkStatus(), "clientIp": ipAddress(), ] var commParams: [String: Any] = [ "appVersionCode": versionCode, "versionCode": versionCode, "system": systemName + " " + systemVersion, "systemVersion": systemName + " " + systemVersion, "appType": "15", "appId": "1570572849", "machineCode": getMachineCode(), "networkType": networkStatus(), "ipAddress": ipAddress(), "clientTimestamp": Int64(Date().timeIntervalSince1970 * 1000), "platform": "iOS", "versionName": versionName, "sessionId": PQSingletoMemoryUtil.shared.sessionId, "subSessionId": PQSingletoMemoryUtil.shared.subSessionid ?? PQSingletoMemoryUtil.shared.sessionId, "mid": getMachineCode(), "machineInfo": dictionaryToJsonString(machineInfo) ?? "", "abInfoData": dictionaryToJsonString(PQSingletoMemoryUtil.shared.abInfoData) ?? "", "requestId": getUniqueId(desc: "requestId"), "idfa": ASIdentifierManager.shared().advertisingIdentifier.uuidString, "idfv": UIDevice.current.identifierForVendor?.uuidString ?? "", "deviceToken": PQSingletoMemoryUtil.shared.deviceToken, ] if BFLoginUserInfo.shared.accessToken.count > 0 { commParams["token"] = BFLoginUserInfo.shared.accessToken } if BFLoginUserInfo.shared.uid.count > 0 { commParams["loginUid"] = BFLoginUserInfo.shared.uid commParams["uid"] = BFLoginUserInfo.shared.uid } // showAlertVc(title: "公参", message: dictionaryToJsonString(commParams)) return commParams } /// 获取网络状态 /// - Returns: <#description#> public func networkStatus() -> String { let status = NetworkReachabilityManager(host: "www.baidu.com")?.networkReachabilityStatus var statusStr: String! switch status { case .unknown: statusStr = "NETWORK_UNKNOWN" case .notReachable: statusStr = "NETWORK_NO" case .reachable(.wwan): statusStr = "4G" case .reachable(.ethernetOrWiFi): statusStr = "Wi-Fi" default: statusStr = "NETWORK_UNKNOWN" } return statusStr } /// 判断是否有网 /// - Returns: <#description#> public func isNetConnected() -> Bool { return NetworkReachabilityManager(host: "www.baidu.com")?.networkReachabilityStatus != .notReachable } /// 获取ip地址 /// - Returns: <#description#> public func ipAddress() -> String { var addresses = [String]() var ifaddr: UnsafeMutablePointer? if getifaddrs(&ifaddr) == 0 { var ptr = ifaddr while ptr != nil { let flags = Int32(ptr!.pointee.ifa_flags) var addr = ptr!.pointee.ifa_addr.pointee if (flags & (IFF_UP | IFF_RUNNING | IFF_LOOPBACK)) == (IFF_UP | IFF_RUNNING) { if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) { var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST)) if getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0 { if let address = String(validatingUTF8: hostname) { addresses.append(address) } } } } ptr = ptr!.pointee.ifa_next } freeifaddrs(ifaddr) } return addresses.first ?? "0.0.0.0" } /// 生成唯一ID / 分享跟冷启动 /// - Parameter desc: <#desc description#> /// - Returns: <#description#> public func getUniqueId(desc: String) -> String { let timeStr: String = "\(Date().timeIntervalSince1970)" let uuid: String = getMachineCode() let code: String = "\(arc4random_uniform(1_000_000_000))" let uniqueId = (timeStr + desc + uuid + code).kf.md5.kf.md5 BFLog(message: "生成唯一码:desc = \(desc),timeStr = \(timeStr),uuid = \(uuid),code = \(code),uniqueId = \(uniqueId)") return uniqueId } // MARK: 字典转字符串 public func dictionaryToJsonString(_ dic: [String: Any]) -> String? { BFLog(message: "dictionaryToJsonString = \(dic)") if !JSONSerialization.isValidJSONObject(dic) { return "" } guard let data = try? JSONSerialization.data(withJSONObject: dic, options: []) else { return "" } BFLog(message: "dictionaryToJsonString - data = \(data)") let str = String(data: data, encoding: String.Encoding.utf8) BFLog(message: "dictionaryToJsonString - str = \(String(describing: str))") return str } // MARK: 字符串转字典 public func jsonStringToDictionary(_ str: String) -> [String: Any]? { let data = str.data(using: String.Encoding.utf8) if data == nil { return [:] } if let dict = try? JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any] { return dict } return [:] } // MARK: 字符串转数组 public func jsonStringToArray(_ str: String) -> [[String: String]]? { let data = str.data(using: String.Encoding.utf8) if let array = try? JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? [[String: String]] { return array } return nil } /// 数组转为string /// - Parameter array: <#array description#> /// - Returns: <#description#> public func arrayToJsonString(_ array: [Any]) -> String { if !JSONSerialization.isValidJSONObject(array) { BFLog(message: "无法解析String") return "" } let data: NSData! = try? JSONSerialization.data(withJSONObject: array, options: []) as NSData? let JSONString = NSString(data: data as Data, encoding: String.Encoding.utf8.rawValue) return JSONString! as String } /// jsonString转为数组 /// - Parameter jsonString: <#jsonString description#> /// - Returns: <#description#> public func jsonStringToArray(jsonString: String) -> [Any]? { let data = jsonString.data(using: String.Encoding.utf8) if data == nil { return nil } if let array = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [Any] { return array } return nil } /// 计算字符串大小 /// - Parameters: /// - text: <#text description#> /// - font: <#font description#> /// - size: <#size description#> /// - Returns: <#description#> public func sizeWithText(text: String, font: UIFont, size: CGSize) -> CGSize { let attributes = [NSAttributedString.Key.font: font] let option = NSStringDrawingOptions.usesLineFragmentOrigin let rect: CGRect = text.boundingRect(with: size, options: option, attributes: attributes, context: nil) return rect.size } /// 根据行数计算字符串大小 /// - Parameters: /// - text: <#text description#> /// - numberOfLines: <#numberOfLines description#> /// - font: <#font description#> /// - maxSize: <#maxSize description#> /// - Returns: <#description#> public func sizeTextFits(attributedText: NSMutableAttributedString?, text: String?, numberOfLines: Int, font: UIFont, maxSize: CGSize) -> CGSize { var newSize: CGSize = CGSize(width: 0, height: 0) let label = UILabel(frame: CGRect.zero) label.font = font label.numberOfLines = numberOfLines if attributedText != nil { label.attributedText = attributedText } else { label.text = text } newSize = label.sizeThatFits(maxSize) return newSize } public func textNumberOfLines(text: String, font: UIFont, maxSize _: CGSize) -> Int { let label = UILabel(frame: CGRect.zero) label.font = font label.numberOfLines = 0 label.text = text return label.numberOfLines } /// 生成渐变色 /// - Parameters: /// - size: <#size description#> /// - endPoint: <#endPoint description#> /// - startColor: <#startColor description#> /// - endColor: <#endColor description#> /// - Returns: <#description#> public func gradientColor(size: CGSize, endPoint: CGPoint, startColor: UIColor, endColor: UIColor) -> UIColor { let gradientLayer = CAGradientLayer() gradientLayer.frame = CGRect(origin: CGPoint(), size: size) gradientLayer.startPoint = CGPoint.zero gradientLayer.endPoint = endPoint gradientLayer.colors = [startColor.cgColor, endColor.cgColor] UIGraphicsBeginImageContext(size) gradientLayer.render(in: UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext()! return UIColor(patternImage: image) } /// 获取设备ID /// - Returns: <#description#> public func getMachineCode() -> String { let userInfo: [String: Any]? = jsonStringToDictionary(UserDefaults.standard.string(forKey: cUserInfoStorageKey) ?? "") 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)) { BFLog(message: "虚拟账号mid:\("\(userInfo?["mid"] ?? "")")") return "\(userInfo?["mid"] ?? "")" } let keychain = Keychain(service: "com.piaoquan.pqspeed") var uuid: String = keychain["machineCode"] ?? "" if uuid.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { uuid = NSUUID().uuidString keychain["machineCode"] = uuid } BFLog(message: "正式账号mid:\(uuid)") return uuid } /// 显示加载中视图 /// - Parameters: /// - superView: <#superView description#> /// - msg: <#msg description#> /// - Returns: <#description#> public func cShowHUB(superView: UIView?, msg: String?) { DispatchQueue.main.async { if superView == nil { if msg == nil { UIApplication.shared.keyWindow?.makeToastActivity(.center) } else { UIApplication.shared.keyWindow?.makeToast(msg, duration: 3.0, position: .center) } } else { if msg == nil { superView!.makeToastActivity(.center) } else { superView!.makeToast(msg, duration: 3.0, position: .center) } } } } /// 隐藏加载中视图 /// - Parameter superView: <#superView description#> /// - Returns: <#description#> public func cHiddenHUB(superView: UIView?) { DispatchQueue.main.async { if superView == nil { UIApplication.shared.keyWindow?.hideAllToasts() UIApplication.shared.keyWindow?.hideToastActivity() } else { superView!.hideAllToasts() superView?.hideToastActivity() } } } /// 获取存储值 /// - Parameter key: key description /// - Returns: description public func getUserDefaults(key: String) -> Any? { return UserDefaults.standard.object(forKey: key) } /// 存储数据 /// - Parameters: /// - key: key description /// - value: value description /// - Returns: description public func saveUserDefaults(key: String, value: String) { UserDefaults.standard.set(value, forKey: key) UserDefaults.standard.synchronize() } /// 存储数据带版本号 /// - Parameters: /// - key: <#key description#> /// - value: <#value description#> public func saveUserDefaultsToJson(key: String, value: Any) { UserDefaults.standard.set(dictionaryToJsonString([key: value, "appVersionCode": versionCode, "versionName": versionName]), forKey: key) UserDefaults.standard.synchronize() } /// 获取数据带版本号 /// - Parameter key: <#key description#> /// - Returns: <#description#> public func getUserDefaultsForJson(key: String) -> Any? { let jsonStr = UserDefaults.standard.object(forKey: key) if jsonStr != nil { return jsonStringToDictionary(jsonStr as! String)?[key] } return UserDefaults.standard.object(forKey: key) } /// 清空数据 /// - Parameters: /// - key: key description /// - value: value description /// - Returns: description public func removeUserDefaults(key: String) { UserDefaults.standard.removeObject(forKey: key) UserDefaults.standard.synchronize() } /// 存储数据 /// - Parameters: /// - key: key description /// - value: value description /// - Returns: description public func saveUserDefaults(key: String, value: Any) { UserDefaults.standard.set(value, forKey: key) UserDefaults.standard.synchronize() } /// 保存自定义model as NSArray 当 OBJ 是数组时不能使用 Array 要使用 NSArray /// - Parameter object: <#object description#> /// - Parameter key: <#key description#> public func saveCustomObject(customObject object: NSCoding, key: String) { let encodedObject = NSKeyedArchiver.archivedData(withRootObject: object) UserDefaults.standard.set(encodedObject, forKey: key) UserDefaults.standard.synchronize() BFLog(message: "保存自定义类成功 key is \(key) \(encodedObject.count)") } /// 取自定义model /// - Parameter key: <#key description#> public func getCustomObject(forKey key: String) -> AnyObject? { let decodedObject = UserDefaults.standard.object(forKey: key) as? Data if decodedObject == nil { BFLog(message: "key is \(key) decodedObject is nil") } if let decoded = decodedObject { let object = NSKeyedUnarchiver.unarchiveObject(with: decoded as Data) return object as AnyObject? } return nil } /// 添加通知 /// - Parameters: /// - observer: <#observer description#> /// - aSelectorName: <#aSelectorName description#> /// - aName: <#aName description#> /// - anObject: <#anObject description#> /// - Returns: <#description#> public func addNotification(_ observer: Any, selector aSelectorName: Selector, name aName: String, object anObject: Any?) { PQNotification.addObserver(observer, selector: aSelectorName, name: NSNotification.Name(rawValue: aName), object: anObject) } /// 发送通知 /// - Parameter aName: <#aName description#> /// - Returns: <#description#> public func postNotification(name aName: String, userInfo: [AnyHashable: Any]? = nil) { PQNotification.post(name: NSNotification.Name(aName), object: nil, userInfo: userInfo) } /// 获取是否打开推送 /// - Parameter completeHander: <#completeHander description#> /// - Returns: <#description#> public func pushNotificationIsOpen(completeHander: ((_ isOpen: Bool) -> Void)?) { if #available(iOS 10.0, *) { UNUserNotificationCenter.current().getNotificationSettings { setttings in completeHander!(setttings.authorizationStatus == .authorized) } } else { completeHander!(UIApplication.shared.currentUserNotificationSettings?.types.contains(UIUserNotificationType.alert) ?? false) } } /// 发送上传本地推送 /// - Parameter isSuccess: 是否上传成功 /// - Returns: <#description#> public func sendUploadNotification(isSuccess: Bool) { let title: String = isSuccess ? "上传完成了!" : "上传失败了!" let body: String = isSuccess ? "请点击发布,完成上传。否则,您的视频可能丢失" : "快来看看怎么了?" sendLocalNotification(title: title, body: body) } /// 发送本地推送 /// - Parameters: /// - title: 标题 /// - body: 内容 /// - Returns: <#description#> public func sendLocalNotification(title: String, body: String) { // 设置推送内容 if #available(iOS 10.0, *) { let content = UNMutableNotificationContent() content.title = title content.body = body content.badge = 1 // 设置通知触发器 let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false) // 设置请求标识符 let requestIdentifier = getUniqueId(desc: "notification\(title)") // 设置一个通知请求 let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger) // 将通知请求添加到发送中心 UNUserNotificationCenter.current().add(request) { error in if error == nil { print("Time Interval Notification scheduled: \(requestIdentifier)") } } } else { // Fallback on earlier versions let notification = UILocalNotification() notification.alertBody = body notification.alertTitle = title notification.applicationIconBadgeNumber = 1 notification.fireDate = Date(timeIntervalSinceNow: 0) UIApplication.shared.scheduledLocalNotifications = [notification] } } /// 打开应用设置 public func openAppSetting() { if UIApplication.shared.canOpenURL(URL(string: UIApplication.openSettingsURLString)!) { UIApplication.shared.openURL(URL(string: UIApplication.openSettingsURLString)!) } } /// dns解析 /// - Parameter hostUrl: speed.piaoquantv.com / /// - Returns: <#description#> public func parseDNS(hostUrl: String) -> [String: Any]? { let host: CFHost? = CFHostCreateWithName(nil, hostUrl as CFString).takeRetainedValue() let start = CFAbsoluteTimeGetCurrent() var success: DarwinBoolean = false var addressList: [String] = Array.init() var addresses: NSArray? if CFHostStartInfoResolution(host!, .addresses, nil) { addresses = (CFHostGetAddressing(host!, &success)?.takeUnretainedValue()) } if success == true { for case let theAddress as NSData in addresses! { var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST)) if getnameinfo(theAddress.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(theAddress.length), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 { let numAddress = String(cString: hostname) addressList.append("\(hostUrl)/\(numAddress)") } } } let end = CFAbsoluteTimeGetCurrent() let duration = end - start BFLog(message: "duration = \(duration)") BFLog(message: "addressList = \(addressList)") if addressList.count > 0 { return ["dnsResult": arrayToJsonString(addressList), "duration": duration * 1000, "hostName": hostUrl, "networkType": networkStatus()] } else { return nil } } /// 获取当前日期 /// - Returns: <#description#> public func systemCurrentDate() -> String { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "YYYY-MM-dd" return dateFormatter.string(from: Date()) } /// 时间戳转日期 /// - Parameter timeInterval: <#timeInterval description#> /// - Returns: <#description#> public func timeIntervalToDateString(timeInterval: TimeInterval) -> String { let date = Date(timeIntervalSince1970: timeInterval) let dateFormatter = DateFormatter() dateFormatter.dateFormat = "MM月dd日 HH:mm" return dateFormatter.string(from: date) } /// 判断字符串或者字典是否为空 /// - Parameter object: <#object description#> /// - Returns: <#description#> public func isEmpty(object: Any?) -> Bool { if object == nil { return true } if object is String { return (object as! String).count <= 0 } if object is [String: Any] { return (object as! [String: Any]).keys.count <= 0 } return false } public func isEmptyObject(object: Any?) -> Bool { if object == nil { return true } if object is String { return object == nil || ((object as? String)?.count ?? 0) <= 0 } if object is [String: Any] { return object == nil || ((object as? [String: Any])?.keys.count ?? 0) <= 0 } if object is List { return object == nil || ((object as? List)?.count ?? 0) <= 0 } return false } /// <#Description#> /// - Parameter string: <#string description#> /// - Returns: <#description#> public func isIncludeChineseIn(string: String) -> Bool { for (_, value) in string.enumerated() { if value >= "\u{4E00}", value <= "\u{9FA5}" { return true } } return false } /// 获取文件内容的MD5 /// - Parameters: /// - path: 地址 /// - data: data /// - Returns: <#description#> public func contentMD5(path: String? = nil, data _: Data? = nil) -> String? { if path == nil || (path?.count ?? 0) <= 0 || !FileManager.default.fileExists(atPath: path ?? "") { BFLog(message: "生成内容md5值:地址错误或者不存在\(String(describing: path))") return "" } let att = try? FileManager.default.attributesOfItem(atPath: path ?? "") let size = Int64(att?[FileAttributeKey.size] as! UInt64) if size <= 0 { BFLog(message: "生成内容md5值:文件大小为0\(size)") return "" } let hash: String = OSSUtil.base64Md5(forFilePath: path) BFLog(message: "生成内容md5值:contentMD5 = \(hash)") return hash // let ctxLen = MemoryLayout.size // let ctx = UnsafeMutablePointer.allocate(capacity: ctxLen) // CC_MD5_Init(ctx) // if path != nil { // let handle = FileHandle(forReadingAtPath: path!) // if handle == nil { // return nil // } // var done = false // while !done { // let fileData = handle?.readData(ofLength: 256) // fileData?.withUnsafeBytes { (bytes: UnsafePointer) -> Void in // CC_MD5_Update(ctx, bytes, CC_LONG(fileData!.count)) // } // // if fileData?.count == 0 { // done = true // } // } // } else if path == nil && data != nil { // let fileData = data // fileData?.withUnsafeBytes { (bytes: UnsafePointer) -> Void in // CC_MD5_Update(ctx, bytes, CC_LONG(fileData!.count)) // } // } else { // return nil // } // // unsigned char digest[CC_MD5_DIGEST_LENGTH]; // let digestLen = Int(CC_MD5_DIGEST_LENGTH) // let digest = UnsafeMutablePointer.allocate(capacity: digestLen) // CC_MD5_Final(digest, ctx) // // var hash = "" // for i in 0 ..< digestLen { // hash += String(format: "%02x", (digest[i])) // } // digest.deinitialize(count: digestLen) // ctx.deinitialize(count: ctxLen) } /// 自适应宽 /// - Parameters: /// - width: <#width description#> /// - baseWidth: <#baseWidth description#> /// - Returns: <#description#> public func adapterWidth(width: CGFloat, baseWidth: CGFloat = 375) -> CGFloat { return width / baseWidth * cScreenWidth } /// 自适应高 /// - Parameters: /// - height: <#height description#> /// - baseHeight: <#baseHeight description#> /// - Returns: <#description#> public func adapterHeight(height: CGFloat, baseHeight: CGFloat = 812) -> CGFloat { return height / baseHeight * cScreenHeigth } /// 检测URL /// - Parameter url: <#url description#> /// - Returns: <#description#> public func isValidURL(url: String?) -> Bool { if url == nil || (url?.count ?? 0) <= 4 || (!(url?.hasPrefix("http") ?? false) && !(url?.hasPrefix("https") ?? false)) { return false } return true } /// 相册数据按创建时间排序 var creaFetchOptions: PHFetchOptions = { let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] return fetchOptions }() /// 相册数据按修改时间排序 var modiFetchOptions: PHFetchOptions = { let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "modificationDate", ascending: false)] return fetchOptions }() /// 获取本地素材 var avAssertOptions: [String: Any]? = { [AVURLAssetPreferPreciseDurationAndTimingKey: NSNumber(value: true)] }() /// 播放动画图 var playGifImages: [UIImage] = { var gifImages = Array.init() for i in 0 ... 44 { gifImages.append(UIImage(named: "\(i).png")!) } return gifImages }() /// 压缩图片 /// - Parameter image: <#image description#> /// - /// - Returns: <#description#> public func zipImage(image: UIImage?, size: Int) -> Data? { var data = image?.pngData() var dataKBytes = Int(data?.count ?? 0) / 1000 var maxQuality = 0.9 while dataKBytes > size, maxQuality > 0.01 { maxQuality = maxQuality - 0.01 data = image?.jpegData(compressionQuality: CGFloat(maxQuality)) dataKBytes = (data?.count ?? 0) / 1000 } return data }