update
This commit is contained in:
154
Pods/CocoaDebug/Sources/Network/JsonViewController.swift
generated
Normal file
154
Pods/CocoaDebug/Sources/Network/JsonViewController.swift
generated
Normal file
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
enum EditType {
|
||||
case unknown
|
||||
case requestHeader
|
||||
case responseHeader
|
||||
case log
|
||||
}
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class JsonViewController: UIViewController {
|
||||
|
||||
@IBOutlet weak var textView: CustomTextView!
|
||||
@IBOutlet weak var imageView: UIImageView!
|
||||
@IBOutlet weak var naviItem: UINavigationItem!
|
||||
|
||||
var naviItemTitleLabel: UILabel?
|
||||
|
||||
var editType: EditType = .unknown
|
||||
var httpModel: _HttpModel?
|
||||
var detailModel: NetworkDetailModel?
|
||||
|
||||
//Edited url
|
||||
var editedURLString: String?
|
||||
//Edited content
|
||||
var editedContent: String?
|
||||
|
||||
//log
|
||||
var logTitleString: String?
|
||||
var logModels: [_OCLogModel]?
|
||||
var logModel: _OCLogModel?
|
||||
var justCancelCallback:(() -> Void)?
|
||||
|
||||
static func instanceFromStoryBoard() -> JsonViewController {
|
||||
let storyboard = UIStoryboard(name: "Network", bundle: Bundle(for: CocoaDebug.self))
|
||||
return storyboard.instantiateViewController(withIdentifier: "JsonViewController") as! JsonViewController
|
||||
}
|
||||
|
||||
//MARK: - tool
|
||||
|
||||
//detect format (JSON/Form)
|
||||
func detectSerializer() {
|
||||
guard let content = detailModel?.content else {
|
||||
detailModel?.requestSerializer = RequestSerializer.JSON//default JSON format
|
||||
return
|
||||
}
|
||||
|
||||
if let _ = content.stringToDictionary() {
|
||||
//JSON format
|
||||
detailModel?.requestSerializer = RequestSerializer.JSON
|
||||
} else {
|
||||
//Form format
|
||||
detailModel?.requestSerializer = RequestSerializer.form
|
||||
|
||||
if let jsonString = detailModel?.content?.formStringToJsonString() {
|
||||
textView.text = jsonString
|
||||
detailModel?.requestSerializer = RequestSerializer.JSON
|
||||
detailModel?.content = textView.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//MARK: - init
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
// navigationController?.hidesBarsOnSwipe = true
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
// navigationController?.hidesBarsOnSwipe = false
|
||||
|
||||
if let index = logModels?.firstIndex(where: { (model) -> Bool in
|
||||
return model.isSelected == true
|
||||
}) {
|
||||
logModels?[index].isSelected = false
|
||||
}
|
||||
|
||||
logModel?.isSelected = true
|
||||
|
||||
if let justCancelCallback = justCancelCallback {
|
||||
justCancelCallback()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
naviItemTitleLabel = UILabel.init(frame: CGRect(x: 0, y: 0, width: 80, height: 40))
|
||||
naviItemTitleLabel?.textAlignment = .center
|
||||
naviItemTitleLabel?.textColor = Color.mainGreen
|
||||
naviItemTitleLabel?.font = .boldSystemFont(ofSize: 20)
|
||||
naviItemTitleLabel?.text = detailModel?.title
|
||||
naviItem.titleView = naviItemTitleLabel
|
||||
|
||||
textView.textContainer.lineFragmentPadding = 15
|
||||
// textView.textContainerInset = .zero
|
||||
|
||||
//detect type (default type URL)
|
||||
if detailModel?.title == "REQUEST HEADER" {
|
||||
editType = .requestHeader
|
||||
}
|
||||
if detailModel?.title == "RESPONSE HEADER" {
|
||||
editType = .responseHeader
|
||||
}
|
||||
|
||||
//setup UI
|
||||
if editType == .requestHeader
|
||||
{
|
||||
imageView.isHidden = true
|
||||
textView.isHidden = false
|
||||
textView.text = String(detailModel?.requestHeaderFields?.dictionaryToString()?.dropFirst().dropLast().dropFirst().dropLast().dropFirst().dropFirst() ?? "").replacingOccurrences(of: "\",\n \"", with: "\",\n\"")
|
||||
}
|
||||
else if editType == .responseHeader
|
||||
{
|
||||
imageView.isHidden = true
|
||||
textView.isHidden = false
|
||||
textView.text = String(detailModel?.responseHeaderFields?.dictionaryToString()?.dropFirst().dropLast().dropFirst().dropLast().dropFirst().dropFirst() ?? "").replacingOccurrences(of: "\",\n \"", with: "\",\n\"")
|
||||
}
|
||||
else if editType == .log
|
||||
{
|
||||
imageView.isHidden = true
|
||||
textView.isHidden = false
|
||||
naviItemTitleLabel?.text = logTitleString
|
||||
|
||||
if let data = logModel?.contentData {
|
||||
textView.text = data.dataToString()
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if let content = detailModel?.content {
|
||||
imageView.isHidden = true
|
||||
textView.isHidden = false
|
||||
textView.text = content
|
||||
detectSerializer()//detect format (JSON/Form)
|
||||
}
|
||||
if let image = detailModel?.image {
|
||||
textView.isHidden = true
|
||||
imageView.isHidden = false
|
||||
imageView.image = image
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
157
Pods/CocoaDebug/Sources/Network/NetworkCell.swift
generated
Normal file
157
Pods/CocoaDebug/Sources/Network/NetworkCell.swift
generated
Normal file
@@ -0,0 +1,157 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class NetworkCell: UITableViewCell {
|
||||
|
||||
@IBOutlet weak var leftAlignLine: UILabel!
|
||||
@IBOutlet weak var statusCodeLabel: UILabel!
|
||||
@IBOutlet weak var methodLabel: UILabel!
|
||||
@IBOutlet weak var requestTimeTextView: CustomTextView!
|
||||
@IBOutlet weak var requestUrlTextView: CustomTextView!
|
||||
@IBOutlet weak var imageLabel: UILabel!
|
||||
@IBOutlet weak var statusCodeView: UIView!
|
||||
|
||||
var index: NSInteger = 0
|
||||
|
||||
var httpModel: _HttpModel? {
|
||||
didSet {
|
||||
|
||||
guard let serverURL = CocoaDebugSettings.shared.serverURL else {return}
|
||||
|
||||
//domain name
|
||||
requestUrlTextView.text = httpModel?.url.absoluteString
|
||||
if requestUrlTextView.text?.contains(serverURL) == true {
|
||||
if #available(iOS 8.2, *) {
|
||||
requestUrlTextView.font = UIFont.systemFont(ofSize: 13, weight: .heavy)
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
requestUrlTextView.font = UIFont.boldSystemFont(ofSize: 13)
|
||||
}
|
||||
} else {
|
||||
if #available(iOS 8.2, *) {
|
||||
requestUrlTextView.font = UIFont.systemFont(ofSize: 13, weight: .regular)
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
requestUrlTextView.font = UIFont.systemFont(ofSize: 13)
|
||||
}
|
||||
}
|
||||
|
||||
//Request method
|
||||
if let method = httpModel?.method {
|
||||
methodLabel.text = "[" + method + "]"
|
||||
}
|
||||
|
||||
//Request time
|
||||
if let startTime = httpModel?.startTime {
|
||||
if (startTime as NSString).doubleValue == 0 {
|
||||
requestTimeTextView.text = _OCLoggerFormat.formatDate(Date())
|
||||
} else {
|
||||
requestTimeTextView.text = _OCLoggerFormat.formatDate(NSDate(timeIntervalSince1970: (startTime as NSString).doubleValue) as Date)
|
||||
}
|
||||
}
|
||||
|
||||
//https://httpcodes.co/status/
|
||||
let successStatusCodes = ["200","201","202","203","204","205","206","207","208","226"]
|
||||
let informationalStatusCodes = ["100","101","102","103","122"]
|
||||
let redirectionStatusCodes = ["300","301","302","303","304","305","306","307","308"]
|
||||
|
||||
//status code
|
||||
statusCodeLabel.text = httpModel?.statusCode
|
||||
|
||||
if successStatusCodes.contains(statusCodeLabel.text ?? "") {
|
||||
statusCodeLabel.textColor = "#42d459".hexColor
|
||||
}
|
||||
else if informationalStatusCodes.contains(statusCodeLabel.text ?? "") {
|
||||
statusCodeLabel.textColor = "#4b8af7".hexColor
|
||||
}
|
||||
else if redirectionStatusCodes.contains(statusCodeLabel.text ?? "") {
|
||||
statusCodeLabel.textColor = "#ff9800".hexColor
|
||||
}
|
||||
else {
|
||||
statusCodeLabel.textColor = "#ff0000".hexColor
|
||||
}
|
||||
|
||||
if statusCodeLabel.text == "0" { //"0" means network unavailable
|
||||
statusCodeLabel.text = "❌"
|
||||
}
|
||||
|
||||
//Whether to display the image label
|
||||
if httpModel?.isImage == true
|
||||
{
|
||||
imageLabel.isHidden = false
|
||||
imageLabel.text = "Image"
|
||||
}
|
||||
else
|
||||
{
|
||||
//js css
|
||||
if let urlString = httpModel?.url.absoluteString {
|
||||
if urlString.suffix(3) == ".js" {
|
||||
imageLabel.isHidden = false
|
||||
imageLabel.text = "JavaScript"
|
||||
} else if urlString.suffix(4) == ".css" {
|
||||
imageLabel.isHidden = false
|
||||
imageLabel.text = "CSS"
|
||||
} else {
|
||||
imageLabel.isHidden = true
|
||||
}
|
||||
} else {
|
||||
imageLabel.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
//tag
|
||||
if httpModel?.isTag == true {
|
||||
self.contentView.backgroundColor = "#007aff".hexColor
|
||||
} else {
|
||||
self.contentView.backgroundColor = .black
|
||||
}
|
||||
|
||||
//isSelected
|
||||
if httpModel?.isSelected == true {
|
||||
statusCodeView.backgroundColor = "#222222".hexColor
|
||||
} else {
|
||||
statusCodeView.backgroundColor = "#333333".hexColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - awakeFromNib
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
imageLabel.backgroundColor = Color.mainGreen
|
||||
requestTimeTextView.textColor = Color.mainGreen
|
||||
|
||||
requestTimeTextView.textContainer.lineFragmentPadding = 0
|
||||
requestTimeTextView.textContainerInset = .zero
|
||||
requestTimeTextView.isSelectable = false
|
||||
|
||||
requestUrlTextView.textContainer.lineFragmentPadding = 0
|
||||
requestUrlTextView.textContainerInset = .zero
|
||||
requestUrlTextView.isSelectable = true
|
||||
|
||||
leftAlignLine.textAlignment = .center
|
||||
leftAlignLine.textColor = .white
|
||||
leftAlignLine.adjustsFontSizeToFitWidth = true
|
||||
if #available(iOS 8.2, *) {
|
||||
leftAlignLine.font = UIFont.systemFont(ofSize: 20, weight: .bold)
|
||||
} else {
|
||||
leftAlignLine.font = UIFont.boldSystemFont(ofSize: 20)
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - layoutSubviews
|
||||
override func layoutSubviews() {
|
||||
superview?.layoutSubviews()
|
||||
|
||||
leftAlignLine.text = String(index + 1)
|
||||
}
|
||||
}
|
||||
83
Pods/CocoaDebug/Sources/Network/NetworkDetailCell.swift
generated
Normal file
83
Pods/CocoaDebug/Sources/Network/NetworkDetailCell.swift
generated
Normal file
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class NetworkDetailCell: UITableViewCell {
|
||||
|
||||
@IBOutlet weak var titleLabel: UILabel!
|
||||
@IBOutlet weak var contentTextView: CustomTextView!
|
||||
@IBOutlet weak var imgView: UIImageView!
|
||||
@IBOutlet weak var titleView: UIView!
|
||||
@IBOutlet weak var topLine: UIView!
|
||||
@IBOutlet weak var middleLine: UIView!
|
||||
@IBOutlet weak var bottomLine: UIView!
|
||||
@IBOutlet weak var editView: UIView!
|
||||
|
||||
@IBOutlet weak var titleViewBottomSpaceToMiddleLine: NSLayoutConstraint!
|
||||
//-12.5
|
||||
|
||||
|
||||
var tapEditViewCallback:((NetworkDetailModel?) -> Void)?
|
||||
|
||||
var detailModel: NetworkDetailModel? {
|
||||
didSet {
|
||||
|
||||
titleLabel.text = detailModel?.title
|
||||
contentTextView.text = detailModel?.content
|
||||
|
||||
//image
|
||||
if detailModel?.image == nil {
|
||||
imgView.isHidden = true
|
||||
} else {
|
||||
imgView.isHidden = false
|
||||
imgView.image = detailModel?.image
|
||||
}
|
||||
|
||||
//Hide content automatically
|
||||
if detailModel?.blankContent == "..." {
|
||||
middleLine.isHidden = true
|
||||
imgView.isHidden = true
|
||||
titleViewBottomSpaceToMiddleLine.constant = -12.5 + 2
|
||||
} else {
|
||||
middleLine.isHidden = false
|
||||
if detailModel?.image != nil {
|
||||
imgView.isHidden = false
|
||||
}
|
||||
titleViewBottomSpaceToMiddleLine.constant = 0
|
||||
}
|
||||
|
||||
//Bottom dividing line
|
||||
if detailModel?.isLast == true {
|
||||
bottomLine.isHidden = false
|
||||
} else {
|
||||
bottomLine.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - awakeFromNib
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
editView.addGestureRecognizer(UITapGestureRecognizer.init(target: self, action: #selector(tapEditView)))
|
||||
|
||||
contentTextView.textContainer.lineFragmentPadding = 0
|
||||
contentTextView.textContainerInset = .zero
|
||||
}
|
||||
|
||||
|
||||
//MARK: - target action
|
||||
//edit
|
||||
@objc func tapEditView() {
|
||||
if let tapEditViewCallback = tapEditViewCallback {
|
||||
tapEditViewCallback(detailModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
104
Pods/CocoaDebug/Sources/Network/NetworkDetailModel.swift
generated
Normal file
104
Pods/CocoaDebug/Sources/Network/NetworkDetailModel.swift
generated
Normal file
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct NetworkDetailModel {
|
||||
var title: String?
|
||||
var content: String?
|
||||
var url: String?
|
||||
var image: UIImage?
|
||||
var blankContent: String?
|
||||
var isLast: Bool = false
|
||||
var requestSerializer: RequestSerializer = RequestSerializer.JSON//default JSON format
|
||||
var requestHeaderFields: [String: Any]?
|
||||
var responseHeaderFields: [String: Any]?
|
||||
var requestData: Data?
|
||||
var responseData: Data?
|
||||
var httpModel: _HttpModel?
|
||||
|
||||
|
||||
init(title: String? = nil, content: String? = "", url: String? = "", image: UIImage? = nil, httpModel: _HttpModel? = nil) {
|
||||
self.title = title?.replacingOccurrences(of: "\\/", with: "/")
|
||||
self.content = content?.replacingOccurrences(of: "\\/", with: "/")
|
||||
self.url = url?.replacingOccurrences(of: "\\/", with: "/")
|
||||
self.image = image
|
||||
self.httpModel = httpModel
|
||||
|
||||
|
||||
|
||||
|
||||
// if title == "REQUEST" {
|
||||
// self.requestData = httpModel?.requestData
|
||||
//
|
||||
// guard let content = content, let url = url, let data = self.requestData, let keys = CocoaDebugSettings.shared.protobufTransferMap?.keys else {return}
|
||||
// if !content.contains("GPBMessage") {return}
|
||||
// self.title = "REQUEST (Protobuf)"
|
||||
//
|
||||
// for key in keys {
|
||||
// if url.contains(key) {
|
||||
// //1.
|
||||
// guard let arr : Array<String> = CocoaDebugSettings.shared.protobufTransferMap?[key] else {return}
|
||||
// if arr.count == 2 {
|
||||
// //2.
|
||||
// let clsNameString = arr[0]
|
||||
// guard let cls : AnyObject.Type = NSClassFromString(clsNameString) else {return}
|
||||
// //protobuf
|
||||
// guard let obj = try? cls.parse(from: data) else {return}
|
||||
// //HuiCao
|
||||
// let jsonString = obj._JSONString(withIgnoreFields: nil)
|
||||
// //pretty print
|
||||
// if let prettyJsonString = jsonString?.jsonStringToPrettyJsonString() {
|
||||
// self.content = prettyJsonString
|
||||
// } else {
|
||||
// self.content = jsonString
|
||||
// }
|
||||
//
|
||||
// self.content = self.content?.replacingOccurrences(of: "\\/", with: "/")
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// else if title == "RESPONSE" {
|
||||
// self.responseData = httpModel?.responseData
|
||||
//
|
||||
// guard let content = content, let url = url, let data = self.responseData, let keys = CocoaDebugSettings.shared.protobufTransferMap?.keys else {return}
|
||||
// if !content.contains("GPBMessage") {return}
|
||||
// self.title = "RESPONSE (Protobuf)"
|
||||
//
|
||||
// for key in keys {
|
||||
// if url.contains(key) {
|
||||
// //1.
|
||||
// guard let arr : Array<String> = CocoaDebugSettings.shared.protobufTransferMap?[key] else {return}
|
||||
// if arr.count == 2 {
|
||||
// //2.
|
||||
// let clsNameString = arr[1]
|
||||
// guard let cls : AnyObject.Type = NSClassFromString(clsNameString) else {return}
|
||||
// //protobuf
|
||||
// guard let obj = try? cls.parse(from: data) else {return}
|
||||
// //HuiCao
|
||||
// let jsonString = obj._JSONString(withIgnoreFields: nil)
|
||||
// //pretty print
|
||||
// if let prettyJsonString = jsonString?.jsonStringToPrettyJsonString() {
|
||||
// self.content = prettyJsonString
|
||||
// } else {
|
||||
// self.content = jsonString
|
||||
// }
|
||||
//
|
||||
// self.content = self.content?.replacingOccurrences(of: "\\/", with: "/")
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
523
Pods/CocoaDebug/Sources/Network/NetworkDetailViewController.swift
generated
Normal file
523
Pods/CocoaDebug/Sources/Network/NetworkDetailViewController.swift
generated
Normal file
@@ -0,0 +1,523 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import MessageUI
|
||||
|
||||
class NetworkDetailViewController: UITableViewController, MFMailComposeViewControllerDelegate {
|
||||
|
||||
@IBOutlet weak var closeItem: UIBarButtonItem!
|
||||
@IBOutlet weak var naviItem: UINavigationItem!
|
||||
|
||||
var naviItemTitleLabel: UILabel?
|
||||
|
||||
var httpModel: _HttpModel?
|
||||
var httpModels: [_HttpModel]?
|
||||
|
||||
var detailModels: [NetworkDetailModel] = [NetworkDetailModel]()
|
||||
|
||||
var requestDictionary: [String: Any]? = Dictionary()
|
||||
|
||||
var headerCell: NetworkCell?
|
||||
|
||||
var messageBody: String = ""
|
||||
|
||||
var justCancelCallback:(() -> Void)?
|
||||
|
||||
static func instanceFromStoryBoard() -> NetworkDetailViewController {
|
||||
let storyboard = UIStoryboard(name: "Network", bundle: Bundle(for: CocoaDebug.self))
|
||||
return storyboard.instantiateViewController(withIdentifier: "NetworkDetailViewController") as! NetworkDetailViewController
|
||||
}
|
||||
|
||||
|
||||
//MARK: - tool
|
||||
func setupModels()
|
||||
{
|
||||
guard let requestSerializer = httpModel?.requestSerializer else {return}
|
||||
var requestContent: String? = nil
|
||||
|
||||
//otherwise it will crash when it is nil
|
||||
if httpModel?.requestData == nil {
|
||||
httpModel?.requestData = Data.init()
|
||||
}
|
||||
if httpModel?.responseData == nil {
|
||||
httpModel?.responseData = Data.init()
|
||||
}
|
||||
|
||||
//detect the request parameter format (JSON/Form)
|
||||
if requestSerializer == RequestSerializer.JSON {
|
||||
//JSON
|
||||
requestContent = httpModel?.requestData.dataToPrettyPrintString()
|
||||
}
|
||||
else if requestSerializer == RequestSerializer.form {
|
||||
if let data = httpModel?.requestData {
|
||||
//1.protobuf
|
||||
// if let message = try? GPBMessage.parse(from: data) {
|
||||
// if message.serializedSize() > 0 {
|
||||
// requestContent = message.description
|
||||
// } else {
|
||||
//2.Form
|
||||
requestContent = data.dataToString()
|
||||
// }
|
||||
// }
|
||||
if requestContent == nil || requestContent == "" || requestContent == "\u{8}\u{1e}" {
|
||||
//3.utf-8 string
|
||||
requestContent = String(data: data, encoding: .utf8)
|
||||
}
|
||||
if requestContent == "" || requestContent == "\u{8}\u{1e}" {
|
||||
requestContent = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if httpModel?.isImage == true {
|
||||
//image:
|
||||
//1.
|
||||
let model_1 = NetworkDetailModel.init(title: "URL", content: "https://github.com/CocoaDebug/CocoaDebug", url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
let model_3 = NetworkDetailModel.init(title: "REQUEST", content: requestContent, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
var model_5 = NetworkDetailModel.init(title: "RESPONSE", content: nil, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
let model_6 = NetworkDetailModel.init(title: "ERROR", content: httpModel?.errorLocalizedDescription, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
let model_7 = NetworkDetailModel.init(title: "ERROR DESCRIPTION", content: httpModel?.errorDescription, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
if let responseData = httpModel?.responseData {
|
||||
model_5 = NetworkDetailModel.init(title: "RESPONSE", content: nil, url: httpModel?.url.absoluteString, image: UIImage.init(gifData: responseData), httpModel: httpModel)
|
||||
}
|
||||
//2.
|
||||
let model_8 = NetworkDetailModel.init(title: "TOTAL TIME", content: httpModel?.totalDuration, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
let model_9 = NetworkDetailModel.init(title: "MIME TYPE", content: httpModel?.mineType, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
var model_2 = NetworkDetailModel.init(title: "REQUEST HEADER", content: nil, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
if let requestHeaderFields = httpModel?.requestHeaderFields {
|
||||
if !requestHeaderFields.isEmpty {
|
||||
model_2 = NetworkDetailModel.init(title: "REQUEST HEADER", content: requestHeaderFields.description, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
model_2.requestHeaderFields = requestHeaderFields
|
||||
model_2.content = String(requestHeaderFields.dictionaryToString()?.dropFirst().dropLast().dropFirst().dropLast().dropFirst().dropFirst() ?? "").replacingOccurrences(of: "\",\n \"", with: "\",\n\"").replacingOccurrences(of: "\\/", with: "/")
|
||||
}
|
||||
}
|
||||
var model_4 = NetworkDetailModel.init(title: "RESPONSE HEADER", content: nil, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
if let responseHeaderFields = httpModel?.responseHeaderFields {
|
||||
if !responseHeaderFields.isEmpty {
|
||||
model_4 = NetworkDetailModel.init(title: "RESPONSE HEADER", content: responseHeaderFields.description, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
model_4.responseHeaderFields = responseHeaderFields
|
||||
model_4.content = String(responseHeaderFields.dictionaryToString()?.dropFirst().dropLast().dropFirst().dropLast().dropFirst().dropFirst() ?? "").replacingOccurrences(of: "\",\n \"", with: "\",\n\"").replacingOccurrences(of: "\\/", with: "/")
|
||||
}
|
||||
}
|
||||
let model_0 = NetworkDetailModel.init(title: "RESPONSE SIZE", content: httpModel?.size, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
//3.
|
||||
detailModels.append(model_1)
|
||||
detailModels.append(model_2)
|
||||
detailModels.append(model_3)
|
||||
detailModels.append(model_4)
|
||||
detailModels.append(model_5)
|
||||
detailModels.append(model_6)
|
||||
detailModels.append(model_7)
|
||||
detailModels.append(model_0)
|
||||
detailModels.append(model_8)
|
||||
detailModels.append(model_9)
|
||||
}
|
||||
else {
|
||||
//not image:
|
||||
//1.
|
||||
let model_1 = NetworkDetailModel.init(title: "URL", content: "https://github.com/CocoaDebug/CocoaDebug", url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
let model_3 = NetworkDetailModel.init(title: "REQUEST", content: requestContent, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
let model_5 = NetworkDetailModel.init(title: "RESPONSE", content: httpModel?.responseData.dataToPrettyPrintString(), url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
let model_6 = NetworkDetailModel.init(title: "ERROR", content: httpModel?.errorLocalizedDescription, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
let model_7 = NetworkDetailModel.init(title: "ERROR DESCRIPTION", content: httpModel?.errorDescription, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
//2.
|
||||
let model_8 = NetworkDetailModel.init(title: "TOTAL TIME", content: httpModel?.totalDuration, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
let model_9 = NetworkDetailModel.init(title: "MIME TYPE", content: httpModel?.mineType, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
var model_2 = NetworkDetailModel.init(title: "REQUEST HEADER", content: nil, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
if let requestHeaderFields = httpModel?.requestHeaderFields {
|
||||
if !requestHeaderFields.isEmpty {
|
||||
model_2 = NetworkDetailModel.init(title: "REQUEST HEADER", content: requestHeaderFields.description, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
model_2.requestHeaderFields = requestHeaderFields
|
||||
model_2.content = String(requestHeaderFields.dictionaryToString()?.dropFirst().dropLast().dropFirst().dropLast().dropFirst().dropFirst() ?? "").replacingOccurrences(of: "\",\n \"", with: "\",\n\"").replacingOccurrences(of: "\\/", with: "/")
|
||||
}
|
||||
}
|
||||
var model_4 = NetworkDetailModel.init(title: "RESPONSE HEADER", content: nil, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
if let responseHeaderFields = httpModel?.responseHeaderFields {
|
||||
if !responseHeaderFields.isEmpty {
|
||||
model_4 = NetworkDetailModel.init(title: "RESPONSE HEADER", content: responseHeaderFields.description, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
model_4.responseHeaderFields = responseHeaderFields
|
||||
model_4.content = String(responseHeaderFields.dictionaryToString()?.dropFirst().dropLast().dropFirst().dropLast().dropFirst().dropFirst() ?? "").replacingOccurrences(of: "\",\n \"", with: "\",\n\"").replacingOccurrences(of: "\\/", with: "/")
|
||||
}
|
||||
}
|
||||
let model_0 = NetworkDetailModel.init(title: "RESPONSE SIZE", content: httpModel?.size, url: httpModel?.url.absoluteString, httpModel: httpModel)
|
||||
//3.
|
||||
detailModels.append(model_1)
|
||||
detailModels.append(model_2)
|
||||
detailModels.append(model_3)
|
||||
detailModels.append(model_4)
|
||||
detailModels.append(model_5)
|
||||
detailModels.append(model_6)
|
||||
detailModels.append(model_7)
|
||||
detailModels.append(model_0)
|
||||
detailModels.append(model_8)
|
||||
detailModels.append(model_9)
|
||||
}
|
||||
}
|
||||
|
||||
//detetc request format (JSON/Form)
|
||||
func detectRequestSerializer() {
|
||||
guard let requestData = httpModel?.requestData else {
|
||||
httpModel?.requestSerializer = RequestSerializer.JSON//default JSON format
|
||||
return
|
||||
}
|
||||
|
||||
if let _ = requestData.dataToDictionary() {
|
||||
//JSON format
|
||||
httpModel?.requestSerializer = RequestSerializer.JSON
|
||||
} else {
|
||||
//Form format
|
||||
httpModel?.requestSerializer = RequestSerializer.form
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//email configure
|
||||
func configureMailComposer(_ copy: Bool = false) -> MFMailComposeViewController? {
|
||||
|
||||
//1.image
|
||||
var img: UIImage? = nil
|
||||
var isImage: Bool = false
|
||||
if let httpModel = httpModel {
|
||||
isImage = httpModel.isImage
|
||||
}
|
||||
|
||||
//2.body message ------------------ start ------------------
|
||||
var string: String = ""
|
||||
messageBody = ""
|
||||
|
||||
for model in detailModels {
|
||||
if let title = model.title, let content = model.content {
|
||||
if content != "" {
|
||||
string = "\n\n" + "------- " + title + " -------" + "\n" + content
|
||||
}
|
||||
}
|
||||
if !messageBody.contains(string) {
|
||||
messageBody.append(string)
|
||||
}
|
||||
//image
|
||||
if isImage == true {
|
||||
if let image = model.image {
|
||||
img = image
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//2.1.url
|
||||
var url: String = ""
|
||||
if let httpModel = httpModel {
|
||||
url = httpModel.url.absoluteString
|
||||
}
|
||||
|
||||
//2.2.method
|
||||
var method: String = ""
|
||||
if let httpModel = httpModel {
|
||||
method = "[" + httpModel.method + "]"
|
||||
}
|
||||
|
||||
//2.3.time
|
||||
var time: String = ""
|
||||
if let httpModel = httpModel {
|
||||
if let startTime = httpModel.startTime {
|
||||
if (startTime as NSString).doubleValue == 0 {
|
||||
time = _OCLoggerFormat.formatDate(Date())
|
||||
} else {
|
||||
time = _OCLoggerFormat.formatDate(NSDate(timeIntervalSince1970: (startTime as NSString).doubleValue) as Date)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//2.4.statusCode
|
||||
var statusCode: String = ""
|
||||
if let httpModel = httpModel {
|
||||
statusCode = httpModel.statusCode
|
||||
if statusCode == "0" { //"0" means network unavailable
|
||||
statusCode = "❌"
|
||||
}
|
||||
}
|
||||
|
||||
//body message ------------------ end ------------------
|
||||
var subString = method + " " + time + " " + "(" + statusCode + ")"
|
||||
if subString.contains("❌") {
|
||||
subString = subString.replacingOccurrences(of: "(", with: "").replacingOccurrences(of: ")", with: "")
|
||||
}
|
||||
|
||||
messageBody = messageBody.replacingOccurrences(of: "https://github.com/CocoaDebug/CocoaDebug", with: url)
|
||||
messageBody = subString + messageBody
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
if !MFMailComposeViewController.canSendMail() {
|
||||
if copy == false {
|
||||
//share via email
|
||||
let alert = UIAlertController.init(title: "No Mail Accounts", message: "Please set up a Mail account in order to send email.", preferredStyle: .alert)
|
||||
let action = UIAlertAction.init(title: "OK", style: .cancel) { _ in
|
||||
}
|
||||
alert.addAction(action)
|
||||
|
||||
alert.popoverPresentationController?.permittedArrowDirections = .init(rawValue: 0)
|
||||
alert.popoverPresentationController?.sourceView = self.view
|
||||
alert.popoverPresentationController?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
|
||||
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
} else {
|
||||
//copy to clipboard
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if copy == true {
|
||||
//copy to clipboard
|
||||
return nil
|
||||
}
|
||||
|
||||
//3.email recipients
|
||||
let mailComposeVC = MFMailComposeViewController()
|
||||
mailComposeVC.mailComposeDelegate = self
|
||||
mailComposeVC.setToRecipients(CocoaDebugSettings.shared.emailToRecipients)
|
||||
mailComposeVC.setCcRecipients(CocoaDebugSettings.shared.emailCcRecipients)
|
||||
|
||||
//4.image
|
||||
if let img = img {
|
||||
if let imageData = img.pngData() {
|
||||
mailComposeVC.addAttachmentData(imageData, mimeType: "image/png", fileName: "image")
|
||||
}
|
||||
}
|
||||
|
||||
//5.body
|
||||
mailComposeVC.setMessageBody(messageBody, isHTML: false)
|
||||
|
||||
//6.subject
|
||||
mailComposeVC.setSubject(url)
|
||||
|
||||
return mailComposeVC
|
||||
}
|
||||
|
||||
|
||||
//MARK: - init
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
naviItemTitleLabel = UILabel.init(frame: CGRect(x: 0, y: 0, width: 80, height: 40))
|
||||
naviItemTitleLabel?.textAlignment = .center
|
||||
naviItemTitleLabel?.textColor = Color.mainGreen
|
||||
naviItemTitleLabel?.font = .boldSystemFont(ofSize: 20)
|
||||
naviItemTitleLabel?.text = "Details"
|
||||
naviItem.titleView = naviItemTitleLabel
|
||||
|
||||
closeItem.tintColor = Color.mainGreen
|
||||
|
||||
//detect the request format (JSON/Form)
|
||||
detectRequestSerializer()
|
||||
|
||||
setupModels()
|
||||
|
||||
if var lastModel = detailModels.last {
|
||||
lastModel.isLast = true
|
||||
detailModels.removeLast()
|
||||
detailModels.append(lastModel)
|
||||
}
|
||||
|
||||
//Use a separate xib-cell file, must be registered, otherwise it will crash
|
||||
let bundle = Bundle(for: type(of: self))
|
||||
let nib = UINib(nibName: "NetworkCell", bundle: bundle)
|
||||
tableView.register(nib, forCellReuseIdentifier: "NetworkCell")
|
||||
|
||||
//header
|
||||
headerCell = bundle.loadNibNamed(String(describing: NetworkCell.self), owner: nil, options: nil)?.first as? NetworkCell
|
||||
headerCell?.httpModel = httpModel
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
if let index = httpModels?.firstIndex(where: { (model) -> Bool in
|
||||
return model.isSelected == true
|
||||
}) {
|
||||
httpModels?[index].isSelected = false
|
||||
}
|
||||
|
||||
httpModel?.isSelected = true
|
||||
|
||||
if let justCancelCallback = justCancelCallback {
|
||||
justCancelCallback()
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - target action
|
||||
@IBAction func close(_ sender: UIBarButtonItem) {
|
||||
(self.navigationController as! CocoaDebugNavigationController).exit()
|
||||
}
|
||||
|
||||
@IBAction func didTapMail(_ sender: UIBarButtonItem) {
|
||||
|
||||
// create an actionSheet
|
||||
let alert: UIAlertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
|
||||
// create an action
|
||||
let firstAction: UIAlertAction = UIAlertAction(title: "share via email", style: .default) { [weak self] action -> Void in
|
||||
if let mailComposeViewController = self?.configureMailComposer() {
|
||||
self?.present(mailComposeViewController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
let secondAction: UIAlertAction = UIAlertAction(title: "copy to clipboard", style: .default) { [weak self] action -> Void in
|
||||
_ = self?.configureMailComposer(true)
|
||||
UIPasteboard.general.string = self?.messageBody
|
||||
}
|
||||
|
||||
let moreAction: UIAlertAction = UIAlertAction(title: "more", style: .default) { [weak self] action -> Void in
|
||||
_ = self?.configureMailComposer(true)
|
||||
let items: [Any] = [self?.messageBody ?? ""]
|
||||
let action = UIActivityViewController(activityItems: items, applicationActivities: nil)
|
||||
if UI_USER_INTERFACE_IDIOM() == .phone {
|
||||
self?.present(action, animated: true, completion: nil)
|
||||
} else {
|
||||
action.popoverPresentationController?.sourceRect = .init(x: self?.view.bounds.midX ?? 0, y: self?.view.bounds.midY ?? 0, width: 0, height: 0)
|
||||
action.popoverPresentationController?.sourceView = self?.view
|
||||
self?.present(action, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .cancel) { action -> Void in
|
||||
}
|
||||
|
||||
// add actions
|
||||
alert.addAction(secondAction)
|
||||
alert.addAction(firstAction)
|
||||
alert.addAction(moreAction)
|
||||
alert.addAction(cancelAction)
|
||||
|
||||
alert.popoverPresentationController?.permittedArrowDirections = .init(rawValue: 0)
|
||||
alert.popoverPresentationController?.sourceView = self.view
|
||||
alert.popoverPresentationController?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
|
||||
|
||||
// present an actionSheet...
|
||||
present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UITableViewDataSource
|
||||
extension NetworkDetailViewController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return detailModels.count
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "NetworkDetailCell", for: indexPath)
|
||||
as! NetworkDetailCell
|
||||
cell.detailModel = detailModels[indexPath.row]
|
||||
|
||||
//2.click edit view
|
||||
cell.tapEditViewCallback = { [weak self] detailModel in
|
||||
let vc = JsonViewController.instanceFromStoryBoard()
|
||||
vc.detailModel = detailModel
|
||||
self?.navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UITableViewDelegate
|
||||
extension NetworkDetailViewController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
|
||||
let detailModel = detailModels[indexPath.row]
|
||||
|
||||
if detailModel.blankContent == "..." {
|
||||
if detailModel.isLast == true {
|
||||
return 50.5
|
||||
}
|
||||
return 50
|
||||
}
|
||||
|
||||
if indexPath.row == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
if detailModel.image == nil {
|
||||
if let content = detailModel.content {
|
||||
if content == "" {
|
||||
return 0
|
||||
}
|
||||
//Calculate NSString height
|
||||
let height = content.height(with: UIFont.systemFont(ofSize: 13), constraintToWidth: (UIScreen.main.bounds.size.width - 30))
|
||||
return height + 70
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
return UIScreen.main.bounds.size.width + 50
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
return headerCell?.contentView
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
guard let serverURL = CocoaDebugSettings.shared.serverURL else {return 0}
|
||||
|
||||
var height: CGFloat = 0.0
|
||||
|
||||
if let cString = httpModel?.url.absoluteString.cString(using: String.Encoding.utf8) {
|
||||
if let content_ = NSString(cString: cString, encoding: String.Encoding.utf8.rawValue) {
|
||||
|
||||
if httpModel?.url.absoluteString.contains(serverURL) == true {
|
||||
//Calculate NSString height
|
||||
if #available(iOS 8.2, *) {
|
||||
height = content_.height(with: UIFont.systemFont(ofSize: 13, weight: .heavy), constraintToWidth: (UIScreen.main.bounds.size.width - 92))
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
height = content_.height(with: UIFont.boldSystemFont(ofSize: 13), constraintToWidth: (UIScreen.main.bounds.size.width - 92))
|
||||
}
|
||||
} else {
|
||||
//Calculate NSString height
|
||||
if #available(iOS 8.2, *) {
|
||||
height = content_.height(with: UIFont.systemFont(ofSize: 13, weight: .regular), constraintToWidth: (UIScreen.main.bounds.size.width - 92))
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
height = content_.height(with: UIFont.systemFont(ofSize: 13), constraintToWidth: (UIScreen.main.bounds.size.width - 92))
|
||||
}
|
||||
}
|
||||
return height + 57
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - MFMailComposeViewControllerDelegate
|
||||
extension NetworkDetailViewController {
|
||||
|
||||
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||
|
||||
controller.dismiss(animated: true) {
|
||||
if error != nil {
|
||||
let alert = UIAlertController.init(title: error?.localizedDescription, message: nil, preferredStyle: .alert)
|
||||
let action = UIAlertAction.init(title: "OK", style: .cancel, handler: { _ in
|
||||
})
|
||||
alert.addAction(action)
|
||||
|
||||
alert.popoverPresentationController?.permittedArrowDirections = .init(rawValue: 0)
|
||||
alert.popoverPresentationController?.sourceView = self.view
|
||||
alert.popoverPresentationController?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
|
||||
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
298
Pods/CocoaDebug/Sources/Network/NetworkViewController.swift
generated
Normal file
298
Pods/CocoaDebug/Sources/Network/NetworkViewController.swift
generated
Normal file
@@ -0,0 +1,298 @@
|
||||
//
|
||||
// CocoaDebug
|
||||
// liman
|
||||
//
|
||||
// Created by liman 02/02/2023.
|
||||
// Copyright © 2023 liman. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class NetworkViewController: UIViewController {
|
||||
|
||||
var reachEnd: Bool = true
|
||||
var firstIn: Bool = true
|
||||
var reloadDataFinish: Bool = true
|
||||
|
||||
var models: Array<_HttpModel>?
|
||||
var cacheModels: Array<_HttpModel>?
|
||||
var searchModels: Array<_HttpModel>?
|
||||
|
||||
var naviItemTitleLabel: UILabel?
|
||||
|
||||
@IBOutlet weak var tableView: UITableView!
|
||||
@IBOutlet weak var searchBar: UISearchBar!
|
||||
@IBOutlet weak var deleteItem: UIBarButtonItem!
|
||||
@IBOutlet weak var naviItem: UINavigationItem!
|
||||
|
||||
//MARK: - tool
|
||||
//搜索逻辑
|
||||
func searchLogic(_ searchText: String = "") {
|
||||
guard let cacheModels = cacheModels else {return}
|
||||
searchModels = cacheModels
|
||||
|
||||
if searchText == "" {
|
||||
models = cacheModels
|
||||
} else {
|
||||
guard let searchModels = searchModels else {return}
|
||||
|
||||
for _ in searchModels {
|
||||
if let index = self.searchModels?.firstIndex(where: { (model) -> Bool in
|
||||
return !model.url.absoluteString.lowercased().contains(searchText.lowercased())//忽略大小写
|
||||
}) {
|
||||
self.searchModels?.remove(at: index)
|
||||
}
|
||||
}
|
||||
models = self.searchModels
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - private
|
||||
func reloadHttp(needScrollToEnd: Bool = false) {
|
||||
|
||||
if reloadDataFinish == false {return}
|
||||
|
||||
if searchBar.isHidden != false {
|
||||
searchBar.isHidden = false
|
||||
}
|
||||
|
||||
self.models = (_HttpDatasource.shared().httpModels as NSArray as? [_HttpModel])
|
||||
self.cacheModels = self.models
|
||||
|
||||
self.searchLogic(CocoaDebugSettings.shared.networkSearchWord ?? "")
|
||||
|
||||
// dispatch_main_async_safe { [weak self] in
|
||||
self.reloadDataFinish = false
|
||||
self.tableView.reloadData {
|
||||
self.reloadDataFinish = true
|
||||
}
|
||||
|
||||
if needScrollToEnd == false {return}
|
||||
|
||||
//table下滑到底部
|
||||
if let count = self.models?.count {
|
||||
if count > 0 {
|
||||
// guard let firstIn = self.firstIn else {return}
|
||||
self.tableView.tableViewScrollToBottom(animated: !firstIn)
|
||||
self.firstIn = false
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
//MARK: - init
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
let tap = UITapGestureRecognizer.init(target: self, action: #selector(didTapView))
|
||||
tap.cancelsTouchesInView = false
|
||||
view.addGestureRecognizer(tap)
|
||||
|
||||
naviItemTitleLabel = UILabel.init(frame: CGRect(x: 0, y: 0, width: 80, height: 40))
|
||||
naviItemTitleLabel?.textAlignment = .center
|
||||
naviItemTitleLabel?.textColor = Color.mainGreen
|
||||
naviItemTitleLabel?.font = .boldSystemFont(ofSize: 20)
|
||||
naviItem.titleView = naviItemTitleLabel
|
||||
|
||||
naviItemTitleLabel?.text = "🚀[0]"
|
||||
deleteItem.tintColor = Color.mainGreen
|
||||
|
||||
//notification
|
||||
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "reloadHttp_CocoaDebug"), object: nil, queue: OperationQueue.main) { [weak self] _ in
|
||||
self?.reloadHttp(needScrollToEnd: self?.reachEnd ?? true)
|
||||
}
|
||||
|
||||
|
||||
tableView.tableFooterView = UIView()
|
||||
tableView.dataSource = self
|
||||
tableView.delegate = self
|
||||
|
||||
//抖动bug
|
||||
tableView.estimatedRowHeight = 0
|
||||
tableView.estimatedSectionHeaderHeight = 0
|
||||
tableView.estimatedSectionFooterHeight = 0
|
||||
|
||||
searchBar.delegate = self
|
||||
searchBar.text = CocoaDebugSettings.shared.networkSearchWord
|
||||
searchBar.isHidden = true
|
||||
|
||||
//hide searchBar icon
|
||||
let textFieldInsideSearchBar = searchBar.value(forKey: "searchField") as! UITextField
|
||||
textFieldInsideSearchBar.leftViewMode = .never
|
||||
textFieldInsideSearchBar.leftView = nil
|
||||
textFieldInsideSearchBar.backgroundColor = .white
|
||||
textFieldInsideSearchBar.returnKeyType = .default
|
||||
|
||||
reloadHttp(needScrollToEnd: true)
|
||||
|
||||
if models?.count ?? 0 > CocoaDebugSettings.shared.networkLastIndex && CocoaDebugSettings.shared.networkLastIndex > 0 {
|
||||
tableView.tableViewScrollToIndex(index: CocoaDebugSettings.shared.networkLastIndex, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
searchBar.resignFirstResponder()
|
||||
}
|
||||
|
||||
deinit {
|
||||
//notification
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
//MARK: - target action
|
||||
@IBAction func didTapDown(_ sender: Any) {
|
||||
tableView.tableViewScrollToBottom(animated: true)
|
||||
searchBar.resignFirstResponder()
|
||||
reachEnd = true
|
||||
CocoaDebugSettings.shared.networkLastIndex = 0
|
||||
}
|
||||
|
||||
@IBAction func didTapUp(_ sender: Any) {
|
||||
tableView.tableViewScrollToHeader(animated: true)
|
||||
searchBar.resignFirstResponder()
|
||||
reachEnd = false
|
||||
CocoaDebugSettings.shared.networkLastIndex = 0
|
||||
}
|
||||
|
||||
|
||||
@IBAction func tapTrashButton(_ sender: UIBarButtonItem) {
|
||||
_HttpDatasource.shared().reset()
|
||||
models = []
|
||||
cacheModels = []
|
||||
// searchBar.text = nil
|
||||
searchBar.resignFirstResponder()
|
||||
// CocoaDebugSettings.shared.networkSearchWord = nil
|
||||
CocoaDebugSettings.shared.networkLastIndex = 0
|
||||
|
||||
// dispatch_main_async_safe { [weak self] in
|
||||
self.tableView.reloadData()
|
||||
self.naviItemTitleLabel?.text = "🚀[0]"
|
||||
// }
|
||||
|
||||
NotificationCenter.default.post(name: NSNotification.Name("deleteAllLogs_CocoaDebug"), object: nil, userInfo: nil)
|
||||
}
|
||||
|
||||
@objc func didTapView() {
|
||||
searchBar.resignFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UITableViewDataSource
|
||||
extension NetworkViewController: UITableViewDataSource {
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if let count = models?.count {
|
||||
naviItemTitleLabel?.text = "🚀[" + String(count) + "]"
|
||||
return count
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "NetworkCell", for: indexPath)
|
||||
as! NetworkCell
|
||||
|
||||
cell.httpModel = models?[indexPath.row]
|
||||
cell.index = indexPath.row
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UITableViewDelegate
|
||||
extension NetworkViewController: UITableViewDelegate {
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
|
||||
guard let serverURL = CocoaDebugSettings.shared.serverURL else {return 0}
|
||||
let model = models?[indexPath.row]
|
||||
var height: CGFloat = 0.0
|
||||
|
||||
if let cString = model?.url.absoluteString.cString(using: String.Encoding.utf8) {
|
||||
if let content_ = NSString(cString: cString, encoding: String.Encoding.utf8.rawValue) {
|
||||
|
||||
if model?.url.absoluteString.contains(serverURL) == true {
|
||||
//计算NSString高度
|
||||
if #available(iOS 8.2, *) {
|
||||
height = content_.height(with: UIFont.systemFont(ofSize: 13, weight: .heavy), constraintToWidth: (UIScreen.main.bounds.size.width - 92))
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
height = content_.height(with: UIFont.boldSystemFont(ofSize: 13), constraintToWidth: (UIScreen.main.bounds.size.width - 92))
|
||||
}
|
||||
} else {
|
||||
//计算NSString高度
|
||||
if #available(iOS 8.2, *) {
|
||||
height = content_.height(with: UIFont.systemFont(ofSize: 13, weight: .regular), constraintToWidth: (UIScreen.main.bounds.size.width - 92))
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
height = content_.height(with: UIFont.systemFont(ofSize: 13), constraintToWidth: (UIScreen.main.bounds.size.width - 92))
|
||||
}
|
||||
}
|
||||
|
||||
return height + 57
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
|
||||
{
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
searchBar.resignFirstResponder()
|
||||
reachEnd = false
|
||||
|
||||
guard let models = models else {return}
|
||||
|
||||
let vc: NetworkDetailViewController = NetworkDetailViewController.instanceFromStoryBoard()
|
||||
vc.httpModels = models
|
||||
vc.httpModel = models[indexPath.row]
|
||||
self.navigationController?.pushViewController(vc, animated: true)
|
||||
|
||||
vc.justCancelCallback = { [weak self] in
|
||||
self?.tableView.reloadData()
|
||||
}
|
||||
|
||||
CocoaDebugSettings.shared.networkLastIndex = indexPath.row
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UIScrollViewDelegate
|
||||
extension NetworkViewController: UIScrollViewDelegate {
|
||||
|
||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
searchBar.resignFirstResponder()
|
||||
reachEnd = false
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if (scrollView.contentOffset.y + 1) >= (scrollView.contentSize.height - scrollView.frame.size.height) {
|
||||
//bottom reached
|
||||
reachEnd = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UISearchBarDelegate
|
||||
extension NetworkViewController: UISearchBarDelegate {
|
||||
|
||||
func searchBarSearchButtonClicked(_ searchBar: UISearchBar)
|
||||
{
|
||||
searchBar.resignFirstResponder()
|
||||
}
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
|
||||
{
|
||||
CocoaDebugSettings.shared.networkSearchWord = searchText
|
||||
searchLogic(searchText)
|
||||
|
||||
// dispatch_main_async_safe { [weak self] in
|
||||
self.tableView.reloadData()
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
27
Pods/CocoaDebug/Sources/Network/_HttpDatasource.h
generated
Executable file
27
Pods/CocoaDebug/Sources/Network/_HttpDatasource.h
generated
Executable file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "_HttpModel.h"
|
||||
|
||||
@interface _HttpDatasource : NSObject
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray<_HttpModel *> *httpModels;
|
||||
|
||||
+ (instancetype)shared;
|
||||
|
||||
///记录
|
||||
- (BOOL)addHttpRequset:(_HttpModel*)model;
|
||||
|
||||
///清空
|
||||
- (void)reset;
|
||||
|
||||
///删除
|
||||
- (void)remove:(_HttpModel *)model;
|
||||
|
||||
@end
|
||||
86
Pods/CocoaDebug/Sources/Network/_HttpDatasource.m
generated
Executable file
86
Pods/CocoaDebug/Sources/Network/_HttpDatasource.m
generated
Executable file
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_HttpDatasource.h"
|
||||
#import "_NetworkHelper.h"
|
||||
|
||||
@implementation _HttpDatasource
|
||||
|
||||
+ (instancetype)shared
|
||||
{
|
||||
static id sharedInstance = nil;
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[self alloc] init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.httpModels = [NSMutableArray arrayWithCapacity:1000 + 100];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)addHttpRequset:(_HttpModel*)model
|
||||
{
|
||||
if ([model.url.absoluteString isEqualToString:@""]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
//url Filter, ignore case
|
||||
for (NSString *urlString in [[_NetworkHelper shared] ignoredURLs]) {
|
||||
if ([[model.url.absoluteString lowercaseString] containsString:[urlString lowercaseString]]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
//Maximum number limit
|
||||
if (self.httpModels.count >= 1000) {
|
||||
if ([self.httpModels count] > 0) {
|
||||
[self.httpModels removeObjectAtIndex:0];
|
||||
}
|
||||
}
|
||||
|
||||
//detect repeated
|
||||
__block BOOL isExist = NO;
|
||||
[self.httpModels enumerateObjectsUsingBlock:^(_HttpModel *obj, NSUInteger index, BOOL *stop) {
|
||||
if ([obj.requestId isEqualToString:model.requestId]) {
|
||||
isExist = YES;
|
||||
}
|
||||
}];
|
||||
if (!isExist) {
|
||||
[self.httpModels addObject:model];
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)reset
|
||||
{
|
||||
[self.httpModels removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)remove:(_HttpModel *)model
|
||||
{
|
||||
[self.httpModels enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(_HttpModel *obj, NSUInteger index, BOOL *stop) {
|
||||
if ([obj.requestId isEqualToString:model.requestId]) {
|
||||
[self.httpModels removeObjectAtIndex:index];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
41
Pods/CocoaDebug/Sources/Network/_HttpModel.h
generated
Normal file
41
Pods/CocoaDebug/Sources/Network/_HttpModel.h
generated
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, RequestSerializer) {
|
||||
RequestSerializerJSON = 0, //JSON format
|
||||
RequestSerializerForm //Form format
|
||||
};
|
||||
|
||||
@interface _HttpModel : NSObject
|
||||
|
||||
@property (nonatomic,strong)NSURL *url;
|
||||
@property (nonatomic,copy)NSData *requestData;
|
||||
@property (nonatomic,copy)NSData *responseData;
|
||||
@property (nonatomic,copy)NSString *requestId;
|
||||
@property (nonatomic,copy)NSString *method;
|
||||
@property (nonatomic,copy)NSString *statusCode;
|
||||
@property (nonatomic,copy)NSString *mineType;
|
||||
@property (nonatomic,copy)NSString *startTime;
|
||||
@property (nonatomic,copy)NSString *endTime;
|
||||
@property (nonatomic,copy)NSString *totalDuration;
|
||||
@property (nonatomic,assign)BOOL isImage;
|
||||
|
||||
|
||||
@property (nonatomic,copy)NSDictionary<NSString*, id> *requestHeaderFields;
|
||||
@property (nonatomic,copy)NSDictionary<NSString*, id> *responseHeaderFields;
|
||||
@property (nonatomic,assign)BOOL isTag;
|
||||
@property (nonatomic,assign)BOOL isSelected;
|
||||
@property (nonatomic,assign)RequestSerializer requestSerializer;//default JSON format
|
||||
@property (nonatomic,copy)NSString *errorDescription;
|
||||
@property (nonatomic,copy)NSString *errorLocalizedDescription;
|
||||
@property (nonatomic,copy)NSString *size;
|
||||
|
||||
@end
|
||||
25
Pods/CocoaDebug/Sources/Network/_HttpModel.m
generated
Normal file
25
Pods/CocoaDebug/Sources/Network/_HttpModel.m
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_HttpModel.h"
|
||||
|
||||
@implementation _HttpModel
|
||||
|
||||
//default value for @property
|
||||
- (id)init {
|
||||
if (self = [super init]) {
|
||||
self.statusCode = @"0";
|
||||
self.url = [[NSURL alloc] initWithString:@""];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
41
Pods/CocoaDebug/Sources/Network/_NetworkHelper.h
generated
Executable file
41
Pods/CocoaDebug/Sources/Network/_NetworkHelper.h
generated
Executable file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface _NetworkHelper : NSObject
|
||||
|
||||
//color for objc
|
||||
@property (nonatomic, strong) UIColor *mainColor;
|
||||
|
||||
//Set domain names not to be crawled, ignore case, and crawl all by default
|
||||
@property (nonatomic, copy) NSArray<NSString *> *ignoredURLs;
|
||||
|
||||
//Set only the domain name to be crawled, ignore case, and crawl all by default
|
||||
@property (nonatomic, copy) NSArray<NSString *> *onlyURLs;
|
||||
|
||||
//Set the log prefix not to be crawled, ignore case, and crawl all by default
|
||||
@property (nonatomic, copy) NSArray<NSString *> *ignoredPrefixLogs;
|
||||
|
||||
//Set the log prefix to be crawled, ignore case, and crawl all by default
|
||||
@property (nonatomic, copy) NSArray<NSString *> *onlyPrefixLogs;
|
||||
|
||||
//protobuf
|
||||
@property (nonatomic, copy) NSDictionary<NSString *, NSArray<NSString*> *> *protobufTransferMap;
|
||||
|
||||
//
|
||||
@property (nonatomic, assign) BOOL isNetworkEnable;
|
||||
|
||||
//
|
||||
- (void)enable;
|
||||
- (void)disable;
|
||||
|
||||
+ (instancetype)shared;
|
||||
|
||||
@end
|
||||
56
Pods/CocoaDebug/Sources/Network/_NetworkHelper.m
generated
Executable file
56
Pods/CocoaDebug/Sources/Network/_NetworkHelper.m
generated
Executable file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_NetworkHelper.h"
|
||||
#import "_CustomHTTPProtocol.h"
|
||||
#import "NSObject+CocoaDebug.h"
|
||||
|
||||
@interface _NetworkHelper()
|
||||
|
||||
@end
|
||||
|
||||
@implementation _NetworkHelper
|
||||
|
||||
+ (instancetype)shared
|
||||
{
|
||||
static id sharedInstance = nil;
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[self alloc] init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
//default value for @property
|
||||
- (id)init {
|
||||
if (self = [super init]) {
|
||||
self.mainColor = [UIColor colorFromHexString:@"#42d459"];
|
||||
self.isNetworkEnable = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)enable {
|
||||
if (self.isNetworkEnable) {
|
||||
return;
|
||||
}
|
||||
self.isNetworkEnable = YES;
|
||||
[_CustomHTTPProtocol start];
|
||||
}
|
||||
|
||||
- (void)disable {
|
||||
if (!self.isNetworkEnable) {
|
||||
return;
|
||||
}
|
||||
self.isNetworkEnable = NO;
|
||||
[_CustomHTTPProtocol stop];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user