فهرست منبع

整理 NXFramework

jsonwang 3 سال پیش
والد
کامیت
a1e0ebd950

+ 2 - 3
BFFramework.podspec

@@ -52,7 +52,7 @@ TODO: Add long description of the pod here.
     s.dependency 'Alamofire','4.9.1' # 网络请求库
     s.dependency 'SnapKit','4.2.0' # 布局库
     s.dependency 'Kingfisher','6.3.0' # 图片加载库
-    s.dependency 'KingfisherWebP','1.3.0' # 加载WebP格式图片库
+    s.dependency 'KingfisherWebP','1.3.0' # 加载WebP格式图片库 使用https://github.com/webmproject/libwebp.git地址可以不翻
     s.dependency 'RealmSwift','10.7.2' # Realm数据库
     s.dependency 'ObjectMapper','4.2.0' # json转model库
     s.dependency 'KeychainAccess','4.2.2' # 钥匙串库
@@ -63,6 +63,5 @@ TODO: Add long description of the pod here.
     s.dependency 'FDFullscreenPopGesture' ,'1.1'
     s.dependency 'LMJHorizontalScrollText' ,'2.0.2'
     s.dependency 'TXLiteAVSDK_Player','8.4.9944' # 腾讯播放器组件
-    s.dependency "NXFramework-Swift"
-    #使用https://github.com/webmproject/libwebp.git地址可以不翻
+  
 end

+ 176 - 0
BFFramework/Classes/BFModules/BFCategorys/NXFundation+Ext.swift

@@ -0,0 +1,176 @@
+//
+//  NXFundation+Ext.swift
+//  NXFramework-Swift-Demo
+//
+//  Created by ak on 2020/10/26.
+//  Copyright © 2020 NXFramework-Swift. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+
+public extension Date {
+    var millisecondsSince1970:Int {
+        return Int((self.timeIntervalSince1970 * 1000.0).rounded())
+    }
+    
+    var secondsSince1970:Int {
+        return Int((self.timeIntervalSince1970).rounded())
+    }
+    
+    init(milliseconds:Int) {
+        self = Date(timeIntervalSince1970: TimeInterval(milliseconds / 1000))
+    }
+    
+    func getCurrentTimeString(_ format: String = "yyyy-MM-dd HH:mm:ss") -> String {
+        let nowDate = Date()
+        let formatter = DateFormatter()
+        formatter.dateFormat = format
+        formatter.locale = Locale(identifier: "en_US_POSIX")
+        formatter.timeZone = Foundation.TimeZone(identifier: "UTC")
+        //formatter.dateStyle = .MediumStyle
+        //formatter.timeStyle = .MediumStyle
+        return formatter.string(from: nowDate)
+    }
+    
+    var iso8601: String {
+        return Formatter.iso8601.string(from: self)
+    }
+}
+
+
+public extension Formatter {
+    static let iso8601: DateFormatter = {
+        let formatter = DateFormatter()
+        formatter.calendar = Calendar(identifier: .chinese)
+        formatter.locale = Locale(identifier: "Asia/Shanghai")
+        formatter.timeZone = TimeZone(secondsFromGMT: 0)
+        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
+        return formatter
+    }()
+}
+
+public extension String {
+    var dateFromISO8601: Date? {
+        return Formatter.iso8601.date(from: self)
+    }
+    
+    func appendLineToURL(fileURL: URL) throws {
+        try (self + "\n").appendToURL(fileURL: fileURL)
+    }
+    
+    func appendToURL(fileURL: URL) throws {
+        let data = self.data(using: String.Encoding.utf8)!
+        try data.append2File(fileURL: fileURL)
+    }
+
+    ///
+    func substring(to index: Int) -> String {
+        guard let end_Index = validEndIndex(original: index) else {
+            return self
+        }
+        return String(self[startIndex..<end_Index])
+    }
+    ///
+    func substring(from index: Int) -> String {
+        guard let start_index = validStartIndex(original: index)  else {
+            return self
+        }
+        return String(self[start_index..<endIndex])
+    }
+    ///
+    func sliceString(_ range: CountableRange<Int>) -> String {
+        guard
+            let startIndex = validStartIndex(original: range.lowerBound),
+            let endIndex   = validEndIndex(original: range.upperBound),
+            startIndex <= endIndex
+            else {
+                return ""
+        }
+        return String(self[startIndex..<endIndex])
+    }
+    ///
+    func sliceString(_ range: CountableClosedRange<Int>) -> String {
+        guard
+            let start_Index = validStartIndex(original: range.lowerBound),
+            let end_Index   = validEndIndex(original: range.upperBound),
+            startIndex <= endIndex
+            else {
+                return ""
+        }
+        if endIndex.encodedOffset <= end_Index.encodedOffset {
+            return String(self[start_Index..<endIndex])
+        }
+        return String(self[start_Index...end_Index])
+    }
+    
+    private func validIndex(original: Int) -> String.Index {
+        switch original {
+        case ...startIndex.encodedOffset : return startIndex
+        case endIndex.encodedOffset...   : return endIndex
+        default                          : return index(startIndex, offsetBy: original)
+        }
+    }
+    
+    private func validStartIndex(original: Int) -> String.Index? {
+        guard original <= endIndex.encodedOffset else { return nil }
+        return validIndex(original: original)
+    }
+    
+    private func validEndIndex(original: Int) -> String.Index? {
+        guard original >= startIndex.encodedOffset else { return nil }
+        return validIndex(original: original)
+    }
+    
+    ///
+    func toDate(formatter: String) -> Date {
+        let dateFormatter = DateFormatter()
+        dateFormatter.locale = Locale.current
+        dateFormatter.dateFormat = formatter
+        let date = dateFormatter.date(from: self)
+        return date!
+    }
+    
+}
+
+extension Data {
+    func append2File(fileURL: URL) throws {
+        if let fileHandle = FileHandle(forWritingAtPath: fileURL.path) {
+            defer {
+                fileHandle.closeFile()
+            }
+            fileHandle.seekToEndOfFile()
+            fileHandle.write(self)
+        }
+        else {
+            try write(to: fileURL, options: .atomic)
+        }
+    }
+}
+
+public extension UInt32 {
+    
+    var double: Double {
+        return Double(self)
+    }
+    
+}
+
+public extension UIApplication {
+    class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
+        if let navigationController = controller as? UINavigationController {
+            return topViewController(controller: navigationController.visibleViewController)
+        }
+        if let tabController = controller as? UITabBarController {
+            if let selected = tabController.selectedViewController {
+                return topViewController(controller: selected)
+            }
+        }
+        if let presented = controller?.presentedViewController {
+            return topViewController(controller: presented)
+        }
+        return controller
+    }
+}
+

+ 392 - 0
BFFramework/Classes/BFModules/BFCategorys/NXUI+Ext.swift

@@ -0,0 +1,392 @@
+//
+//  NXUI+Ext.swift
+//  NXFramework-Swift-Demo
+//
+//  Created by ak on 2020/10/26.
+//  Copyright © 2020 NXFramework-Swift. All rights reserved.
+//
+
+import UIKit
+
+public extension UIViewController {
+    func showAlert(withTitle title: String?, message: String?) {
+        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
+        let action = UIAlertAction(title: "OK", style: .cancel, handler: nil)
+        alert.addAction(action)
+        
+        present(alert, animated: true, completion: nil)
+    }
+    
+    var contentViewController: UIViewController {
+        if let navcon = self as? UINavigationController {
+            return navcon.visibleViewController ?? self
+        } else {
+            return self
+        }
+    }
+}
+
+// Shake oritention
+public enum ShakeDirection: Int {
+    case horizontal
+    case vertical
+}
+
+public extension UIView {
+    func shake(direction: ShakeDirection = .horizontal, times: Int = 5,
+                      interval: TimeInterval = 0.1, delta: CGFloat = 2,
+                      completion: (() -> Void)? = nil) {
+        UIView.animate(withDuration: interval, animations: { () -> Void in
+            switch direction {
+            case .horizontal:
+                self.layer.setAffineTransform( CGAffineTransform(translationX: delta, y: 0))
+                break
+            case .vertical:
+                self.layer.setAffineTransform( CGAffineTransform(translationX: 0, y: delta))
+                break
+            }
+        }) { (complete) -> Void in
+            if (times == 0) {
+                // last shaking finish, reset location, callback
+                UIView.animate(withDuration: interval, animations: { () -> Void in
+                    self.layer.setAffineTransform(CGAffineTransform.identity)
+                }, completion: { (complete) -> Void in
+                    completion?()
+                })
+            }
+            else {
+                // not last shaking, continue
+                self.shake(direction: direction, times: times - 1,  interval: interval,
+                           delta: delta * -1, completion:completion)
+            }
+        }
+    }
+}
+
+
+extension UIView {
+    var x: CGFloat {
+        set {
+            var frame = self.frame
+            frame.origin.x = newValue
+            self.frame = frame
+        }
+        get {
+            return self.frame.origin.x
+        }
+    }
+    
+    var y: CGFloat {
+        set {
+            var frame = self.frame
+            frame.origin.y = newValue
+            self.frame = frame
+        }
+        get {
+            return self.frame.origin.y
+        }
+    }
+    
+    var centerX: CGFloat {
+        set {
+            var center = self.center
+            center.x = newValue
+            self.center = center
+        }
+        get {
+            return self.center.x
+        }
+    }
+    
+    var centerY: CGFloat {
+        set {
+            var center = self.center
+            center.y = newValue
+            self.center = center
+        }
+        get {
+            return self.center.y
+        }
+    }
+    
+    var width: CGFloat {
+        set {
+            var frame = self.frame
+            frame.size.width = newValue
+            self.frame = frame
+        }
+        get {
+            return self.frame.size.width
+        }
+    }
+    
+    var height: CGFloat {
+        set {
+            var frame = self.frame
+            frame.size.height = newValue
+            self.frame = frame
+        }
+        get {
+            return self.frame.size.height
+        }
+    }
+    
+    var size: CGSize {
+        set {
+            var frame = self.frame
+            frame.size = newValue
+            self.frame = frame
+        }
+        get {
+            return self.frame.size
+        }
+    }
+    
+    var origin: CGPoint {
+        set {
+            var frame = self.frame
+            frame.origin = newValue
+            self.frame = frame
+        }
+        get {
+            return self.frame.origin
+        }
+    }
+    
+    var bottomY: CGFloat {
+        set {
+            var frame = self.frame
+            frame.origin.y = newValue - frame.size.height
+            self.frame = frame
+        }
+        get {
+            return self.height + self.y
+        }
+    }
+    
+    var rightX: CGFloat {
+        set {
+            var frame = self.frame
+            frame.origin.x = newValue - frame.size.width
+            self.frame = frame
+        }
+        get {
+            return self.width + self.x
+        }
+    }
+    
+    // MARK: - UIView round corner
+    ///
+    /// - Parameter cornerRadius: radius
+    func roundedCorners(cornerRadius: CGFloat) {
+        roundedCorners(cornerRadius: cornerRadius, borderWidth: 0, borderColor: nil)
+    }
+    
+    ///
+    /// - Parameters:
+    ///   - cornerRadius:
+    ///   - borderWidth:
+    ///   - borderColor:
+    func roundedCorners(cornerRadius: CGFloat?, borderWidth: CGFloat?, borderColor: UIColor?) {
+        self.layer.cornerRadius = cornerRadius!
+        self.layer.borderWidth = borderWidth!
+        self.layer.borderColor = borderColor?.cgColor
+        self.layer.masksToBounds = true
+    }
+    
+    ///
+    /// - Parameters:
+    ///   - cornerRadius:
+    ///   - rectCorner:
+    func roundedCorners(cornerRadius: CGFloat?, rectCorner: UIRectCorner?) {
+        let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: rectCorner!, cornerRadii: CGSize(width: cornerRadius!, height: cornerRadius!))
+        let layer = CAShapeLayer()
+        layer.frame = self.bounds
+        layer.path = path.cgPath
+        self.layer.mask = layer
+    }
+    
+    ///
+    /// - Parameters:
+    ///   - colors:
+    ///   - locations:
+    ///   - startPoint: [0...1]
+    ///   - endPoint: [0...1]
+    func gradientColor(colors: [CGColor], locations: [NSNumber], startPoint: CGPoint, endPoint: CGPoint) {
+        let gradientLayer = CAGradientLayer()
+        gradientLayer.colors = colors
+        gradientLayer.locations = locations
+        /*
+         // vertical
+         gradientLayer.startPoint = CGPoint(x: 0, y: 0)
+         gradientLayer.endPoint = CGPoint(x: 0, y: 1)
+         */
+        gradientLayer.startPoint = startPoint
+        gradientLayer.endPoint = endPoint
+        gradientLayer.frame = self.frame
+        self.layer.insertSublayer(gradientLayer, at: 0)
+    }
+    
+    // MARK: - UIView blur
+    ///
+    /// - Parameter style: UIBlurEffectStyle
+    func addBlurEffect(style: UIBlurEffect.Style) {
+        let effect = UIBlurEffect(style: UIBlurEffect.Style.light)
+        let effectView = UIVisualEffectView(effect: effect)
+        effectView.frame = self.bounds
+        self.backgroundColor = .clear
+        self.addSubview(effectView)
+        self.sendSubviewToBack(effectView)
+    }
+}
+public extension UIView {
+    
+    /// 往当前视图添加一个子视图
+    /// - Parameters:
+    ///   - rect: 子视图大小
+    ///   - bgColor: 子视图背景色
+    /// - Returns: 子视图
+    func nx_addView(rect:CGRect = .zero,bgColor:UIColor = .white) ->UIView{
+        let view = UIView(frame: rect)
+        view.backgroundColor = bgColor
+        self.addSubview(view)
+        return view
+    }
+    
+    /// 往当前视图添加UIImageView
+    /// - Parameters:
+    ///   - image: 图片对象
+    ///   - rect: UIImageView
+    ///   - contentMode: 图片填充模式
+    /// - Returns: 图片
+     func nx_addImageView(image:UIImage?,rect:CGRect = .zero, contentMode:ContentMode = .scaleAspectFit)->UIImageView{
+        let imageView = UIImageView(frame: rect);
+        imageView.image = image
+        imageView.contentMode = contentMode
+        self.addSubview(imageView)
+        return imageView
+    }
+    
+    /// 添加文本控件
+    /// - Parameters:
+    ///   - fontSize: 文本大小
+    ///   - text: 文本
+    ///   - textColor: 文本颜色
+    ///   - bgColor: 背景颜色
+    /// - Returns: 文本控件
+    func nx_addLabel(fontSize: CGFloat, text: String, textColor: UIColor, bgColor: UIColor) -> UILabel {
+        return nx_addLabel(font: UIFont.systemFont(ofSize: fontSize),
+                        text: text,
+                        textColor: textColor,
+                        bgColor: bgColor)
+    }
+    
+    /// 添加文本控件
+    /// - Parameters:
+    ///   - font: 文本大小
+    ///   - text: 文本
+    ///   - textColor: 文本颜色
+    ///   - bgColor: 背景颜色
+    /// - Returns: 文本控件
+    func nx_addLabel(font: UIFont, text: String, textColor: UIColor, bgColor: UIColor) -> UILabel {
+        let label = UILabel(frame: .zero)
+        label.font = font
+        label.text = text
+        label.textColor = textColor
+        label.backgroundColor = bgColor
+        self.addSubview(label)
+        return label
+    }
+    
+    /// 添加按钮控件
+    /// - Parameters:
+    ///   - rect: 控件大小
+    ///   - title: 标题
+    ///   - titleColor: 标题颜色
+    ///   - font: 字体
+    ///   - image: 图片
+    ///   - bgImg: 背景图片
+    ///   - target: 事件响应者
+    ///   - action: 事件响应方法
+    ///   - event: 响应事件
+    /// - Returns: 按钮
+    func nx_addButton(rect: CGRect, title: String, titleColor: UIColor, font: UIFont, image: UIImage?, bgImg: UIImage?, target: Any?, action: Selector?, event: UIControl.Event?) -> UIButton {
+            let btn = UIButton(type: .custom)
+            btn.frame = rect
+            btn.setTitle(title, for: .normal)
+            btn.setTitle(title, for: .highlighted)
+            btn.setTitleColor(titleColor, for: .normal)
+            btn.setTitleColor(titleColor, for: .highlighted)
+            btn.setImage(image, for: .normal)
+            btn.setImage(image, for: .highlighted)
+            btn.setBackgroundImage(bgImg, for: .normal)
+            btn.setBackgroundImage(bgImg, for: .highlighted)
+            btn.titleLabel?.font = font
+            if let sel = action, let e = event {
+                btn.addTarget(target, action: sel, for: e)
+            }
+            addSubview(btn)
+            return btn
+        }
+    
+    /// 添加一个文本类型的按钮控件
+    /// - Parameters:
+    ///   - rect: 按钮大小
+    ///   - title: 文本
+    ///   - titleColor: 文本颜色
+    ///   - target: 事件响应者
+    ///   - action: 事件响应方法
+    ///   - event:响应事件
+    /// - Returns: 按钮控件
+        func nx_addButton(rect: CGRect, title: String, titleColor: UIColor, target: Any?, action: Selector?, event: UIControl.Event?) -> UIButton {
+            return nx_addButton(rect: rect,
+                             title: title,
+                             titleColor: titleColor,
+                             font: UIFont.systemFont(ofSize: 14),
+                             image: nil,
+                             bgImg: nil,
+                             target: target,
+                             action: action,
+                             event: event)
+        }
+    
+    /// 添加图片类型按钮
+    /// - Parameters:
+    ///   - rect: 按钮大小
+    ///   - image: 图片
+    ///   - target: 事件响应者
+    ///   - action: 事件响应方法
+    ///   - event: 响应事件
+    /// - Returns: 按钮控件
+        func nx_addButton(rect: CGRect, image: UIImage, target: Any?, action: Selector?, event: UIControl.Event?) -> UIButton {
+            return nx_addButton(rect: rect,
+                             title: "",
+                             titleColor: .white,
+                             font: UIFont.systemFont(ofSize: 14),
+                             image: image,
+                             bgImg: nil,
+                             target: target,
+                             action: action,
+                             event: event)
+        }
+    
+    /// 添加tableView
+    /// - Parameters:
+    ///   - rect: 大小
+    ///   - delegate: delegate对象
+    ///   - dataSource: dataSource 对象
+    /// - Returns: 表视图
+    func nx_addTableView(rect: CGRect, delegate: UITableViewDelegate?,dataSource:UITableViewDataSource?) -> UITableView {
+          let tableView = UITableView(frame: rect)
+          tableView.delegate = delegate
+          tableView.dataSource = dataSource
+          backgroundColor = .white
+          tableView.tableFooterView = UIView()
+          if #available(iOS 11.0, *) {
+              tableView.contentInsetAdjustmentBehavior = .never
+          }
+          return tableView
+      }
+
+}
+ 

+ 77 - 0
BFFramework/Classes/BFModules/BFCategorys/NXUIColor+Ext.swift

@@ -0,0 +1,77 @@
+//
+//  NXUIColor+Ext.swift
+//  NXFramework-Swift-Demo
+//
+//  Created by ak on 2020/10/26.
+//  Copyright © 2020 NXFramework-Swift. All rights reserved.
+//
+
+import UIKit
+
+extension UIColor {
+    
+    // MARK: - hex (0x000000) -> UIColor
+    ///
+    /// - Parameter hex (0x000000)
+    /// - Returns: UIColor
+    class func hex(hex: Int) -> UIColor {
+        return UIColor.hex(hex: hex, alpha: 1.0)
+    }
+    ///
+    /// - Parameters:
+    /// - Returns: UIColor
+    class func hex(hex: Int, alpha: CGFloat) -> UIColor {
+        return UIColor(red: CGFloat((hex >> 16) & 0xFF)/255.0, green: CGFloat((hex >> 8) & 0xFF)/255.0, blue: CGFloat(hex & 0xFF)/255.0, alpha: alpha)
+    }
+ 
+    private class func colorComponent(hex: String, start: Int, length: Int) -> CGFloat {
+        let subString = hex.sliceString(start..<(start + length))
+        let fullHex = length == 2 ? subString : (subString + subString)
+        var val: CUnsignedInt = 0
+        Scanner(string: fullHex).scanHexInt32(&val)
+        return CGFloat(val) / 255.0
+    }
+    
+    var hex: String {
+        var color = self
+        if color.cgColor.numberOfComponents < 4 {
+            let components = color.cgColor.components
+            
+            color = UIColor(red: components![0], green: components![0], blue: components![0], alpha: components![1])
+        }
+        if color.cgColor.colorSpace?.model != CGColorSpaceModel.rgb {
+            return "#FFFFFF"
+        }
+        return String(format: "#%02X%02X%02X", Int(color.cgColor.components![0]*255.0), Int(color.cgColor.components![1]*255.0), Int(color.cgColor.components![2]*255.0))
+    }
+    
+    // MARK: - RGB -> UIColor
+    class func rgba(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -> UIColor {
+        return UIColor(red: red/255.0, green: green/255.0, blue: blue/255.0, alpha: alpha)
+    }
+    // MARK: - RGBA -> UIColor
+    class func rgb(red: CGFloat, green: CGFloat, blue: CGFloat) -> UIColor {
+        return rgba(red: red, green: green, blue: blue, alpha: 1.0)
+    }
+    
+    var rgba: [Int] {
+        var red: CGFloat = 0
+        var green: CGFloat = 0
+        var blue: CGFloat = 0
+        var alpha: CGFloat = 0
+        self.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
+        return [Int(red*255.0), Int(green*255.0), Int(blue*255.0), Int(alpha)]
+    }
+    
+    class func randomColor() -> UIColor {
+        let red = CGFloat(arc4random()%255)
+        let green = CGFloat(arc4random()%255)
+        let blue = CGFloat(arc4random()%255)
+        let color = UIColor(red: red/255.0, green: green/255.0, blue: blue/255.0, alpha: 1.0)
+        return color
+    }
+    
+    
+    
+}
+

+ 174 - 0
BFFramework/Classes/BFModules/BFDebug/NXLogger.swift

@@ -0,0 +1,174 @@
+//
+//  NXLogger.swift
+//  NXFramework-Swift-Demo
+//
+//  Created by ak on 2020/10/26.
+//  Copyright © 2020 NXFramework-Swift. All rights reserved.
+//
+
+import UIKit
+
+
+public enum NXLoggerLevel: Int {
+    case info = 1
+    case debug = 2
+    case warning = 3
+    case error = 4
+    case none = 5
+    
+    var name: String {
+        switch self {
+            case .info: return "i"
+            case .debug: return "d"
+            case .warning: return "w"
+            case .error: return "e"
+            case .none: return "N"
+        }
+    }
+}
+
+public enum LoggerOutput: String {
+    case debuggerConsole
+    case deviceConsole
+    case fileOnly
+    case debugerConsoleAndFile
+    case deviceConsoleAndFile
+}
+
+
+private let fileExtension = "txt"
+private let LOG_BUFFER_SIZE = 10
+
+public class NXLogger: NSObject {
+
+    // MARK: - Output
+    public var tag: String?
+    public var level: NXLoggerLevel = .none
+    public var ouput: LoggerOutput = .debuggerConsole
+    public var showThread: Bool = false
+    
+    // MARK: - Init
+    private let isolationQueue = DispatchQueue(label: "com.nxframework.isolation", qos: .background, attributes: .concurrent)
+    private let serialQueue = DispatchQueue(label: "com.nxframework.swiftylog")
+    private let logSubdiretory = NXFileManager.documentDirectoryURL.appendingPathComponent(fileExtension)
+
+    public static let shared = NXLogger()
+    
+    private var _data: [String] = []
+    private var data: [String] {
+        get { return isolationQueue.sync { return _data } }
+        set { isolationQueue.async(flags: .barrier) { self._data = newValue } }
+    }
+    
+    private var logUrl: URL? {
+        let fileName = "NSFrameworkSwiftyLog"
+        try? FileManager.default.createDirectory(at: logSubdiretory, withIntermediateDirectories: false)
+        let url = logSubdiretory.appendingPathComponent(fileName).appendingPathExtension(fileExtension)
+        return url
+    }
+    
+    private override init() {
+        super.init()
+     
+        NotificationCenter.default.addObserver(self, selector: #selector(appMovedToBackground), name:   UIApplication.willResignActiveNotification, object: nil)
+   
+    }
+    
+    // MARK: - Methods
+    @objc private func appMovedToBackground() {
+         self.saveAsync()
+    }
+    
+    func saveAsync() {
+        guard let url = logUrl else { return }
+        serialQueue.async { [weak self] in
+            guard let count = self?.data.count, count > 0 else { return }
+
+            var stringsData = Data()
+            
+            self?.data.forEach { (string) in
+                if let stringData = (string + "\n").data(using: String.Encoding.utf8) {
+                    stringsData.append(stringData)
+                } else {
+                    print("MutalbeData failed")
+                }
+            }
+
+            do {
+                try stringsData.append2File(fileURL: url)
+                self?.data.removeAll()
+            } catch let error as NSError {
+                print("wrote failed: \(url.absoluteString), \(error.localizedDescription)")
+            }
+        }
+    }
+    
+    func removeAllAsync() {
+        guard let url = logUrl else { return }
+        DispatchQueue.global(qos: .userInitiated).async {
+            try? FileManager.default.removeItem(at: url)
+        }
+    }
+    
+    func load() -> [String]? {
+        guard let url = logUrl else { return nil }
+        guard let strings = try? String(contentsOf: url, encoding: String.Encoding.utf8) else { return nil }
+
+        return strings.components(separatedBy: "\n")
+    }
+
+    private func log(_ level: NXLoggerLevel, message: String, currentTime: Date, fileName: String , functionName: String, lineNumber: Int, thread: Thread) {
+        
+        guard level.rawValue >= self.level.rawValue else { return }
+        
+        
+        let _fileName = fileName.split(separator: "/")
+        let text = "\(level.name)-\(showThread ? thread.description : "")[\(_fileName.last ?? "?")#\(functionName)#\(lineNumber)]\(tag ?? ""): \(message)"
+        
+        switch self.ouput {
+            case .fileOnly:
+                addToBuffer(text: "\(currentTime.iso8601) \(text)")
+            case .debuggerConsole:
+                print("\(currentTime.iso8601) \(text)")
+            case .deviceConsole:
+                NSLog(text)
+            case .debugerConsoleAndFile:
+                print("\(currentTime.iso8601) \(text)")
+                addToBuffer(text: "\(currentTime.iso8601) \(text)")
+            case .deviceConsoleAndFile:
+                NSLog(text)
+                addToBuffer(text: "\(currentTime.iso8601) \(text)")
+        }
+    }
+    
+    private func addToBuffer(text: String) {
+        isolationQueue.async(flags: .barrier) { self._data.append(text) }
+        if data.count > LOG_BUFFER_SIZE {
+            saveAsync()
+        }
+    }
+    
+}
+
+// MARK: - Output
+extension NXLogger {
+    public func i(_ message: String, currentTime: Date = Date(), fileName: String = #file, functionName: String = #function, lineNumber: Int = #line, thread: Thread = Thread.current ) {
+        log(.info, message: message, currentTime: currentTime, fileName: fileName, functionName: functionName, lineNumber: lineNumber, thread: thread)
+    }
+    public func d(_ message: String, currentTime: Date = Date(), fileName: String = #file, functionName: String = #function, lineNumber: Int = #line, thread: Thread = Thread.current ) {
+        log(.debug, message: message, currentTime: currentTime, fileName: fileName, functionName: functionName, lineNumber: lineNumber, thread: thread)
+    }
+    public func w(_ message: String, currentTime: Date = Date(), fileName: String = #file, functionName: String = #function, lineNumber: Int = #line, thread: Thread = Thread.current ) {
+        log(.warning, message: message, currentTime: currentTime, fileName: fileName, functionName: functionName, lineNumber: lineNumber, thread: thread)
+    }
+    public func e(_ message: String, currentTime: Date = Date(), fileName: String = #file, functionName: String = #function, lineNumber: Int = #line, thread: Thread = Thread.current ) {
+        log(.error, message: message, currentTime: currentTime, fileName: fileName, functionName: functionName, lineNumber: lineNumber, thread: thread)
+    }
+    
+    public func synchronize() {
+        saveAsync()
+    }
+}
+
+
+

+ 68 - 0
BFFramework/Classes/BFModules/BFDebug/NXLoggerManager.swift

@@ -0,0 +1,68 @@
+//
+//  NXLoggerManager.swift
+//  NXFramework-Swift-Demo
+//
+//  Created by ak on 2020/10/26.
+//  Copyright © 2020 NXFramework-Swift. All rights reserved.
+//
+
+import UIKit
+
+extension UIWindow {
+    open override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
+        guard NXLogger.shared.level != .none else { return }
+        guard NXLogger.shared.ouput == .debugerConsoleAndFile
+            || NXLogger.shared.ouput == .deviceConsoleAndFile
+            || NXLogger.shared.ouput == .fileOnly else { return }
+        
+        NXLogger.shared.saveAsync()
+        let manager = LoggerManager()
+        manager.show()
+    }
+}
+
+protocol LoggerAction {
+    func removeAll()
+}
+
+class LoggerManager: NSObject {
+    let controller = NXLoggerVC()
+    public func show() {
+        guard let topViewController = UIApplication.topViewController() else { return }
+        guard topViewController .isKind(of: NXLoggerVC.self) == false else { return }
+        
+        controller.data = " \(loadLog())\(deviceInfo())"
+        controller.delegate = self
+        
+        topViewController.present(controller, animated: true, completion: nil)
+    }
+    
+    private func loadLog() -> String {
+        var texts: [String] = []
+        
+        guard let data = NXLogger.shared.load() else { return "" }
+        
+        data.forEach { (string) in
+            texts.append("<pre style=\"line-height:8px;\">\(string)</pre>")
+        }
+        
+        return texts.joined()
+    }
+    
+    private func deviceInfo() -> String {
+        var texts:[String] = []
+        
+        texts.append("<pre style=\"line-height:8px;\">==============================================</pre>")
+        NXDeviceManager.info().forEach { (string) in
+            texts.append("<pre style=\"line-height:8px;\">\(string)</pre>")
+        }
+        return texts.joined()
+    }
+}
+
+extension LoggerManager: LoggerAction {
+    func removeAll() {
+        NXLogger.shared.removeAllAsync()
+        controller.data = deviceInfo()
+    }
+}

+ 188 - 0
BFFramework/Classes/BFModules/BFDebug/NXLoggerVC.swift

@@ -0,0 +1,188 @@
+//
+//  NXLoggerVC.swift
+//  NXFramework-Swift-Demo
+//
+//  Created by ak on 2020/10/26.
+//  Copyright © 2020 NXFramework-Swift. All rights reserved.
+//
+ 
+import UIKit
+import MessageUI
+import WebKit
+
+
+private let screenWidth = UIScreen.main.bounds.width
+private let screenHeight = UIScreen.main.bounds.height
+private let keyWindow = UIApplication.shared.keyWindow
+
+let themeColor: UIColor = UIColor.hex(hex: 0x00B3C4)
+
+class NXLoggerVC: UIViewController {
+
+    var delegate: LoggerAction?
+    
+    var data: String = "" {
+        didSet {
+            loadWebView()
+        }
+    }
+
+    var webView: WKWebView = {
+        
+        let source: String = "var meta = document.createElement('meta');" +
+            "meta.name = 'viewport';" +
+            "meta.content = 'width=device-width, initial-scale=0.6, maximum-scale=0.8, user-scalable=yes';" +
+            "var head = document.getElementsByTagName('head')[0];" + "head.appendChild(meta);";
+        let script: WKUserScript = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
+        let userContentController: WKUserContentController = WKUserContentController()
+        let conf = WKWebViewConfiguration()
+        conf.userContentController = userContentController
+        userContentController.addUserScript(script)
+        let view = WKWebView(frame: CGRect.zero, configuration: conf)
+        
+        /*
+        view.scrollView.isScrollEnabled = true               // Make sure our view is interactable
+        view.scrollView.bounces = true                    // Things like this should be handled in web code
+        view.allowsBackForwardNavigationGestures = false   // Disable swiping to navigate
+         */
+        return view
+    }()
+    
+    var textView: UITextView = {
+        let view = UITextView()
+        view.isEditable = false
+        view.backgroundColor = UIColor.lightGray
+        return view
+    }()
+    
+    var btnSend: UIButton = {
+        let button = UIButton(type: .system)
+        button.backgroundColor = themeColor
+        button.setTitleColor(.white, for: .normal)
+        button.roundedCorners(cornerRadius: 5)
+        button.setTitle("Send email", for: .normal)
+        button.addTarget(self, action: #selector(btnSendPressed(_:)), for: .touchUpInside)
+        
+        return button
+    }()
+    
+    var btnRemove: UIButton = {
+        let button = UIButton(type: .system)
+        button.backgroundColor = themeColor
+        button.setTitleColor(.white, for: .normal)
+        button.roundedCorners(cornerRadius: 5)
+        button.setTitle("Remove All", for: .normal)
+        button.addTarget(self, action: #selector(btnRemovePressed(_:)), for: .touchUpInside)
+        return button
+    }()
+    var btnCancel: UIButton = {
+        let button = UIButton(type: .system)
+        button.backgroundColor = themeColor
+        button.setTitleColor(.white, for: .normal)
+        button.roundedCorners(cornerRadius: 5)
+        button.setTitle("Cancel", for: .normal)
+        button.addTarget(self, action: #selector(btnCancelPressed(_:)), for: .touchUpInside)
+        return button
+    }()
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        addSubViews()
+        
+        loadWebView()
+
+    }
+    
+    private func addSubViews() {
+        self.view.backgroundColor = UIColor.white
+        
+        [webView, btnSend, btnRemove, btnCancel].forEach { (subView: UIView) in
+            subView.translatesAutoresizingMaskIntoConstraints = false
+            view.addSubview(subView)
+        }
+        
+        let views: [String:UIView] = ["webView": webView, "btnSend": btnSend, "btnRemove": btnRemove, "btnCancel": btnCancel]
+        
+        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|[webView]|", options: [], metrics: nil, views: views))
+        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|-(16)-[btnSend]-(16)-|", options: [], metrics: nil, views: views))
+        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|-(16)-[btnRemove]-(16)-|", options: [], metrics: nil, views: views))
+        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|-(16)-[btnCancel]-(16)-|", options: [], metrics: nil, views: views))
+        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(20)-[webView]-[btnSend(==32)]-[btnRemove(==32)]-[btnCancel(==32)]-(8)-|", options: [], metrics: nil, views: views))
+    }
+    
+    @objc func btnCancelPressed(_ button: UIButton) {
+        self.dismiss(animated: true, completion: nil)
+    }
+    
+    @objc func btnSendPressed(_ button: UIButton) {
+        sendEmail()
+    }
+    
+    @objc func btnRemovePressed(_ button: UIButton) {
+        delegate?.removeAll()
+    }
+    
+    private func sendEmail() {
+        guard MFMailComposeViewController.canSendMail() == true else {
+            self.showAlert(withTitle: "No email client", message: "Please configure your email client first")
+            return
+        }
+
+        let mailComposer = MFMailComposeViewController()
+        mailComposer.mailComposeDelegate = self as! MFMailComposeViewControllerDelegate
+        
+        var body = "Host App: \(Bundle.main.bundleIdentifier ?? "")\n"
+        if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
+            let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
+            body += "Host App Version: \(version).\(buildNumber)\n"
+        }
+        if let venderId = UIDevice.current.identifierForVendor {
+            body += "identifierForVendor: \(venderId)\n"
+        }
+
+        mailComposer.setSubject("Log of \(Bundle.main.bundleIdentifier ?? "")")
+        mailComposer.setMessageBody(body, isHTML: false)
+
+
+        webView.evaluateJavaScript("document.documentElement.outerHTML.toString()") { (html, error) in
+            if let string = html as? String, let data = string.data(using: String.Encoding.utf16) {
+                
+                mailComposer.addAttachmentData(data, mimeType: "html", fileName: "\(Bundle.main.bundleIdentifier ?? "log").html" )
+            } else {
+                NXLogger.shared.e("get data from webview failed")
+            }
+        }
+        /*
+        if let data = try? Data(html) {
+            mailComposer.addAttachmentData(data, mimeType: "text/txt", fileName: "SwiftyLog.txt")
+        }
+        */
+        self.present(mailComposer, animated: true, completion: nil)
+    }
+    
+    private func loadWebView() {
+        webView.loadHTMLString(data, baseURL: nil)
+    }
+}
+    
+extension NXLoggerVC: MFMailComposeViewControllerDelegate {
+    func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
+        controller.dismiss(animated: true, completion: nil)
+        switch result {
+            case .cancelled:
+                self.showAlert(withTitle: "Cancel", message: "Send email canceled")
+                break
+            case .sent:
+                break
+            case .failed:
+                self.showAlert(withTitle: "Failed", message: "Send email failed")
+                break
+            case .saved:
+                break
+        @unknown default:
+            fatalError("default ")
+        }
+        self.dismiss(animated: true, completion: nil)
+    }
+}

+ 13 - 0
BFFramework/Classes/BFModules/BFMacro/NXConfig.swift

@@ -0,0 +1,13 @@
+//
+//  NXConfig.swift
+//  NXFramework-Swift-Demo
+//
+//  Created by ak on 2020/10/26.
+//  Copyright © 2020 NXFramework-Swift. All rights reserved.
+//
+
+import UIKit
+
+class NXConfig: NSObject {
+
+}

+ 113 - 0
BFFramework/Classes/BFModules/BFUtility/NXDeviceManager.swift

@@ -0,0 +1,113 @@
+//
+//  NXDeviceManager.swift
+//  NXFramework-Swift-Demo
+//
+//  Created by ak on 2020/10/26.
+//  Copyright © 2020 NXFramework-Swift. All rights reserved.
+//
+
+import UIKit
+
+class NXDeviceManager: NSObject {
+    
+    class func info() -> [String] {
+        var data: [String] = []
+        
+        data.append("Device Name: \(deviceNameAlias())")
+        if let bundleId = Bundle.main.bundleIdentifier {
+            data.append("Bundle Identifier: \(bundleId)")
+        }
+        
+        if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
+            let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
+            data.append( "Host App Version: \(version).\(buildNumber)" )
+        }
+        
+        if let venderId = UIDevice.current.identifierForVendor {
+            data.append( "Identifier For Vendor: \(venderId)" )
+        }
+        
+        data.append("System Version: \(getSystemVersion())")
+        data.append("Model: \(platformModelString())")
+        
+//        data.append("Total Disk Space(MB): \(UIDevice.totalDiskSpaceInMB)")
+//        data.append("Free Disk Space(MB): \(UIDevice.freeDiskSpaceInMB)")
+        
+        let lastRestarted = Date(timeIntervalSince1970: TimeInterval(Date().timeIntervalSince1970 - Double(uptime())))
+        data.append("Uptime: \(uptime())/\(lastRestarted)")
+        
+        return data
+    }
+    
+    class var isIpad:Bool {
+        if #available(iOS 8.0, *) {
+            return UIScreen.main.traitCollection.userInterfaceIdiom == .pad
+        } else {
+            return UIDevice.current.userInterfaceIdiom == .pad
+        }
+    }
+    class var isIphone:Bool {
+        if #available(iOS 8.0, *) {
+            return UIScreen.main.traitCollection.userInterfaceIdiom == .phone
+        } else {
+            return UIDevice.current.userInterfaceIdiom == .phone
+        }
+    }
+    
+    ///Name of the devices, like Baudins's Iphone
+    class func deviceNameAlias() -> String {
+        return  UIDevice.current.name
+    }
+    
+    class func processorCount() -> Int {
+        return ProcessInfo.processInfo.activeProcessorCount
+    }
+    
+    //Verion of the OS, like 9.0.1
+    class func osVersion()-> String {
+        return UIDevice.current.systemVersion;
+    }
+    
+    class func platformModelString() -> String {
+        if let key = "hw.machine".cString(using: String.Encoding.utf8) {
+            var size: Int = 0
+            sysctlbyname(key, nil, &size, nil, 0)
+            var machine = [CChar](repeating: 0, count: Int(size))
+            sysctlbyname(key, &machine, &size, nil, 0)
+            return String(cString: machine)
+        }
+        return "Unknown"
+    }
+    
+    /** uptime in seconds **/
+    class func uptime()  -> Int {
+        var currentTime = time_t()
+        var bootTime    = timeval()
+        var mib         = [CTL_KERN, KERN_BOOTTIME]
+        
+        var size = MemoryLayout<timeval>.stride
+        
+        if sysctl(&mib, u_int(mib.count), &bootTime, &size, nil, 0) != -1 && bootTime.tv_sec != 0 {
+            time(&currentTime)
+            
+            if (currentTime < bootTime.tv_sec) {
+                return 0
+            }
+            
+            return  currentTime - bootTime.tv_sec
+        }
+        return 0
+    }
+    
+    class func getScreenBrightness() -> CGFloat {
+        return UIScreen.main.brightness
+    }
+    
+    class func getPhysicalMemory() -> UInt64 {
+        return ProcessInfo.processInfo.physicalMemory
+    }
+    
+    class func getSystemVersion() -> String {
+        return UIDevice.current.systemVersion
+    }
+}

+ 43 - 0
BFFramework/Classes/BFModules/BFUtility/NXFileManager.swift

@@ -0,0 +1,43 @@
+//
+//  NXFileManager.swift
+//  NXFramework-Swift-Demo
+//
+//  Created by ak on 2020/10/26.
+//  Copyright © 2020 NXFramework-Swift. All rights reserved.
+//
+/*
+
+本类功能, 文件操作.(ios, mac)
+
+一,iOS目录结构说明
+1,沙盒目录结构
+├── Documents - 存储用户数据或其它应该定期备份的
+├── Library
+│   ├── Caches -
+用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息
+│   │   └── Snapshots
+│   │       └── com.youyouxingyuan.re
+│   │           ├── A85B73F0-26A8-44E4-A761-446CAB8DAB38@2x.png
+│   │           └── BFAD5885-B767-4320-9A4B-555EC881C50D@2x.png
+│   └── Preferences - 偏好设置文件 NSUserDefaults 保存的数据
+└── tmp - 这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息
+
+2,在iOS8之后,应用每一次重启,沙盒路径都动态的发生了变化但不用担心数据问题,苹果会把你上一个路径中的数据转移到你新的路径中。你上一个路径也会被苹果毫无保留的删除,只保留最新的路径。
+
+@see  <Foundation/NSPathUtilities.h>
+
+*/
+import UIKit
+
+class NXFileManager: NSObject {
+    
+    static var documentDirectoryURL: URL {
+      return try! FileManager.default.url(
+        for: .documentDirectory,
+        in: .userDomainMask,
+        appropriateFor: nil,
+        create: false
+      )
+    }
+
+}

+ 0 - 1
BFFramework/Classes/Base/Controller/PQBaseViewController.swift

@@ -8,7 +8,6 @@
 
 // import MediaPlayer
 import Alamofire
-import NXFramework_Swift
 import UIKit
 public class PQBaseViewController: UIViewController, UIGestureRecognizerDelegate {
     // 侧滑拦截返回

+ 0 - 1
BFFramework/Classes/Utils/PQCommonMethodUtil.swift

@@ -15,7 +15,6 @@ import KingfisherWebP
 import Photos
 import RealmSwift
 import Toast_Swift
-import NXFramework_Swift
 /// Home文件地址
 public let homeDirectory = NSHomeDirectory()
 /// docdocumens文件地址

+ 0 - 2
Example/BFFramework.xcodeproj/project.pbxproj

@@ -259,7 +259,6 @@
 				"${BUILT_PRODUCTS_DIR}/KingfisherWebP/KingfisherWebP.framework",
 				"${BUILT_PRODUCTS_DIR}/LMJHorizontalScrollText/LMJHorizontalScrollText.framework",
 				"${BUILT_PRODUCTS_DIR}/MJRefresh/MJRefresh.framework",
-				"${BUILT_PRODUCTS_DIR}/NXFramework-Swift/NXFramework_Swift.framework",
 				"${BUILT_PRODUCTS_DIR}/ObjectMapper/ObjectMapper.framework",
 				"${BUILT_PRODUCTS_DIR}/Realm/Realm.framework",
 				"${BUILT_PRODUCTS_DIR}/RealmSwift/RealmSwift.framework",
@@ -277,7 +276,6 @@
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KingfisherWebP.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LMJHorizontalScrollText.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MJRefresh.framework",
-				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NXFramework_Swift.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ObjectMapper.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RealmSwift.framework",

+ 1 - 5
Example/Podfile.lock

@@ -10,7 +10,6 @@ PODS:
     - KingfisherWebP (= 1.3.0)
     - LMJHorizontalScrollText (= 2.0.2)
     - MJRefresh (= 3.5.0)
-    - NXFramework-Swift
     - ObjectMapper (= 4.2.0)
     - RealmSwift (= 10.7.2)
     - SnapKit (= 4.2.0)
@@ -34,7 +33,6 @@ PODS:
   - libwebp/webp (1.2.0)
   - LMJHorizontalScrollText (2.0.2)
   - MJRefresh (3.5.0)
-  - NXFramework-Swift (0.1.0)
   - ObjectMapper (4.2.0)
   - Realm (10.7.2):
     - Realm/Headers (= 10.7.2)
@@ -60,7 +58,6 @@ SPEC REPOS:
     - libwebp
     - LMJHorizontalScrollText
     - MJRefresh
-    - NXFramework-Swift
     - ObjectMapper
     - Realm
     - RealmSwift
@@ -76,7 +73,7 @@ EXTERNAL SOURCES:
 SPEC CHECKSUMS:
   Alamofire: 85e8a02c69d6020a0d734f6054870d7ecb75cf18
   AliyunOSSiOS: 8db92936545593b9e5c66d680ef2ac0738946651
-  BFFramework: 86ed86fad296b3cdc7fce3fc342c91782fd885e4
+  BFFramework: a6a05169a9504af7ea4932b381e0c0494e77b8d9
   FDFullscreenPopGesture: a8a620179e3d9c40e8e00256dcee1c1a27c6d0f0
   KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51
   Kingfisher: 6c3df386db71d82c0817a429d2c9421a77396529
@@ -84,7 +81,6 @@ SPEC CHECKSUMS:
   libwebp: e90b9c01d99205d03b6bb8f2c8c415e5a4ef66f0
   LMJHorizontalScrollText: ebc9b908db297f603c5b98c9b4e5f4582f5a14b8
   MJRefresh: 6afc955813966afb08305477dd7a0d9ad5e79a16
-  NXFramework-Swift: a86291255f47d469da796602cc79cf8b2d418346
   ObjectMapper: 1eb41f610210777375fa806bf161dc39fb832b81
   Realm: e523da9ade306c5ae87e85dc09fdef148d3e1cc1
   RealmSwift: 4f6758c3adbdcc87f7b7777107226532a077f61c

+ 1 - 5
Example/Pods/Manifest.lock

@@ -10,7 +10,6 @@ PODS:
     - KingfisherWebP (= 1.3.0)
     - LMJHorizontalScrollText (= 2.0.2)
     - MJRefresh (= 3.5.0)
-    - NXFramework-Swift
     - ObjectMapper (= 4.2.0)
     - RealmSwift (= 10.7.2)
     - SnapKit (= 4.2.0)
@@ -34,7 +33,6 @@ PODS:
   - libwebp/webp (1.2.0)
   - LMJHorizontalScrollText (2.0.2)
   - MJRefresh (3.5.0)
-  - NXFramework-Swift (0.1.0)
   - ObjectMapper (4.2.0)
   - Realm (10.7.2):
     - Realm/Headers (= 10.7.2)
@@ -60,7 +58,6 @@ SPEC REPOS:
     - libwebp
     - LMJHorizontalScrollText
     - MJRefresh
-    - NXFramework-Swift
     - ObjectMapper
     - Realm
     - RealmSwift
@@ -76,7 +73,7 @@ EXTERNAL SOURCES:
 SPEC CHECKSUMS:
   Alamofire: 85e8a02c69d6020a0d734f6054870d7ecb75cf18
   AliyunOSSiOS: 8db92936545593b9e5c66d680ef2ac0738946651
-  BFFramework: 86ed86fad296b3cdc7fce3fc342c91782fd885e4
+  BFFramework: a6a05169a9504af7ea4932b381e0c0494e77b8d9
   FDFullscreenPopGesture: a8a620179e3d9c40e8e00256dcee1c1a27c6d0f0
   KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51
   Kingfisher: 6c3df386db71d82c0817a429d2c9421a77396529
@@ -84,7 +81,6 @@ SPEC CHECKSUMS:
   libwebp: e90b9c01d99205d03b6bb8f2c8c415e5a4ef66f0
   LMJHorizontalScrollText: ebc9b908db297f603c5b98c9b4e5f4582f5a14b8
   MJRefresh: 6afc955813966afb08305477dd7a0d9ad5e79a16
-  NXFramework-Swift: a86291255f47d469da796602cc79cf8b2d418346
   ObjectMapper: 1eb41f610210777375fa806bf161dc39fb832b81
   Realm: e523da9ade306c5ae87e85dc09fdef148d3e1cc1
   RealmSwift: 4f6758c3adbdcc87f7b7777107226532a077f61c