update
@@ -43,6 +43,7 @@
|
||||
794FBB1F2A92F7C300D57BB8 /* WebViewTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794FBB1E2A92F7C300D57BB8 /* WebViewTool.swift */; };
|
||||
794FBB212A934E0A00D57BB8 /* AppUpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794FBB202A934E0A00D57BB8 /* AppUpdateView.swift */; };
|
||||
794FBB232A93519800D57BB8 /* AppUpdateTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794FBB222A93519800D57BB8 /* AppUpdateTool.swift */; };
|
||||
795FF3E02AA991E70058454C /* appointmentOrder.wav in Resources */ = {isa = PBXBuildFile; fileRef = 795FF3DF2AA991E70058454C /* appointmentOrder.wav */; };
|
||||
79CB07CC2AA8465A00154B61 /* UserPermission.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79CB07CB2AA8465A00154B61 /* UserPermission.swift */; };
|
||||
79CECC122A89BD1A00B95D8B /* MessageCenterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79CECC112A89BD1A00B95D8B /* MessageCenterController.swift */; };
|
||||
79CECC192A89EE6A00B95D8B /* ReviewFailedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79CECC182A89EE6A00B95D8B /* ReviewFailedController.swift */; };
|
||||
@@ -142,6 +143,7 @@
|
||||
794FBB1E2A92F7C300D57BB8 /* WebViewTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewTool.swift; sourceTree = "<group>"; };
|
||||
794FBB202A934E0A00D57BB8 /* AppUpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateView.swift; sourceTree = "<group>"; };
|
||||
794FBB222A93519800D57BB8 /* AppUpdateTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateTool.swift; sourceTree = "<group>"; };
|
||||
795FF3DF2AA991E70058454C /* appointmentOrder.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = appointmentOrder.wav; sourceTree = "<group>"; };
|
||||
79CB07CB2AA8465A00154B61 /* UserPermission.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPermission.swift; sourceTree = "<group>"; };
|
||||
79CECC112A89BD1A00B95D8B /* MessageCenterController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCenterController.swift; sourceTree = "<group>"; };
|
||||
79CECC182A89EE6A00B95D8B /* ReviewFailedController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewFailedController.swift; sourceTree = "<group>"; };
|
||||
@@ -637,6 +639,7 @@
|
||||
79DD0DAB2A94A0EE00768FE7 /* Source */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
795FF3DF2AA991E70058454C /* appointmentOrder.wav */,
|
||||
79FB760E2A9C6CAF00DB00A4 /* newJuheOrderAudio.wav */,
|
||||
79FB76122A9C6CC100DB00A4 /* newOrderAudio.wav */,
|
||||
);
|
||||
@@ -863,6 +866,7 @@
|
||||
files = (
|
||||
79FB76102A9C6CAF00DB00A4 /* newJuheOrderAudio.wav in Resources */,
|
||||
79EAD8172A7B86610036E093 /* LaunchScreen.storyboard in Resources */,
|
||||
795FF3E02AA991E70058454C /* appointmentOrder.wav in Resources */,
|
||||
79EAD8142A7B86610036E093 /* Assets.xcassets in Resources */,
|
||||
79FB76132A9C6CC100DB00A4 /* newOrderAudio.wav in Resources */,
|
||||
);
|
||||
|
||||
@@ -253,8 +253,8 @@
|
||||
filePath = "OrderScheduling/Main/Initial.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "155"
|
||||
endingLineNumber = "155"
|
||||
startingLineNumber = "146"
|
||||
endingLineNumber = "146"
|
||||
landmarkName = "networkErrorHandler(response:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
@@ -269,8 +269,8 @@
|
||||
filePath = "OrderScheduling/Main/Initial.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "156"
|
||||
endingLineNumber = "156"
|
||||
startingLineNumber = "147"
|
||||
endingLineNumber = "147"
|
||||
landmarkName = "Initial"
|
||||
landmarkType = "21">
|
||||
<Locations>
|
||||
@@ -333,8 +333,8 @@
|
||||
filePath = "OrderScheduling/Rescue/ViewController/DispatchOrderController.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "315"
|
||||
endingLineNumber = "315"
|
||||
startingLineNumber = "359"
|
||||
endingLineNumber = "359"
|
||||
landmarkName = "dd_mapView(_:didAnnotationViewTapped:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
@@ -1252,8 +1252,8 @@
|
||||
filePath = "OrderScheduling/Main/AppDelegate.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "149"
|
||||
endingLineNumber = "149"
|
||||
startingLineNumber = "152"
|
||||
endingLineNumber = "152"
|
||||
landmarkName = "jpushNotificationAuthorization(_:withInfo:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
@@ -1428,8 +1428,8 @@
|
||||
filePath = "OrderScheduling/Main/Initial.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "138"
|
||||
endingLineNumber = "138"
|
||||
startingLineNumber = "130"
|
||||
endingLineNumber = "130"
|
||||
landmarkName = "changeRootViewController(window:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
@@ -2044,8 +2044,8 @@
|
||||
filePath = "OrderScheduling/Main/Initial.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "129"
|
||||
endingLineNumber = "129"
|
||||
startingLineNumber = "121"
|
||||
endingLineNumber = "121"
|
||||
landmarkName = "changeRootViewController(window:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
@@ -2172,8 +2172,8 @@
|
||||
filePath = "OrderScheduling/Main/Initial.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "147"
|
||||
endingLineNumber = "147"
|
||||
startingLineNumber = "138"
|
||||
endingLineNumber = "138"
|
||||
landmarkName = "Initial"
|
||||
landmarkType = "3">
|
||||
</BreakpointContent>
|
||||
@@ -3283,8 +3283,8 @@
|
||||
filePath = "OrderScheduling/Main/AppDelegate.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "148"
|
||||
endingLineNumber = "148"
|
||||
startingLineNumber = "151"
|
||||
endingLineNumber = "151"
|
||||
landmarkName = "jpushNotificationAuthorization(_:withInfo:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
@@ -3907,8 +3907,8 @@
|
||||
filePath = "OrderSchedulingNotificationService/NotificationService.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "65"
|
||||
endingLineNumber = "65"
|
||||
startingLineNumber = "68"
|
||||
endingLineNumber = "68"
|
||||
landmarkName = "didReceive(_:withContentHandler:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
@@ -4019,8 +4019,8 @@
|
||||
filePath = "OrderScheduling/Main/Initial.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "73"
|
||||
endingLineNumber = "73"
|
||||
startingLineNumber = "65"
|
||||
endingLineNumber = "65"
|
||||
landmarkName = "initCall()"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
@@ -4137,5 +4137,53 @@
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "756D03C7-2C2D-426D-82BD-B991BC9F37C0"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "OrderScheduling/Main/AppDelegate.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "148"
|
||||
endingLineNumber = "148"
|
||||
landmarkName = "jpushNotificationAuthorization(_:withInfo:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "5A81CE66-448A-461D-AB6F-F419C61BA852"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "../../PodRepository/DDAudioPlayerKit_Private/DDAudioPlayerKit_Private/Classes/DDSpeechSynthesizer.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "19"
|
||||
endingLineNumber = "19"
|
||||
landmarkName = "play(text:name:numberOfLoops:timeInterval:eachLoopCompletionBlock:endCompletionBlock:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "35D6B500-910A-4706-952A-5B406AF3B6D4"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "OrderScheduling/Global/Tool/Tool.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "200"
|
||||
endingLineNumber = "200"
|
||||
landmarkName = "playVoiceWith(userInfo:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
||||
|
||||
BIN
OrderScheduling/.DS_Store
vendored
@@ -46,7 +46,7 @@ class WebViewController : ZDViewController {
|
||||
webView.uiDelegate = self
|
||||
view.addSubview(webView)
|
||||
|
||||
let request = URLRequest(url: URL(string: url!)!)
|
||||
let request = URLRequest(url: URL(string: url!)!,cachePolicy: .reloadIgnoringLocalAndRemoteCacheData)
|
||||
webView.load(request)
|
||||
|
||||
if showNavBar == false {
|
||||
|
||||
@@ -44,6 +44,19 @@ open class GroupData {
|
||||
return false
|
||||
}
|
||||
|
||||
// 判断新推送是不是预约订单
|
||||
func isAppointmentPushNotification(userInfo:[AnyHashable:Any]?) -> Bool {
|
||||
if let userInfo {
|
||||
if let dict = parserAlertToDictionary(userInfo: userInfo), dict[pushNoti_title_key] == pushNoti_appointmentOrder_key {
|
||||
return true
|
||||
}
|
||||
if let string = parserAlertToString(userInfo: userInfo), string.contains(pushNoti_appointmentOrder_key) == true {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 解析需要播放的文字
|
||||
func parserBroadcastToString(userInfo:[AnyHashable:Any]?) -> String? {
|
||||
if let broadcastKeywords = (userInfo?[pushNoti_broadcastKeywords_key] as? String),broadcastKeywords.isEmpty == false {
|
||||
@@ -86,17 +99,17 @@ open class GroupData {
|
||||
groudUserDefault?.set(newMessageSound, forKey: newMessageSound_key)
|
||||
}
|
||||
|
||||
// 不需要记录
|
||||
public var newMessageAlert : Bool {
|
||||
return (groudUserDefault?.object(forKey: newMessageAlert_key) as? Bool) ?? true
|
||||
}
|
||||
|
||||
func setNewMessageAlert(newMessageAlert:Bool?) {
|
||||
groudUserDefault?.set(newMessageAlert, forKey: newMessageAlert_key)
|
||||
}
|
||||
// // 不需要记录
|
||||
// public var newMessageAlert : Bool {
|
||||
// return (groudUserDefault?.object(forKey: newMessageAlert_key) as? Bool) ?? true
|
||||
// }
|
||||
//
|
||||
// func setNewMessageAlert(newMessageAlert:Bool?) {
|
||||
// groudUserDefault?.set(newMessageAlert, forKey: newMessageAlert_key)
|
||||
// }
|
||||
|
||||
func clear() {
|
||||
GROUP.setNewMessageAlert(newMessageAlert: nil)
|
||||
// GROUP.setNewMessageAlert(newMessageAlert: nil)
|
||||
GROUP.setNewMessageSound(newMessageSound: nil)
|
||||
GROUP.setBestAttemptContentUserInfo(bestAttemptContentUserInfo: nil)
|
||||
}
|
||||
|
||||
@@ -167,9 +167,9 @@ open class Tool {
|
||||
}
|
||||
|
||||
func playAlertWith(userInfo:[AnyHashable:Any]?) {
|
||||
if GROUP.newMessageAlert != true {
|
||||
return
|
||||
}
|
||||
// if GROUP.newMessageAlert != true {
|
||||
// return
|
||||
// }
|
||||
if GROUP.isOrderPushNotification(userInfo: userInfo) == true {
|
||||
ACCEPTORDERTOOL.open(userInfo: userInfo)
|
||||
}else{
|
||||
|
||||
@@ -141,9 +141,12 @@ extension AppDelegate : JPUSHRegisterDelegate {
|
||||
|
||||
func jpushNotificationAuthorization(_ status: JPAuthorizationStatus, withInfo info: [AnyHashable : Any]?) {
|
||||
DDLog(message:"通知授权状态\(status)")
|
||||
USER.regId = JPUSHService.registrationID()
|
||||
DDLog(message:"registrationID:\(JPUSHService.registrationID())")
|
||||
|
||||
JPUSHService.registrationIDCompletionHandler { errorCode, registrationID in
|
||||
USER.regId = registrationID
|
||||
DDLog(message:"registrationID:\(JPUSHService.registrationID())")
|
||||
}
|
||||
|
||||
if status != .statusAuthorized {
|
||||
NOTIAUTHTOOL.requestNotiAuthRelay.accept(nil)
|
||||
}else{
|
||||
|
||||
@@ -14,9 +14,6 @@ import Alamofire
|
||||
import DDToastKit_Private
|
||||
import DDCategoryKit_Private
|
||||
import DDControlsKit_Private
|
||||
#if DEBUG
|
||||
import CocoaDebug
|
||||
#endif
|
||||
import RxSwift
|
||||
|
||||
class Initial : NSObject {
|
||||
@@ -39,8 +36,9 @@ class Initial : NSObject {
|
||||
// af
|
||||
initAF()
|
||||
|
||||
// cocoadebug
|
||||
initCocoaDebug()
|
||||
// initCall
|
||||
initCall()
|
||||
|
||||
}
|
||||
|
||||
func initIQKeyboard(){
|
||||
@@ -62,12 +60,6 @@ class Initial : NSObject {
|
||||
DDAF.delegate = self
|
||||
}
|
||||
|
||||
func initCocoaDebug() {
|
||||
#if DEBUG
|
||||
CocoaDebug.hideBubble()
|
||||
#endif
|
||||
}
|
||||
|
||||
func initCall() {
|
||||
SC.callEvent { (isCallSuccess,callTime,duration) in
|
||||
SC.uploadRelay.accept(nil)
|
||||
@@ -139,7 +131,6 @@ class Initial : NSObject {
|
||||
self?.initRefreshToken()
|
||||
self?.initPermission()
|
||||
self?.initAppUpdate()
|
||||
self?.initCall()
|
||||
window?.rootViewController = MainTabBarController.init()
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ extension NotificationSetUpController : UITableViewDelegate,UITableViewDataSourc
|
||||
if model.type == .sound {
|
||||
GROUP.setNewMessageSound(newMessageSound: isOn)
|
||||
}else if model.type == .alert {
|
||||
GROUP.setNewMessageAlert(newMessageAlert: isOn)
|
||||
// GROUP.setNewMessageAlert(newMessageAlert: isOn)
|
||||
}
|
||||
})
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
@@ -252,6 +252,50 @@ extension DispatchOrderController : UITableViewDelegate,UITableViewDataSource {
|
||||
self?.dispatchOrderRelay.accept(model)
|
||||
})
|
||||
.disposed(by: cell!.disposeBag)
|
||||
|
||||
if USERP.canDealWith == true {
|
||||
cell?.dispatchButton.isHidden = false
|
||||
cell?.stateLabel.snp.updateConstraints({ make in
|
||||
make.width.equalTo(auto(97))
|
||||
})
|
||||
|
||||
cell?.name.snp.updateConstraints({ make in
|
||||
make.width.equalTo(auto(40))
|
||||
})
|
||||
|
||||
cell?.phone.snp.updateConstraints({ make in
|
||||
make.width.equalTo(auto(100))
|
||||
})
|
||||
|
||||
cell?.distance.snp.updateConstraints({ make in
|
||||
make.width.equalTo(auto(60))
|
||||
})
|
||||
|
||||
cell?.dispatchButton.snp.updateConstraints({ make in
|
||||
make.width.equalTo(auto(50))
|
||||
})
|
||||
}else{
|
||||
cell?.dispatchButton.isHidden = true
|
||||
cell?.stateLabel.snp.updateConstraints({ make in
|
||||
make.width.equalTo(auto(107.5))
|
||||
})
|
||||
|
||||
cell?.name.snp.updateConstraints({ make in
|
||||
make.width.equalTo(auto(65))
|
||||
})
|
||||
|
||||
cell?.phone.snp.updateConstraints({ make in
|
||||
make.width.equalTo(auto(112.5))
|
||||
})
|
||||
|
||||
cell?.distance.snp.updateConstraints({ make in
|
||||
make.width.equalTo(auto(60))
|
||||
})
|
||||
|
||||
cell?.dispatchButton.snp.updateConstraints({ make in
|
||||
make.width.equalTo(0)
|
||||
})
|
||||
}
|
||||
return cell!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -753,7 +753,7 @@ extension RescuePendingDispatchController : UITableViewDelegate,UITableViewDataS
|
||||
if USERP.canWaitdispatchBtn == true {
|
||||
cell?.dispatchButton.isHidden = false
|
||||
cell?.dispatchButton.snp.updateConstraints({ make in
|
||||
make.right.equalTo(auto(10))
|
||||
make.right.equalTo(-auto(10))
|
||||
make.width.equalTo(auto(80))
|
||||
})
|
||||
}else{
|
||||
|
||||
BIN
OrderScheduling/Source/appointmentOrder.wav
Normal file
@@ -22,6 +22,8 @@ public let pushNoti_taskCode_key = "taskCode"
|
||||
|
||||
public let pushNoti_newOrder_key = "新订单"
|
||||
|
||||
public let pushNoti_appointmentOrder_key = "预约订单提醒"
|
||||
|
||||
public let pushNoti_broadcastKeywords_key = "broadcastKeywords"
|
||||
|
||||
public let pushNoti_isShowPop_key = "isShowPop"
|
||||
@@ -37,6 +39,7 @@ public let juheNewOrderAudio_key = "juheNewOrderAudio.wav"
|
||||
|
||||
public let newOrderAudio_key = "newOrderAudio.wav"
|
||||
|
||||
public let appointmentOrderAudio_key = "appointmentOrder.wav"
|
||||
// 电话消息的键
|
||||
public let noti_isCallSuccess_key = "isCallSuccess"
|
||||
|
||||
|
||||
@@ -61,7 +61,10 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
}
|
||||
// 语音播报能力不具备,只播放默认声音
|
||||
if let broadcastKeywords = (userInfo[pushNoti_broadcastKeywords_key] as? String),broadcastKeywords.isEmpty == false {
|
||||
if GROUP.isAppointmentPushNotification(userInfo: userInfo) == true {
|
||||
bestAttemptContent.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: appointmentOrderAudio_key))
|
||||
contentHandler(bestAttemptContent)
|
||||
}else if let broadcastKeywords = (userInfo[pushNoti_broadcastKeywords_key] as? String),broadcastKeywords.isEmpty == false {
|
||||
bestAttemptContent.sound = .default
|
||||
contentHandler(bestAttemptContent)
|
||||
}else{
|
||||
|
||||
4
Podfile
@@ -7,7 +7,7 @@ target 'OrderScheduling' do
|
||||
# Comment the next line if you don't want to use dynamic frameworks
|
||||
use_modular_headers!
|
||||
|
||||
pod 'CocoaDebug', :configurations => ['Debug']
|
||||
# pod 'CocoaDebug', :configurations => ['Debug']
|
||||
pod 'ESTabBarController-swift'
|
||||
pod 'DDCategoryKit_Private'
|
||||
pod 'DDColorKit_Private'
|
||||
@@ -32,7 +32,7 @@ target 'OrderScheduling' do
|
||||
pod 'DDZFPlayerKit_Private',:subspecs => ['ControlView','ijkplayer']
|
||||
pod 'JPush',:modular_headers => true
|
||||
pod 'BRPickerView'
|
||||
pod 'DDAudioPlayerKit_Private'
|
||||
pod 'DDAudioPlayerKit_Private',:path => "/Users/zd/Desktop/PodRepository/DDAudioPlayerKit_Private"
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
|
||||
26
Podfile.lock
@@ -22,8 +22,8 @@ PODS:
|
||||
- BRPickerView/Base
|
||||
- BRPickerView/StringPickerView (2.8.1):
|
||||
- BRPickerView/Base
|
||||
- CocoaDebug (1.7.7)
|
||||
- DDAudioPlayerKit_Private (0.1.3)
|
||||
- DDAudioPlayerKit_Private (0.1.3):
|
||||
- DDFFmpegKit_Private
|
||||
- DDAutoUIKit_Private (0.1.3)
|
||||
- DDBasicControlsKit_Private/DDBaseAnimation/DDTransitionAnimation (0.3.2)
|
||||
- DDBasicControlsKit_Private/DDBaseAttributedString (0.3.2):
|
||||
@@ -179,6 +179,12 @@ PODS:
|
||||
- DDBasicControlsKit_Private/DDBaseView
|
||||
- DDControlsKit_Private/DDViewController (0.1.2):
|
||||
- DDBasicControlsKit_Private/DDBaseViewController
|
||||
- DDFFmpegKit_Private (0.2.0):
|
||||
- DDFFmpegKit_Private/DDFFmpegBase (= 0.2.0)
|
||||
- DDFFmpegKit_Private/DDFFmpegManager (= 0.2.0)
|
||||
- DDFFmpegKit_Private/DDFFmpegBase (0.2.0)
|
||||
- DDFFmpegKit_Private/DDFFmpegManager (0.2.0):
|
||||
- DDFFmpegKit_Private/DDFFmpegBase
|
||||
- DDFontKit_Private (0.1.0)
|
||||
- DDLogKit_Private (0.1.4):
|
||||
- DDLogKit_Private/OCLog (= 0.1.4)
|
||||
@@ -296,8 +302,7 @@ PODS:
|
||||
|
||||
DEPENDENCIES:
|
||||
- BRPickerView
|
||||
- CocoaDebug
|
||||
- DDAudioPlayerKit_Private
|
||||
- DDAudioPlayerKit_Private (from `/Users/zd/Desktop/PodRepository/DDAudioPlayerKit_Private`)
|
||||
- DDAutoUIKit_Private
|
||||
- DDCategoryKit_Private
|
||||
- DDColorKit_Private
|
||||
@@ -326,12 +331,12 @@ DEPENDENCIES:
|
||||
|
||||
SPEC REPOS:
|
||||
"git@github.com:DDIsFriend/DDSpecs.git":
|
||||
- DDAudioPlayerKit_Private
|
||||
- DDAutoUIKit_Private
|
||||
- DDBasicControlsKit_Private
|
||||
- DDCategoryKit_Private
|
||||
- DDColorKit_Private
|
||||
- DDControlsKit_Private
|
||||
- DDFFmpegKit_Private
|
||||
- DDFontKit_Private
|
||||
- DDLogKit_Private
|
||||
- DDMAMapKit_Private
|
||||
@@ -349,7 +354,6 @@ SPEC REPOS:
|
||||
- AMapSearch-NO-IDFA
|
||||
- AMapTrack-NO-IDFA
|
||||
- BRPickerView
|
||||
- CocoaDebug
|
||||
- ESTabBarController-swift
|
||||
- IQKeyboardManagerSwift
|
||||
- JCore
|
||||
@@ -365,6 +369,10 @@ SPEC REPOS:
|
||||
- SwiftEntryKit
|
||||
- ZLPhotoBrowser
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
DDAudioPlayerKit_Private:
|
||||
:path: "/Users/zd/Desktop/PodRepository/DDAudioPlayerKit_Private"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Alamofire: 0123a34370cb170936ae79a8df46cc62b2edeb88
|
||||
AMapFoundation-NO-IDFA: 6ce0ef596d4eb8d934ff498e56747b6de1247b05
|
||||
@@ -373,13 +381,13 @@ SPEC CHECKSUMS:
|
||||
AMapSearch-NO-IDFA: 10cbd3ad7d122045a0c1f5760f6d46ff576ca918
|
||||
AMapTrack-NO-IDFA: 7109cf1867f5d6c407c8191492b1f73101e1682e
|
||||
BRPickerView: 2531a2d4d0fea0b57a1c738de215af0f88863a2f
|
||||
CocoaDebug: b38d31464b91a9775928f8667d114db07b136565
|
||||
DDAudioPlayerKit_Private: b727c241c5cd06f09d64bb2b3d5682b6e9eb8165
|
||||
DDAudioPlayerKit_Private: 3870b30ef8c6a19f4d8c6e6d9643ba31fa7018b4
|
||||
DDAutoUIKit_Private: 188066b4d13c8096676ddd9efa15974238f6dca3
|
||||
DDBasicControlsKit_Private: d3aebc505eaac273a8f6b85238662ad6e64a1a08
|
||||
DDCategoryKit_Private: 19d515c43d5e9c4ee80b8ccf3fb8ab40368b8668
|
||||
DDColorKit_Private: 77fddb62410a2d5e6fb003083d233c0d7ea0a08f
|
||||
DDControlsKit_Private: c3ac79b391cec6ecb26262a0d3b02718f696ab19
|
||||
DDFFmpegKit_Private: 6bf928d9107e1cb976473af0d02ae382b3b97800
|
||||
DDFontKit_Private: 7b8f4ebf0f60622874036202734d8460dc7b3806
|
||||
DDLogKit_Private: 1ed442cc7be004bd05f27bfda9b525e113df54e0
|
||||
DDMAMapKit_Private: fda2942ba7a3b7da4d440e59a89db9fd6708d842
|
||||
@@ -404,6 +412,6 @@ SPEC CHECKSUMS:
|
||||
SwiftEntryKit: 61b5fa36f34a97dd8013e48a7345bc4c4720be9a
|
||||
ZLPhotoBrowser: 0563c2bfc7b247b65d023d646012f46cba94101b
|
||||
|
||||
PODFILE CHECKSUM: 8b73e7b8269156e4fa26d46c40f768817fa76230
|
||||
PODFILE CHECKSUM: 36cddc5f02f0061051031b17aebeb5e8a2cf4201
|
||||
|
||||
COCOAPODS: 1.11.3
|
||||
|
||||
104
Pods/CocoaDebug/README.md
generated
@@ -1,104 +0,0 @@
|
||||
| <img alt="logo" src="https://raw.githubusercontent.com/CocoaDebug/CocoaDebug/master/pic/logo.png" width="250"/> | <ul align="left"><li><a href="#introduction">Introduction</a><li><a href="#installation">Installation</a><li><a href="#usage">Usage</a><li><a href="#parameters">Parameters</a></ul> |
|
||||
| -------------- | -------------- |
|
||||
| Version | [](https://img.shields.io/cocoapods/v/CocoaDebug.svg) |
|
||||
| Platform |  |
|
||||
| Languages |  |
|
||||
|
||||
<span style="float:none" />
|
||||
|
||||
## Screenshot
|
||||
|
||||
<img src="https://raw.githubusercontent.com/CocoaDebug/CocoaDebug/master/pic/a1.png" width="250">
|
||||
<img src="https://raw.githubusercontent.com/CocoaDebug/CocoaDebug/master/pic/a2.png" width="250">
|
||||
<img src="https://raw.githubusercontent.com/CocoaDebug/CocoaDebug/master/pic/a3.png" width="250">
|
||||
<img src="https://raw.githubusercontent.com/CocoaDebug/CocoaDebug/master/pic/a4.png" width="250">
|
||||
<img src="https://raw.githubusercontent.com/CocoaDebug/CocoaDebug/master/pic/a5.png" width="250">
|
||||
<img src="https://raw.githubusercontent.com/CocoaDebug/CocoaDebug/master/pic/a6.png" width="250">
|
||||
|
||||
## Introduction
|
||||
|
||||
- [x] Shake to hide or show the black bubble. (Support iPhone device and simulator)
|
||||
|
||||
- [x] Share network details via email or copy to clipboard when you are in the *Network Details* page.
|
||||
|
||||
- [x] Copy logs. (Long press the text, then select all or select copy)
|
||||
|
||||
- [x] Search logs by keyword.
|
||||
|
||||
- [x] Long press the black bubble to clean all network logs.
|
||||
|
||||
- [x] Detect *UI Blocking*.
|
||||
|
||||
- [x] List crash errors.
|
||||
|
||||
- [x] List application and device informations, including: *version*, *build*, *bundle name*, *bundle id*, *screen resolution*, *device*, *iOS version*
|
||||
|
||||
- [x] List all network requests sent by the application. (Support *JSON* and Google's *Protocol buffers*)
|
||||
|
||||
- [x] List all sandbox folders and files, supporting to preview and edit.
|
||||
|
||||
- [x] List all *WKWebView* consoles.
|
||||
|
||||
- [x] List all *React Native* JavaScript consoles and Native logs.
|
||||
|
||||
- [x] List all *print()* and *NSLog()* messages which have been written by developer in Xcode.
|
||||
|
||||
## Installation
|
||||
|
||||
### *CocoaPods* *(Preferred)*
|
||||
|
||||
```ruby
|
||||
target 'YourTargetName' do
|
||||
use_frameworks!
|
||||
pod 'CocoaDebug', :configurations => ['Debug']
|
||||
end
|
||||
```
|
||||
|
||||
### *Carthage*
|
||||
|
||||
```ruby
|
||||
github "CocoaDebug/CocoaDebug"
|
||||
```
|
||||
|
||||
### *Framework*
|
||||
|
||||
*[CocoaDebug.framework](https://raw.githubusercontent.com/CocoaDebug/CocoaDebug/master/CocoaDebug.framework.zip) (Version 1.7.2)*
|
||||
|
||||
> WARNING: Never ship a product which has been linked with the CocoaDebug framework. The [Integration Guide](https://github.com/CocoaDebug/CocoaDebug/wiki/Integration-Guide) outline a way to use build configurations to isolate linking the framework to Debug builds.
|
||||
|
||||
## Usage
|
||||
|
||||
- Don't need to do anything. CocoaDebug will start automatically.
|
||||
- To capture logs from Xcode with codes: (You can also set this in *CocoaDebug->App->Monitor->Applogs* without any codes.)
|
||||
```swift
|
||||
CocoaDebugSettings.shared.enableLogMonitoring = true //The default value is false
|
||||
```
|
||||
- Check [AppDelegate.m](https://github.com/CocoaDebug/CocoaDebug/blob/master/Example_Objc/Example_Objc/AppDelegate.m) OR [AppDelegate.swift](https://github.com/CocoaDebug/CocoaDebug/blob/master/Example_Swift/Example_Swift/AppDelegate.swift) for more advanced usage.
|
||||
|
||||
## Parameters
|
||||
|
||||
When you initialize CocoaDebug, you can customize the following parameter values before `CocoaDebug.enable()`.
|
||||
|
||||
- `serverURL` - If the captured URLs contain server URL, CocoaDebug set server URL bold font to be marked. Not mark when this value is nil. Default value is **nil**.
|
||||
|
||||
- `ignoredURLs` - Set the URLs which should not been captured, CocoaDebug capture all URLs when the value is nil. Default value is **nil**.
|
||||
|
||||
- `onlyURLs` - Set the URLs which are only been captured, CocoaDebug capture all URLs when the value is nil. Default value is **nil**.
|
||||
|
||||
- `ignoredPrefixLogs` - Set the prefix Logs which should not been captured, CocoaDebug capture all Logs when the value is nil. Default value is **nil**.
|
||||
|
||||
- `onlyPrefixLogs` - Set the prefix Logs which are only been captured, CocoaDebug capture all Logs when the value is nil. Default value is **nil**.
|
||||
|
||||
- `additionalViewController` - Add an additional UIViewController as child controller of CocoaDebug's main UITabBarController. Default value is **nil**.
|
||||
|
||||
- `emailToRecipients` - Set the initial recipients to include in the email’s “To” field when share via email. Default value is **nil**.
|
||||
|
||||
- `emailCcRecipients` - Set the initial recipients to include in the email’s “Cc” field when share via email. Default value is **nil**.
|
||||
|
||||
- `mainColor` - Set CocoaDebug's main color with hexadecimal format. Default value is **#42d459**.
|
||||
|
||||
- `protobufTransferMap` - Protobuf data transfer to JSON map. Default value is **nil**.
|
||||
|
||||
## Reference
|
||||
|
||||
[https://developer.apple.com/library/archive/samplecode/CustomHTTPProtocol/Introduction/Intro.html](https://developer.apple.com/library/archive/samplecode/CustomHTTPProtocol/Introduction/Intro.html)
|
||||
@@ -1,37 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class AboutViewController: UITableViewController {
|
||||
|
||||
@IBOutlet weak var versionLabel: UILabel!
|
||||
@IBOutlet weak var naviItem: UINavigationItem!
|
||||
|
||||
var naviItemTitleLabel: UILabel?
|
||||
|
||||
//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 = "About"
|
||||
naviItem.titleView = naviItemTitleLabel
|
||||
|
||||
|
||||
// let version = "1.7.2"
|
||||
//
|
||||
// self.versionLabel.text = "CocoaDebug Version ".appending(version)
|
||||
|
||||
tableView.tableFooterView = UIView()
|
||||
}
|
||||
}
|
||||
217
Pods/CocoaDebug/Sources/App/AppInfoViewController.swift
generated
@@ -1,217 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class AppInfoViewController: UITableViewController {
|
||||
|
||||
@IBOutlet weak var labelVersionNumber: UILabel!
|
||||
@IBOutlet weak var labelBuildNumber: UILabel!
|
||||
@IBOutlet weak var labelBundleName: UILabel!
|
||||
@IBOutlet weak var labelScreenResolution: UILabel!
|
||||
@IBOutlet weak var labelDeviceModel: UILabel!
|
||||
@IBOutlet weak var labelCrashCount: UILabel!
|
||||
@IBOutlet weak var labelBundleID: UILabel!
|
||||
@IBOutlet weak var labelserverURL: UILabel!
|
||||
@IBOutlet weak var labelIOSVersion: UILabel!
|
||||
@IBOutlet weak var labelHtml: UILabel!
|
||||
@IBOutlet weak var crashSwitch: UISwitch!
|
||||
@IBOutlet weak var logSwitch: UISwitch!
|
||||
@IBOutlet weak var networkSwitch: UISwitch!
|
||||
@IBOutlet weak var webViewSwitch: UISwitch!
|
||||
@IBOutlet weak var slowAnimationsSwitch: UISwitch!
|
||||
@IBOutlet weak var naviItem: UINavigationItem!
|
||||
@IBOutlet weak var rnSwitch: UISwitch!
|
||||
@IBOutlet weak var uiBlockingSwitch: UISwitch!
|
||||
|
||||
var naviItemTitleLabel: UILabel?
|
||||
|
||||
//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 = "App"
|
||||
naviItem.titleView = naviItemTitleLabel
|
||||
|
||||
labelCrashCount.frame.size = CGSize(width: 30, height: 20)
|
||||
|
||||
labelVersionNumber.text = CocoaDebugDeviceInfo.sharedInstance().appVersion
|
||||
labelBuildNumber.text = CocoaDebugDeviceInfo.sharedInstance().appBuiltVersion
|
||||
labelBundleName.text = CocoaDebugDeviceInfo.sharedInstance().appBundleName
|
||||
|
||||
labelScreenResolution.text = "\(Int(CocoaDebugDeviceInfo.sharedInstance().resolution.width))" + "*" + "\(Int(CocoaDebugDeviceInfo.sharedInstance().resolution.height))"
|
||||
labelDeviceModel.text = "\(CocoaDebugDeviceInfo.sharedInstance().getPlatformString)"
|
||||
|
||||
labelBundleID.text = CocoaDebugDeviceInfo.sharedInstance().appBundleID
|
||||
|
||||
labelserverURL.text = CocoaDebugSettings.shared.serverURL
|
||||
labelIOSVersion.text = UIDevice.current.systemVersion
|
||||
|
||||
if UIScreen.main.bounds.size.width == 320 {
|
||||
labelHtml.font = UIFont.systemFont(ofSize: 15)
|
||||
}
|
||||
|
||||
logSwitch.isOn = CocoaDebugSettings.shared.enableLogMonitoring
|
||||
networkSwitch.isOn = !CocoaDebugSettings.shared.disableNetworkMonitoring
|
||||
rnSwitch.isOn = CocoaDebugSettings.shared.enableRNMonitoring
|
||||
webViewSwitch.isOn = CocoaDebugSettings.shared.enableWKWebViewMonitoring
|
||||
slowAnimationsSwitch.isOn = CocoaDebugSettings.shared.slowAnimations
|
||||
crashSwitch.isOn = CocoaDebugSettings.shared.enableCrashRecording
|
||||
uiBlockingSwitch.isOn = CocoaDebugSettings.shared.enableUIBlockingMonitoring
|
||||
|
||||
logSwitch.addTarget(self, action: #selector(logSwitchChanged), for: UIControl.Event.valueChanged)
|
||||
networkSwitch.addTarget(self, action: #selector(networkSwitchChanged), for: UIControl.Event.valueChanged)
|
||||
rnSwitch.addTarget(self, action: #selector(rnSwitchChanged), for: UIControl.Event.valueChanged)
|
||||
webViewSwitch.addTarget(self, action: #selector(webViewSwitchChanged), for: UIControl.Event.valueChanged)
|
||||
slowAnimationsSwitch.addTarget(self, action: #selector(slowAnimationsSwitchChanged), for: UIControl.Event.valueChanged)
|
||||
crashSwitch.addTarget(self, action: #selector(crashSwitchChanged), for: UIControl.Event.valueChanged)
|
||||
uiBlockingSwitch.addTarget(self, action: #selector(uiBlockingSwitchChanged), for: UIControl.Event.valueChanged)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
let count = UserDefaults.standard.integer(forKey: "crashCount_CocoaDebug")
|
||||
labelCrashCount.text = "\(count)"
|
||||
labelCrashCount.textColor = count > 0 ? .red : .white
|
||||
}
|
||||
|
||||
//MARK: - alert
|
||||
func showAlert() {
|
||||
let alert = UIAlertController.init(title: nil, message: "You must restart APP to ensure the changes take effect", preferredStyle: .alert)
|
||||
let cancelAction = UIAlertAction.init(title: "Restart later", style: .cancel, handler: nil)
|
||||
let okAction = UIAlertAction.init(title: "Restart now", style: .destructive) { _ in
|
||||
exit(0)
|
||||
}
|
||||
|
||||
alert.addAction(cancelAction)
|
||||
alert.addAction(okAction)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
//MARK: - target action
|
||||
@objc func slowAnimationsSwitchChanged(sender: UISwitch) {
|
||||
CocoaDebugSettings.shared.slowAnimations = slowAnimationsSwitch.isOn
|
||||
// self.showAlert()
|
||||
}
|
||||
|
||||
@objc func uiBlockingSwitchChanged(sender: UISwitch) {
|
||||
CocoaDebugSettings.shared.enableUIBlockingMonitoring = uiBlockingSwitch.isOn
|
||||
if uiBlockingSwitch.isOn == true {
|
||||
WindowHelper.shared.startUIBlockingMonitoring()
|
||||
} else {
|
||||
WindowHelper.shared.stopUIBlockingMonitoring()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func crashSwitchChanged(sender: UISwitch) {
|
||||
CocoaDebugSettings.shared.enableCrashRecording = crashSwitch.isOn
|
||||
self.showAlert()
|
||||
}
|
||||
|
||||
@objc func networkSwitchChanged(sender: UISwitch) {
|
||||
CocoaDebugSettings.shared.disableNetworkMonitoring = !networkSwitch.isOn
|
||||
self.showAlert()
|
||||
}
|
||||
|
||||
@objc func logSwitchChanged(sender: UISwitch) {
|
||||
CocoaDebugSettings.shared.enableLogMonitoring = logSwitch.isOn
|
||||
self.showAlert()
|
||||
}
|
||||
|
||||
@objc func rnSwitchChanged(sender: UISwitch) {
|
||||
CocoaDebugSettings.shared.enableRNMonitoring = rnSwitch.isOn
|
||||
self.showAlert()
|
||||
}
|
||||
|
||||
@objc func webViewSwitchChanged(sender: UISwitch) {
|
||||
CocoaDebugSettings.shared.enableWKWebViewMonitoring = webViewSwitch.isOn
|
||||
self.showAlert()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//MARK: - UITableViewDelegate
|
||||
extension AppInfoViewController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
|
||||
{
|
||||
if section == 0 {
|
||||
return 56
|
||||
}
|
||||
return 38
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
|
||||
{
|
||||
if indexPath.section == 1 && indexPath.row == 4 {
|
||||
if labelserverURL.text == nil || labelserverURL.text == "" {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
return 44
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
|
||||
{
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
|
||||
if indexPath.section == 1 && indexPath.row == 2 {
|
||||
UIPasteboard.general.string = CocoaDebugDeviceInfo.sharedInstance().appBundleName
|
||||
|
||||
let alert = UIAlertController.init(title: "copied bundle name to clipboard", message: nil, preferredStyle: .alert)
|
||||
let action = UIAlertAction.init(title: "OK", style: .cancel, handler: nil)
|
||||
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)
|
||||
}
|
||||
|
||||
if indexPath.section == 1 && indexPath.row == 3 {
|
||||
UIPasteboard.general.string = CocoaDebugDeviceInfo.sharedInstance().appBundleID
|
||||
|
||||
let alert = UIAlertController.init(title: "copied bundle id to clipboard", message: nil, preferredStyle: .alert)
|
||||
let action = UIAlertAction.init(title: "OK", style: .cancel, handler: nil)
|
||||
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)
|
||||
}
|
||||
|
||||
if indexPath.section == 1 && indexPath.row == 4 {
|
||||
if labelserverURL.text == nil || labelserverURL.text == "" {return}
|
||||
|
||||
UIPasteboard.general.string = CocoaDebugSettings.shared.serverURL
|
||||
|
||||
let alert = UIAlertController.init(title: "copied server to clipboard", message: nil, preferredStyle: .alert)
|
||||
let action = UIAlertAction.init(title: "OK", style: .cancel, handler: nil)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Pods/CocoaDebug/Sources/App/CrashCell.swift
generated
@@ -1,41 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class CrashCell: UITableViewCell {
|
||||
|
||||
@IBOutlet weak var textview: CustomTextView!
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
textview.isUserInteractionEnabled = false
|
||||
}
|
||||
|
||||
var crash: _CrashModel? {
|
||||
didSet {
|
||||
guard let crash = crash else {return}
|
||||
|
||||
if let formatDate = _OCLoggerFormat.formatDate(crash.date) {
|
||||
let content = "\("\(String(describing: formatDate))\n")\(crash.name ?? "unknown crash")"
|
||||
|
||||
textview.text = content
|
||||
let attstr = NSMutableAttributedString(string: content)
|
||||
|
||||
attstr.addAttribute(.foregroundColor,
|
||||
value: UIColor.white, range: NSMakeRange(0, content.count))
|
||||
|
||||
let range = NSMakeRange(0, formatDate.count)
|
||||
attstr.addAttribute(.foregroundColor, value: Color.mainGreen, range: range)
|
||||
attstr.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: 12), range: range)
|
||||
|
||||
textview.attributedText = attstr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class CrashDetailViewController: UITableViewController {
|
||||
|
||||
@IBOutlet weak var textviewName: CustomTextView!
|
||||
@IBOutlet weak var textviewReason: CustomTextView!
|
||||
@IBOutlet weak var textviewStackTraces: CustomTextView!
|
||||
@IBOutlet weak var naviItem: UINavigationItem!
|
||||
|
||||
var naviItemTitleLabel: UILabel?
|
||||
|
||||
var crash: _CrashModel?
|
||||
|
||||
static func instanceFromStoryBoard() -> CrashDetailViewController {
|
||||
let storyboard = UIStoryboard(name: "App", bundle: Bundle(for: CocoaDebug.self))
|
||||
return storyboard.instantiateViewController(withIdentifier: "CrashDetailViewController") as! CrashDetailViewController
|
||||
}
|
||||
|
||||
//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
|
||||
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.estimatedRowHeight = 50
|
||||
tableView.delegate = self
|
||||
|
||||
textviewName.text = "\(crash?.name ?? "N/A")"
|
||||
textviewReason.text = "\(crash?.reason ?? "N/A")"
|
||||
|
||||
let contentStack = crash?.callStacks?.reduce("", {
|
||||
$0 == "" ? $1 : $0 + "\n" + $1
|
||||
})
|
||||
textviewStackTraces.text = contentStack
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UITableViewDelegate
|
||||
extension CrashDetailViewController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
return 50
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class CrashListViewController: UITableViewController {
|
||||
|
||||
var models: [_CrashModel] = [_CrashModel]()
|
||||
|
||||
@IBOutlet weak var naviItem: UINavigationItem!
|
||||
|
||||
var naviItemTitleLabel: UILabel?
|
||||
|
||||
//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 = "Crash"
|
||||
naviItem.titleView = naviItemTitleLabel
|
||||
|
||||
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .trash, target: self, action:#selector(CrashListViewController.deleteCrashes))
|
||||
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
tableView.tableFooterView = UIView()
|
||||
|
||||
models = CrashStoreManager.shared.crashArray
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
//MARK: - target action
|
||||
@objc func deleteCrashes() {
|
||||
models = []
|
||||
CrashStoreManager.shared.resetCrashs()
|
||||
|
||||
// dispatch_main_async_safe { [weak self] in
|
||||
tableView.reloadData()
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UITableViewDataSource
|
||||
extension CrashListViewController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return models.count
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "CrashCell", for: indexPath)
|
||||
as! CrashCell
|
||||
cell.crash = models[indexPath.row]
|
||||
cell.accessoryType = .none
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UITableViewDelegate
|
||||
extension CrashListViewController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
|
||||
let vc = CrashDetailViewController.instanceFromStoryBoard()
|
||||
vc.crash = models[indexPath.row]
|
||||
|
||||
self.navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
@available(iOS 11.0, *)
|
||||
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
||||
|
||||
let delete = UIContextualAction(style: .destructive, title: "Delete") { [weak self] (action, sourceView, completionHandler) in
|
||||
guard let models = self?.models else {return}
|
||||
CrashStoreManager.shared.removeCrash(models[indexPath.row])
|
||||
self?.models.remove(at: indexPath.row)
|
||||
// self?.dispatch_main_async_safe { [weak self] in
|
||||
self?.tableView.deleteRows(at: [indexPath], with: .automatic)
|
||||
// }
|
||||
completionHandler(true)
|
||||
}
|
||||
|
||||
return UISwipeActionsConfiguration(actions: [delete])
|
||||
}
|
||||
|
||||
//MARK: - only for ios8/ios9/ios10, not ios11
|
||||
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
|
||||
return .delete
|
||||
}
|
||||
override func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
|
||||
return "Delete"
|
||||
}
|
||||
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||
return true
|
||||
}
|
||||
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
|
||||
if (editingStyle == .delete) {
|
||||
CrashStoreManager.shared.removeCrash(models[indexPath.row])
|
||||
self.models.remove(at: indexPath.row)
|
||||
// self.dispatch_main_async_safe { [weak self] in
|
||||
self.tableView.deleteRows(at: [indexPath], with: .automatic)
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
98
Pods/CocoaDebug/Sources/App/CrashLogger.swift
generated
@@ -1,98 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Darwin
|
||||
|
||||
func exceptionHandler(exception: NSException) {
|
||||
if CrashLogger.shared.crashed {
|
||||
return
|
||||
}
|
||||
CrashLogger.shared.crashed = true
|
||||
CrashLogger.addCrash(name: exception.name.rawValue, reason: exception.reason)
|
||||
}
|
||||
|
||||
func handleSignal(signal: Int32) {
|
||||
if CrashLogger.shared.crashed {
|
||||
return
|
||||
}
|
||||
CrashLogger.shared.crashed = true
|
||||
switch signal {
|
||||
case SIGILL:
|
||||
CrashLogger.addCrash(name: "SIGILL", reason: nil)
|
||||
case SIGABRT:
|
||||
CrashLogger.addCrash(name: "SIGABRT", reason: nil)
|
||||
case SIGFPE:
|
||||
CrashLogger.addCrash(name: "SIGFPE", reason: nil)
|
||||
case SIGBUS:
|
||||
CrashLogger.addCrash(name: "SIGBUS", reason: nil)
|
||||
case SIGSEGV:
|
||||
CrashLogger.addCrash(name: "SIGSEGV", reason: nil)
|
||||
case SIGSYS:
|
||||
CrashLogger.addCrash(name: "SIGSYS", reason: nil)
|
||||
case SIGPIPE:
|
||||
CrashLogger.addCrash(name: "SIGPIPE", reason: nil)
|
||||
case SIGTRAP:
|
||||
CrashLogger.addCrash(name: "SIGTRAP", reason: nil)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
class CrashLogger {
|
||||
|
||||
static let shared = CrashLogger()
|
||||
private init() {}
|
||||
|
||||
var hasBeenRegistered = false
|
||||
var crashed = false
|
||||
var enable: Bool = false {
|
||||
didSet {
|
||||
if enable {
|
||||
CrashLogger.shared.register()
|
||||
}
|
||||
else {
|
||||
CrashLogger.shared.unregister()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func register() {
|
||||
if hasBeenRegistered == false {
|
||||
hasBeenRegistered = true
|
||||
NSSetUncaughtExceptionHandler(exceptionHandler)
|
||||
signal(SIGILL, handleSignal)
|
||||
signal(SIGABRT, handleSignal)
|
||||
signal(SIGFPE, handleSignal)
|
||||
signal(SIGBUS, handleSignal)
|
||||
signal(SIGSEGV, handleSignal)
|
||||
signal(SIGSYS, handleSignal)
|
||||
signal(SIGPIPE, handleSignal)
|
||||
signal(SIGTRAP, handleSignal)
|
||||
}
|
||||
}
|
||||
|
||||
func unregister() {
|
||||
if hasBeenRegistered == true {
|
||||
hasBeenRegistered = false
|
||||
NSSetUncaughtExceptionHandler(nil)
|
||||
signal(SIGILL, SIG_DFL)
|
||||
signal(SIGABRT, SIG_DFL)
|
||||
signal(SIGFPE, SIG_DFL)
|
||||
signal(SIGBUS, SIG_DFL)
|
||||
signal(SIGSEGV, SIG_DFL)
|
||||
signal(SIGSYS, SIG_DFL)
|
||||
signal(SIGPIPE, SIG_DFL)
|
||||
signal(SIGTRAP, SIG_DFL)
|
||||
}
|
||||
}
|
||||
|
||||
static func addCrash(name: String, reason: String?) {
|
||||
let newCrash = _CrashModel(name: name, reason: reason)
|
||||
CrashStoreManager.shared.addCrash(newCrash)
|
||||
}
|
||||
}
|
||||
79
Pods/CocoaDebug/Sources/App/CrashStoreManager.swift
generated
@@ -1,79 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class CrashStoreManager {
|
||||
|
||||
var crashArray: [_CrashModel] = [_CrashModel]()
|
||||
|
||||
static let shared = CrashStoreManager()
|
||||
private init() {
|
||||
crashArray = self.getCrashs()
|
||||
}
|
||||
|
||||
//MARK: - public
|
||||
func addCrash(_ crash: _CrashModel) {
|
||||
if self.crashArray.count >= 1000 {
|
||||
if self.crashArray.count > 0 {
|
||||
self.crashArray.remove(at: 0)
|
||||
}
|
||||
}
|
||||
self.crashArray.append(crash)
|
||||
archiveCrashs(self.crashArray)
|
||||
}
|
||||
|
||||
func removeCrash(_ model: _CrashModel) {
|
||||
if let index = self.crashArray.firstIndex(where: { (crash) -> Bool in
|
||||
return crash.id == model.id
|
||||
}) {
|
||||
self.crashArray.remove(at: index)
|
||||
}
|
||||
archiveCrashs(self.crashArray)
|
||||
}
|
||||
|
||||
func resetCrashs() {
|
||||
if self.crashArray.count > 0 {
|
||||
self.crashArray.removeAll()
|
||||
UserDefaults.standard.removeObject(forKey: "crashArchive_CocoaDebug")
|
||||
UserDefaults.standard.removeObject(forKey: "crashCount_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - private
|
||||
private func archiveCrashs(_ crashs: [_CrashModel]) {
|
||||
do {
|
||||
var dataArchive: Data
|
||||
if #available(iOS 11.0, *) {
|
||||
dataArchive = try NSKeyedArchiver.archivedData(withRootObject: crashs, requiringSecureCoding: false)
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
dataArchive = NSKeyedArchiver.archivedData(withRootObject: crashs)
|
||||
}
|
||||
UserDefaults.standard.set(dataArchive, forKey: "crashArchive_CocoaDebug")
|
||||
UserDefaults.standard.set(crashs.count, forKey: "crashCount_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
private func getCrashs() -> [_CrashModel] {
|
||||
guard let data = UserDefaults.standard.object(forKey: "crashArchive_CocoaDebug") as? Data else {return []}
|
||||
do {
|
||||
if #available(iOS 9.0, *) {
|
||||
let dataArchive = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data)
|
||||
return dataArchive as! [_CrashModel]
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
return []
|
||||
}
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class IgnoredURLsViewController: UITableViewController {
|
||||
|
||||
var ignoredURLs: Array<String>?
|
||||
var onlyURLs: Array<String>?
|
||||
|
||||
var ignoredPrefixLogs: Array<String>?
|
||||
var onlyPrefixLogs: Array<String>?
|
||||
|
||||
@IBOutlet weak var naviItem: UINavigationItem!
|
||||
|
||||
var naviItemTitleLabel: UILabel?
|
||||
|
||||
//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 = "Settings"
|
||||
naviItem.titleView = naviItemTitleLabel
|
||||
|
||||
tableView.tableFooterView = UIView()
|
||||
|
||||
ignoredURLs = CocoaDebugSettings.shared.ignoredURLs
|
||||
onlyURLs = CocoaDebugSettings.shared.onlyURLs
|
||||
|
||||
ignoredPrefixLogs = CocoaDebugSettings.shared.ignoredPrefixLogs
|
||||
onlyPrefixLogs = CocoaDebugSettings.shared.onlyPrefixLogs
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UITableViewDataSource
|
||||
extension IgnoredURLsViewController {
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return 4
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
switch section {
|
||||
case 0:
|
||||
return ignoredURLs?.count ?? 0
|
||||
case 1:
|
||||
return onlyURLs?.count ?? 0
|
||||
case 2:
|
||||
return ignoredPrefixLogs?.count ?? 0
|
||||
case 3:
|
||||
return onlyPrefixLogs?.count ?? 0
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = UITableViewCell.init(style: .default, reuseIdentifier: "CocoaDebugSettingsCell")
|
||||
cell.textLabel?.textColor = .white
|
||||
cell.contentView.backgroundColor = .black
|
||||
cell.selectionStyle = .none
|
||||
|
||||
switch indexPath.section {
|
||||
case 0:
|
||||
cell.textLabel?.text = ignoredURLs?[indexPath.row]
|
||||
case 1:
|
||||
cell.textLabel?.text = onlyURLs?[indexPath.row]
|
||||
case 2:
|
||||
cell.textLabel?.text = ignoredPrefixLogs?[indexPath.row]
|
||||
case 3:
|
||||
cell.textLabel?.text = onlyPrefixLogs?[indexPath.row]
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
|
||||
switch section {
|
||||
case 0:
|
||||
return "ignored URLs"
|
||||
case 1:
|
||||
return "only URLs"
|
||||
case 2:
|
||||
return "ignored Prefix Logs"
|
||||
case 3:
|
||||
return "only Prefix Logs"
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UITableViewDelegate
|
||||
extension IgnoredURLsViewController {
|
||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
return 60
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
return 20
|
||||
}
|
||||
}
|
||||
57
Pods/CocoaDebug/Sources/App/_CrashModel.swift
generated
@@ -1,57 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class _CrashModel: NSObject, NSCoding {
|
||||
|
||||
var id: String
|
||||
var date: Date
|
||||
var reason: String?
|
||||
var name: String?
|
||||
var callStacks: [String]?
|
||||
|
||||
|
||||
init(name: String, reason: String?) {
|
||||
id = UUID().uuidString
|
||||
date = Date()
|
||||
self.reason = reason
|
||||
self.name = name
|
||||
callStacks = Thread.callStackSymbols
|
||||
}
|
||||
|
||||
func encode(with aCoder: NSCoder) {
|
||||
aCoder.encode(id, forKey: "id")
|
||||
aCoder.encode(date, forKey: "date")
|
||||
aCoder.encode(reason, forKey: "reason")
|
||||
aCoder.encode(name, forKey: "name")
|
||||
aCoder.encode(callStacks, forKey: "callstacks")
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
id = aDecoder.decodeObject(forKey: "id") as? String ?? ""
|
||||
date = aDecoder.decodeObject(forKey: "date") as? Date ?? Date()
|
||||
reason = aDecoder.decodeObject(forKey: "reason") as? String
|
||||
name = aDecoder.decodeObject(forKey: "name") as? String
|
||||
callStacks = aDecoder.decodeObject(forKey: "callstacks") as? [String]
|
||||
}
|
||||
|
||||
func toString() -> String {
|
||||
let stringContent = NSMutableString()
|
||||
stringContent.append("Date: \(String(describing: _OCLoggerFormat.formatDate(date)))\n")
|
||||
stringContent.append("Name: \(name ?? "N/A")\n")
|
||||
stringContent.append("Reason: \(reason ?? "N/A")\n")
|
||||
|
||||
let stacks = (callStacks ?? []).reduce("", {
|
||||
return "\($0)\($1)\n"
|
||||
})
|
||||
stringContent.append(stacks)
|
||||
return stringContent as String
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
////
|
||||
//// Example
|
||||
//// man.li
|
||||
////
|
||||
//// Created by man 11/11/2018.
|
||||
//// Copyright © 2020 man. All rights reserved.
|
||||
////
|
||||
//
|
||||
//#import <Protobuf/GPBMessage.h>
|
||||
//
|
||||
//@interface GPBMessage (CocoaDebug)
|
||||
//
|
||||
//- (id _Nullable)initWithDictionary:(NSDictionary *_Nullable)dict;
|
||||
//
|
||||
//- (NSDictionary *_Nullable)containerType;
|
||||
//- (NSDictionary *_Nullable)nameMap;
|
||||
//- (NSString *_Nullable)_JSONStringWithIgnoreFields:(NSArray * _Nullable)ignoreFields;
|
||||
//- (NSDictionary *_Nullable)dictionaryWithIgnoreFields:(NSArray * _Nullable)ignoreFields;
|
||||
//
|
||||
//@end
|
||||
@@ -1,411 +0,0 @@
|
||||
////
|
||||
//// Example
|
||||
//// man.li
|
||||
////
|
||||
//// Created by man 11/11/2018.
|
||||
//// Copyright © 2020 man. All rights reserved.
|
||||
////
|
||||
//
|
||||
//#import "GPBMessage+CocoaDebug.h"
|
||||
//#import "NSObject+CocoaDebug.h"
|
||||
//#import <objc/runtime.h>
|
||||
//#import <objc/message.h>
|
||||
//#import "GPBArray.h"
|
||||
//
|
||||
//@implementation GPBMessage (CocoaDebug)
|
||||
//
|
||||
//#pragma mark - Public Methods
|
||||
//- (id)initWithDictionary:(NSDictionary *)dict {
|
||||
// self = [self init];
|
||||
// if (dict && [dict isKindOfClass:[NSDictionary class]]) {
|
||||
// NSMutableDictionary<NSString *, NSString *> *keyMap = [NSMutableDictionary<NSString *, NSString *> dictionary];
|
||||
// NSDictionary *nameMap = [self nameMap];
|
||||
// for (NSString *keyName in nameMap) {
|
||||
// id keyNameObject = [nameMap objectForKey:keyName];
|
||||
// if ([keyNameObject isKindOfClass:[NSString class]]) {
|
||||
// [keyMap setObject:keyName forKey:keyNameObject];
|
||||
// }
|
||||
// if ([keyNameObject isKindOfClass:[NSArray class]]) {
|
||||
// for (id keyPath in (NSArray *)keyNameObject) {
|
||||
// [keyMap setObject:keyName forKey:keyPath];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// for (NSString *keyName in dict) {
|
||||
// NSString *keyPath = [keyMap _stringForKey:keyName default:keyName];
|
||||
// [self setKeyPath:keyPath value:[dict objectForKey:keyName]];
|
||||
// }
|
||||
// }
|
||||
// return self;
|
||||
//}
|
||||
//
|
||||
//- (NSDictionary *)containerType {
|
||||
// return @{};
|
||||
//}
|
||||
//
|
||||
//- (NSDictionary *)nameMap {
|
||||
// return @{};
|
||||
//}
|
||||
//
|
||||
//- (NSString *)_JSONStringWithIgnoreFields:(NSArray * _Nullable)ignoreFields {
|
||||
// NSData *data = [NSJSONSerialization dataWithJSONObject:[self dictionaryWithIgnoreFields:ignoreFields] options:kNilOptions error:nil];
|
||||
// return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
//}
|
||||
//
|
||||
//#pragma mark - Private Methods
|
||||
//- (NSDictionary *)dictionaryWithIgnoreFields:(NSArray * _Nullable)ignoreFields {
|
||||
// NSMutableDictionary *dict = [NSMutableDictionary dictionary];
|
||||
//
|
||||
// unsigned int count;
|
||||
// objc_property_t *properties = class_copyPropertyList([self class], &count);
|
||||
// for (unsigned int i = 0; i < count; i++) {
|
||||
// const char *propertyName = property_getName(properties[i]);
|
||||
// NSString *keyPath = [NSString stringWithUTF8String:propertyName];
|
||||
//
|
||||
// id serializeObject = [self serializeValueForKey:keyPath];
|
||||
// if (nil == serializeObject) {
|
||||
// continue;
|
||||
// }
|
||||
// id keyNameObject = [[self nameMap] objectForKey:keyPath];
|
||||
//
|
||||
// if (keyNameObject == nil && ![ignoreFields containsObject:keyPath]) {
|
||||
// [dict setObject:serializeObject forKey:keyPath];
|
||||
// }
|
||||
// if ([keyNameObject isKindOfClass:[NSString class]] && ![ignoreFields containsObject:keyNameObject]) {
|
||||
// [dict setObject:serializeObject forKey:keyNameObject];
|
||||
// }
|
||||
// if ([keyNameObject isKindOfClass:[NSArray class]] && ![ignoreFields containsObject:keyNameObject]) {
|
||||
// NSString *keyName = [keyNameObject objectAtIndex:0]; // Serialize only the first value
|
||||
// [dict setObject:serializeObject forKey:keyName];
|
||||
// }
|
||||
// }
|
||||
// free(properties);
|
||||
// return dict;
|
||||
//}
|
||||
//
|
||||
//- (NSDictionary *)dictionary {
|
||||
// return [self dictionaryWithIgnoreFields:nil];
|
||||
//}
|
||||
//
|
||||
//- (id)serializeValueForKey:(NSString *)keyPath {
|
||||
// id item = [self valueForKey:keyPath];
|
||||
// if ([item isKindOfClass:[NSNumber class]] || [item isKindOfClass:[NSString class]]) {
|
||||
// return item;
|
||||
// }
|
||||
// if ([item isKindOfClass:[GPBMessage class]]) {
|
||||
// return [item dictionary];
|
||||
// }
|
||||
// if ([item isKindOfClass:[GPBInt32Array class]]) {
|
||||
// NSMutableArray *array = [NSMutableArray array];
|
||||
// GPBInt32Array *itemArray = (GPBInt32Array *)item;
|
||||
// [itemArray enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
// [array addObject:@(value)];
|
||||
// }];
|
||||
// return array;
|
||||
// }
|
||||
// if ([item isKindOfClass:[GPBUInt32Array class]]) {
|
||||
// NSMutableArray *array = [NSMutableArray array];
|
||||
// GPBUInt32Array *itemArray = (GPBUInt32Array *)item;
|
||||
// [itemArray enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
// [array addObject:@(value)];
|
||||
// }];
|
||||
// return array;
|
||||
// }
|
||||
// if ([item isKindOfClass:[GPBInt64Array class]]) {
|
||||
// NSMutableArray *array = [NSMutableArray array];
|
||||
// GPBInt64Array *itemArray = (GPBInt64Array *)item;
|
||||
// [itemArray enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
// [array addObject:@(value)];
|
||||
// }];
|
||||
// return array;
|
||||
// }
|
||||
// if ([item isKindOfClass:[GPBUInt64Array class]]) {
|
||||
// NSMutableArray *array = [NSMutableArray array];
|
||||
// GPBUInt64Array *itemArray = (GPBUInt64Array *)item;
|
||||
// [itemArray enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
// [array addObject:@(value)];
|
||||
// }];
|
||||
// return array;
|
||||
// }
|
||||
// if ([item isKindOfClass:[GPBFloatArray class]]) {
|
||||
// NSMutableArray *array = [NSMutableArray array];
|
||||
// GPBFloatArray *itemArray = (GPBFloatArray *)item;
|
||||
// [itemArray enumerateValuesWithBlock:^(float value, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
// [array addObject:@(value)];
|
||||
// }];
|
||||
// return array;
|
||||
// }
|
||||
// if ([item isKindOfClass:[GPBDoubleArray class]]) {
|
||||
// NSMutableArray *array = [NSMutableArray array];
|
||||
// GPBDoubleArray *itemArray = (GPBDoubleArray *)item;
|
||||
// [itemArray enumerateValuesWithBlock:^(double value, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
// [array addObject:@(value)];
|
||||
// }];
|
||||
// return array;
|
||||
// }
|
||||
// if ([item isKindOfClass:[GPBBoolArray class]]) {
|
||||
// NSMutableArray *array = [NSMutableArray array];
|
||||
// GPBBoolArray *itemArray = (GPBBoolArray *)item;
|
||||
// [itemArray enumerateValuesWithBlock:^(BOOL value, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
// [array addObject:@(value)];
|
||||
// }];
|
||||
// return array;
|
||||
// }
|
||||
// if ([item isKindOfClass:[GPBEnumArray class]]) {
|
||||
// NSMutableArray *array = [NSMutableArray array];
|
||||
// GPBEnumArray *itemArray = (GPBEnumArray *)item;
|
||||
// [itemArray enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
// [array addObject:@(value)];
|
||||
// }];
|
||||
// return array;
|
||||
// }
|
||||
// if ([item isKindOfClass:[NSArray class]]) {
|
||||
// NSMutableArray *array = [NSMutableArray array];
|
||||
// for (id arrayItem in item) {
|
||||
// if ([arrayItem isKindOfClass:[NSNumber class]] || [arrayItem isKindOfClass:[NSString class]]) {
|
||||
// [array addObject:arrayItem];
|
||||
// continue;
|
||||
// }
|
||||
// if ([arrayItem isKindOfClass:[GPBMessage class]]) {
|
||||
// [array addObject:[arrayItem dictionary]];
|
||||
// }
|
||||
// }
|
||||
// return array;
|
||||
// }
|
||||
// return nil;
|
||||
//}
|
||||
//
|
||||
//- (void)setKeyPath:(NSString *)keyPath value:(id)value {
|
||||
// NSMutableArray *propertiesNameArray = [NSMutableArray<NSString *> array];
|
||||
// NSMutableDictionary *propertiesTypeDic = [NSMutableDictionary<NSString *, NSString *> dictionary];
|
||||
// unsigned int count;
|
||||
// objc_property_t *properties = class_copyPropertyList([self class], &count);
|
||||
// for (NSUInteger i = 0; i < count; i++) {
|
||||
// objc_property_t property = properties[i];
|
||||
// NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
|
||||
// [propertiesNameArray addObject:propertyName];
|
||||
//
|
||||
// NSString *attr = [NSString stringWithUTF8String:&(property_getAttributes(property)[1])];
|
||||
// NSString *type = [[attr componentsSeparatedByString:@","] objectAtIndex:0];
|
||||
// [propertiesTypeDic setObject:type forKey:propertyName];
|
||||
// }
|
||||
// free(properties);
|
||||
//
|
||||
// NSString *type = [propertiesTypeDic _stringForKey:keyPath default:@""];
|
||||
// SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [keyPath substringToIndex:1].uppercaseString, [keyPath substringFromIndex:1]]);
|
||||
//
|
||||
// if ([value isKindOfClass:[NSString class]] == YES) {
|
||||
// NSString *str = (NSString *)value;
|
||||
// if ([type isEqualToString:@"@\"NSString\""]) {
|
||||
// ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)self, setter, str);
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(bool)]]) {
|
||||
// NSAssert([value isKindOfClass:[NSString class]], @"%@: property (%@) type mismatch, require bool but string", self, keyPath);
|
||||
// ((void (*)(id, SEL, bool))(void *) objc_msgSend)(self, setter, str.boolValue);
|
||||
// return;
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(BOOL)]]) {
|
||||
// NSAssert([value isKindOfClass:[NSString class]], @"%@: property (%@) type mismatch, require BOOL but string", self, keyPath);
|
||||
// ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(self, setter, str.boolValue);
|
||||
// return;
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(int32_t)]]) {
|
||||
// NSAssert([value isKindOfClass:[NSString class]], @"%@: property (%@) type mismatch, require int32_t but string", self, keyPath);
|
||||
// ((void (*)(id, SEL, int32_t))(void *) objc_msgSend)(self, setter, (int32_t)str.intValue);
|
||||
// return;
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(uint32_t)]]) {
|
||||
// NSAssert([value isKindOfClass:[NSString class]], @"%@: property (%@) type mismatch, require uint32_t but string", self, keyPath);
|
||||
// NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
|
||||
// [numberFormatter setNumberStyle:NSNumberFormatterNoStyle];
|
||||
// NSNumber *num = [numberFormatter numberFromString:str];
|
||||
// ((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)(self, setter, (uint32_t)num.unsignedIntValue);
|
||||
// return;
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(int64_t)]]) {
|
||||
// NSAssert([value isKindOfClass:[NSString class]], @"%@: property (%@) type mismatch, require int64_t but string", self, keyPath);
|
||||
// ((void (*)(id, SEL, int64_t))(void *) objc_msgSend)(self, setter, (int64_t)str.longLongValue);
|
||||
// return;
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(uint64_t)]]) {
|
||||
// NSAssert([value isKindOfClass:[NSString class]], @"%@: property (%@) type mismatch, require uint64_t but string", self, keyPath);
|
||||
// NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
|
||||
// [numberFormatter setNumberStyle:NSNumberFormatterNoStyle];
|
||||
// NSNumber *num = [numberFormatter numberFromString:str];
|
||||
// ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)(self, setter, (uint64_t)num.unsignedLongLongValue);
|
||||
// return;
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(float)]]) {
|
||||
// NSAssert([value isKindOfClass:[NSString class]], @"%@: property (%@) type mismatch, require float but string", self, keyPath);
|
||||
// float f = str.floatValue;
|
||||
// if (isnan(f) == NO && isinf(f) == NO) {
|
||||
// ((void (*)(id, SEL, float))(void *) objc_msgSend)(self, setter, f);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(double)]]) {
|
||||
// NSAssert([value isKindOfClass:[NSString class]], @"%@: property (%@) type mismatch, require double but string", self, keyPath);
|
||||
// double d = str.doubleValue;
|
||||
// if (isnan(d) == NO && isinf(d) == NO) {
|
||||
// ((void (*)(id, SEL, double))(void *) objc_msgSend)(self, setter, d);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if ([value isKindOfClass:[NSNumber class]] == YES) {
|
||||
// NSNumber *number = (NSNumber *)value;
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(bool)]]) {
|
||||
// ((void (*)(id, SEL, bool))(void *) objc_msgSend)(self, setter, number.boolValue);
|
||||
// return;
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(BOOL)]]) {
|
||||
// ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(self, setter, number.boolValue);
|
||||
// return;
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(int32_t)]]) {
|
||||
// ((void (*)(id, SEL, int32_t))(void *) objc_msgSend)(self, setter, (int32_t)number.intValue);
|
||||
// return;
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(uint32_t)]]) {
|
||||
// ((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)(self, setter, (uint32_t)number.unsignedIntValue);
|
||||
// return;
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(int64_t)]]) {
|
||||
// ((void (*)(id, SEL, int64_t))(void *) objc_msgSend)(self, setter, (int64_t)number.longLongValue);
|
||||
// return;
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(uint64_t)]]) {
|
||||
// ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)(self, setter, (uint64_t)number.longLongValue);
|
||||
// return;
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(float)]]) {
|
||||
// float f = number.floatValue;
|
||||
// if (isnan(f) == NO && isinf(f) == NO) {
|
||||
// ((void (*)(id, SEL, float))(void *) objc_msgSend)(self, setter, f);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// if ([type isEqualToString:[NSString stringWithUTF8String:@encode(double)]]) {
|
||||
// double d = number.doubleValue;
|
||||
// if (isnan(d) == NO && isinf(d) == NO) {
|
||||
// ((void (*)(id, SEL, double))(void *) objc_msgSend)(self, setter, d);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// if ([type isEqualToString:@"@\"NSString\""]) {
|
||||
// NSAssert([value isKindOfClass:[NSString class]], @"%@: property (%@) type mismatch, require string but number", self, keyPath);
|
||||
// ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)self, setter, [number stringValue]);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// if (([type isEqualToString:@"@\"NSMutableArray\""] || [type isEqualToString:@"@\"NSArray\""]) && [value isKindOfClass:[NSArray class]] == YES) {
|
||||
// NSMutableArray *array = [NSMutableArray array];
|
||||
// for (id arrayValue in value) {
|
||||
// if ([arrayValue isKindOfClass:[NSNumber class]] == YES || [arrayValue isKindOfClass:[NSString class]] == YES) {
|
||||
// [array addObject:arrayValue];
|
||||
// continue;
|
||||
// }
|
||||
// if ([arrayValue isKindOfClass:[NSDictionary class]] == YES) {
|
||||
// NSString *arrayItemType = [[self containerType] _stringForKey:keyPath default:nil];
|
||||
// if (arrayItemType == nil) {
|
||||
// [array addObject:arrayValue];
|
||||
// break;
|
||||
// }
|
||||
// Class itemClass = NSClassFromString(arrayItemType);
|
||||
// if (!itemClass) {
|
||||
// //SSPWarning(@"Can't find class of %@", arrayItemType);
|
||||
// return;
|
||||
// }
|
||||
// Class parentClass = class_getSuperclass(itemClass);
|
||||
// if ([parentClass isEqual:[GPBMessage class]] == NO) {
|
||||
// //SSPWarning(@"%@ is not GPBMessage", arrayItemType);
|
||||
// return;
|
||||
// }
|
||||
// [array addObject:[[itemClass alloc] initWithDictionary:arrayValue]];
|
||||
// }
|
||||
// if ([arrayValue isKindOfClass:[NSArray class]] == YES) {
|
||||
// //SSPError(@"Not support NSArray in NSArray");
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// ((void (*)(id, SEL, id))(void *) objc_msgSend)(self, setter, array);
|
||||
// return;
|
||||
// }
|
||||
// if ([type hasPrefix:@"@\""] && [value isKindOfClass:[NSDictionary class]] == YES) {
|
||||
// if ([type isEqualToString:@"@\"NSDictionary\""] || [type isEqualToString:@"@\"NSMutableDictionary\""]) {
|
||||
// NSMutableDictionary *dictValue = [NSMutableDictionary dictionary];
|
||||
// NSArray *dictTypes = [[self containerType] _arrayForKey:keyPath default:nil];
|
||||
// if (dictTypes && [dictTypes count] == 2) {
|
||||
// for (id dictKey in value) {
|
||||
// Class itemClass = NSClassFromString(dictTypes[1]);
|
||||
// if (!itemClass) {
|
||||
// //SSPWarning(@"Can't find class of %@", dictTypes[1]);
|
||||
// return;
|
||||
// }
|
||||
// if ([itemClass isEqual:[NSString class]]||[itemClass isEqual:[NSNumber class]]) {
|
||||
// [dictValue setObject:value[dictKey] forKey:dictKey];
|
||||
// continue;
|
||||
// }
|
||||
// Class parentClass = class_getSuperclass(itemClass);
|
||||
// if ([parentClass isEqual:[GPBMessage class]] == NO) {
|
||||
// //SSPWarning(@"%@ is not GPBMessage", dictTypes[1]);
|
||||
// return;
|
||||
// }
|
||||
// [dictValue setObject:[[itemClass alloc] initWithDictionary:value[dictKey]] forKey:dictKey];
|
||||
// }
|
||||
// } else if (dictTypes && [dictTypes count] == 3){
|
||||
// for (id dictKey in value) {
|
||||
// Class itemClass = NSClassFromString(dictTypes[1]);
|
||||
// if (!itemClass || ![itemClass isEqual:[NSArray class]] || ![value[dictKey] isKindOfClass:[NSArray class]]) {
|
||||
// //SSPWarning(@"Map<obj, Array> parse error!");
|
||||
// return;
|
||||
// }
|
||||
// NSMutableArray *array = [NSMutableArray array];
|
||||
// for (id arrayValue in value[dictKey]) {
|
||||
// if ([arrayValue isKindOfClass:[NSNumber class]] == YES || [arrayValue isKindOfClass:[NSString class]] == YES) {
|
||||
// [array addObject:arrayValue];
|
||||
// continue;
|
||||
// }
|
||||
// if ([arrayValue isKindOfClass:[NSDictionary class]] == YES) {
|
||||
// Class inItemClass = NSClassFromString(dictTypes[2]);
|
||||
// if (!inItemClass) {
|
||||
// //SSPWarning(@"Can't find class of %@", dictTypes[2]);
|
||||
// return;
|
||||
// }
|
||||
// Class parentClass = class_getSuperclass(inItemClass);
|
||||
// if ([parentClass isEqual:[GPBMessage class]] == NO) {
|
||||
// //SSPWarning(@"%@ is not GPBMessage", dictTypes[2]);
|
||||
// return;
|
||||
// }
|
||||
// [array addObject:[[inItemClass alloc] initWithDictionary:arrayValue]];
|
||||
// }
|
||||
// if ([arrayValue isKindOfClass:[NSArray class]] == YES) {
|
||||
// //SSPError(@"Not support NSArray in NSArray");
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// [dictValue setObject:array forKey:dictKey];
|
||||
// }
|
||||
// }
|
||||
// ((void (*)(id, SEL, id))(void *) objc_msgSend)(self, setter, dictValue);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// NSString *itemType = [type substringWithRange:NSMakeRange(2, [type length]-3)];
|
||||
// Class itemClass = NSClassFromString(itemType);
|
||||
// if (!itemClass) {
|
||||
// //SSPWarning(@"Can't find class of %@", itemType);
|
||||
// return;
|
||||
// }
|
||||
// Class parentClass = class_getSuperclass(itemClass);
|
||||
// if ([parentClass isEqual:[GPBMessage class]] == NO) {
|
||||
// //SSPWarning(@"%@ is not GPBMessage", itemType);
|
||||
// return;
|
||||
// }
|
||||
// id item = [[itemClass alloc] initWithDictionary:value];
|
||||
// ((void (*)(id, SEL, id))(void *) objc_msgSend)(self, setter, item);
|
||||
// return;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//@end
|
||||
@@ -1,84 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
/*************************************************/
|
||||
|
||||
@interface NSData (CocoaDebug)
|
||||
|
||||
+ (NSData *_Nullable)dataWithInputStream:(NSInputStream *_Nullable)stream;
|
||||
|
||||
@end
|
||||
|
||||
/*************************************************/
|
||||
|
||||
@interface NSString (CocoaDebug)
|
||||
|
||||
- (CGFloat)heightWithFont:(UIFont *_Nullable)font constraintToWidth:(CGFloat)width;
|
||||
|
||||
@end
|
||||
|
||||
/*************************************************/
|
||||
|
||||
@interface NSURLRequest (CocoaDebug)
|
||||
|
||||
- (NSString *_Nullable)requestId;
|
||||
- (void)setRequestId:(NSString *_Nullable)requestId;
|
||||
|
||||
- (NSNumber*_Nullable)startTime;
|
||||
- (void)setStartTime:(NSNumber*_Nullable)startTime;
|
||||
|
||||
@end
|
||||
|
||||
/*************************************************/
|
||||
|
||||
@interface UIColor (CocoaDebug)
|
||||
|
||||
+ (UIColor *_Nullable)colorFromHexString:(NSString *_Nullable)hexString;
|
||||
|
||||
@end
|
||||
|
||||
/*************************************************/
|
||||
|
||||
@interface NSDictionary (CocoaDebug)
|
||||
|
||||
- (NSString *_Nullable)_stringForKey:(id<NSCopying>_Nullable)key;
|
||||
- (NSArray *_Nullable)_arrayForKey:(id<NSCopying>_Nullable)key;
|
||||
- (NSDictionary *_Nullable)_dictionaryForKey:(id<NSCopying>_Nullable)key;
|
||||
- (NSInteger)_integerForKey:(id<NSCopying>_Nullable)key;
|
||||
- (int64_t)_int64ForKey:(id<NSCopying>_Nullable)key;
|
||||
- (int32_t)_int32ForKey:(id<NSCopying>_Nullable)key;
|
||||
- (float)_floatForKey:(id<NSCopying>_Nullable)key;
|
||||
- (double)_doubleForKey:(id<NSCopying>_Nullable)key;
|
||||
- (BOOL)_boolForKey:(id<NSCopying>_Nullable)key;
|
||||
|
||||
- (NSString *_Nullable)_stringForKey:(id<NSCopying>_Nullable)key default:(NSString * _Nullable)defaultValue;
|
||||
- (bool)_boolForKey:(id<NSCopying>_Nullable)key default:(bool)defaultValue;
|
||||
- (NSInteger)_integerForKey:(id<NSCopying>_Nullable)key default:(NSInteger)defaultValue;
|
||||
- (float)_floatForKey:(id<NSCopying>_Nullable)key default:(float)defaultValue;
|
||||
- (NSArray *_Nullable)_arrayForKey:(id<NSCopying>_Nullable)key default:(NSArray * _Nullable)defaultValue;
|
||||
- (NSDictionary *_Nullable)_dictionaryForKey:(id<NSCopying>_Nullable)key default:(NSDictionary * _Nullable)defaultValue;
|
||||
|
||||
@end
|
||||
|
||||
/*************************************************/
|
||||
|
||||
@interface UIImage (CocoaDebug)
|
||||
|
||||
//Obtain the GIF image object according to the data data of a GIF image
|
||||
+ (UIImage *_Nullable)imageWithGIFData:(NSData *_Nullable)data;
|
||||
|
||||
//Obtain the GIF image object according to the name of the local GIF image
|
||||
+ (UIImage *_Nullable)imageWithGIFNamed:(NSString *_Nullable)name;
|
||||
|
||||
//Obtain the GIF image object according to the URL of a GIF image
|
||||
+ (void)imageWithGIFUrl:(NSString *_Nullable)url gifImageBlock:(void(^_Nullable)(UIImage *_Nullable gifImage))gifImageBlock;
|
||||
|
||||
@end
|
||||
367
Pods/CocoaDebug/Sources/Categories/NSObject+CocoaDebug.m
generated
@@ -1,367 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSObject+CocoaDebug.h"
|
||||
#import <objc/runtime.h>
|
||||
#import <ImageIO/ImageIO.h>
|
||||
|
||||
/*************************************************/
|
||||
|
||||
@implementation NSData (CocoaDebug)
|
||||
|
||||
+ (NSData *)dataWithInputStream:(NSInputStream *)stream
|
||||
{
|
||||
NSMutableData * data = [NSMutableData data];
|
||||
[stream open];
|
||||
NSInteger result;
|
||||
uint8_t buffer[1024]; // BUFFER_LEN can be any positive integer
|
||||
|
||||
while((result = [stream read:buffer maxLength:1024]) != 0) {
|
||||
if (result > 0) {
|
||||
// buffer contains result bytes of data to be handled
|
||||
[data appendBytes:buffer length:result];
|
||||
} else {
|
||||
// The stream had an error. You can get an NSError object using [iStream streamError]
|
||||
if (result < 0) {
|
||||
// [NSException raise:@"STREAM_ERROR" format:@"%@", [stream streamError]];
|
||||
return nil;//liman
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*************************************************/
|
||||
|
||||
@implementation NSString (CocoaDebug)
|
||||
|
||||
- (CGFloat)heightWithFont:(UIFont *)font constraintToWidth:(CGFloat)width
|
||||
{
|
||||
CGRect rect = [self boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:font} context:nil];
|
||||
|
||||
return rect.size.height;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*************************************************/
|
||||
|
||||
@implementation NSURLRequest (CocoaDebug)
|
||||
|
||||
- (NSString *)requestId {
|
||||
return objc_getAssociatedObject(self, @"requestId");
|
||||
}
|
||||
|
||||
- (void)setRequestId:(NSString *)requestId {
|
||||
objc_setAssociatedObject(self, @"requestId", requestId, OBJC_ASSOCIATION_COPY_NONATOMIC);
|
||||
}
|
||||
|
||||
- (NSNumber*)startTime {
|
||||
return objc_getAssociatedObject(self, @"startTime");
|
||||
}
|
||||
|
||||
- (void)setStartTime:(NSNumber*)startTime {
|
||||
objc_setAssociatedObject(self, @"startTime", startTime, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*************************************************/
|
||||
|
||||
@implementation UIColor (CocoaDebug)
|
||||
|
||||
+ (UIColor *)colorFromHexString:(NSString *)hexString
|
||||
{
|
||||
unsigned rgbValue = 0;
|
||||
NSScanner *scanner = [NSScanner scannerWithString:hexString];
|
||||
[scanner setScanLocation:1];
|
||||
[scanner scanHexInt:&rgbValue];
|
||||
return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:1.0];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*************************************************/
|
||||
|
||||
@implementation NSDictionary (CocoaDebug)
|
||||
|
||||
- (NSString *)_stringForKey:(id<NSCopying>)key {
|
||||
id obj = [self objectForKey:key];
|
||||
if (![obj isKindOfClass:[NSString class]]) {
|
||||
return nil;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
- (NSArray *)_arrayForKey:(id<NSCopying>)key {
|
||||
id obj = [self objectForKey:key];
|
||||
if (![obj isKindOfClass:[NSArray class]]) {
|
||||
return nil;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
- (NSDictionary *)_dictionaryForKey:(id<NSCopying>)key {
|
||||
id obj = [self objectForKey:key];
|
||||
if (![obj isKindOfClass:[NSDictionary class]]) {
|
||||
return nil;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
- (NSInteger)_integerForKey:(id<NSCopying>)key {
|
||||
id obj = [self objectForKey:key];
|
||||
if ([obj isKindOfClass:[NSNumber class]]) {
|
||||
return [((NSNumber *)obj) integerValue];
|
||||
}
|
||||
if ([obj isKindOfClass:[NSString class]]) {
|
||||
return [((NSString *)obj) integerValue];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (int64_t)_int64ForKey:(id<NSCopying>)key {
|
||||
id obj = [self objectForKey:key];
|
||||
if ([obj isKindOfClass:[NSNumber class]]) {
|
||||
return [((NSNumber *)obj) longLongValue];
|
||||
}
|
||||
if ([obj isKindOfClass:[NSString class]]) {
|
||||
return [((NSString *)obj) longLongValue];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (int32_t)_int32ForKey:(id<NSCopying>)key {
|
||||
id obj = [self objectForKey:key];
|
||||
if ([obj isKindOfClass:[NSNumber class]]) {
|
||||
return [((NSNumber *)obj) intValue];
|
||||
}
|
||||
if ([obj isKindOfClass:[NSString class]]) {
|
||||
return [((NSString *)obj) intValue];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (float)_floatForKey:(id<NSCopying>)key {
|
||||
id obj = [self objectForKey:key];
|
||||
if ([obj isKindOfClass:[NSNumber class]]) {
|
||||
return [((NSNumber *)obj) floatValue];
|
||||
}
|
||||
if ([obj isKindOfClass:[NSString class]]) {
|
||||
return [((NSString *)obj) floatValue];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (double)_doubleForKey:(id<NSCopying>)key {
|
||||
id obj = [self objectForKey:key];
|
||||
if ([obj isKindOfClass:[NSNumber class]]) {
|
||||
return [((NSNumber *)obj) doubleValue];
|
||||
}
|
||||
if ([obj isKindOfClass:[NSString class]]) {
|
||||
return [((NSString *)obj) doubleValue];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (BOOL)_boolForKey:(id<NSCopying>)key {
|
||||
id obj = [self objectForKey:key];
|
||||
if ([obj isKindOfClass:[NSNumber class]]) {
|
||||
return [((NSNumber *)obj) boolValue];
|
||||
}
|
||||
if ([obj isKindOfClass:[NSString class]]) {
|
||||
return [((NSString *)obj) boolValue];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString *)_stringForKey:(id<NSCopying>)key default:(NSString * _Nullable)defaultValue {
|
||||
id obj = [self objectForKey:key];
|
||||
|
||||
if ([obj isKindOfClass:[NSString class]]) {
|
||||
return obj;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
- (bool)_boolForKey:(id<NSCopying>)key default:(bool)defaultValue {
|
||||
id obj = [self objectForKey:key];
|
||||
|
||||
if ([obj isKindOfClass:[NSNumber class]] || [obj isKindOfClass:[NSString class]]) {
|
||||
return [obj boolValue];
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
- (NSInteger)_integerForKey:(id<NSCopying>)key default:(NSInteger)defaultValue {
|
||||
id obj = [self objectForKey:key];
|
||||
|
||||
if ([obj isKindOfClass:[NSNumber class]] || [obj isKindOfClass:[NSString class]]) {
|
||||
return [obj integerValue];
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
- (float)_floatForKey:(id<NSCopying>)key default:(float)defaultValue {
|
||||
id obj = [self objectForKey:key];
|
||||
|
||||
if ([obj isKindOfClass:[NSNumber class]] || [obj isKindOfClass:[NSString class]]) {
|
||||
return [obj floatValue];
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
- (NSArray *)_arrayForKey:(id<NSCopying>)key default:(NSArray * _Nullable)defaultValue {
|
||||
id obj = [self objectForKey:key];
|
||||
|
||||
if ([obj isKindOfClass:[NSArray class]]) {
|
||||
return obj;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
- (NSDictionary *)_dictionaryForKey:(id<NSCopying>)key default:(NSDictionary * _Nullable)defaultValue {
|
||||
id obj = [self objectForKey:key];
|
||||
|
||||
if ([obj isKindOfClass:[NSDictionary class]]) {
|
||||
return obj;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*************************************************/
|
||||
|
||||
@implementation UIImage (CocoaDebug)
|
||||
|
||||
//Obtain the GIF image object according to the data data of a GIF image
|
||||
+ (UIImage *_Nullable)imageWithGIFData:(NSData *_Nullable)data {
|
||||
if (!data) return nil;
|
||||
|
||||
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
|
||||
size_t count = CGImageSourceGetCount(source);
|
||||
UIImage *animatedImage;
|
||||
|
||||
if (count <= 1) {
|
||||
animatedImage = [[UIImage alloc] initWithData:data];
|
||||
|
||||
} else {
|
||||
|
||||
NSMutableArray *images = [NSMutableArray array];
|
||||
NSTimeInterval duration = 0.0f;
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
|
||||
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
|
||||
|
||||
NSTimeInterval frameDuration = [UIImage ssz_frameDurationAtIndex:i source:source];
|
||||
|
||||
duration += frameDuration;
|
||||
|
||||
[images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
|
||||
|
||||
CFRelease(image);
|
||||
}
|
||||
|
||||
if (!duration) {
|
||||
duration = (1.0f / 10.0f) * count;
|
||||
}
|
||||
|
||||
animatedImage = [UIImage animatedImageWithImages:images duration:duration];
|
||||
}
|
||||
|
||||
CFRelease(source);
|
||||
|
||||
return animatedImage;
|
||||
}
|
||||
|
||||
//Obtain the GIF image object according to the name of the local GIF image
|
||||
+ (UIImage *_Nullable)imageWithGIFNamed:(NSString *_Nullable)name {
|
||||
NSUInteger scale = (NSUInteger)[UIScreen mainScreen].scale;
|
||||
return [self GIFName:name scale:scale];
|
||||
}
|
||||
|
||||
+ (UIImage *)GIFName:(NSString *)name scale:(NSUInteger)scale {
|
||||
NSString *imagePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@@%zdx", name, scale] ofType:@"gif"];
|
||||
|
||||
if (!imagePath) {
|
||||
(scale + 1 > 3) ? (scale -= 1) : (scale += 1);
|
||||
imagePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@@%zdx", name, scale] ofType:@"gif"];
|
||||
}
|
||||
|
||||
if (imagePath) {
|
||||
NSData *imageData = [NSData dataWithContentsOfFile:imagePath];
|
||||
return [UIImage imageWithGIFData:imageData];
|
||||
|
||||
} else {
|
||||
|
||||
imagePath = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"];
|
||||
|
||||
if (imagePath) {
|
||||
NSData *imageData = [NSData dataWithContentsOfFile:imagePath];
|
||||
return [UIImage imageWithGIFData:imageData];
|
||||
|
||||
} else {
|
||||
return [UIImage imageNamed:name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Obtain the GIF image object according to the URL of a GIF image
|
||||
+ (void)imageWithGIFUrl:(NSString *_Nullable)url gifImageBlock:(void(^_Nullable)(UIImage *_Nullable gifImage))gifImageBlock {
|
||||
NSURL *GIFUrl = [NSURL URLWithString:url];
|
||||
|
||||
if (!GIFUrl) return;
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
NSData *CIFData = [NSData dataWithContentsOfURL:GIFUrl];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
gifImageBlock([UIImage imageWithGIFData:CIFData]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+ (float)ssz_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
|
||||
float frameDuration = 0.1f;
|
||||
|
||||
CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
|
||||
NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
|
||||
NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary];
|
||||
NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
|
||||
|
||||
if (delayTimeUnclampedProp) {
|
||||
frameDuration = [delayTimeUnclampedProp floatValue];
|
||||
|
||||
} else {
|
||||
NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
|
||||
|
||||
if (delayTimeProp) {
|
||||
frameDuration = [delayTimeProp floatValue];
|
||||
}
|
||||
}
|
||||
|
||||
// Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
|
||||
// We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
|
||||
// a duration of <= 10 ms. See and
|
||||
// for more information.
|
||||
|
||||
if (frameDuration < 0.011f) {
|
||||
frameDuration = 0.100f;
|
||||
}
|
||||
|
||||
CFRelease(cfFrameProperties);
|
||||
|
||||
return frameDuration;
|
||||
}
|
||||
|
||||
@end
|
||||
393
Pods/CocoaDebug/Sources/Core/CocoaDebug+Extensions.swift
generated
@@ -1,393 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Dictionary {
|
||||
///JSON/Form format conversion
|
||||
func dictionaryToFormString() -> String? {
|
||||
var array = [String]()
|
||||
|
||||
for (key, value) in self {
|
||||
array.append(String(describing: key) + "=" + String(describing: value))
|
||||
}
|
||||
if array.count > 0 {
|
||||
return array.joined(separator: "&")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
///JSON/Form format conversion
|
||||
func formStringToDictionary() -> [String: Any]? {
|
||||
var dictionary = [String: Any]()
|
||||
let array = self.components(separatedBy: "&")
|
||||
|
||||
for str in array {
|
||||
let arr = str.components(separatedBy: "=")
|
||||
if arr.count == 2 {
|
||||
dictionary.updateValue(arr[1], forKey: arr[0])
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if dictionary.count > 0 {
|
||||
return dictionary
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - *********************************************************************
|
||||
|
||||
extension Data {
|
||||
func dataToDictionary() -> [String: Any]? {
|
||||
do {
|
||||
return try JSONSerialization.jsonObject(with: self, options: []) as? [String : Any]
|
||||
} catch {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension Dictionary {
|
||||
func dictionaryToData() -> Data? {
|
||||
do {
|
||||
return try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
|
||||
} catch {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension Data {
|
||||
func dataToString() -> String? {
|
||||
return String(bytes: self, encoding: .utf8)
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func stringToData() -> Data? {
|
||||
return self.data(using: .utf8)
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - *********************************************************************
|
||||
|
||||
extension String {
|
||||
func stringToDictionary() -> [String: Any]? {
|
||||
return self.stringToData()?.dataToDictionary()
|
||||
}
|
||||
}
|
||||
|
||||
extension Dictionary {
|
||||
func dictionaryToString() -> String? {
|
||||
return self.dictionaryToData()?.dataToString()
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func jsonStringToPrettyJsonString() -> String? {
|
||||
return self.stringToDictionary()?.dictionaryToString()
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func isValidURL() -> Bool {
|
||||
if let url = URL(string: self) {
|
||||
return UIApplication.shared.canOpenURL(url)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func isValidJsonString() -> Bool {
|
||||
if let _ = self.stringToDictionary() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func isValidFormString() -> Bool {
|
||||
if let _ = self.formStringToDictionary() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func jsonStringToFormString() -> String? {
|
||||
return self.stringToDictionary()?.dictionaryToFormString()
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func formStringToJsonString() -> String? {
|
||||
return self.formStringToDictionary()?.dictionaryToString()
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func formStringToData() -> Data? {
|
||||
return self.formStringToDictionary()?.dictionaryToData()
|
||||
}
|
||||
}
|
||||
|
||||
extension Data {
|
||||
func formDataToDictionary() -> [String: Any]? {
|
||||
return self.dataToString()?.formStringToDictionary()
|
||||
}
|
||||
}
|
||||
|
||||
extension Data {
|
||||
func dataToPrettyPrintString() -> String? {
|
||||
//1.pretty json
|
||||
if let str = self.dataToDictionary()?.dictionaryToString() {
|
||||
return str
|
||||
} else {
|
||||
//2.protobuf
|
||||
// if let message = try? GPBMessage.parse(from: self) {
|
||||
// if message.serializedSize() > 0 {
|
||||
// return message.description
|
||||
// } else {
|
||||
// //3.utf-8 string
|
||||
// return String(data: self, encoding: .utf8)
|
||||
// }
|
||||
// } else {
|
||||
//3.utf-8 string
|
||||
return String(data: self, encoding: .utf8)
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - *********************************************************************
|
||||
|
||||
//https://gist.github.com/arshad/de147c42d7b3063ef7bc
|
||||
///Color
|
||||
extension String {
|
||||
var hexColor: UIColor {
|
||||
let hex = trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
|
||||
var int = UInt32()
|
||||
Scanner(string: hex).scanHexInt32(&int)
|
||||
var a, r, g, b: UInt32
|
||||
switch hex.count {
|
||||
case 3: // RGB (12-bit)
|
||||
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
|
||||
case 6: // RGB (24-bit)
|
||||
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
|
||||
case 8: // ARGB (32-bit)
|
||||
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
|
||||
default:
|
||||
return .clear
|
||||
}
|
||||
return UIColor(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
func addCorner(roundingCorners: UIRectCorner, cornerSize: CGSize) {
|
||||
let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: roundingCorners, cornerRadii: cornerSize)
|
||||
let cornerLayer = CAShapeLayer()
|
||||
cornerLayer.frame = bounds
|
||||
cornerLayer.path = path.cgPath
|
||||
self.layer.mask = cornerLayer
|
||||
}
|
||||
}
|
||||
|
||||
//extension NSObject {
|
||||
// func dispatch_main_async_safe(callback: @escaping ()->Void ) {
|
||||
// if Thread.isMainThread {
|
||||
// callback()
|
||||
// } else {
|
||||
// DispatchQueue.main.async( execute: {
|
||||
// callback()
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//https://stackoverflow.com/questions/26244293/scrolltorowatindexpath-with-uitableview-does-not-work
|
||||
///tableView
|
||||
extension UITableView {
|
||||
func tableViewScrollToBottom(animated: Bool) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1)) {
|
||||
let numberOfSections = self.numberOfSections
|
||||
let numberOfRows = self.numberOfRows(inSection: numberOfSections-1)
|
||||
|
||||
if numberOfRows > 0 {
|
||||
let indexPath = IndexPath(row: numberOfRows-1, section: (numberOfSections-1))
|
||||
self.scrollToRow(at: indexPath, at: .bottom, animated: animated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func tableViewScrollToIndex(index: Int, animated: Bool) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1)) {
|
||||
self.scrollToRow(at: IndexPath(row: index, section: 0), at: .middle, animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
func tableViewScrollToHeader(animated: Bool) {
|
||||
self.scrollRectToVisible(CGRect(x: 0, y: 0, width: 1, height: 1), animated: animated)
|
||||
}
|
||||
|
||||
func reloadData(completion: @escaping () -> ()) {
|
||||
UIView.animate(withDuration: 0, animations: { self.reloadData()})
|
||||
{_ in completion() }
|
||||
}
|
||||
}
|
||||
|
||||
///shake
|
||||
extension UIWindow {
|
||||
|
||||
private static var _cocoadebugShakeProperty = [String:Bool]()
|
||||
|
||||
var cocoadebugShakeProperty:Bool {
|
||||
get {
|
||||
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
||||
return UIWindow._cocoadebugShakeProperty[tmpAddress] ?? false
|
||||
}
|
||||
set(newValue) {
|
||||
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
||||
UIWindow._cocoadebugShakeProperty[tmpAddress] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
open override var canBecomeFirstResponder: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
open override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
|
||||
super.motionBegan(motion, with: event)
|
||||
|
||||
self.cocoadebugShakeProperty = true
|
||||
|
||||
if CocoaDebugSettings.shared.responseShake == false {return}
|
||||
if motion == .motionShake {
|
||||
if CocoaDebugSettings.shared.visible == true { return }
|
||||
CocoaDebugSettings.shared.showBubbleAndWindow = !CocoaDebugSettings.shared.showBubbleAndWindow
|
||||
}
|
||||
}
|
||||
|
||||
open override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
|
||||
super.motionEnded(motion, with: event)
|
||||
|
||||
if self.cocoadebugShakeProperty == true {
|
||||
self.cocoadebugShakeProperty = false
|
||||
return
|
||||
}
|
||||
|
||||
if CocoaDebugSettings.shared.responseShake == false {return}
|
||||
if motion == .motionShake {
|
||||
if CocoaDebugSettings.shared.visible == true { return }
|
||||
CocoaDebugSettings.shared.showBubbleAndWindow = !CocoaDebugSettings.shared.showBubbleAndWindow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///CocoaDebug
|
||||
extension CocoaDebug {
|
||||
|
||||
///init
|
||||
static func initializationMethod(serverURL: String? = nil, ignoredURLs: [String]? = nil, onlyURLs: [String]? = nil, ignoredPrefixLogs: [String]? = nil, onlyPrefixLogs: [String]? = nil, additionalViewController: UIViewController? = nil, emailToRecipients: [String]? = nil, emailCcRecipients: [String]? = nil, mainColor: String? = nil, protobufTransferMap: [String: [String]]? = nil)
|
||||
{
|
||||
if serverURL == nil {
|
||||
CocoaDebugSettings.shared.serverURL = ""
|
||||
} else {
|
||||
CocoaDebugSettings.shared.serverURL = serverURL
|
||||
}
|
||||
|
||||
if ignoredURLs == nil {
|
||||
CocoaDebugSettings.shared.ignoredURLs = []
|
||||
} else {
|
||||
CocoaDebugSettings.shared.ignoredURLs = ignoredURLs
|
||||
}
|
||||
if onlyURLs == nil {
|
||||
CocoaDebugSettings.shared.onlyURLs = []
|
||||
} else {
|
||||
CocoaDebugSettings.shared.onlyURLs = onlyURLs
|
||||
}
|
||||
|
||||
if ignoredPrefixLogs == nil {
|
||||
CocoaDebugSettings.shared.ignoredPrefixLogs = []
|
||||
} else {
|
||||
CocoaDebugSettings.shared.ignoredPrefixLogs = ignoredPrefixLogs
|
||||
}
|
||||
if onlyPrefixLogs == nil {
|
||||
CocoaDebugSettings.shared.onlyPrefixLogs = []
|
||||
} else {
|
||||
CocoaDebugSettings.shared.onlyPrefixLogs = onlyPrefixLogs
|
||||
}
|
||||
|
||||
if CocoaDebugSettings.shared.firstIn == nil {//first launch
|
||||
CocoaDebugSettings.shared.firstIn = ""
|
||||
CocoaDebugSettings.shared.showBubbleAndWindow = true
|
||||
} else {//not first launch
|
||||
CocoaDebugSettings.shared.showBubbleAndWindow = CocoaDebugSettings.shared.showBubbleAndWindow
|
||||
}
|
||||
|
||||
CocoaDebugSettings.shared.visible = false
|
||||
CocoaDebugSettings.shared.logSearchWordNormal = nil
|
||||
CocoaDebugSettings.shared.logSearchWordRN = nil
|
||||
CocoaDebugSettings.shared.logSearchWordWeb = nil
|
||||
CocoaDebugSettings.shared.networkSearchWord = nil
|
||||
CocoaDebugSettings.shared.protobufTransferMap = protobufTransferMap
|
||||
CocoaDebugSettings.shared.additionalViewController = additionalViewController
|
||||
|
||||
var _ = _OCLogStoreManager.shared()
|
||||
CocoaDebugSettings.shared.responseShake = true
|
||||
|
||||
//share via email
|
||||
CocoaDebugSettings.shared.emailToRecipients = emailToRecipients
|
||||
CocoaDebugSettings.shared.emailCcRecipients = emailCcRecipients
|
||||
|
||||
//color
|
||||
CocoaDebugSettings.shared.mainColor = mainColor ?? "#42d459"
|
||||
|
||||
//slow animations
|
||||
CocoaDebugSettings.shared.slowAnimations = false
|
||||
|
||||
//log
|
||||
let enableLogMonitoring = UserDefaults.standard.bool(forKey: "enableLogMonitoring_CocoaDebug")
|
||||
if enableLogMonitoring == false {
|
||||
_SwiftLogHelper.shared.enable = false
|
||||
// _OCLogHelper.shared()?.enable = false
|
||||
} else {
|
||||
_SwiftLogHelper.shared.enable = true
|
||||
// _OCLogHelper.shared()?.enable = true
|
||||
}
|
||||
|
||||
//network
|
||||
let disableNetworkMonitoring = UserDefaults.standard.bool(forKey: "disableNetworkMonitoring_CocoaDebug")
|
||||
if disableNetworkMonitoring == true {
|
||||
_NetworkHelper.shared().disable()
|
||||
} else {
|
||||
_NetworkHelper.shared().enable()
|
||||
}
|
||||
}
|
||||
|
||||
///deinit
|
||||
static func deinitializationMethod() {
|
||||
WindowHelper.shared.disable()
|
||||
_NetworkHelper.shared().disable()
|
||||
_SwiftLogHelper.shared.enable = false
|
||||
// _OCLogHelper.shared()?.enable = false
|
||||
CrashLogger.shared.enable = false
|
||||
CocoaDebugSettings.shared.responseShake = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
40
Pods/CocoaDebug/Sources/Core/CocoaDebug.h
generated
@@ -1,40 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CocoaDebug.h"
|
||||
#import "CocoaDebugTool.h"
|
||||
#import "CocoaDebugDeviceInfo.h"
|
||||
#import "NSObject+CocoaDebug.h"
|
||||
|
||||
#import "_fishhook.h"
|
||||
#import "_FileInfo.h"
|
||||
#import "_Swizzling.h"
|
||||
#import "_ObjcLog.h"
|
||||
#import "_Sandboxer.h"
|
||||
#import "_HttpModel.h"
|
||||
#import "_OCLogModel.h"
|
||||
#import "_OCLogHelper.h"
|
||||
#import "_NetworkHelper.h"
|
||||
#import "_HttpDatasource.h"
|
||||
#import "_ImageResources.h"
|
||||
#import "_OCLoggerFormat.h"
|
||||
#import "_RunloopMonitor.h"
|
||||
#import "_BacktraceLogger.h"
|
||||
#import "_ImageController.h"
|
||||
#import "_SandboxerHelper.h"
|
||||
#import "_CanonicalRequest.h"
|
||||
#import "_Sandboxer-Header.h"
|
||||
#import "_FileTableViewCell.h"
|
||||
#import "_OCLogStoreManager.h"
|
||||
#import "_DebugConsoleLabel.h"
|
||||
#import "_CustomHTTPProtocol.h"
|
||||
#import "_CacheStoragePolicy.h"
|
||||
#import "_QNSURLSessionDemux.h"
|
||||
#import "GPBMessage+CocoaDebug.h"
|
||||
#import "_FilePreviewController.h"
|
||||
#import "_DirectoryContentsTableViewController.h"
|
||||
65
Pods/CocoaDebug/Sources/Core/CocoaDebug.swift
generated
@@ -1,65 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
@objc public class CocoaDebug : NSObject {
|
||||
|
||||
///if the captured URLs contain server URL, CocoaDebug set server URL bold font to be marked. Not mark when this value is nil. Default value is `nil`.
|
||||
@objc public static var serverURL: String? = nil
|
||||
///set the URLs which should not been captured, CocoaDebug capture all URLs when the value is nil. Default value is `nil`.
|
||||
@objc public static var ignoredURLs: [String]? = nil
|
||||
///set the URLs which are only been captured, CocoaDebug capture all URLs when the value is nil. Default value is `nil`.
|
||||
@objc public static var onlyURLs: [String]? = nil
|
||||
///set the prefix Logs which should not been captured, CocoaDebug capture all Logs when the value is nil. Default value is `nil`.
|
||||
@objc public static var ignoredPrefixLogs: [String]? = nil
|
||||
///set the prefix Logs which are only been captured, CocoaDebug capture all Logs when the value is nil. Default value is `nil`.
|
||||
@objc public static var onlyPrefixLogs: [String]? = nil
|
||||
///add an additional UIViewController as child controller of CocoaDebug's main UITabBarController. Default value is `nil`.
|
||||
@objc public static var additionalViewController: UIViewController? = nil
|
||||
///set the initial recipients to include in the email’s “To” field when share via email. Default value is `nil`.
|
||||
@objc public static var emailToRecipients: [String]? = nil
|
||||
///set the initial recipients to include in the email’s “Cc” field when share via email. Default value is `nil`.
|
||||
@objc public static var emailCcRecipients: [String]? = nil
|
||||
///set CocoaDebug's main color with hexadecimal format. Default value is `#42d459`.
|
||||
@objc public static var mainColor: String = "#42d459"
|
||||
///protobuf url and response class transfer map. Default value is `nil`.
|
||||
@objc public static var protobufTransferMap: [String: [String]]? = nil
|
||||
|
||||
//MARK: - CocoaDebug enable
|
||||
@objc public static func enable() {
|
||||
initializationMethod(serverURL: serverURL, ignoredURLs: ignoredURLs, onlyURLs: onlyURLs, ignoredPrefixLogs: ignoredPrefixLogs, onlyPrefixLogs: onlyPrefixLogs, additionalViewController: additionalViewController, emailToRecipients: emailToRecipients, emailCcRecipients: emailCcRecipients, mainColor: mainColor, protobufTransferMap: protobufTransferMap)
|
||||
}
|
||||
|
||||
//MARK: - CocoaDebug disable
|
||||
@objc public static func disable() {
|
||||
deinitializationMethod()
|
||||
}
|
||||
|
||||
//MARK: - hide Bubble
|
||||
@objc public static func hideBubble() {
|
||||
CocoaDebugSettings.shared.showBubbleAndWindow = false
|
||||
}
|
||||
|
||||
//MARK: - show Bubble
|
||||
@objc public static func showBubble() {
|
||||
CocoaDebugSettings.shared.showBubbleAndWindow = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//MARK: - override Swift `print` method
|
||||
public func print<T>(file: String = #file, function: String = #function, line: Int = #line, _ message: T, color: UIColor = .white) {
|
||||
Swift.print(message)
|
||||
_SwiftLogHelper.shared.handleLog(file: file, function: function, line: line, message: message, color: color)
|
||||
}
|
||||
|
||||
|
||||
|
||||
37
Pods/CocoaDebug/Sources/Core/CocoaDebugDeviceInfo.h
generated
@@ -1,37 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
// https://github.com/maybeliu/MBDeviceTool_OC
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface CocoaDebugDeviceInfo : NSObject
|
||||
|
||||
@property (nonatomic, assign, readonly) CGSize resolution;
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *systemType;
|
||||
@property (nonatomic, copy, readonly) NSString *userName;
|
||||
@property (nonatomic, copy, readonly) NSString *systemVersion;
|
||||
@property (nonatomic, copy, readonly) NSString *deviceModel;
|
||||
@property (nonatomic, copy, readonly) NSString *deviceUUID;
|
||||
@property (nonatomic, copy, readonly) NSString *userPhoneName;
|
||||
@property (nonatomic, copy, readonly) NSString *deviceName;
|
||||
@property (nonatomic, copy, readonly) NSString *getPlatformString;
|
||||
@property (nonatomic, copy, readonly) NSString *localizedModel;
|
||||
@property (nonatomic, copy, readonly) NSString *appVersion;
|
||||
@property (nonatomic, copy, readonly) NSString *appBuiltVersion;
|
||||
@property (nonatomic, copy, readonly) NSString *appBundleID ;
|
||||
@property (nonatomic, copy, readonly) NSString *appBundleName ;
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
89
Pods/CocoaDebug/Sources/Core/CocoaDebugDeviceInfo.m
generated
@@ -1,89 +0,0 @@
|
||||
//
|
||||
// CocoaDebug
|
||||
// liman
|
||||
//
|
||||
// Created by liman 02/02/2023.
|
||||
// Copyright © 2023 liman. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CocoaDebugDeviceInfo.h"
|
||||
#import "sys/utsname.h"
|
||||
#import "_DeviceUtil.h"
|
||||
|
||||
@implementation CocoaDebugDeviceInfo
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
|
||||
static CocoaDebugDeviceInfo *sharedInstance;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[CocoaDebugDeviceInfo alloc] init];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (NSString *)systemType {
|
||||
|
||||
return [[UIDevice currentDevice] systemName];
|
||||
}
|
||||
|
||||
- (NSString *)userName {
|
||||
|
||||
return [[UIDevice currentDevice] name];
|
||||
}
|
||||
|
||||
- (NSString *)systemVersion {
|
||||
|
||||
return [[UIDevice currentDevice] systemVersion];
|
||||
}
|
||||
|
||||
- (NSString *)deviceModel {
|
||||
|
||||
return [[UIDevice currentDevice] model];
|
||||
}
|
||||
|
||||
- (NSString *)deviceUUID {
|
||||
|
||||
return [[[UIDevice currentDevice] identifierForVendor] UUIDString];
|
||||
}
|
||||
|
||||
- (NSString *)deviceName {
|
||||
struct utsname systemInfo;
|
||||
uname(&systemInfo);
|
||||
NSString *code = [NSString stringWithCString:systemInfo.machine
|
||||
encoding:NSUTF8StringEncoding];
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)getPlatformString {
|
||||
return [[_DeviceUtil new] hardwareSimpleDescription];
|
||||
}
|
||||
|
||||
- (NSString *)localizedModel {
|
||||
return [[UIDevice currentDevice] localizedModel];
|
||||
}
|
||||
|
||||
- (NSString *)appVersion {
|
||||
return [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"];
|
||||
}
|
||||
|
||||
- (NSString *)appBuiltVersion {
|
||||
return [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"];
|
||||
}
|
||||
|
||||
- (NSString *)appBundleID {
|
||||
NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
|
||||
return bundleID;
|
||||
}
|
||||
|
||||
- (NSString *)appBundleName {
|
||||
NSString *bundleName = [[NSBundle mainBundle] objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleNameKey];
|
||||
return bundleName;
|
||||
}
|
||||
|
||||
- (CGSize)resolution {
|
||||
return CGSizeMake([UIScreen mainScreen].bounds.size.width * [[UIScreen mainScreen] scale], [UIScreen mainScreen].bounds.size.height * [[UIScreen mainScreen] scale]);
|
||||
}
|
||||
|
||||
@end
|
||||
277
Pods/CocoaDebug/Sources/Core/CocoaDebugSettings.swift
generated
@@ -1,277 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc public class CocoaDebugSettings: NSObject {
|
||||
|
||||
@objc public static let shared = CocoaDebugSettings()
|
||||
|
||||
@objc public var slowAnimations: Bool = false {
|
||||
didSet {
|
||||
if slowAnimations == false {
|
||||
UIApplication.shared.windows.first?.layer.speed = 1;
|
||||
} else {
|
||||
UIApplication.shared.windows.first?.layer.speed = 0.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc public var responseShake: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(responseShake, forKey: "responseShake_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
|
||||
@objc public var firstIn: String? = nil {
|
||||
didSet {
|
||||
UserDefaults.standard.set(firstIn, forKey: "firstIn_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var enableCrashRecording: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(enableCrashRecording, forKey: "enableCrashRecording_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
|
||||
if enableCrashRecording == true {
|
||||
CrashLogger.shared.enable = true
|
||||
} else {
|
||||
CrashLogger.shared.enable = false
|
||||
CrashStoreManager.shared.resetCrashs()
|
||||
}
|
||||
}
|
||||
}
|
||||
@objc public var enableUIBlockingMonitoring: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(enableUIBlockingMonitoring, forKey: "enableUIBlockingMonitoring_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var enableWKWebViewMonitoring: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(enableWKWebViewMonitoring, forKey: "enableWKWebViewMonitoring_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var enableLogMonitoring: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(enableLogMonitoring, forKey: "enableLogMonitoring_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var disableNetworkMonitoring: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(disableNetworkMonitoring, forKey: "disableNetworkMonitoring_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var enableRNMonitoring: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(enableRNMonitoring, forKey: "enableRNMonitoring_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var enableMemoryLeaksMonitoring_ViewController: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(enableMemoryLeaksMonitoring_ViewController, forKey: "enableMemoryLeaksMonitoring_UIViewController_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var enableMemoryLeaksMonitoring_View: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(enableMemoryLeaksMonitoring_View, forKey: "enableMemoryLeaksMonitoring_UIView_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var enableMemoryLeaksMonitoring_MemberVariables: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(enableMemoryLeaksMonitoring_MemberVariables, forKey: "enableMemoryLeaksMonitoring_MemberVariables_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var visible: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(visible, forKey: "visible_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var showBubbleAndWindow: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(showBubbleAndWindow, forKey: "showBubbleAndWindow_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
|
||||
let x = WindowHelper.shared.vc.bubble.frame.origin.x
|
||||
let width = WindowHelper.shared.vc.bubble.frame.size.width
|
||||
|
||||
if showBubbleAndWindow == true
|
||||
{
|
||||
if x > UIScreen.main.bounds.size.width/2 {
|
||||
WindowHelper.shared.vc.bubble.frame.origin.x = UIScreen.main.bounds.size.width - width/8*8.25
|
||||
} else {
|
||||
WindowHelper.shared.vc.bubble.frame.origin.x = -width + width/8*8.25
|
||||
}
|
||||
WindowHelper.shared.enable()
|
||||
}
|
||||
else
|
||||
{
|
||||
if x > UIScreen.main.bounds.size.width/2 {
|
||||
WindowHelper.shared.vc.bubble.frame.origin.x = UIScreen.main.bounds.size.width
|
||||
} else {
|
||||
WindowHelper.shared.vc.bubble.frame.origin.x = -width
|
||||
}
|
||||
WindowHelper.shared.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
@objc public var serverURL: String? = nil {
|
||||
didSet {
|
||||
UserDefaults.standard.set(serverURL, forKey: "serverURL_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var tabBarSelectItem: Int {
|
||||
didSet {
|
||||
UserDefaults.standard.set(tabBarSelectItem, forKey: "tabBarSelectItem_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var logSelectIndex: Int {
|
||||
didSet {
|
||||
UserDefaults.standard.set(logSelectIndex, forKey: "logSelectIndex_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var networkLastIndex: Int {
|
||||
didSet {
|
||||
UserDefaults.standard.set(networkLastIndex, forKey: "networkLastIndex_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var bubbleFrameX: Float {
|
||||
didSet {
|
||||
UserDefaults.standard.set(bubbleFrameX, forKey: "bubbleFrameX_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var bubbleFrameY: Float {
|
||||
didSet {
|
||||
UserDefaults.standard.set(bubbleFrameY, forKey: "bubbleFrameY_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var logSearchWordNormal: String? = nil {
|
||||
didSet {
|
||||
UserDefaults.standard.set(logSearchWordNormal, forKey: "logSearchWordNormal_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var logSearchWordRN: String? = nil {
|
||||
didSet {
|
||||
UserDefaults.standard.set(logSearchWordRN, forKey: "logSearchWordRN_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var logSearchWordWeb: String? = nil {
|
||||
didSet {
|
||||
UserDefaults.standard.set(logSearchWordWeb, forKey: "logSearchWordWeb_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var networkSearchWord: String? = nil {
|
||||
didSet {
|
||||
UserDefaults.standard.set(networkSearchWord, forKey: "networkSearchWord_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
@objc public var mainColor: String {
|
||||
didSet {
|
||||
UserDefaults.standard.set(mainColor, forKey: "mainColor_CocoaDebug")
|
||||
UserDefaults.standard.synchronize()
|
||||
_NetworkHelper.shared().mainColor = mainColor.hexColor
|
||||
}
|
||||
}
|
||||
@objc public var additionalViewController: UIViewController? = nil
|
||||
|
||||
//share via email
|
||||
@objc public var emailToRecipients: [String]? = nil
|
||||
@objc public var emailCcRecipients: [String]? = nil
|
||||
|
||||
//objc
|
||||
|
||||
@objc public var ignoredURLs: [String]? = nil {
|
||||
didSet {
|
||||
_NetworkHelper.shared().ignoredURLs = ignoredURLs
|
||||
}
|
||||
}
|
||||
@objc public var onlyURLs: [String]? = nil {
|
||||
didSet {
|
||||
_NetworkHelper.shared().onlyURLs = onlyURLs
|
||||
}
|
||||
}
|
||||
|
||||
@objc public var ignoredPrefixLogs: [String]? = nil {
|
||||
didSet {
|
||||
_NetworkHelper.shared().ignoredPrefixLogs = ignoredPrefixLogs
|
||||
}
|
||||
}
|
||||
@objc public var onlyPrefixLogs: [String]? = nil {
|
||||
didSet {
|
||||
_NetworkHelper.shared().onlyPrefixLogs = onlyPrefixLogs
|
||||
}
|
||||
}
|
||||
|
||||
//protobuf
|
||||
@objc public var protobufTransferMap: [String: [String]]? = nil {
|
||||
didSet {
|
||||
_NetworkHelper.shared().protobufTransferMap = protobufTransferMap
|
||||
}
|
||||
}
|
||||
|
||||
private override init() {
|
||||
responseShake = UserDefaults.standard.bool(forKey: "responseShake_CocoaDebug")
|
||||
firstIn = UserDefaults.standard.string(forKey: "firstIn_CocoaDebug")
|
||||
serverURL = UserDefaults.standard.string(forKey: "serverURL_CocoaDebug")
|
||||
visible = UserDefaults.standard.bool(forKey: "visible_CocoaDebug")
|
||||
showBubbleAndWindow = UserDefaults.standard.bool(forKey: "showBubbleAndWindow_CocoaDebug")
|
||||
enableCrashRecording = UserDefaults.standard.bool(forKey: "enableCrashRecording_CocoaDebug")
|
||||
enableUIBlockingMonitoring = UserDefaults.standard.bool(forKey: "enableUIBlockingMonitoring_CocoaDebug")
|
||||
enableWKWebViewMonitoring = UserDefaults.standard.bool(forKey: "enableWKWebViewMonitoring_CocoaDebug")
|
||||
enableLogMonitoring = UserDefaults.standard.bool(forKey: "enableLogMonitoring_CocoaDebug")
|
||||
disableNetworkMonitoring = UserDefaults.standard.bool(forKey: "disableNetworkMonitoring_CocoaDebug")
|
||||
enableRNMonitoring = UserDefaults.standard.bool(forKey: "enableRNMonitoring_CocoaDebug")
|
||||
tabBarSelectItem = UserDefaults.standard.integer(forKey: "tabBarSelectItem_CocoaDebug")
|
||||
logSelectIndex = UserDefaults.standard.integer(forKey: "logSelectIndex_CocoaDebug")
|
||||
networkLastIndex = UserDefaults.standard.integer(forKey: "networkLastIndex_CocoaDebug")
|
||||
bubbleFrameX = UserDefaults.standard.float(forKey: "bubbleFrameX_CocoaDebug")
|
||||
bubbleFrameY = UserDefaults.standard.float(forKey: "bubbleFrameY_CocoaDebug")
|
||||
logSearchWordNormal = UserDefaults.standard.string(forKey: "logSearchWordNormal_CocoaDebug")
|
||||
logSearchWordRN = UserDefaults.standard.string(forKey: "logSearchWordRN_CocoaDebug")
|
||||
logSearchWordWeb = UserDefaults.standard.string(forKey: "logSearchWordWeb_CocoaDebug")
|
||||
networkSearchWord = UserDefaults.standard.string(forKey: "networkSearchWord_CocoaDebug")
|
||||
mainColor = UserDefaults.standard.string(forKey: "mainColor_CocoaDebug") ?? "#42d459"
|
||||
|
||||
|
||||
//objc
|
||||
|
||||
ignoredURLs = _NetworkHelper.shared().ignoredURLs
|
||||
onlyURLs = _NetworkHelper.shared().onlyURLs
|
||||
|
||||
ignoredPrefixLogs = _NetworkHelper.shared().ignoredPrefixLogs
|
||||
onlyPrefixLogs = _NetworkHelper.shared().onlyPrefixLogs
|
||||
|
||||
//protobuf
|
||||
protobufTransferMap = _NetworkHelper.shared().protobufTransferMap
|
||||
|
||||
//Memory
|
||||
enableMemoryLeaksMonitoring_ViewController = UserDefaults.standard.bool(forKey: "enableMemoryLeaksMonitoring_UIViewController_CocoaDebug")
|
||||
enableMemoryLeaksMonitoring_View = UserDefaults.standard.bool(forKey: "enableMemoryLeaksMonitoring_UIView_CocoaDebug")
|
||||
enableMemoryLeaksMonitoring_MemberVariables = UserDefaults.standard.bool(forKey: "enableMemoryLeaksMonitoring_MemberVariables_CocoaDebug")
|
||||
}
|
||||
}
|
||||
51
Pods/CocoaDebug/Sources/Core/_AutoLaunch.m
generated
@@ -1,51 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
static const char *kPropertyKey = "kApplicationDidFinishLaunching_CocoaDebug_Key";
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <objc/runtime.h>
|
||||
#import "_NetworkHelper.h"
|
||||
|
||||
@interface NSObject (CocoaDebugAutoLaunch)
|
||||
|
||||
@property (nonatomic, assign) BOOL cocoadebug_applicationDidFinishLaunching;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSObject (CocoaDebugAutoLaunch)
|
||||
|
||||
#pragma mark - load
|
||||
+ (void)load {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cocoadebug_applicationDidFinishLaunchingNotification:) name:UIApplicationDidFinishLaunchingNotification object:nil];
|
||||
}
|
||||
|
||||
#pragma mark - notification
|
||||
- (void)cocoadebug_applicationDidFinishLaunchingNotification:(NSNotification *)notification {
|
||||
if (self.cocoadebug_applicationDidFinishLaunching) {return;}
|
||||
self.cocoadebug_applicationDidFinishLaunching = YES;
|
||||
|
||||
Class CocoaDebug = NSClassFromString(@"_TtC10CocoaDebug10CocoaDebug");
|
||||
if (CocoaDebug) {
|
||||
[[CocoaDebug class] performSelector:@selector(enable)];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - getter setter
|
||||
- (BOOL)cocoadebug_applicationDidFinishLaunching {
|
||||
NSNumber *number = objc_getAssociatedObject(self, kPropertyKey);
|
||||
return [number boolValue];
|
||||
}
|
||||
|
||||
- (void)setCocoadebug_applicationDidFinishLaunching:(BOOL)applicationDidFinishLaunching {
|
||||
NSNumber *number = [NSNumber numberWithBool:applicationDidFinishLaunching];
|
||||
objc_setAssociatedObject(self, kPropertyKey, number, OBJC_ASSOCIATION_RETAIN);
|
||||
}
|
||||
|
||||
@end
|
||||
325
Pods/CocoaDebug/Sources/Core/_DeviceUtil+Constant.h
generated
@@ -1,325 +0,0 @@
|
||||
//
|
||||
// CocoaDebug
|
||||
// liman
|
||||
//
|
||||
// Created by liman 02/02/2023.
|
||||
// Copyright © 2023 liman. All rights reserved.
|
||||
//
|
||||
|
||||
typedef NS_ENUM(NSUInteger, Hardware) {
|
||||
|
||||
|
||||
IPHONE_2G,
|
||||
IPHONE_3G,
|
||||
IPHONE_3GS,
|
||||
IPHONE_4,
|
||||
IPHONE_4_CDMA,
|
||||
IPHONE_4S,
|
||||
IPHONE_5,
|
||||
IPHONE_5_CDMA_GSM,
|
||||
IPHONE_5C,
|
||||
IPHONE_5C_CDMA_GSM,
|
||||
IPHONE_5S,
|
||||
IPHONE_5S_CDMA_GSM,
|
||||
IPHONE_6_PLUS,
|
||||
IPHONE_6,
|
||||
IPHONE_6S,
|
||||
IPHONE_6S_PLUS,
|
||||
IPHONE_SE,
|
||||
IPHONE_7,
|
||||
IPHONE_7_PLUS,
|
||||
IPHONE_7_GSM,
|
||||
IPHONE_7_PLUS_GSM,
|
||||
IPHONE_8_CN,
|
||||
IPHONE_8_PLUS_CN,
|
||||
IPHONE_X_CN,
|
||||
IPHONE_8,
|
||||
IPHONE_8_PLUS,
|
||||
IPHONE_X,
|
||||
IPHONE_XS,
|
||||
IPHONE_XS_MAX,
|
||||
IPHONE_XS_MAX_CN,
|
||||
IPHONE_XR,
|
||||
IPHONE_11,
|
||||
IPHONE_11_PRO,
|
||||
IPHONE_11_PRO_MAX,
|
||||
IPHONE_SE_2G,
|
||||
IPHONE_12_MINI,
|
||||
IPHONE_12,
|
||||
IPHONE_12_PRO,
|
||||
IPHONE_12_PRO_MAX,
|
||||
IPHONE_13_PRO,
|
||||
IPHONE_13_PRO_MAX,
|
||||
IPHONE_13_MINI,
|
||||
IPHONE_13,
|
||||
IPHONE_SE_3G,
|
||||
IPHONE_14,
|
||||
IPHONE_14_PLUS,
|
||||
IPHONE_14_PRO,
|
||||
IPHONE_14_PRO_MAX,
|
||||
|
||||
IPOD_TOUCH_1G,
|
||||
IPOD_TOUCH_2G,
|
||||
IPOD_TOUCH_3G,
|
||||
IPOD_TOUCH_4G,
|
||||
IPOD_TOUCH_5G,
|
||||
IPOD_TOUCH_6G,
|
||||
IPOD_TOUCH_7G,
|
||||
|
||||
IPAD,
|
||||
IPAD_3G,
|
||||
IPAD_2_WIFI,
|
||||
IPAD_2,
|
||||
IPAD_2_CDMA,
|
||||
IPAD_MINI_WIFI,
|
||||
IPAD_MINI,
|
||||
IPAD_MINI_WIFI_CDMA,
|
||||
IPAD_3_WIFI,
|
||||
IPAD_3_WIFI_CDMA,
|
||||
IPAD_3,
|
||||
IPAD_4_WIFI,
|
||||
IPAD_4,
|
||||
IPAD_4_GSM_CDMA,
|
||||
IPAD_AIR_WIFI,
|
||||
IPAD_AIR_WIFI_GSM,
|
||||
IPAD_AIR_WIFI_CDMA,
|
||||
IPAD_MINI_RETINA_WIFI,
|
||||
IPAD_MINI_RETINA_WIFI_CDMA,
|
||||
IPAD_MINI_RETINA_WIFI_CELLULAR_CN,
|
||||
IPAD_MINI_3_WIFI,
|
||||
IPAD_MINI_3_WIFI_CELLULAR,
|
||||
IPAD_MINI_3_WIFI_CELLULAR_CN,
|
||||
IPAD_MINI_4_WIFI,
|
||||
IPAD_MINI_4_WIFI_CELLULAR,
|
||||
IPAD_AIR_2_WIFI,
|
||||
IPAD_AIR_2_WIFI_CELLULAR,
|
||||
IPAD_5_WIFI,
|
||||
IPAD_5_WIFI_CELLULAR,
|
||||
IPAD_PRO_97_WIFI,
|
||||
IPAD_PRO_97_WIFI_CELLULAR,
|
||||
IPAD_PRO_WIFI,
|
||||
IPAD_PRO_WIFI_CELLULAR,
|
||||
IPAD_PRO_2G_WIFI,
|
||||
IPAD_7_WIFI,
|
||||
IPAD_7_WIFI_CELLULAR,
|
||||
IPAD_PRO_2G_WIFI_CELLULAR,
|
||||
IPAD_PRO_105_WIFI,
|
||||
IPAD_PRO_105_WIFI_CELLULAR,
|
||||
IPAD_6_WIFI,
|
||||
IPAD_6_WIFI_CELLULAR,
|
||||
IPAD_PRO_11_2G_WIFI_CELLULAR,
|
||||
IPAD_PRO_11_WIFI,
|
||||
IPAD_PRO_4G_WIFI,
|
||||
IPAD_PRO_11_1TB_WIFI,
|
||||
IPAD_PRO_11_WIFI_CELLULAR,
|
||||
IPAD_PRO_11_1TB_WIFI_CELLULAR,
|
||||
IPAD_PRO_3G_WIFI,
|
||||
IPAD_PRO_3G_1TB_WIFI,
|
||||
IPAD_PRO_3G_WIFI_CELLULAR,
|
||||
IPAD_PRO_4G_WIFI_CELLULAR,
|
||||
IPAD_PRO_3G_1TB_WIFI_CELLULAR,
|
||||
IPAD_PRO_11_2G_WIFI,
|
||||
IPAD_MINI_5_WIFI,
|
||||
IPAD_MINI_5_WIFI_CELLULAR,
|
||||
IPAD_AIR_3_WIFI,
|
||||
IPAD_AIR_3_WIFI_CELLULAR,
|
||||
IPAD_9_WIFI,
|
||||
IPAD_9_WIFI_CELLULAR,
|
||||
IPAD_PRO_5_WIFI_CELLULAR,
|
||||
IPAD_AIR_4_WIFI,
|
||||
IPAD_AIR_5_WIFI,
|
||||
IPAD_AIR_5_WIFI_CELLULAR,
|
||||
IPAD_AIR_4_WIFI_CELLULAR,
|
||||
IPAD_PRO_11_3_WIFI,
|
||||
IPAD_PRO_11_3_WIFI_CELLULAR,
|
||||
IPAD_PRO_5_WIFI,
|
||||
IPAD_MINI_6_WIFI,
|
||||
IPAD_MINI_6_WIFI_CELLULAR,
|
||||
|
||||
APPLE_WATCH_38,
|
||||
APPLE_WATCH_42,
|
||||
APPLE_WATCH_SERIES_2_38,
|
||||
APPLE_WATCH_SERIES_2_42,
|
||||
APPLE_WATCH_SERIES_1_38,
|
||||
APPLE_WATCH_SERIES_1_42,
|
||||
APPLE_WATCH_SERIES_3_38_CELLULAR,
|
||||
APPLE_WATCH_SERIES_3_42_CELLULAR,
|
||||
APPLE_WATCH_SERIES_3_38,
|
||||
APPLE_WATCH_SERIES_3_42,
|
||||
APPLE_WATCH_SERIES_4_40,
|
||||
APPLE_WATCH_SERIES_4_44,
|
||||
APPLE_WATCH_SERIES_4_40_CELLULAR,
|
||||
APPLE_WATCH_SERIES_4_44_CELLULAR,
|
||||
APPLE_WATCH_SERIES_5_40,
|
||||
APPLE_WATCH_SERIES_5_44,
|
||||
APPLE_WATCH_SERIES_5_40_CELLULAR,
|
||||
APPLE_WATCH_SERIES_5_44_CELLULAR,
|
||||
|
||||
APPLE_TV_1G,
|
||||
APPLE_TV_2G,
|
||||
APPLE_TV_3G,
|
||||
APPLE_TV_3_2G,
|
||||
APPLE_TV_4G,
|
||||
APPLE_TV_4K,
|
||||
|
||||
SIMULATOR,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
extern NSString* const AppleTV1_1;
|
||||
extern NSString* const AppleTV2_1;
|
||||
extern NSString* const AppleTV3_1;
|
||||
extern NSString* const AppleTV3_2;
|
||||
extern NSString* const AppleTV5_3;
|
||||
extern NSString* const AppleTV6_2;
|
||||
extern NSString* const Watch1_1;
|
||||
extern NSString* const Watch1_2;
|
||||
extern NSString* const Watch2_3;
|
||||
extern NSString* const Watch2_4;
|
||||
extern NSString* const Watch2_6;
|
||||
extern NSString* const Watch2_7;
|
||||
extern NSString* const Watch3_1;
|
||||
extern NSString* const Watch3_2;
|
||||
extern NSString* const Watch3_3;
|
||||
extern NSString* const Watch3_4;
|
||||
extern NSString* const Watch4_1;
|
||||
extern NSString* const Watch4_2;
|
||||
extern NSString* const Watch4_3;
|
||||
extern NSString* const Watch4_4;
|
||||
extern NSString* const Watch5_1;
|
||||
extern NSString* const Watch5_2;
|
||||
extern NSString* const Watch5_3;
|
||||
extern NSString* const Watch5_4;
|
||||
extern NSString* const i386_Simulator;
|
||||
extern NSString* const iPad1_1;
|
||||
extern NSString* const iPad1_2;
|
||||
extern NSString* const iPad11_1;
|
||||
extern NSString* const iPad11_2;
|
||||
extern NSString* const iPad11_3;
|
||||
extern NSString* const iPad11_4;
|
||||
extern NSString* const iPad12_1;
|
||||
extern NSString* const iPad12_2;
|
||||
extern NSString* const iPad13_1;
|
||||
extern NSString* const iPad13_10;
|
||||
extern NSString* const iPad13_11;
|
||||
extern NSString* const iPad13_16;
|
||||
extern NSString* const iPad13_17;
|
||||
extern NSString* const iPad13_2;
|
||||
extern NSString* const iPad13_4;
|
||||
extern NSString* const iPad13_5;
|
||||
extern NSString* const iPad13_6;
|
||||
extern NSString* const iPad13_7;
|
||||
extern NSString* const iPad13_8;
|
||||
extern NSString* const iPad13_9;
|
||||
extern NSString* const iPad14_1;
|
||||
extern NSString* const iPad14_2;
|
||||
extern NSString* const iPad2_1;
|
||||
extern NSString* const iPad2_2;
|
||||
extern NSString* const iPad2_3;
|
||||
extern NSString* const iPad2_4;
|
||||
extern NSString* const iPad2_5;
|
||||
extern NSString* const iPad2_6;
|
||||
extern NSString* const iPad2_7;
|
||||
extern NSString* const iPad3_1;
|
||||
extern NSString* const iPad3_2;
|
||||
extern NSString* const iPad3_3;
|
||||
extern NSString* const iPad3_4;
|
||||
extern NSString* const iPad3_5;
|
||||
extern NSString* const iPad3_6;
|
||||
extern NSString* const iPad4_1;
|
||||
extern NSString* const iPad4_2;
|
||||
extern NSString* const iPad4_3;
|
||||
extern NSString* const iPad4_4;
|
||||
extern NSString* const iPad4_5;
|
||||
extern NSString* const iPad4_6;
|
||||
extern NSString* const iPad4_7;
|
||||
extern NSString* const iPad4_8;
|
||||
extern NSString* const iPad4_9;
|
||||
extern NSString* const iPad5_1;
|
||||
extern NSString* const iPad5_2;
|
||||
extern NSString* const iPad5_3;
|
||||
extern NSString* const iPad5_4;
|
||||
extern NSString* const iPad6_11;
|
||||
extern NSString* const iPad6_12;
|
||||
extern NSString* const iPad6_3;
|
||||
extern NSString* const iPad6_4;
|
||||
extern NSString* const iPad6_7;
|
||||
extern NSString* const iPad6_8;
|
||||
extern NSString* const iPad7_1;
|
||||
extern NSString* const iPad7_11;
|
||||
extern NSString* const iPad7_12;
|
||||
extern NSString* const iPad7_2;
|
||||
extern NSString* const iPad7_3;
|
||||
extern NSString* const iPad7_4;
|
||||
extern NSString* const iPad7_5;
|
||||
extern NSString* const iPad7_6;
|
||||
extern NSString* const iPad8_1;
|
||||
extern NSString* const iPad8_10;
|
||||
extern NSString* const iPad8_11;
|
||||
extern NSString* const iPad8_12;
|
||||
extern NSString* const iPad8_2;
|
||||
extern NSString* const iPad8_3;
|
||||
extern NSString* const iPad8_4;
|
||||
extern NSString* const iPad8_5;
|
||||
extern NSString* const iPad8_6;
|
||||
extern NSString* const iPad8_7;
|
||||
extern NSString* const iPad8_8;
|
||||
extern NSString* const iPad8_9;
|
||||
extern NSString* const iPhone1_1;
|
||||
extern NSString* const iPhone1_2;
|
||||
extern NSString* const iPhone10_1;
|
||||
extern NSString* const iPhone10_2;
|
||||
extern NSString* const iPhone10_3;
|
||||
extern NSString* const iPhone10_4;
|
||||
extern NSString* const iPhone10_5;
|
||||
extern NSString* const iPhone10_6;
|
||||
extern NSString* const iPhone11_2;
|
||||
extern NSString* const iPhone11_4;
|
||||
extern NSString* const iPhone11_6;
|
||||
extern NSString* const iPhone11_8;
|
||||
extern NSString* const iPhone12_1;
|
||||
extern NSString* const iPhone12_3;
|
||||
extern NSString* const iPhone12_5;
|
||||
extern NSString* const iPhone12_8;
|
||||
extern NSString* const iPhone13_1;
|
||||
extern NSString* const iPhone13_2;
|
||||
extern NSString* const iPhone13_3;
|
||||
extern NSString* const iPhone13_4;
|
||||
extern NSString* const iPhone14_2;
|
||||
extern NSString* const iPhone14_3;
|
||||
extern NSString* const iPhone14_4;
|
||||
extern NSString* const iPhone14_5;
|
||||
extern NSString* const iPhone14_6;
|
||||
extern NSString* const iPhone14_7;
|
||||
extern NSString* const iPhone14_8;
|
||||
extern NSString* const iPhone15_2;
|
||||
extern NSString* const iPhone15_3;
|
||||
extern NSString* const iPhone2_1;
|
||||
extern NSString* const iPhone3_1;
|
||||
extern NSString* const iPhone3_2;
|
||||
extern NSString* const iPhone3_3;
|
||||
extern NSString* const iPhone4_1;
|
||||
extern NSString* const iPhone5_1;
|
||||
extern NSString* const iPhone5_2;
|
||||
extern NSString* const iPhone5_3;
|
||||
extern NSString* const iPhone5_4;
|
||||
extern NSString* const iPhone6_1;
|
||||
extern NSString* const iPhone6_2;
|
||||
extern NSString* const iPhone7_1;
|
||||
extern NSString* const iPhone7_2;
|
||||
extern NSString* const iPhone8_1;
|
||||
extern NSString* const iPhone8_2;
|
||||
extern NSString* const iPhone8_4;
|
||||
extern NSString* const iPhone9_1;
|
||||
extern NSString* const iPhone9_2;
|
||||
extern NSString* const iPhone9_3;
|
||||
extern NSString* const iPhone9_4;
|
||||
extern NSString* const iPod1_1;
|
||||
extern NSString* const iPod2_1;
|
||||
extern NSString* const iPod3_1;
|
||||
extern NSString* const iPod4_1;
|
||||
extern NSString* const iPod5_1;
|
||||
extern NSString* const iPod7_1;
|
||||
extern NSString* const iPod9_1;
|
||||
extern NSString* const x86_64_Simulator;
|
||||
|
||||
334
Pods/CocoaDebug/Sources/Core/_DeviceUtil+Constant.m
generated
@@ -1,334 +0,0 @@
|
||||
//
|
||||
// CocoaDebug
|
||||
// liman
|
||||
//
|
||||
// Created by liman 02/02/2023.
|
||||
// Copyright © 2023 liman. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_DeviceUtil.h"
|
||||
|
||||
NSString* const AppleTV1_1 = @"AppleTV1,1";
|
||||
NSString* const AppleTV2_1 = @"AppleTV2,1";
|
||||
NSString* const AppleTV3_1 = @"AppleTV3,1";
|
||||
NSString* const AppleTV3_2 = @"AppleTV3,2";
|
||||
NSString* const AppleTV5_3 = @"AppleTV5,3";
|
||||
NSString* const AppleTV6_2 = @"AppleTV6,2";
|
||||
NSString* const Watch1_1 = @"Watch1,1";
|
||||
NSString* const Watch1_2 = @"Watch1,2";
|
||||
NSString* const Watch2_3 = @"Watch2,3";
|
||||
NSString* const Watch2_4 = @"Watch2,4";
|
||||
NSString* const Watch2_6 = @"Watch2,6";
|
||||
NSString* const Watch2_7 = @"Watch2,7";
|
||||
NSString* const Watch3_1 = @"Watch3,1";
|
||||
NSString* const Watch3_2 = @"Watch3,2";
|
||||
NSString* const Watch3_3 = @"Watch3,3";
|
||||
NSString* const Watch3_4 = @"Watch3,4";
|
||||
NSString* const Watch4_1 = @"Watch4,1";
|
||||
NSString* const Watch4_2 = @"Watch4,2";
|
||||
NSString* const Watch4_3 = @"Watch4,3";
|
||||
NSString* const Watch4_4 = @"Watch4,4";
|
||||
NSString* const Watch5_1 = @"Watch5,1";
|
||||
NSString* const Watch5_2 = @"Watch5,2";
|
||||
NSString* const Watch5_3 = @"Watch5,3";
|
||||
NSString* const Watch5_4 = @"Watch5,4";
|
||||
NSString* const i386_Simulator = @"i386";
|
||||
NSString* const iPad1_1 = @"iPad1,1";
|
||||
NSString* const iPad1_2 = @"iPad1,2";
|
||||
NSString* const iPad11_1 = @"iPad11,1";
|
||||
NSString* const iPad11_2 = @"iPad11,2";
|
||||
NSString* const iPad11_3 = @"iPad11,3";
|
||||
NSString* const iPad11_4 = @"iPad11,4";
|
||||
NSString* const iPad12_1 = @"iPad12,1";
|
||||
NSString* const iPad12_2 = @"iPad12,2";
|
||||
NSString* const iPad13_1 = @"iPad13,1";
|
||||
NSString* const iPad13_10 = @"iPad13,10";
|
||||
NSString* const iPad13_11 = @"iPad13,11";
|
||||
NSString* const iPad13_16 = @"iPad13,16";
|
||||
NSString* const iPad13_17 = @"iPad13,17";
|
||||
NSString* const iPad13_2 = @"iPad13,2";
|
||||
NSString* const iPad13_4 = @"iPad13,4";
|
||||
NSString* const iPad13_5 = @"iPad13,5";
|
||||
NSString* const iPad13_6 = @"iPad13,6";
|
||||
NSString* const iPad13_7 = @"iPad13,7";
|
||||
NSString* const iPad13_8 = @"iPad13,8";
|
||||
NSString* const iPad13_9 = @"iPad13,9";
|
||||
NSString* const iPad14_1 = @"iPad14,1";
|
||||
NSString* const iPad14_2 = @"iPad14,2";
|
||||
NSString* const iPad2_1 = @"iPad2,1";
|
||||
NSString* const iPad2_2 = @"iPad2,2";
|
||||
NSString* const iPad2_3 = @"iPad2,3";
|
||||
NSString* const iPad2_4 = @"iPad2,4";
|
||||
NSString* const iPad2_5 = @"iPad2,5";
|
||||
NSString* const iPad2_6 = @"iPad2,6";
|
||||
NSString* const iPad2_7 = @"iPad2,7";
|
||||
NSString* const iPad3_1 = @"iPad3,1";
|
||||
NSString* const iPad3_2 = @"iPad3,2";
|
||||
NSString* const iPad3_3 = @"iPad3,3";
|
||||
NSString* const iPad3_4 = @"iPad3,4";
|
||||
NSString* const iPad3_5 = @"iPad3,5";
|
||||
NSString* const iPad3_6 = @"iPad3,6";
|
||||
NSString* const iPad4_1 = @"iPad4,1";
|
||||
NSString* const iPad4_2 = @"iPad4,2";
|
||||
NSString* const iPad4_3 = @"iPad4,3";
|
||||
NSString* const iPad4_4 = @"iPad4,4";
|
||||
NSString* const iPad4_5 = @"iPad4,5";
|
||||
NSString* const iPad4_6 = @"iPad4,6";
|
||||
NSString* const iPad4_7 = @"iPad4,7";
|
||||
NSString* const iPad4_8 = @"iPad4,8";
|
||||
NSString* const iPad4_9 = @"iPad4,9";
|
||||
NSString* const iPad5_1 = @"iPad5,1";
|
||||
NSString* const iPad5_2 = @"iPad5,2";
|
||||
NSString* const iPad5_3 = @"iPad5,3";
|
||||
NSString* const iPad5_4 = @"iPad5,4";
|
||||
NSString* const iPad6_11 = @"iPad6,11";
|
||||
NSString* const iPad6_12 = @"iPad6,12";
|
||||
NSString* const iPad6_3 = @"iPad6,3";
|
||||
NSString* const iPad6_4 = @"iPad6,4";
|
||||
NSString* const iPad6_7 = @"iPad6,7";
|
||||
NSString* const iPad6_8 = @"iPad6,8";
|
||||
NSString* const iPad7_1 = @"iPad7,1";
|
||||
NSString* const iPad7_11 = @"iPad7,11";
|
||||
NSString* const iPad7_12 = @"iPad7,12";
|
||||
NSString* const iPad7_2 = @"iPad7,2";
|
||||
NSString* const iPad7_3 = @"iPad7,3";
|
||||
NSString* const iPad7_4 = @"iPad7,4";
|
||||
NSString* const iPad7_5 = @"iPad7,5";
|
||||
NSString* const iPad7_6 = @"iPad7,6";
|
||||
NSString* const iPad8_1 = @"iPad8,1";
|
||||
NSString* const iPad8_10 = @"iPad8,10";
|
||||
NSString* const iPad8_11 = @"iPad8,11";
|
||||
NSString* const iPad8_12 = @"iPad8,12";
|
||||
NSString* const iPad8_2 = @"iPad8,2";
|
||||
NSString* const iPad8_3 = @"iPad8,3";
|
||||
NSString* const iPad8_4 = @"iPad8,4";
|
||||
NSString* const iPad8_5 = @"iPad8,5";
|
||||
NSString* const iPad8_6 = @"iPad8,6";
|
||||
NSString* const iPad8_7 = @"iPad8,7";
|
||||
NSString* const iPad8_8 = @"iPad8,8";
|
||||
NSString* const iPad8_9 = @"iPad8,9";
|
||||
NSString* const iPhone1_1 = @"iPhone1,1";
|
||||
NSString* const iPhone1_2 = @"iPhone1,2";
|
||||
NSString* const iPhone10_1 = @"iPhone10,1";
|
||||
NSString* const iPhone10_2 = @"iPhone10,2";
|
||||
NSString* const iPhone10_3 = @"iPhone10,3";
|
||||
NSString* const iPhone10_4 = @"iPhone10,4";
|
||||
NSString* const iPhone10_5 = @"iPhone10,5";
|
||||
NSString* const iPhone10_6 = @"iPhone10,6";
|
||||
NSString* const iPhone11_2 = @"iPhone11,2";
|
||||
NSString* const iPhone11_4 = @"iPhone11,4";
|
||||
NSString* const iPhone11_6 = @"iPhone11,6";
|
||||
NSString* const iPhone11_8 = @"iPhone11,8";
|
||||
NSString* const iPhone12_1 = @"iPhone12,1";
|
||||
NSString* const iPhone12_3 = @"iPhone12,3";
|
||||
NSString* const iPhone12_5 = @"iPhone12,5";
|
||||
NSString* const iPhone12_8 = @"iPhone12,8";
|
||||
NSString* const iPhone13_1 = @"iPhone13,1";
|
||||
NSString* const iPhone13_2 = @"iPhone13,2";
|
||||
NSString* const iPhone13_3 = @"iPhone13,3";
|
||||
NSString* const iPhone13_4 = @"iPhone13,4";
|
||||
NSString* const iPhone14_2 = @"iPhone14,2";
|
||||
NSString* const iPhone14_3 = @"iPhone14,3";
|
||||
NSString* const iPhone14_4 = @"iPhone14,4";
|
||||
NSString* const iPhone14_5 = @"iPhone14,5";
|
||||
NSString* const iPhone14_6 = @"iPhone14,6";
|
||||
NSString* const iPhone14_7 = @"iPhone14,7";
|
||||
NSString* const iPhone14_8 = @"iPhone14,8";
|
||||
NSString* const iPhone15_2 = @"iPhone15,2";
|
||||
NSString* const iPhone15_3 = @"iPhone15,3";
|
||||
NSString* const iPhone2_1 = @"iPhone2,1";
|
||||
NSString* const iPhone3_1 = @"iPhone3,1";
|
||||
NSString* const iPhone3_2 = @"iPhone3,2";
|
||||
NSString* const iPhone3_3 = @"iPhone3,3";
|
||||
NSString* const iPhone4_1 = @"iPhone4,1";
|
||||
NSString* const iPhone5_1 = @"iPhone5,1";
|
||||
NSString* const iPhone5_2 = @"iPhone5,2";
|
||||
NSString* const iPhone5_3 = @"iPhone5,3";
|
||||
NSString* const iPhone5_4 = @"iPhone5,4";
|
||||
NSString* const iPhone6_1 = @"iPhone6,1";
|
||||
NSString* const iPhone6_2 = @"iPhone6,2";
|
||||
NSString* const iPhone7_1 = @"iPhone7,1";
|
||||
NSString* const iPhone7_2 = @"iPhone7,2";
|
||||
NSString* const iPhone8_1 = @"iPhone8,1";
|
||||
NSString* const iPhone8_2 = @"iPhone8,2";
|
||||
NSString* const iPhone8_4 = @"iPhone8,4";
|
||||
NSString* const iPhone9_1 = @"iPhone9,1";
|
||||
NSString* const iPhone9_2 = @"iPhone9,2";
|
||||
NSString* const iPhone9_3 = @"iPhone9,3";
|
||||
NSString* const iPhone9_4 = @"iPhone9,4";
|
||||
NSString* const iPod1_1 = @"iPod1,1";
|
||||
NSString* const iPod2_1 = @"iPod2,1";
|
||||
NSString* const iPod3_1 = @"iPod3,1";
|
||||
NSString* const iPod4_1 = @"iPod4,1";
|
||||
NSString* const iPod5_1 = @"iPod5,1";
|
||||
NSString* const iPod7_1 = @"iPod7,1";
|
||||
NSString* const iPod9_1 = @"iPod9,1";
|
||||
NSString* const x86_64_Simulator = @"x86_64";
|
||||
|
||||
|
||||
@implementation _DeviceUtil (Constant)
|
||||
|
||||
- (Hardware)hardware {
|
||||
NSString *hardware = [self hardwareString];
|
||||
if ([hardware isEqualToString:AppleTV1_1]) return APPLE_TV_1G;
|
||||
if ([hardware isEqualToString:AppleTV2_1]) return APPLE_TV_2G;
|
||||
if ([hardware isEqualToString:AppleTV3_1]) return APPLE_TV_3G;
|
||||
if ([hardware isEqualToString:AppleTV3_2]) return APPLE_TV_3_2G;
|
||||
if ([hardware isEqualToString:AppleTV5_3]) return APPLE_TV_4G;
|
||||
if ([hardware isEqualToString:AppleTV6_2]) return APPLE_TV_4K;
|
||||
if ([hardware isEqualToString:Watch1_1]) return APPLE_WATCH_38;
|
||||
if ([hardware isEqualToString:Watch1_2]) return APPLE_WATCH_42;
|
||||
if ([hardware isEqualToString:Watch2_3]) return APPLE_WATCH_SERIES_2_38;
|
||||
if ([hardware isEqualToString:Watch2_4]) return APPLE_WATCH_SERIES_2_42;
|
||||
if ([hardware isEqualToString:Watch2_6]) return APPLE_WATCH_SERIES_1_38;
|
||||
if ([hardware isEqualToString:Watch2_7]) return APPLE_WATCH_SERIES_1_42;
|
||||
if ([hardware isEqualToString:Watch3_1]) return APPLE_WATCH_SERIES_3_38_CELLULAR;
|
||||
if ([hardware isEqualToString:Watch3_2]) return APPLE_WATCH_SERIES_3_42_CELLULAR;
|
||||
if ([hardware isEqualToString:Watch3_3]) return APPLE_WATCH_SERIES_3_38;
|
||||
if ([hardware isEqualToString:Watch3_4]) return APPLE_WATCH_SERIES_3_42;
|
||||
if ([hardware isEqualToString:Watch4_1]) return APPLE_WATCH_SERIES_4_40;
|
||||
if ([hardware isEqualToString:Watch4_2]) return APPLE_WATCH_SERIES_4_44;
|
||||
if ([hardware isEqualToString:Watch4_3]) return APPLE_WATCH_SERIES_4_40_CELLULAR;
|
||||
if ([hardware isEqualToString:Watch4_4]) return APPLE_WATCH_SERIES_4_44_CELLULAR;
|
||||
if ([hardware isEqualToString:Watch5_1]) return APPLE_WATCH_SERIES_5_40;
|
||||
if ([hardware isEqualToString:Watch5_2]) return APPLE_WATCH_SERIES_5_44;
|
||||
if ([hardware isEqualToString:Watch5_3]) return APPLE_WATCH_SERIES_5_40_CELLULAR;
|
||||
if ([hardware isEqualToString:Watch5_4]) return APPLE_WATCH_SERIES_5_44_CELLULAR;
|
||||
if ([hardware isEqualToString:i386_Simulator]) return SIMULATOR;
|
||||
if ([hardware isEqualToString:iPad1_1]) return IPAD;
|
||||
if ([hardware isEqualToString:iPad1_2]) return IPAD_3G;
|
||||
if ([hardware isEqualToString:iPad11_1]) return IPAD_MINI_5_WIFI;
|
||||
if ([hardware isEqualToString:iPad11_2]) return IPAD_MINI_5_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad11_3]) return IPAD_AIR_3_WIFI;
|
||||
if ([hardware isEqualToString:iPad11_4]) return IPAD_AIR_3_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad12_1]) return IPAD_9_WIFI;
|
||||
if ([hardware isEqualToString:iPad12_2]) return IPAD_9_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad13_1]) return IPAD_AIR_4_WIFI;
|
||||
if ([hardware isEqualToString:iPad13_10]) return IPAD_PRO_5_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad13_11]) return IPAD_PRO_5_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad13_16]) return IPAD_AIR_5_WIFI;
|
||||
if ([hardware isEqualToString:iPad13_17]) return IPAD_AIR_5_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad13_2]) return IPAD_AIR_4_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad13_4]) return IPAD_PRO_11_3_WIFI;
|
||||
if ([hardware isEqualToString:iPad13_5]) return IPAD_AIR_4_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad13_6]) return IPAD_PRO_11_3_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad13_7]) return IPAD_PRO_11_3_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad13_8]) return IPAD_PRO_5_WIFI;
|
||||
if ([hardware isEqualToString:iPad13_9]) return IPAD_PRO_5_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad14_1]) return IPAD_MINI_6_WIFI;
|
||||
if ([hardware isEqualToString:iPad14_2]) return IPAD_MINI_6_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad2_1]) return IPAD_2_WIFI;
|
||||
if ([hardware isEqualToString:iPad2_2]) return IPAD_2;
|
||||
if ([hardware isEqualToString:iPad2_3]) return IPAD_2_CDMA;
|
||||
if ([hardware isEqualToString:iPad2_4]) return IPAD_2;
|
||||
if ([hardware isEqualToString:iPad2_5]) return IPAD_MINI_WIFI;
|
||||
if ([hardware isEqualToString:iPad2_6]) return IPAD_MINI;
|
||||
if ([hardware isEqualToString:iPad2_7]) return IPAD_MINI_WIFI_CDMA;
|
||||
if ([hardware isEqualToString:iPad3_1]) return IPAD_3_WIFI;
|
||||
if ([hardware isEqualToString:iPad3_2]) return IPAD_3_WIFI_CDMA;
|
||||
if ([hardware isEqualToString:iPad3_3]) return IPAD_3;
|
||||
if ([hardware isEqualToString:iPad3_4]) return IPAD_4_WIFI;
|
||||
if ([hardware isEqualToString:iPad3_5]) return IPAD_4;
|
||||
if ([hardware isEqualToString:iPad3_6]) return IPAD_4_GSM_CDMA;
|
||||
if ([hardware isEqualToString:iPad4_1]) return IPAD_AIR_WIFI;
|
||||
if ([hardware isEqualToString:iPad4_2]) return IPAD_AIR_WIFI_GSM;
|
||||
if ([hardware isEqualToString:iPad4_3]) return IPAD_AIR_WIFI_CDMA;
|
||||
if ([hardware isEqualToString:iPad4_4]) return IPAD_MINI_RETINA_WIFI;
|
||||
if ([hardware isEqualToString:iPad4_5]) return IPAD_MINI_RETINA_WIFI_CDMA;
|
||||
if ([hardware isEqualToString:iPad4_6]) return IPAD_MINI_RETINA_WIFI_CELLULAR_CN;
|
||||
if ([hardware isEqualToString:iPad4_7]) return IPAD_MINI_3_WIFI;
|
||||
if ([hardware isEqualToString:iPad4_8]) return IPAD_MINI_3_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad4_9]) return IPAD_MINI_3_WIFI_CELLULAR_CN;
|
||||
if ([hardware isEqualToString:iPad5_1]) return IPAD_MINI_4_WIFI;
|
||||
if ([hardware isEqualToString:iPad5_2]) return IPAD_MINI_4_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad5_3]) return IPAD_AIR_2_WIFI;
|
||||
if ([hardware isEqualToString:iPad5_4]) return IPAD_AIR_2_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad6_11]) return IPAD_5_WIFI;
|
||||
if ([hardware isEqualToString:iPad6_12]) return IPAD_5_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad6_3]) return IPAD_PRO_97_WIFI;
|
||||
if ([hardware isEqualToString:iPad6_4]) return IPAD_PRO_97_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad6_7]) return IPAD_PRO_WIFI;
|
||||
if ([hardware isEqualToString:iPad6_8]) return IPAD_PRO_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad7_1]) return IPAD_PRO_2G_WIFI;
|
||||
if ([hardware isEqualToString:iPad7_11]) return IPAD_7_WIFI;
|
||||
if ([hardware isEqualToString:iPad7_12]) return IPAD_7_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad7_2]) return IPAD_PRO_2G_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad7_3]) return IPAD_PRO_105_WIFI;
|
||||
if ([hardware isEqualToString:iPad7_4]) return IPAD_PRO_105_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad7_5]) return IPAD_6_WIFI;
|
||||
if ([hardware isEqualToString:iPad7_6]) return IPAD_6_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad8_1]) return IPAD_PRO_11_WIFI;
|
||||
if ([hardware isEqualToString:iPad8_10]) return IPAD_PRO_11_2G_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad8_11]) return IPAD_PRO_4G_WIFI;
|
||||
if ([hardware isEqualToString:iPad8_12]) return IPAD_PRO_4G_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad8_2]) return IPAD_PRO_11_1TB_WIFI;
|
||||
if ([hardware isEqualToString:iPad8_3]) return IPAD_PRO_11_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad8_4]) return IPAD_PRO_11_1TB_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad8_5]) return IPAD_PRO_3G_WIFI;
|
||||
if ([hardware isEqualToString:iPad8_6]) return IPAD_PRO_3G_1TB_WIFI;
|
||||
if ([hardware isEqualToString:iPad8_7]) return IPAD_PRO_3G_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad8_8]) return IPAD_PRO_3G_1TB_WIFI_CELLULAR;
|
||||
if ([hardware isEqualToString:iPad8_9]) return IPAD_PRO_11_2G_WIFI;
|
||||
if ([hardware isEqualToString:iPhone1_1]) return IPHONE_2G;
|
||||
if ([hardware isEqualToString:iPhone1_2]) return IPHONE_3G;
|
||||
if ([hardware isEqualToString:iPhone10_1]) return IPHONE_8_CN;
|
||||
if ([hardware isEqualToString:iPhone10_2]) return IPHONE_8_PLUS_CN;
|
||||
if ([hardware isEqualToString:iPhone10_3]) return IPHONE_X_CN;
|
||||
if ([hardware isEqualToString:iPhone10_4]) return IPHONE_8;
|
||||
if ([hardware isEqualToString:iPhone10_5]) return IPHONE_8_PLUS;
|
||||
if ([hardware isEqualToString:iPhone10_6]) return IPHONE_X;
|
||||
if ([hardware isEqualToString:iPhone11_2]) return IPHONE_XS;
|
||||
if ([hardware isEqualToString:iPhone11_4]) return IPHONE_XS_MAX;
|
||||
if ([hardware isEqualToString:iPhone11_6]) return IPHONE_XS_MAX_CN;
|
||||
if ([hardware isEqualToString:iPhone11_8]) return IPHONE_XR;
|
||||
if ([hardware isEqualToString:iPhone12_1]) return IPHONE_11;
|
||||
if ([hardware isEqualToString:iPhone12_3]) return IPHONE_11_PRO;
|
||||
if ([hardware isEqualToString:iPhone12_5]) return IPHONE_11_PRO_MAX;
|
||||
if ([hardware isEqualToString:iPhone12_8]) return IPHONE_SE_2G;
|
||||
if ([hardware isEqualToString:iPhone13_1]) return IPHONE_12_MINI;
|
||||
if ([hardware isEqualToString:iPhone13_2]) return IPHONE_12;
|
||||
if ([hardware isEqualToString:iPhone13_3]) return IPHONE_12_PRO;
|
||||
if ([hardware isEqualToString:iPhone13_4]) return IPHONE_12_PRO_MAX;
|
||||
if ([hardware isEqualToString:iPhone14_2]) return IPHONE_13_PRO;
|
||||
if ([hardware isEqualToString:iPhone14_3]) return IPHONE_13_PRO_MAX;
|
||||
if ([hardware isEqualToString:iPhone14_4]) return IPHONE_13_MINI;
|
||||
if ([hardware isEqualToString:iPhone14_5]) return IPHONE_13;
|
||||
if ([hardware isEqualToString:iPhone14_6]) return IPHONE_SE_3G;
|
||||
if ([hardware isEqualToString:iPhone14_7]) return IPHONE_14;
|
||||
if ([hardware isEqualToString:iPhone14_8]) return IPHONE_14_PLUS;
|
||||
if ([hardware isEqualToString:iPhone15_2]) return IPHONE_14_PRO;
|
||||
if ([hardware isEqualToString:iPhone15_3]) return IPHONE_14_PRO_MAX;
|
||||
if ([hardware isEqualToString:iPhone2_1]) return IPHONE_3GS;
|
||||
if ([hardware isEqualToString:iPhone3_1]) return IPHONE_4;
|
||||
if ([hardware isEqualToString:iPhone3_2]) return IPHONE_4;
|
||||
if ([hardware isEqualToString:iPhone3_3]) return IPHONE_4_CDMA;
|
||||
if ([hardware isEqualToString:iPhone4_1]) return IPHONE_4S;
|
||||
if ([hardware isEqualToString:iPhone5_1]) return IPHONE_5;
|
||||
if ([hardware isEqualToString:iPhone5_2]) return IPHONE_5_CDMA_GSM;
|
||||
if ([hardware isEqualToString:iPhone5_3]) return IPHONE_5C;
|
||||
if ([hardware isEqualToString:iPhone5_4]) return IPHONE_5C_CDMA_GSM;
|
||||
if ([hardware isEqualToString:iPhone6_1]) return IPHONE_5S;
|
||||
if ([hardware isEqualToString:iPhone6_2]) return IPHONE_5S_CDMA_GSM;
|
||||
if ([hardware isEqualToString:iPhone7_1]) return IPHONE_6_PLUS;
|
||||
if ([hardware isEqualToString:iPhone7_2]) return IPHONE_6;
|
||||
if ([hardware isEqualToString:iPhone8_1]) return IPHONE_6S;
|
||||
if ([hardware isEqualToString:iPhone8_2]) return IPHONE_6S_PLUS;
|
||||
if ([hardware isEqualToString:iPhone8_4]) return IPHONE_SE;
|
||||
if ([hardware isEqualToString:iPhone9_1]) return IPHONE_7;
|
||||
if ([hardware isEqualToString:iPhone9_2]) return IPHONE_7_PLUS;
|
||||
if ([hardware isEqualToString:iPhone9_3]) return IPHONE_7_GSM;
|
||||
if ([hardware isEqualToString:iPhone9_4]) return IPHONE_7_PLUS_GSM;
|
||||
if ([hardware isEqualToString:iPod1_1]) return IPOD_TOUCH_1G;
|
||||
if ([hardware isEqualToString:iPod2_1]) return IPOD_TOUCH_2G;
|
||||
if ([hardware isEqualToString:iPod3_1]) return IPOD_TOUCH_3G;
|
||||
if ([hardware isEqualToString:iPod4_1]) return IPOD_TOUCH_4G;
|
||||
if ([hardware isEqualToString:iPod5_1]) return IPOD_TOUCH_5G;
|
||||
if ([hardware isEqualToString:iPod7_1]) return IPOD_TOUCH_6G;
|
||||
if ([hardware isEqualToString:iPod9_1]) return IPOD_TOUCH_7G;
|
||||
if ([hardware isEqualToString:x86_64_Simulator]) return SIMULATOR;
|
||||
|
||||
NSLog(@"This is a device which is not listed in this category. Please visit https://github.com/InderKumarRathore/DeviceUtil and add a comment there.");
|
||||
NSLog(@"Your device hardware string is: %@", hardware);
|
||||
return UNKNOWN;
|
||||
}
|
||||
@end
|
||||
55
Pods/CocoaDebug/Sources/Core/_DeviceUtil.h
generated
@@ -1,55 +0,0 @@
|
||||
//
|
||||
// CocoaDebug
|
||||
// liman
|
||||
//
|
||||
// Created by liman 02/02/2023.
|
||||
// Copyright © 2023 liman. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "_DeviceUtil+Constant.h"
|
||||
|
||||
/// Enum of the different Apple's device platforms
|
||||
typedef NS_ENUM(NSUInteger, Platform) {
|
||||
iPhone,
|
||||
iPodTouch,
|
||||
iPad,
|
||||
AppleTV,
|
||||
AppleWatch,
|
||||
Unknown
|
||||
};
|
||||
|
||||
|
||||
@interface _DeviceUtil : NSObject
|
||||
|
||||
/// This method returns the hardware type
|
||||
- (NSString*)hardwareString;
|
||||
|
||||
/// This method returns the Platform enum depending upon hardware string
|
||||
- (Platform)platform;
|
||||
|
||||
|
||||
|
||||
/// This method returns the readable description of hardware string
|
||||
- (NSString*)hardwareDescription;
|
||||
|
||||
/// This method returns the readable simple description of hardware string
|
||||
- (NSString*)hardwareSimpleDescription;
|
||||
|
||||
/// This method returns the hardware number not actual but logically. e.g. if the hardware string is 5,1 then hardware number would be 5.1
|
||||
- (float)hardwareNumber;
|
||||
|
||||
/// This method returns if we are running in the simulator
|
||||
- (BOOL)isSimulator;
|
||||
|
||||
/// This method returns the resolution for still image that can be received from back camera of the current device. Resolution returned for image oriented landscape right.
|
||||
- (CGSize)backCameraStillImageResolutionInPixels;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface _DeviceUtil (Constant)
|
||||
|
||||
/// This method returns the Hardware enum depending upon hardware string
|
||||
- (Hardware)hardware;
|
||||
@end
|
||||
849
Pods/CocoaDebug/Sources/Core/_DeviceUtil.m
generated
@@ -1,849 +0,0 @@
|
||||
//
|
||||
// CocoaDebug
|
||||
// liman
|
||||
//
|
||||
// Created by liman 02/02/2023.
|
||||
// Copyright © 2023 liman. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_DeviceUtil.h"
|
||||
#import "_DeviceUtil+Constant.h"
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
@implementation _DeviceUtil {
|
||||
NSDictionary *deviceList;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
deviceList = @{
|
||||
@"AppleTV1,1" : @{
|
||||
@"name": @"Apple TV 1G",
|
||||
@"version": @"1.1",
|
||||
},
|
||||
@"AppleTV2,1" : @{
|
||||
@"name": @"Apple TV 2G",
|
||||
@"version": @"2.1",
|
||||
},
|
||||
@"AppleTV3,1" : @{
|
||||
@"name": @"Apple TV 2012",
|
||||
@"version": @"3.1",
|
||||
},
|
||||
@"AppleTV3,2" : @{
|
||||
@"name": @"Apple TV 2013",
|
||||
@"version": @"3.2",
|
||||
},
|
||||
@"AppleTV5,3" : @{
|
||||
@"name": @"Apple TV 4G",
|
||||
@"version": @"5.3",
|
||||
},
|
||||
@"AppleTV6,2" : @{
|
||||
@"name": @"Apple TV 4K",
|
||||
@"version": @"6.2",
|
||||
},
|
||||
@"Watch1,1" : @{
|
||||
@"name": @"Apple Watch (38 mm)",
|
||||
@"version": @"1.1",
|
||||
},
|
||||
@"Watch1,2" : @{
|
||||
@"name": @"Apple Watch (42 mm)",
|
||||
@"version": @"1.2",
|
||||
},
|
||||
@"Watch2,3" : @{
|
||||
@"name": @"Apple Watch Series 2 (38 mm)",
|
||||
@"version": @"2.3",
|
||||
},
|
||||
@"Watch2,4" : @{
|
||||
@"name": @"Apple Watch Series 2 (42 mm)",
|
||||
@"version": @"2.4",
|
||||
},
|
||||
@"Watch2,6" : @{
|
||||
@"name": @"Apple Watch Series 1 (38 mm)",
|
||||
@"version": @"2.6",
|
||||
},
|
||||
@"Watch2,7" : @{
|
||||
@"name": @"Apple Watch Series 1 (42 mm)",
|
||||
@"version": @"2.7",
|
||||
},
|
||||
@"Watch3,1" : @{
|
||||
@"name": @"Apple Watch Series 3 (38 mm/Cellular)",
|
||||
@"version": @"3.1",
|
||||
},
|
||||
@"Watch3,2" : @{
|
||||
@"name": @"Apple Watch Series 3 (42 mm/Cellular)",
|
||||
@"version": @"3.2",
|
||||
},
|
||||
@"Watch3,3" : @{
|
||||
@"name": @"Apple Watch Series 3 (38 mm)",
|
||||
@"version": @"3.3",
|
||||
},
|
||||
@"Watch3,4" : @{
|
||||
@"name": @"Apple Watch Series 3 (42 mm)",
|
||||
@"version": @"3.4",
|
||||
},
|
||||
@"Watch4,1" : @{
|
||||
@"name": @"Apple Watch Series 4 (40 mm)",
|
||||
@"version": @"4.1",
|
||||
},
|
||||
@"Watch4,2" : @{
|
||||
@"name": @"Apple Watch Series 4 (44 mm)",
|
||||
@"version": @"4.2",
|
||||
},
|
||||
@"Watch4,3" : @{
|
||||
@"name": @"Apple Watch Series 4 (40 mm/Cellular)",
|
||||
@"version": @"4.3",
|
||||
},
|
||||
@"Watch4,4" : @{
|
||||
@"name": @"Apple Watch Series 4 (44 mm/Cellular)",
|
||||
@"version": @"4.4",
|
||||
},
|
||||
@"Watch5,1" : @{
|
||||
@"name": @"Apple Watch Series 5 (40 mm)",
|
||||
@"version": @"5.1",
|
||||
},
|
||||
@"Watch5,2" : @{
|
||||
@"name": @"Apple Watch Series 5 (44 mm)",
|
||||
@"version": @"5.2",
|
||||
},
|
||||
@"Watch5,3" : @{
|
||||
@"name": @"Apple Watch Series 5 (40 mm/Cellular)",
|
||||
@"version": @"5.3",
|
||||
},
|
||||
@"Watch5,4" : @{
|
||||
@"name": @"Apple Watch Series 5 (44 mm/Cellular)",
|
||||
@"version": @"5.4",
|
||||
},
|
||||
@"i386" : @{
|
||||
@"name": @"Simulator",
|
||||
@"version": @"-1",
|
||||
},
|
||||
@"iPad1,1" : @{
|
||||
@"name": @"iPad (WiFi)",
|
||||
@"version": @"1.1",
|
||||
},
|
||||
@"iPad1,2" : @{
|
||||
@"name": @"iPad 3G",
|
||||
@"version": @"1.2",
|
||||
},
|
||||
@"iPad11,1" : @{
|
||||
@"name": @"iPad mini 5 (Wi-Fi Only)",
|
||||
@"version": @"11.1",
|
||||
},
|
||||
@"iPad11,2" : @{
|
||||
@"name": @"iPad mini 5 (Wi-Fi/Cellular)",
|
||||
@"version": @"11.2",
|
||||
},
|
||||
@"iPad11,3" : @{
|
||||
@"name": @"iPad Air 3 (Wi-Fi)",
|
||||
@"version": @"11.3",
|
||||
},
|
||||
@"iPad11,4" : @{
|
||||
@"name": @"iPad Air 3 (Wi-Fi + Cellular)",
|
||||
@"version": @"11.4",
|
||||
},
|
||||
@"iPad12,1" : @{
|
||||
@"name": @"iPad 9 (Wi-Fi)",
|
||||
@"version": @"12.1",
|
||||
},
|
||||
@"iPad12,2" : @{
|
||||
@"name": @"iPad 9 (Wi-Fi + Cellular)",
|
||||
@"version": @"12.2",
|
||||
},
|
||||
@"iPad13,1" : @{
|
||||
@"name": @"iPad Air 4 (Wi-Fi)",
|
||||
@"version": @"13.1",
|
||||
},
|
||||
@"iPad13,10" : @{
|
||||
@"name": @"iPad Pro 12.9\" 5th Gen (Wi-Fi + Cellular)",
|
||||
@"version": @"13.1",
|
||||
},
|
||||
@"iPad13,11" : @{
|
||||
@"name": @"iPad Pro 12.9\" 5th Gen (Wi-Fi + Cellular)",
|
||||
@"version": @"13.11",
|
||||
},
|
||||
@"iPad13,16" : @{
|
||||
@"name": @"iPad Air 5th Gen (Wi-Fi)",
|
||||
@"version": @"13.16",
|
||||
},
|
||||
@"iPad13,17" : @{
|
||||
@"name": @"iPad Air 5th Gen (Wi-Fi + Cellular)",
|
||||
@"version": @"13.17",
|
||||
},
|
||||
@"iPad13,2" : @{
|
||||
@"name": @"iPad Air 4 (Wi-Fi + Cellular)",
|
||||
@"version": @"13.2",
|
||||
},
|
||||
@"iPad13,4" : @{
|
||||
@"name": @"iPad Pro 11\" 3rd Gen (Wi-Fi)",
|
||||
@"version": @"13.4",
|
||||
},
|
||||
@"iPad13,5" : @{
|
||||
@"name": @"iPad Pro 11\" 3rd Gen (Wi-Fi + Cellular)",
|
||||
@"version": @"13.5",
|
||||
},
|
||||
@"iPad13,6" : @{
|
||||
@"name": @"iPad Pro 11\" 3rd Gen (Wi-Fi + Cellular)",
|
||||
@"version": @"13.6",
|
||||
},
|
||||
@"iPad13,7" : @{
|
||||
@"name": @"iPad Pro 11\" 3rd Gen (Wi-Fi + Cellular)",
|
||||
@"version": @"13.7",
|
||||
},
|
||||
@"iPad13,8" : @{
|
||||
@"name": @"iPad Pro 12.9\" 5th Gen (Wi-Fi)",
|
||||
@"version": @"13.8",
|
||||
},
|
||||
@"iPad13,9" : @{
|
||||
@"name": @"iPad Pro 12.9\" 5th Gen (Wi-Fi + Cellular)",
|
||||
@"version": @"13.9",
|
||||
},
|
||||
@"iPad14,1" : @{
|
||||
@"name": @"iPad Mini 6 (Wi-Fi)",
|
||||
@"version": @"14.1",
|
||||
},
|
||||
@"iPad14,2" : @{
|
||||
@"name": @"iPad Mini 6 (Wi-Fi + Cellular)",
|
||||
@"version": @"14.2",
|
||||
},
|
||||
@"iPad2,1" : @{
|
||||
@"name": @"iPad 2 (WiFi)",
|
||||
@"version": @"2.1",
|
||||
},
|
||||
@"iPad2,2" : @{
|
||||
@"name": @"iPad 2 (GSM)",
|
||||
@"version": @"2.2",
|
||||
},
|
||||
@"iPad2,3" : @{
|
||||
@"name": @"iPad 2 (CDMA)",
|
||||
@"version": @"2.3",
|
||||
},
|
||||
@"iPad2,4" : @{
|
||||
@"name": @"iPad 2 (WiFi Rev. A)",
|
||||
@"version": @"2.4",
|
||||
},
|
||||
@"iPad2,5" : @{
|
||||
@"name": @"iPad Mini (WiFi)",
|
||||
@"version": @"2.5",
|
||||
},
|
||||
@"iPad2,6" : @{
|
||||
@"name": @"iPad Mini (GSM)",
|
||||
@"version": @"2.6",
|
||||
},
|
||||
@"iPad2,7" : @{
|
||||
@"name": @"iPad Mini (CDMA)",
|
||||
@"version": @"2.7",
|
||||
},
|
||||
@"iPad3,1" : @{
|
||||
@"name": @"iPad 3 (WiFi)",
|
||||
@"version": @"3.1",
|
||||
},
|
||||
@"iPad3,2" : @{
|
||||
@"name": @"iPad 3 (CDMA)",
|
||||
@"version": @"3.2",
|
||||
},
|
||||
@"iPad3,3" : @{
|
||||
@"name": @"iPad 3 (Global)",
|
||||
@"version": @"3.3",
|
||||
},
|
||||
@"iPad3,4" : @{
|
||||
@"name": @"iPad 4 (WiFi)",
|
||||
@"version": @"3.4",
|
||||
},
|
||||
@"iPad3,5" : @{
|
||||
@"name": @"iPad 4 (CDMA)",
|
||||
@"version": @"3.5",
|
||||
},
|
||||
@"iPad3,6" : @{
|
||||
@"name": @"iPad 4 (Global)",
|
||||
@"version": @"3.6",
|
||||
},
|
||||
@"iPad4,1" : @{
|
||||
@"name": @"iPad Air (WiFi)",
|
||||
@"version": @"4.1",
|
||||
},
|
||||
@"iPad4,2" : @{
|
||||
@"name": @"iPad Air (WiFi+GSM)",
|
||||
@"version": @"4.2",
|
||||
},
|
||||
@"iPad4,3" : @{
|
||||
@"name": @"iPad Air (WiFi+CDMA)",
|
||||
@"version": @"4.3",
|
||||
},
|
||||
@"iPad4,4" : @{
|
||||
@"name": @"iPad Mini Retina (WiFi)",
|
||||
@"version": @"4.4",
|
||||
},
|
||||
@"iPad4,5" : @{
|
||||
@"name": @"iPad Mini Retina (WiFi+CDMA)",
|
||||
@"version": @"4.5",
|
||||
},
|
||||
@"iPad4,6" : @{
|
||||
@"name": @"iPad Mini Retina (Wi-Fi + Cellular CN)",
|
||||
@"version": @"4.6",
|
||||
},
|
||||
@"iPad4,7" : @{
|
||||
@"name": @"iPad Mini 3 (Wi-Fi)",
|
||||
@"version": @"4.7",
|
||||
},
|
||||
@"iPad4,8" : @{
|
||||
@"name": @"iPad Mini 3 (Wi-Fi + Cellular)",
|
||||
@"version": @"4.8",
|
||||
},
|
||||
@"iPad4,9" : @{
|
||||
@"name": @"iPad mini 3 (Wi-Fi/Cellular, China)",
|
||||
@"version": @"4.9",
|
||||
},
|
||||
@"iPad5,1" : @{
|
||||
@"name": @"iPad mini 4 (Wi-Fi Only)",
|
||||
@"version": @"5.1",
|
||||
},
|
||||
@"iPad5,2" : @{
|
||||
@"name": @"iPad mini 4 (Wi-Fi/Cellular)",
|
||||
@"version": @"5.2",
|
||||
},
|
||||
@"iPad5,3" : @{
|
||||
@"name": @"iPad Air 2 (Wi-Fi)",
|
||||
@"version": @"5.3",
|
||||
},
|
||||
@"iPad5,4" : @{
|
||||
@"name": @"iPad Air 2 (Wi-Fi + Cellular)",
|
||||
@"version": @"5.4",
|
||||
},
|
||||
@"iPad6,11" : @{
|
||||
@"name": @"9.7-inch iPad (Wi-Fi)",
|
||||
@"version": @"6.11",
|
||||
},
|
||||
@"iPad6,12" : @{
|
||||
@"name": @"9.7-inch iPad (Wi-Fi + Cellular)",
|
||||
@"version": @"6.12",
|
||||
},
|
||||
@"iPad6,3" : @{
|
||||
@"name": @"iPad Pro 9.7-inch (Wi-Fi Only)",
|
||||
@"version": @"6.3",
|
||||
},
|
||||
@"iPad6,4" : @{
|
||||
@"name": @"iPad Pro 9.7-inch (Wi-Fi + Cellular)",
|
||||
@"version": @"6.4",
|
||||
},
|
||||
@"iPad6,7" : @{
|
||||
@"name": @"iPad Pro (Wi-Fi Only)",
|
||||
@"version": @"6.7",
|
||||
},
|
||||
@"iPad6,8" : @{
|
||||
@"name": @"iPad Pro (Wi-Fi/Cellular)",
|
||||
@"version": @"6.8",
|
||||
},
|
||||
@"iPad7,1" : @{
|
||||
@"name": @"iPad Pro 12.9-Inch (Wi-Fi Only - 2nd Gen)",
|
||||
@"version": @"7.1",
|
||||
},
|
||||
@"iPad7,11" : @{
|
||||
@"name": @"iPad 10.2-Inch 7th Gen (Wi-Fi Only)",
|
||||
@"version": @"7.11",
|
||||
},
|
||||
@"iPad7,12" : @{
|
||||
@"name": @"iPad 10.2-Inch 7th Gen (Wi-Fi/Cellular Only)",
|
||||
@"version": @"7.12",
|
||||
},
|
||||
@"iPad7,2" : @{
|
||||
@"name": @"iPad Pro 12.9-Inch (Wi-Fi/Cell - 2nd Gen)",
|
||||
@"version": @"7.2",
|
||||
},
|
||||
@"iPad7,3" : @{
|
||||
@"name": @"iPad Pro 10.5-Inch (Wi-Fi Only)",
|
||||
@"version": @"7.3",
|
||||
},
|
||||
@"iPad7,4" : @{
|
||||
@"name": @"iPad Pro 10.5-Inch (Wi-Fi/Cellular)",
|
||||
@"version": @"7.4",
|
||||
},
|
||||
@"iPad7,5" : @{
|
||||
@"name": @"iPad 9.7-Inch 6th Gen (Wi-Fi Only)",
|
||||
@"version": @"7.5",
|
||||
},
|
||||
@"iPad7,6" : @{
|
||||
@"name": @"iPad 9.7-Inch 6th Gen (Wi-Fi/Cellular)",
|
||||
@"version": @"7.6",
|
||||
},
|
||||
@"iPad8,1" : @{
|
||||
@"name": @"iPad Pro 11-Inch (Wi-Fi Only)",
|
||||
@"version": @"8.1",
|
||||
},
|
||||
@"iPad8,10" : @{
|
||||
@"name": @"iPad Pro 11-Inch (Wi-Fi/Cellular - 2nd Gen)",
|
||||
@"version": @"8.1",
|
||||
},
|
||||
@"iPad8,11" : @{
|
||||
@"name": @"iPad Pro 12.9-Inch 1TB (Wi-Fi Only - 4th Gen)",
|
||||
@"version": @"8.109999999999999",
|
||||
},
|
||||
@"iPad8,12" : @{
|
||||
@"name": @"iPad Pro 12.9-Inch (Wi-Fi/Cell - 4th Gen)",
|
||||
@"version": @"8.800000000000001",
|
||||
},
|
||||
@"iPad8,2" : @{
|
||||
@"name": @"iPad Pro 11-Inch 1TB (Wi-Fi Only)",
|
||||
@"version": @"8.199999999999999",
|
||||
},
|
||||
@"iPad8,3" : @{
|
||||
@"name": @"iPad Pro 11-Inch (Wi-Fi/Cellular)",
|
||||
@"version": @"8.300000000000001",
|
||||
},
|
||||
@"iPad8,4" : @{
|
||||
@"name": @"iPad Pro 11-Inch 1TB (Wi-Fi/Cellular)",
|
||||
@"version": @"8.4",
|
||||
},
|
||||
@"iPad8,5" : @{
|
||||
@"name": @"iPad Pro 12.9-Inch (Wi-Fi Only - 3rd Gen)",
|
||||
@"version": @"8.5",
|
||||
},
|
||||
@"iPad8,6" : @{
|
||||
@"name": @"iPad Pro 12.9-Inch 1TB (Wi-Fi Only - 3rd Gen)",
|
||||
@"version": @"8.6",
|
||||
},
|
||||
@"iPad8,7" : @{
|
||||
@"name": @"iPad Pro 12.9-Inch (Wi-Fi/Cell - 3rd Gen)",
|
||||
@"version": @"8.699999999999999",
|
||||
},
|
||||
@"iPad8,8" : @{
|
||||
@"name": @"iPad Pro 12.9-Inch 1TB (Wi-Fi/Cell - 3rd Gen)",
|
||||
@"version": @"8.800000000000001",
|
||||
},
|
||||
@"iPad8,9" : @{
|
||||
@"name": @"iPad Pro 11-Inch (Wi-Fi Only - 2nd Gen)",
|
||||
@"version": @"8.9",
|
||||
},
|
||||
@"iPhone1,1" : @{
|
||||
@"name": @"iPhone 2G",
|
||||
@"version": @"1.1",
|
||||
},
|
||||
@"iPhone1,2" : @{
|
||||
@"name": @"iPhone 3G",
|
||||
@"version": @"1.2",
|
||||
},
|
||||
@"iPhone10,1" : @{
|
||||
@"name": @"iPhone 8",
|
||||
@"version": @"10.1",
|
||||
},
|
||||
@"iPhone10,2" : @{
|
||||
@"name": @"iPhone 8 Plus",
|
||||
@"version": @"10.2",
|
||||
},
|
||||
@"iPhone10,3" : @{
|
||||
@"name": @"iPhone X",
|
||||
@"version": @"10.3",
|
||||
},
|
||||
@"iPhone10,4" : @{
|
||||
@"name": @"iPhone 8",
|
||||
@"version": @"10.4",
|
||||
},
|
||||
@"iPhone10,5" : @{
|
||||
@"name": @"iPhone 8 Plus",
|
||||
@"version": @"10.5",
|
||||
},
|
||||
@"iPhone10,6" : @{
|
||||
@"name": @"iPhone X",
|
||||
@"version": @"10.6",
|
||||
},
|
||||
@"iPhone11,2" : @{
|
||||
@"name": @"iPhone XS",
|
||||
@"version": @"11.2",
|
||||
},
|
||||
@"iPhone11,4" : @{
|
||||
@"name": @"iPhone XS Max",
|
||||
@"version": @"11.4",
|
||||
},
|
||||
@"iPhone11,6" : @{
|
||||
@"name": @"iPhone XS Max China",
|
||||
@"version": @"11.6",
|
||||
},
|
||||
@"iPhone11,8" : @{
|
||||
@"name": @"iPhone XR",
|
||||
@"version": @"11.8",
|
||||
},
|
||||
@"iPhone12,1" : @{
|
||||
@"name": @"iPhone 11",
|
||||
@"version": @"12.1",
|
||||
},
|
||||
@"iPhone12,3" : @{
|
||||
@"name": @"iPhone 11 Pro",
|
||||
@"version": @"12.3",
|
||||
},
|
||||
@"iPhone12,5" : @{
|
||||
@"name": @"iPhone 11 Pro Max",
|
||||
@"version": @"12.5",
|
||||
},
|
||||
@"iPhone12,8" : @{
|
||||
@"name": @"iPhone SE (2 Gen)",
|
||||
@"version": @"12.8",
|
||||
},
|
||||
@"iPhone13,1" : @{
|
||||
@"name": @"iPhone 12 mini",
|
||||
@"version": @"13.1",
|
||||
},
|
||||
@"iPhone13,2" : @{
|
||||
@"name": @"iPhone 12",
|
||||
@"version": @"13.2",
|
||||
},
|
||||
@"iPhone13,3" : @{
|
||||
@"name": @"iPhone 12 Pro",
|
||||
@"version": @"13.3",
|
||||
},
|
||||
@"iPhone13,4" : @{
|
||||
@"name": @"iPhone 12 Pro Max",
|
||||
@"version": @"13.4",
|
||||
},
|
||||
@"iPhone14,2" : @{
|
||||
@"name": @"iPhone 13 Pro",
|
||||
@"version": @"14.2",
|
||||
},
|
||||
@"iPhone14,3" : @{
|
||||
@"name": @"iPhone 13 Pro Max",
|
||||
@"version": @"14.3",
|
||||
},
|
||||
@"iPhone14,4" : @{
|
||||
@"name": @"iPhone 13 mini",
|
||||
@"version": @"14.4",
|
||||
},
|
||||
@"iPhone14,5" : @{
|
||||
@"name": @"iPhone 13",
|
||||
@"version": @"14.5",
|
||||
},
|
||||
@"iPhone14,6" : @{
|
||||
@"name": @"iPhone SE (3 Gen)",
|
||||
@"version": @"14.6",
|
||||
},
|
||||
@"iPhone14,7" : @{
|
||||
@"name": @"iPhone 14",
|
||||
@"version": @"14.7",
|
||||
},
|
||||
@"iPhone14,8" : @{
|
||||
@"name": @"iPhone 14 Plus",
|
||||
@"version": @"14.8",
|
||||
},
|
||||
@"iPhone15,2" : @{
|
||||
@"name": @"iPhone 14 Pro",
|
||||
@"version": @"15.2",
|
||||
},
|
||||
@"iPhone15,3" : @{
|
||||
@"name": @"iPhone 14 Pro Max",
|
||||
@"version": @"15.3",
|
||||
},
|
||||
@"iPhone2,1" : @{
|
||||
@"name": @"iPhone 3GS",
|
||||
@"version": @"2.1",
|
||||
},
|
||||
@"iPhone3,1" : @{
|
||||
@"name": @"iPhone 4 (GSM)",
|
||||
@"version": @"3.1",
|
||||
},
|
||||
@"iPhone3,2" : @{
|
||||
@"name": @"iPhone 4 (GSM Rev. A)",
|
||||
@"version": @"3.2",
|
||||
},
|
||||
@"iPhone3,3" : @{
|
||||
@"name": @"iPhone 4 (CDMA)",
|
||||
@"version": @"3.3",
|
||||
},
|
||||
@"iPhone4,1" : @{
|
||||
@"name": @"iPhone 4S",
|
||||
@"version": @"4.1",
|
||||
},
|
||||
@"iPhone5,1" : @{
|
||||
@"name": @"iPhone 5 (GSM)",
|
||||
@"version": @"5.1",
|
||||
},
|
||||
@"iPhone5,2" : @{
|
||||
@"name": @"iPhone 5 (Global)",
|
||||
@"version": @"5.2",
|
||||
},
|
||||
@"iPhone5,3" : @{
|
||||
@"name": @"iPhone 5c (GSM)",
|
||||
@"version": @"5.3",
|
||||
},
|
||||
@"iPhone5,4" : @{
|
||||
@"name": @"iPhone 5c (Global)",
|
||||
@"version": @"5.4",
|
||||
},
|
||||
@"iPhone6,1" : @{
|
||||
@"name": @"iPhone 5s (GSM)",
|
||||
@"version": @"6.1",
|
||||
},
|
||||
@"iPhone6,2" : @{
|
||||
@"name": @"iPhone 5s (Global)",
|
||||
@"version": @"6.2",
|
||||
},
|
||||
@"iPhone7,1" : @{
|
||||
@"name": @"iPhone 6 Plus",
|
||||
@"version": @"7.1",
|
||||
},
|
||||
@"iPhone7,2" : @{
|
||||
@"name": @"iPhone 6",
|
||||
@"version": @"7.2",
|
||||
},
|
||||
@"iPhone8,1" : @{
|
||||
@"name": @"iPhone 6s",
|
||||
@"version": @"8.1",
|
||||
},
|
||||
@"iPhone8,2" : @{
|
||||
@"name": @"iPhone 6s Plus",
|
||||
@"version": @"8.199999999999999",
|
||||
},
|
||||
@"iPhone8,4" : @{
|
||||
@"name": @"iPhone SE",
|
||||
@"version": @"8.4",
|
||||
},
|
||||
@"iPhone9,1" : @{
|
||||
@"name": @"iPhone 7",
|
||||
@"version": @"9.1",
|
||||
},
|
||||
@"iPhone9,2" : @{
|
||||
@"name": @"iPhone 7 Plus",
|
||||
@"version": @"9.199999999999999",
|
||||
},
|
||||
@"iPhone9,3" : @{
|
||||
@"name": @"iPhone 7",
|
||||
@"version": @"9.300000000000001",
|
||||
},
|
||||
@"iPhone9,4" : @{
|
||||
@"name": @"iPhone 7 Plus",
|
||||
@"version": @"9.4",
|
||||
},
|
||||
@"iPod1,1" : @{
|
||||
@"name": @"iPod Touch (1 Gen)",
|
||||
@"version": @"1.1",
|
||||
},
|
||||
@"iPod2,1" : @{
|
||||
@"name": @"iPod Touch (2 Gen)",
|
||||
@"version": @"2.1",
|
||||
},
|
||||
@"iPod3,1" : @{
|
||||
@"name": @"iPod Touch (3 Gen)",
|
||||
@"version": @"3.1",
|
||||
},
|
||||
@"iPod4,1" : @{
|
||||
@"name": @"iPod Touch (4 Gen)",
|
||||
@"version": @"4.1",
|
||||
},
|
||||
@"iPod5,1" : @{
|
||||
@"name": @"iPod Touch (5 Gen)",
|
||||
@"version": @"5.1",
|
||||
},
|
||||
@"iPod7,1" : @{
|
||||
@"name": @"iPod Touch (6 Gen)",
|
||||
@"version": @"7.1",
|
||||
},
|
||||
@"iPod9,1" : @{
|
||||
@"name": @"iPod Touch (7 Gen)",
|
||||
@"version": @"9.1",
|
||||
},
|
||||
@"x86_64" : @{
|
||||
@"name": @"Simulator",
|
||||
@"version": @"-1",
|
||||
},
|
||||
};
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString*)nativeHardwareString {
|
||||
int name[] = {CTL_HW,HW_MACHINE};
|
||||
size_t size = 100;
|
||||
sysctl(name, 2, NULL, &size, NULL, 0); // getting size of answer
|
||||
char *hw_machine = malloc(size);
|
||||
|
||||
sysctl(name, 2, hw_machine, &size, NULL, 0);
|
||||
NSString *hardware = [NSString stringWithUTF8String:hw_machine];
|
||||
free(hw_machine);
|
||||
|
||||
return hardware;
|
||||
}
|
||||
|
||||
- (NSString*)hardwareString {
|
||||
NSString *hardware = [self nativeHardwareString];
|
||||
|
||||
// Check if the hardware is simulator
|
||||
if ([hardware isEqualToString:i386_Simulator] || [hardware isEqualToString:x86_64_Simulator]) {
|
||||
NSString *deviceID = [[[NSProcessInfo processInfo] environment] objectForKey:@"SIMULATOR_MODEL_IDENTIFIER"];
|
||||
if (deviceID != nil) {
|
||||
hardware = deviceID;
|
||||
}
|
||||
}
|
||||
return hardware;
|
||||
}
|
||||
|
||||
/* This is another way of gtting the system info
|
||||
* For this you have to #import <sys/utsname.h>
|
||||
*/
|
||||
|
||||
/*
|
||||
NSString* machineName
|
||||
{
|
||||
struct utsname systemInfo;
|
||||
uname(&systemInfo);
|
||||
return [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// This method returns the Platform enum depending upon hardware string
|
||||
///
|
||||
///
|
||||
/// - returns: `Platform` type of the device
|
||||
///
|
||||
- (Platform)platform {
|
||||
|
||||
NSString *hardware = [self hardwareString];
|
||||
|
||||
if ([hardware hasPrefix:@"iPhone"]) return iPhone;
|
||||
if ([hardware hasPrefix:@"iPod"]) return iPodTouch;
|
||||
if ([hardware hasPrefix:@"iPad"]) return iPad;
|
||||
if ([hardware hasPrefix:@"Watch"]) return AppleWatch;
|
||||
if ([hardware hasPrefix:@"AppleTV"]) return AppleTV;
|
||||
|
||||
return Unknown;
|
||||
}
|
||||
|
||||
- (Hardware)nativeHardware {
|
||||
NSString *hardware = [self nativeHardwareString];
|
||||
if ([hardware isEqualToString:i386_Simulator]) return SIMULATOR;
|
||||
if ([hardware isEqualToString:x86_64_Simulator]) return SIMULATOR;
|
||||
return [self hardware];
|
||||
}
|
||||
|
||||
- (NSString*)hardwareDescription {
|
||||
NSString *hardware = [self hardwareString];
|
||||
NSString *hardwareDescription = [[deviceList objectForKey:hardware] objectForKey:@"name"];
|
||||
if (hardwareDescription) {
|
||||
return hardwareDescription;
|
||||
}
|
||||
else {
|
||||
//log message that your device is not present in the list
|
||||
[self logMessage:hardware];
|
||||
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)hardwareSimpleDescription {
|
||||
NSString *hardwareDescription = [self hardwareDescription];
|
||||
if (hardwareDescription == nil) {
|
||||
return nil;
|
||||
}
|
||||
NSError *error = nil;
|
||||
// this expression matches all strings between round brackets (e.g (Wifi), (GSM)) except the pattern "[0-9]+ Gen"
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\((?![0-9]+ Gen).*\\)" options:NSRegularExpressionCaseInsensitive error:&error];
|
||||
NSString *hardwareSimpleDescription = [regex stringByReplacingMatchesInString:hardwareDescription options:0 range:NSMakeRange(0, [hardwareDescription length]) withTemplate:@""];
|
||||
if (error) {
|
||||
return nil;
|
||||
} else {
|
||||
return hardwareSimpleDescription;
|
||||
}
|
||||
}
|
||||
|
||||
- (float)hardwareNumber {
|
||||
NSString *hardware = [self hardwareString];
|
||||
float version = [[[deviceList objectForKey:hardware] objectForKey:@"version"] floatValue];
|
||||
if (version != 0.0f) {
|
||||
return version;
|
||||
}
|
||||
else {
|
||||
//log message that your device is not present in the list
|
||||
[self logMessage:hardware];
|
||||
|
||||
return 200.0f; //device might be new one of missing one so returning 200.0f
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isSimulator {
|
||||
return [self nativeHardware] == SIMULATOR;
|
||||
}
|
||||
|
||||
- (CGSize)backCameraStillImageResolutionInPixels {
|
||||
switch ([self hardware]) {
|
||||
case IPHONE_2G:
|
||||
case IPHONE_3G:
|
||||
return CGSizeMake(1600, 1200);
|
||||
|
||||
case IPHONE_3GS:
|
||||
return CGSizeMake(2048, 1536);
|
||||
|
||||
case IPHONE_4:
|
||||
case IPHONE_4_CDMA:
|
||||
case IPAD_3_WIFI:
|
||||
case IPAD_3_WIFI_CDMA:
|
||||
case IPAD_3:
|
||||
case IPAD_4_WIFI:
|
||||
case IPAD_4:
|
||||
case IPAD_4_GSM_CDMA:
|
||||
return CGSizeMake(2592, 1936);
|
||||
|
||||
case IPHONE_4S:
|
||||
case IPHONE_5:
|
||||
case IPHONE_5_CDMA_GSM:
|
||||
case IPHONE_5C:
|
||||
case IPHONE_5C_CDMA_GSM:
|
||||
case IPHONE_6:
|
||||
case IPHONE_6_PLUS:
|
||||
case IPOD_TOUCH_6G:
|
||||
case IPAD_AIR_2_WIFI:
|
||||
case IPAD_AIR_2_WIFI_CELLULAR:
|
||||
case IPHONE_6S:
|
||||
case IPHONE_6S_PLUS:
|
||||
case IPAD_MINI_4_WIFI:
|
||||
case IPAD_MINI_4_WIFI_CELLULAR:
|
||||
case IPAD_MINI_5_WIFI:
|
||||
case IPAD_MINI_5_WIFI_CELLULAR:
|
||||
case IPAD_AIR_3_WIFI:
|
||||
case IPAD_AIR_3_WIFI_CELLULAR:
|
||||
return CGSizeMake(3264, 2448);
|
||||
|
||||
case IPHONE_7:
|
||||
case IPHONE_7_GSM:
|
||||
case IPHONE_7_PLUS:
|
||||
case IPHONE_7_PLUS_GSM:
|
||||
case IPHONE_8:
|
||||
case IPHONE_8_CN:
|
||||
case IPHONE_8_PLUS:
|
||||
case IPHONE_8_PLUS_CN:
|
||||
case IPHONE_X:
|
||||
case IPHONE_X_CN:
|
||||
return CGSizeMake(4032, 3024);
|
||||
|
||||
case IPOD_TOUCH_4G:
|
||||
return CGSizeMake(960, 720);
|
||||
|
||||
case IPOD_TOUCH_5G:
|
||||
return CGSizeMake(2440, 1605);
|
||||
|
||||
case IPAD_2_WIFI:
|
||||
case IPAD_2:
|
||||
case IPAD_2_CDMA:
|
||||
return CGSizeMake(872, 720);
|
||||
|
||||
case IPAD_MINI_WIFI:
|
||||
case IPAD_MINI:
|
||||
case IPAD_MINI_WIFI_CDMA:
|
||||
return CGSizeMake(1820, 1304);
|
||||
|
||||
case IPAD_PRO_97_WIFI:
|
||||
case IPAD_PRO_97_WIFI_CELLULAR:
|
||||
return CGSizeMake(4032, 3024);
|
||||
|
||||
default:
|
||||
NSLog(@"We have no resolution for your device's camera listed in this category. Please, make photo with back camera of your device, get its resolution in pixels (via Preview Cmd+I for example) and add a comment to this repository (https://github.com/InderKumarRathore/DeviceUtil) on GitHub.com in format Device = Hpx x Wpx.");
|
||||
NSLog(@"Your device is: %@", [self hardwareDescription]);
|
||||
break;
|
||||
}
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
- (void)logMessage:(NSString *)hardware {
|
||||
NSLog(@"This is a device which is not listed in this category. Please visit https://github.com/InderKumarRathore/DeviceUtil and add a comment there.");
|
||||
NSLog(@"Your device hardware string is: %@", hardware);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,22 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
||||
/*! Determines the cache storage policy for a response.
|
||||
* \details When we provide a response up to the client we need to tell the client whether
|
||||
* the response is cacheable or not. The default HTTP/HTTPS protocol has a reasonable
|
||||
* complex chunk of code to determine this, but we can't get at it. Thus, we have to
|
||||
* reimplement it ourselves. This is split off into a separate file to emphasise that
|
||||
* this is standard boilerplate that you probably don't need to look at.
|
||||
* \param request The request that generated the response; must not be nil.
|
||||
* \param response The response itself; must not be nil.
|
||||
* \returns A cache storage policy to use.
|
||||
*/
|
||||
|
||||
extern NSURLCacheStoragePolicy CacheStoragePolicyForRequestAndResponse(NSURLRequest * request, NSHTTPURLResponse * response);
|
||||
@@ -1,85 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_CacheStoragePolicy.h"
|
||||
|
||||
extern NSURLCacheStoragePolicy CacheStoragePolicyForRequestAndResponse(NSURLRequest * request, NSHTTPURLResponse * response)
|
||||
// See comment in header.
|
||||
{
|
||||
BOOL cacheable;
|
||||
NSURLCacheStoragePolicy result;
|
||||
|
||||
//assert(request != NULL);
|
||||
//assert(response != NULL);
|
||||
|
||||
// First determine if the request is cacheable based on its status code.
|
||||
|
||||
switch ([response statusCode]) {
|
||||
case 200:
|
||||
case 203:
|
||||
case 206:
|
||||
case 301:
|
||||
case 304:
|
||||
case 404:
|
||||
case 410: {
|
||||
cacheable = YES;
|
||||
} break;
|
||||
default: {
|
||||
cacheable = NO;
|
||||
} break;
|
||||
}
|
||||
|
||||
// If the response might be cacheable, look at the "Cache-Control" header in
|
||||
// the response.
|
||||
|
||||
// IMPORTANT: We can't rely on -rangeOfString: returning valid results if the target
|
||||
// string is nil, so we have to explicitly test for nil in the following two cases.
|
||||
|
||||
if (cacheable) {
|
||||
NSString * responseHeader;
|
||||
|
||||
responseHeader = [[response allHeaderFields][@"Cache-Control"] lowercaseString];
|
||||
if ( (responseHeader != nil) && [responseHeader rangeOfString:@"no-store"].location != NSNotFound) {
|
||||
cacheable = NO;
|
||||
}
|
||||
}
|
||||
|
||||
// If we still think it might be cacheable, look at the "Cache-Control" header in
|
||||
// the request.
|
||||
|
||||
if (cacheable) {
|
||||
NSString * requestHeader;
|
||||
|
||||
requestHeader = [[request allHTTPHeaderFields][@"Cache-Control"] lowercaseString];
|
||||
if ( (requestHeader != nil)
|
||||
&& ([requestHeader rangeOfString:@"no-store"].location != NSNotFound)
|
||||
&& ([requestHeader rangeOfString:@"no-cache"].location != NSNotFound) ) {
|
||||
cacheable = NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Use the cacheable flag to determine the result.
|
||||
|
||||
if (cacheable) {
|
||||
|
||||
// This code only caches HTTPS data in memory. This is inline with earlier versions of
|
||||
// iOS. Modern versions of iOS use file protection to protect the cache, and thus are
|
||||
// happy to cache HTTPS on disk. I've not made the correspondencing change because
|
||||
// it's nice to see all three cache policies in action.
|
||||
|
||||
if ([[[[request URL] scheme] lowercaseString] isEqual:@"https"]) {
|
||||
result = NSURLCacheStorageAllowedInMemoryOnly;
|
||||
} else {
|
||||
result = NSURLCacheStorageAllowed;
|
||||
}
|
||||
} else {
|
||||
result = NSURLCacheStorageNotAllowed;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
||||
/*! Returns a canonical form of the supplied request.
|
||||
* \details The Foundation URL loading system needs to be able to canonicalize URL
|
||||
* requests for various reasons (for example, to look for cache hits). The default
|
||||
* HTTP/HTTPS protocol has a complex chunk of code to perform this function. Unfortunately
|
||||
* there's no way for third party code to access this. Instead, we have to reimplement
|
||||
* it all ourselves. This is split off into a separate file to emphasise that this
|
||||
* is standard boilerplate that you probably don't need to look at.
|
||||
*
|
||||
* IMPORTANT: While you can take most of this code as read, you might want to tweak
|
||||
* the handling of the "Accept-Language" in the CanonicaliseHeaders routine.
|
||||
* \param request The request to canonicalise; must not be nil.
|
||||
* \returns The canonical request; should never be nil.
|
||||
*/
|
||||
|
||||
extern NSMutableURLRequest * CanonicalRequestForRequest(NSURLRequest *request);
|
||||
@@ -1,397 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_CanonicalRequest.h"
|
||||
|
||||
#include <xlocale.h>
|
||||
|
||||
#pragma mark * URL canonicalization steps
|
||||
|
||||
/*! A step in the canonicalisation process.
|
||||
* \details The canonicalisation process is made up of a sequence of steps, each of which is
|
||||
* implemented by a function that matches this function pointer. The function gets a URL
|
||||
* and a mutable buffer holding that URL as bytes. The function can mutate the buffer as it
|
||||
* sees fit. It typically does this by calling CFURLGetByteRangeForComponent to find the range
|
||||
* of interest in the buffer. In that case bytesInserted is the amount to adjust that range,
|
||||
* and the function should modify that to account for any bytes it inserts or deletes. If
|
||||
* the function modifies the buffer too much, it can return kCFNotFound to force the system
|
||||
* to re-create the URL from the buffer.
|
||||
* \param url The original URL to work on.
|
||||
* \param urlData The URL as a mutable buffer; the routine modifies this.
|
||||
* \param bytesInserted The number of bytes that have been inserted so far the mutable buffer.
|
||||
* \returns An updated value of bytesInserted or kCFNotFound if the URL must be reparsed.
|
||||
*/
|
||||
|
||||
typedef CFIndex (*CanonicalRequestStepFunction)(NSURL *url, NSMutableData *urlData, CFIndex bytesInserted);
|
||||
|
||||
/*! The post-scheme separate should be "://"; if that's not the case, fix it.
|
||||
* \param url The original URL to work on.
|
||||
* \param urlData The URL as a mutable buffer; the routine modifies this.
|
||||
* \param bytesInserted The number of bytes that have been inserted so far the mutable buffer.
|
||||
* \returns An updated value of bytesInserted or kCFNotFound if the URL must be reparsed.
|
||||
*/
|
||||
|
||||
static CFIndex FixPostSchemeSeparator(NSURL *url, NSMutableData *urlData, CFIndex bytesInserted)
|
||||
{
|
||||
CFRange range;
|
||||
uint8_t * urlDataBytes;
|
||||
NSUInteger urlDataLength;
|
||||
NSUInteger cursor;
|
||||
NSUInteger separatorLength;
|
||||
NSUInteger expectedSeparatorLength;
|
||||
|
||||
//assert(url != nil);
|
||||
//assert(urlData != nil);
|
||||
//assert(bytesInserted >= 0);
|
||||
|
||||
range = CFURLGetByteRangeForComponent( (CFURLRef) url, kCFURLComponentScheme, NULL);
|
||||
if (range.location != kCFNotFound) {
|
||||
//assert(range.location >= 0);
|
||||
//assert(range.length >= 0);
|
||||
|
||||
urlDataBytes = [urlData mutableBytes];
|
||||
urlDataLength = [urlData length];
|
||||
|
||||
separatorLength = 0;
|
||||
cursor = (NSUInteger) range.location + (NSUInteger) bytesInserted + (NSUInteger) range.length;
|
||||
if ( (cursor < urlDataLength) && (urlDataBytes[cursor] == ':') ) {
|
||||
cursor += 1;
|
||||
separatorLength += 1;
|
||||
if ( (cursor < urlDataLength) && (urlDataBytes[cursor] == '/') ) {
|
||||
cursor += 1;
|
||||
separatorLength += 1;
|
||||
if ( (cursor < urlDataLength) && (urlDataBytes[cursor] == '/') ) {
|
||||
cursor += 1;
|
||||
separatorLength += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma unused(cursor) // quietens an analyser warning
|
||||
|
||||
expectedSeparatorLength = strlen("://");
|
||||
if (separatorLength != expectedSeparatorLength) {
|
||||
[urlData replaceBytesInRange:NSMakeRange((NSUInteger) range.location + (NSUInteger) bytesInserted + (NSUInteger) range.length, separatorLength) withBytes:"://" length:expectedSeparatorLength];
|
||||
bytesInserted = kCFNotFound; // have to build everything now
|
||||
}
|
||||
}
|
||||
|
||||
return bytesInserted;
|
||||
}
|
||||
|
||||
/*! The scheme should be lower case; if it's not, make it so.
|
||||
* \param url The original URL to work on.
|
||||
* \param urlData The URL as a mutable buffer; the routine modifies this.
|
||||
* \param bytesInserted The number of bytes that have been inserted so far the mutable buffer.
|
||||
* \returns An updated value of bytesInserted or kCFNotFound if the URL must be reparsed.
|
||||
*/
|
||||
|
||||
static CFIndex LowercaseScheme(NSURL *url, NSMutableData *urlData, CFIndex bytesInserted)
|
||||
{
|
||||
CFRange range;
|
||||
uint8_t * urlDataBytes;
|
||||
CFIndex i;
|
||||
|
||||
//assert(url != nil);
|
||||
//assert(urlData != nil);
|
||||
//assert(bytesInserted >= 0);
|
||||
|
||||
range = CFURLGetByteRangeForComponent( (CFURLRef) url, kCFURLComponentScheme, NULL);
|
||||
if (range.location != kCFNotFound) {
|
||||
//assert(range.location >= 0);
|
||||
//assert(range.length >= 0);
|
||||
|
||||
urlDataBytes = [urlData mutableBytes];
|
||||
for (i = range.location + bytesInserted; i < (range.location + bytesInserted + range.length); i++) {
|
||||
urlDataBytes[i] = (uint8_t) tolower_l(urlDataBytes[i], NULL);
|
||||
}
|
||||
}
|
||||
return bytesInserted;
|
||||
}
|
||||
|
||||
/*! The host should be lower case; if it's not, make it so.
|
||||
* \param url The original URL to work on.
|
||||
* \param urlData The URL as a mutable buffer; the routine modifies this.
|
||||
* \param bytesInserted The number of bytes that have been inserted so far the mutable buffer.
|
||||
* \returns An updated value of bytesInserted or kCFNotFound if the URL must be reparsed.
|
||||
*/
|
||||
|
||||
static CFIndex LowercaseHost(NSURL *url, NSMutableData *urlData, CFIndex bytesInserted)
|
||||
// The host should be lower case; if it's not, make it so.
|
||||
{
|
||||
CFRange range;
|
||||
uint8_t * urlDataBytes;
|
||||
CFIndex i;
|
||||
|
||||
//assert(url != nil);
|
||||
//assert(urlData != nil);
|
||||
//assert(bytesInserted >= 0);
|
||||
|
||||
range = CFURLGetByteRangeForComponent( (CFURLRef) url, kCFURLComponentHost, NULL);
|
||||
if (range.location != kCFNotFound) {
|
||||
//assert(range.location >= 0);
|
||||
//assert(range.length >= 0);
|
||||
|
||||
urlDataBytes = [urlData mutableBytes];
|
||||
for (i = range.location + bytesInserted; i < (range.location + bytesInserted + range.length); i++) {
|
||||
urlDataBytes[i] = (uint8_t) tolower_l(urlDataBytes[i], NULL);
|
||||
}
|
||||
}
|
||||
return bytesInserted;
|
||||
}
|
||||
|
||||
/*! An empty host should be treated as "localhost" case; if it's not, make it so.
|
||||
* \param url The original URL to work on.
|
||||
* \param urlData The URL as a mutable buffer; the routine modifies this.
|
||||
* \param bytesInserted The number of bytes that have been inserted so far the mutable buffer.
|
||||
* \returns An updated value of bytesInserted or kCFNotFound if the URL must be reparsed.
|
||||
*/
|
||||
|
||||
static CFIndex FixEmptyHost(NSURL *url, NSMutableData *urlData, CFIndex bytesInserted)
|
||||
{
|
||||
CFRange range;
|
||||
CFRange rangeWithSeparator;
|
||||
|
||||
//assert(url != nil);
|
||||
//assert(urlData != nil);
|
||||
//assert(bytesInserted >= 0);
|
||||
|
||||
range = CFURLGetByteRangeForComponent( (CFURLRef) url, kCFURLComponentHost, &rangeWithSeparator);
|
||||
if (range.length == 0) {
|
||||
NSUInteger localhostLength;
|
||||
|
||||
//assert(range.location >= 0);
|
||||
//assert(range.length >= 0);
|
||||
|
||||
localhostLength = strlen("localhost");
|
||||
if (range.location != kCFNotFound) {
|
||||
[urlData replaceBytesInRange:NSMakeRange( (NSUInteger) range.location + (NSUInteger) bytesInserted, 0) withBytes:"localhost" length:localhostLength];
|
||||
bytesInserted += localhostLength;
|
||||
} else if ( (rangeWithSeparator.location != kCFNotFound) && (rangeWithSeparator.length == 0) ) {
|
||||
[urlData replaceBytesInRange:NSMakeRange((NSUInteger) rangeWithSeparator.location + (NSUInteger) bytesInserted, 0) withBytes:"localhost" length:localhostLength];
|
||||
bytesInserted += localhostLength;
|
||||
}
|
||||
}
|
||||
return bytesInserted;
|
||||
}
|
||||
|
||||
/*! Transform an empty URL path to "/". For example, "http://www.apple.com" becomes "http://www.apple.com/".
|
||||
* \param url The original URL to work on.
|
||||
* \param urlData The URL as a mutable buffer; the routine modifies this.
|
||||
* \param bytesInserted The number of bytes that have been inserted so far the mutable buffer.
|
||||
* \returns An updated value of bytesInserted or kCFNotFound if the URL must be reparsed.
|
||||
*/
|
||||
|
||||
static CFIndex FixEmptyPath(NSURL *url, NSMutableData *urlData, CFIndex bytesInserted)
|
||||
{
|
||||
CFRange range;
|
||||
CFRange rangeWithSeparator;
|
||||
|
||||
//assert(url != nil);
|
||||
//assert(urlData != nil);
|
||||
//assert(bytesInserted >= 0);
|
||||
|
||||
range = CFURLGetByteRangeForComponent( (CFURLRef) url, kCFURLComponentPath, &rangeWithSeparator);
|
||||
// The following is not a typo. We use rangeWithSeparator to find where to insert the
|
||||
// "/" and the range length to decide whether we /need/ to insert the "/".
|
||||
if ( (rangeWithSeparator.location != kCFNotFound) && (range.length == 0) ) {
|
||||
//assert(range.location >= 0);
|
||||
//assert(range.length >= 0);
|
||||
//assert(rangeWithSeparator.location >= 0);
|
||||
//assert(rangeWithSeparator.length >= 0);
|
||||
|
||||
[urlData replaceBytesInRange:NSMakeRange( (NSUInteger) rangeWithSeparator.location + (NSUInteger) bytesInserted, 0) withBytes:"/" length:1];
|
||||
bytesInserted += 1;
|
||||
}
|
||||
return bytesInserted;
|
||||
}
|
||||
|
||||
/*! If the user specified the default port (80 for HTTP, 443 for HTTPS), remove it from the URL.
|
||||
* \details Actually this code is disabled because the equivalent code in the default protocol
|
||||
* handler has also been disabled; some setups depend on get the port number in the URL, even if it
|
||||
* is the default.
|
||||
* \param url The original URL to work on.
|
||||
* \param urlData The URL as a mutable buffer; the routine modifies this.
|
||||
* \param bytesInserted The number of bytes that have been inserted so far the mutable buffer.
|
||||
* \returns An updated value of bytesInserted or kCFNotFound if the URL must be reparsed.
|
||||
*/
|
||||
|
||||
__attribute__((unused)) static CFIndex DeleteDefaultPort(NSURL *url, NSMutableData *urlData, CFIndex bytesInserted)
|
||||
{
|
||||
NSString * scheme;
|
||||
BOOL isHTTP;
|
||||
BOOL isHTTPS;
|
||||
CFRange range;
|
||||
uint8_t * urlDataBytes;
|
||||
NSString * portNumberStr;
|
||||
int portNumber;
|
||||
|
||||
//assert(url != nil);
|
||||
//assert(urlData != nil);
|
||||
//assert(bytesInserted >= 0);
|
||||
|
||||
scheme = [[url scheme] lowercaseString];
|
||||
//assert(scheme != nil);
|
||||
|
||||
isHTTP = [scheme isEqual:@"http" ];
|
||||
isHTTPS = [scheme isEqual:@"https"];
|
||||
|
||||
range = CFURLGetByteRangeForComponent( (CFURLRef) url, kCFURLComponentPort, NULL);
|
||||
if (range.location != kCFNotFound) {
|
||||
//assert(range.location >= 0);
|
||||
//assert(range.length >= 0);
|
||||
|
||||
urlDataBytes = [urlData mutableBytes];
|
||||
|
||||
portNumberStr = [[NSString alloc] initWithBytes:&urlDataBytes[range.location + bytesInserted] length:(NSUInteger) range.length encoding:NSUTF8StringEncoding];
|
||||
if (portNumberStr != nil) {
|
||||
portNumber = [portNumberStr intValue];
|
||||
if ( (isHTTP && (portNumber == 80)) || (isHTTPS && (portNumber == 443)) ) {
|
||||
// -1 and +1 to account for the leading ":"
|
||||
[urlData replaceBytesInRange:NSMakeRange((NSUInteger) range.location + (NSUInteger) bytesInserted - 1, (NSUInteger) range.length + 1) withBytes:NULL length:0];
|
||||
bytesInserted -= (range.length + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bytesInserted;
|
||||
}
|
||||
|
||||
#pragma mark * Other request canonicalization
|
||||
|
||||
/*! Canonicalise the request headers.
|
||||
* \param request The request to canonicalise.
|
||||
*/
|
||||
|
||||
static void CanonicaliseHeaders(NSMutableURLRequest * request)
|
||||
{
|
||||
// If there's no content type and the request is a POST with a body, add a default
|
||||
// content type of "application/x-www-form-urlencoded".
|
||||
|
||||
if ( ([request valueForHTTPHeaderField:@"Content-Type"] == nil)
|
||||
&& ([[request HTTPMethod] caseInsensitiveCompare:@"POST"] == NSOrderedSame)
|
||||
&& (([request HTTPBody] != nil) || ([request HTTPBodyStream] != nil)) ) {
|
||||
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
|
||||
}
|
||||
|
||||
// If there's no "Accept" header, add a default.
|
||||
|
||||
if ([request valueForHTTPHeaderField:@"Accept"] == nil) {
|
||||
[request setValue:@"*/*" forHTTPHeaderField:@"Accept"];
|
||||
}
|
||||
|
||||
// If there's not "Accept-Encoding" header, add a default.
|
||||
|
||||
if ([request valueForHTTPHeaderField:@"Accept-Encoding"] == nil) {
|
||||
[request setValue:@"gzip, deflate" forHTTPHeaderField:@"Accept-Encoding"];
|
||||
}
|
||||
|
||||
// If there's not an "Accept-Language" headre, add a default. This is quite bogus; ideally we
|
||||
// should derive the correct "Accept-Language" value from the langauge that the app is running
|
||||
// in. However, that's quite difficult to get right, so rather than show some general purpose
|
||||
// code that might fail in some circumstances, I've decided to just hardwire US English.
|
||||
// If you use this code in your own app you can customise it as you see fit. One option might be
|
||||
// to base this value on -[NSBundle preferredLocalizations], so that the web page comes back in
|
||||
// the language that the app is running in.
|
||||
|
||||
if ([request valueForHTTPHeaderField:@"Accept-Language"] == nil) {
|
||||
[request setValue:@"en-us" forHTTPHeaderField:@"Accept-Language"];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark * API
|
||||
|
||||
extern NSMutableURLRequest * CanonicalRequestForRequest(NSURLRequest *request)
|
||||
{
|
||||
NSMutableURLRequest * result;
|
||||
NSString * scheme;
|
||||
|
||||
//assert(request != nil);
|
||||
|
||||
// Make a mutable copy of the request.
|
||||
|
||||
result = [request mutableCopy];
|
||||
|
||||
// First up check that we're dealing with HTTP or HTTPS. If not, do nothing (why were we
|
||||
// we even called?).
|
||||
|
||||
scheme = [[[request URL] scheme] lowercaseString];
|
||||
//assert(scheme != nil);
|
||||
|
||||
if ( ! [scheme isEqual:@"http" ] && ! [scheme isEqual:@"https"]) {
|
||||
//assert(NO);
|
||||
} else {
|
||||
CFIndex bytesInserted;
|
||||
NSURL * requestURL;
|
||||
NSMutableData * urlData;
|
||||
static const CanonicalRequestStepFunction kStepFunctions[] = {
|
||||
FixPostSchemeSeparator,
|
||||
LowercaseScheme,
|
||||
LowercaseHost,
|
||||
FixEmptyHost,
|
||||
// DeleteDefaultPort, -- The built-in canonicalizer has stopped doing this, so we don't do it either.
|
||||
FixEmptyPath
|
||||
};
|
||||
size_t stepIndex;
|
||||
size_t stepCount;
|
||||
|
||||
// Canonicalise the URL by executing each of our step functions.
|
||||
|
||||
bytesInserted = kCFNotFound;
|
||||
urlData = nil;
|
||||
requestURL = [request URL];
|
||||
//assert(requestURL != nil);
|
||||
|
||||
stepCount = sizeof(kStepFunctions) / sizeof(*kStepFunctions);
|
||||
for (stepIndex = 0; stepIndex < stepCount; stepIndex++) {
|
||||
|
||||
// If we don't have valid URL data, create it from the URL.
|
||||
|
||||
//assert(requestURL != nil);
|
||||
if (bytesInserted == kCFNotFound) {
|
||||
NSData * urlDataImmutable;
|
||||
|
||||
urlDataImmutable = CFBridgingRelease( CFURLCreateData(NULL, (CFURLRef) requestURL, kCFStringEncodingUTF8, true) );
|
||||
//assert(urlDataImmutable != nil);
|
||||
|
||||
urlData = [urlDataImmutable mutableCopy];
|
||||
//assert(urlData != nil);
|
||||
|
||||
bytesInserted = 0;
|
||||
}
|
||||
//assert(urlData != nil);
|
||||
|
||||
// Run the step.
|
||||
|
||||
bytesInserted = kStepFunctions[stepIndex](requestURL, urlData, bytesInserted);
|
||||
|
||||
// Note: The following logging is useful when debugging this code. Change the
|
||||
// if expression to YES to enable it.
|
||||
|
||||
if (/* DISABLES CODE */ (NO)) {
|
||||
// fprintf(stderr, " [%zu] %.*s\n", stepIndex, (int) [urlData length], (const char *) [urlData bytes]);
|
||||
}
|
||||
|
||||
// If the step invalidated our URL (or we're on the last step, whereupon we'll need
|
||||
// the URL outside of the loop), recreate the URL from the URL data.
|
||||
|
||||
if ( (bytesInserted == kCFNotFound) || ((stepIndex + 1) == stepCount) ) {
|
||||
requestURL = CFBridgingRelease( CFURLCreateWithBytes(NULL, [urlData bytes], (CFIndex) [urlData length], kCFStringEncodingUTF8, NULL) );
|
||||
//assert(requestURL != nil);
|
||||
|
||||
urlData = nil;
|
||||
}
|
||||
}
|
||||
|
||||
[result setURL:requestURL];
|
||||
|
||||
// Canonicalise the headers.
|
||||
|
||||
CanonicaliseHeaders(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
||||
@protocol _CustomHTTPProtocolDelegate;
|
||||
|
||||
/*! An NSURLProtocol subclass that overrides the built-in HTTP/HTTPS protocol to intercept
|
||||
* authentication challenges for subsystems, ilke UIWebView, that don't otherwise allow it.
|
||||
* To use this class you should set up your delegate (+setDelegate:) and then call +start.
|
||||
* If you don't call +start the class is completely benign.
|
||||
*
|
||||
* The really tricky stuff here is related to the authentication challenge delegate
|
||||
* callbacks; see the docs for _CustomHTTPProtocolDelegate for the details.
|
||||
*/
|
||||
|
||||
@interface _CustomHTTPProtocol : NSURLProtocol
|
||||
|
||||
/*! Call this to start the module. Prior to this the module is just dormant, and
|
||||
* all HTTP requests proceed as normal. After this all HTTP and HTTPS requests
|
||||
* go through this module.
|
||||
*/
|
||||
|
||||
+ (void)start;
|
||||
|
||||
//liman
|
||||
+ (void)stop;
|
||||
|
||||
/*! Sets the delegate for the class.
|
||||
* \details Note that there's one delegate for the entire class, not one per
|
||||
* instance of the class as is more normal. The delegate is not retained in general,
|
||||
* but is retained for the duration of any given call. Once you set the delegate to nil
|
||||
* you can be assured that it won't be called unretained (that is, by the time that
|
||||
* -setDelegate: returns, we've already done all possible retains on the delegate).
|
||||
* \param newValue The new delegate to use; may be nil.
|
||||
*/
|
||||
|
||||
+ (void)setDelegate:(id<_CustomHTTPProtocolDelegate>)newValue;
|
||||
|
||||
/*! Returns the class delegate.
|
||||
*/
|
||||
|
||||
+ (id<_CustomHTTPProtocolDelegate>)delegate;
|
||||
|
||||
@property (atomic, strong, readonly ) NSURLAuthenticationChallenge * pendingChallenge; ///< The current authentication challenge; it's only safe to access this from the main thread.
|
||||
|
||||
/*! Call this method to resolve an authentication challeng. This must be called on the main thread.
|
||||
* \param challenge The challenge to resolve. This must match the pendingChallenge property.
|
||||
* \param credential The credential to use, or nil to continue without a credential.
|
||||
*/
|
||||
|
||||
- (void)resolveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge withCredential:(NSURLCredential *)credential;
|
||||
|
||||
@end
|
||||
|
||||
/*! The delegate for the CustomHTTPProtocol class (not its instances).
|
||||
* \details The delegate handles two different types of callbacks:
|
||||
*
|
||||
* - authentication challenges
|
||||
*
|
||||
* - logging
|
||||
*
|
||||
* The latter is very simple. The former is quite tricky. The basic idea is that each CustomHTTPProtocol
|
||||
* instance sends the delegate a serialised stream of authentication challenges, each of which it is
|
||||
* expected to resolve. The sequence is as follows:
|
||||
*
|
||||
* -# It calls -customHTTPProtocol:canAuthenticateAgainstProtectionSpace: to determine if the delegate
|
||||
* can handle the challenge. This can be call on an arbitrary background thread.
|
||||
*
|
||||
* -# If the delegate returns YES, it calls -customHTTPProtocol:didReceiveAuthenticationChallenge: to
|
||||
* actually process the challenge. This is always called on the main thread. The delegate can resolve
|
||||
* the challenge synchronously (that is, before returning from the call) or it can return from the call
|
||||
* and then, later on, resolve the challenge. Resolving the challenge involves calling
|
||||
* -[CustomHTTPProtocol resolveAuthenticationChallenge:withCredential:], which also must be called
|
||||
* on the main thread. Between the calls to -customHTTPProtocol:didReceiveAuthenticationChallenge:
|
||||
* and -[CustomHTTPProtocol resolveAuthenticationChallenge:withCredential:], the protocol's
|
||||
* pendingChallenge property will contain the challenge.
|
||||
*
|
||||
* -# While there is a pending challenge, the protocol may call -customHTTPProtocol:didCancelAuthenticationChallenge:
|
||||
* to cancel the challenge. This is always called on the main thread.
|
||||
*
|
||||
* Note that this design follows the original NSURLConnection model, not the newer NSURLConnection model
|
||||
* (introduced in OS X 10.7 / iOS 5) or the NSURLSession model, because of my concerns about performance.
|
||||
* Specifically, -customHTTPProtocol:canAuthenticateAgainstProtectionSpace: can be called on any thread
|
||||
* but -customHTTPProtocol:didReceiveAuthenticationChallenge: is called on the main thread. If I unified
|
||||
* them I'd end up calling the resulting single routine on the main thread, which meanings a lot more
|
||||
* bouncing between threads, much of which would be pointless in the common case where you don't want to
|
||||
* customise the default behaviour. Alternatively I could call the unified routine on an arbitrary thread,
|
||||
* but that would make it harder for clients and require a major rework of my implementation.
|
||||
*/
|
||||
|
||||
@protocol _CustomHTTPProtocolDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
/*! Called by an CustomHTTPProtocol instance to ask the delegate whether it's prepared to handle
|
||||
* a particular authentication challenge. Can be called on any thread.
|
||||
* \param protocol The protocol instance itself; will not be nil.
|
||||
* \param protectionSpace The protection space for the authentication challenge; will not be nil.
|
||||
* \returns Return YES if you want the -customHTTPProtocol:didReceiveAuthenticationChallenge: delegate
|
||||
* callback, or NO for the challenge to be handled in the default way.
|
||||
*/
|
||||
|
||||
- (BOOL)customHTTPProtocol:(_CustomHTTPProtocol *)protocol canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
|
||||
|
||||
/*! Called by an CustomHTTPProtocol instance to request that the delegate process on authentication
|
||||
* challenge. Will be called on the main thread. Unless the challenge is cancelled (see below)
|
||||
* the delegate must eventually resolve it by calling -resolveAuthenticationChallenge:withCredential:.
|
||||
* \param protocol The protocol instance itself; will not be nil.
|
||||
* \param challenge The authentication challenge; will not be nil.
|
||||
*/
|
||||
|
||||
- (void)customHTTPProtocol:(_CustomHTTPProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
|
||||
|
||||
/*! Called by an CustomHTTPProtocol instance to cancel an issued authentication challenge.
|
||||
* Will be called on the main thread.
|
||||
* \param protocol The protocol instance itself; will not be nil.
|
||||
* \param challenge The authentication challenge; will not be nil; will match the challenge
|
||||
* previously issued by -customHTTPProtocol:canAuthenticateAgainstProtectionSpace:.
|
||||
*/
|
||||
|
||||
- (void)customHTTPProtocol:(_CustomHTTPProtocol *)protocol didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
|
||||
|
||||
/*! Called by the CustomHTTPProtocol to log various bits of information.
|
||||
* Can be called on any thread.
|
||||
* \param protocol The protocol instance itself; nil to indicate log messages from the class itself.
|
||||
* \param format A standard NSString-style format string; will not be nil.
|
||||
* \param arguments Arguments for that format string.
|
||||
*/
|
||||
|
||||
- (void)customHTTPProtocol:(_CustomHTTPProtocol *)protocol logWithFormat:(NSString *)format arguments:(va_list)arguments;
|
||||
|
||||
@end
|
||||
@@ -1,52 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/*! A simple class for demultiplexing NSURLSession delegate callbacks to a per-task delegate object.
|
||||
|
||||
You initialise the class with a session configuration. After that you can create data tasks
|
||||
within that session by calling -dataTaskWithRequest:delegate:modes:. Any delegate callbacks
|
||||
for that data task are redirected to the delegate on the thread that created the task in
|
||||
one of the specified run loop modes. That thread must run its run loop in order to get
|
||||
these callbacks.
|
||||
*/
|
||||
|
||||
@interface _QNSURLSessionDemux : NSObject
|
||||
|
||||
/*! Create a demultiplex for the specified session configuration.
|
||||
* \param configuration The session configuration to use; if nil, a default session is created.
|
||||
* \returns An initialised instance.
|
||||
*/
|
||||
|
||||
- (instancetype)initWithConfiguration:(NSURLSessionConfiguration *)configuration;
|
||||
|
||||
@property (atomic, copy, readonly ) NSURLSessionConfiguration * configuration; ///< A copy of the configuration passed to -initWithConfiguration:.
|
||||
@property (atomic, strong, readonly ) NSURLSession * session; ///< The session created from the configuration passed to -initWithConfiguration:.
|
||||
|
||||
/*! Creates a new data task whose delegate callbacks are routed to the supplied delegate.
|
||||
* \details The callbacks are run on the current thread (that is, the thread that called this
|
||||
* method) in the specified modes.
|
||||
*
|
||||
* The delegate is retained until the task completes, that is, until after your
|
||||
* -URLSession:task:didCompleteWithError: delegate callback returns.
|
||||
*
|
||||
* The returned task is suspend. You must resume the returned task for the task to
|
||||
* make progress. Furthermore, it's not safe to simply discard the returned task
|
||||
* because in that case the task's delegate is never released.
|
||||
*
|
||||
* \param request The request that the data task executes; must not be nil.
|
||||
* \param delegate The delegate to receive the data task's delegate callbacks; must not be nil.
|
||||
* \param modes The run loop modes in which to run the data task's delegate callbacks; if nil or
|
||||
* empty, the default run loop mode (NSDefaultRunLoopMode is used).
|
||||
* \returns A suspended data task that you must resume.
|
||||
*/
|
||||
|
||||
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request delegate:(id<NSURLSessionDataDelegate>)delegate modes:(NSArray *)modes;
|
||||
|
||||
@end
|
||||
@@ -1,279 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_QNSURLSessionDemux.h"
|
||||
|
||||
@interface _QNSURLSessionDemuxTaskInfo : NSObject
|
||||
|
||||
- (instancetype)initWithTask:(NSURLSessionDataTask *)task delegate:(id<NSURLSessionDataDelegate>)delegate modes:(NSArray *)modes;
|
||||
|
||||
@property (atomic, strong, readonly ) NSURLSessionDataTask * task;
|
||||
@property (atomic, strong, readonly ) id<NSURLSessionDataDelegate> delegate;
|
||||
@property (atomic, strong, readonly ) NSThread * thread;
|
||||
@property (atomic, copy, readonly ) NSArray * modes;
|
||||
|
||||
- (void)performBlock:(dispatch_block_t)block;
|
||||
|
||||
- (void)invalidate;
|
||||
|
||||
@end
|
||||
|
||||
@interface _QNSURLSessionDemuxTaskInfo ()
|
||||
|
||||
@property (atomic, strong, readwrite) id<NSURLSessionDataDelegate> delegate;
|
||||
@property (atomic, strong, readwrite) NSThread * thread;
|
||||
|
||||
@end
|
||||
|
||||
@implementation _QNSURLSessionDemuxTaskInfo
|
||||
|
||||
- (instancetype)initWithTask:(NSURLSessionDataTask *)task delegate:(id<NSURLSessionDataDelegate>)delegate modes:(NSArray *)modes
|
||||
{
|
||||
//assert(task != nil);
|
||||
//assert(delegate != nil);
|
||||
//assert(modes != nil);
|
||||
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
self->_task = task;
|
||||
self->_delegate = delegate;
|
||||
self->_thread = [NSThread currentThread];
|
||||
self->_modes = [modes copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)performBlock:(dispatch_block_t)block
|
||||
{
|
||||
//assert(self.delegate != nil);
|
||||
//assert(self.thread != nil);
|
||||
[self performSelector:@selector(performBlockOnClientThread:) onThread:self.thread withObject:[block copy] waitUntilDone:NO modes:self.modes];
|
||||
}
|
||||
|
||||
- (void)performBlockOnClientThread:(dispatch_block_t)block
|
||||
{
|
||||
//assert([NSThread currentThread] == self.thread);
|
||||
block();
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
self.delegate = nil;
|
||||
self.thread = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface _QNSURLSessionDemux () <NSURLSessionDataDelegate>
|
||||
|
||||
@property (atomic, strong, readonly ) NSMutableDictionary * taskInfoByTaskID; // keys NSURLSessionTask taskIdentifier, values are SessionManager
|
||||
@property (atomic, strong, readonly ) NSOperationQueue * sessionDelegateQueue;
|
||||
|
||||
@end
|
||||
|
||||
@implementation _QNSURLSessionDemux
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithConfiguration:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithConfiguration:(NSURLSessionConfiguration *)configuration
|
||||
{
|
||||
// configuration may be nil
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
if (configuration == nil) {
|
||||
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
}
|
||||
self->_configuration = [configuration copy];
|
||||
|
||||
self->_taskInfoByTaskID = [[NSMutableDictionary alloc] init];
|
||||
|
||||
self->_sessionDelegateQueue = [[NSOperationQueue alloc] init];
|
||||
[self->_sessionDelegateQueue setMaxConcurrentOperationCount:1];
|
||||
[self->_sessionDelegateQueue setName:@"_QNSURLSessionDemux"];
|
||||
|
||||
self->_session = [NSURLSession sessionWithConfiguration:self->_configuration delegate:self delegateQueue:self->_sessionDelegateQueue];
|
||||
self->_session.sessionDescription = @"_QNSURLSessionDemux";
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request delegate:(id<NSURLSessionDataDelegate>)delegate modes:(NSArray *)modes
|
||||
{
|
||||
NSURLSessionDataTask * task;
|
||||
_QNSURLSessionDemuxTaskInfo * taskInfo;
|
||||
|
||||
//assert(request != nil);
|
||||
//assert(delegate != nil);
|
||||
// modes may be nil
|
||||
|
||||
if ([modes count] == 0) {
|
||||
modes = @[ NSDefaultRunLoopMode ];
|
||||
}
|
||||
|
||||
task = [self.session dataTaskWithRequest:request];
|
||||
//assert(task != nil);
|
||||
|
||||
taskInfo = [[_QNSURLSessionDemuxTaskInfo alloc] initWithTask:task delegate:delegate modes:modes];
|
||||
|
||||
@synchronized (self) {
|
||||
self.taskInfoByTaskID[@(task.taskIdentifier)] = taskInfo;
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
- (_QNSURLSessionDemuxTaskInfo *)taskInfoForTask:(NSURLSessionTask *)task
|
||||
{
|
||||
_QNSURLSessionDemuxTaskInfo * result;
|
||||
|
||||
//assert(task != nil);
|
||||
|
||||
@synchronized (self) {
|
||||
result = self.taskInfoByTaskID[@(task.taskIdentifier)];
|
||||
//assert(result != nil);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)newRequest completionHandler:(void (^)(NSURLRequest *))completionHandler
|
||||
{
|
||||
_QNSURLSessionDemuxTaskInfo * taskInfo;
|
||||
|
||||
taskInfo = [self taskInfoForTask:task];
|
||||
if ([taskInfo.delegate respondsToSelector:@selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)]) {
|
||||
[taskInfo performBlock:^{
|
||||
[taskInfo.delegate URLSession:session task:task willPerformHTTPRedirection:response newRequest:newRequest completionHandler:completionHandler];
|
||||
}];
|
||||
} else {
|
||||
completionHandler(newRequest);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
|
||||
{
|
||||
_QNSURLSessionDemuxTaskInfo * taskInfo;
|
||||
|
||||
taskInfo = [self taskInfoForTask:task];
|
||||
if ([taskInfo.delegate respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)]) {
|
||||
[taskInfo performBlock:^{
|
||||
[taskInfo.delegate URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler];
|
||||
}];
|
||||
} else {
|
||||
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
|
||||
{
|
||||
_QNSURLSessionDemuxTaskInfo * taskInfo;
|
||||
|
||||
taskInfo = [self taskInfoForTask:task];
|
||||
if ([taskInfo.delegate respondsToSelector:@selector(URLSession:task:needNewBodyStream:)]) {
|
||||
[taskInfo performBlock:^{
|
||||
[taskInfo.delegate URLSession:session task:task needNewBodyStream:completionHandler];
|
||||
}];
|
||||
} else {
|
||||
completionHandler(nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
|
||||
{
|
||||
_QNSURLSessionDemuxTaskInfo * taskInfo;
|
||||
|
||||
taskInfo = [self taskInfoForTask:task];
|
||||
if ([taskInfo.delegate respondsToSelector:@selector(URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)]) {
|
||||
[taskInfo performBlock:^{
|
||||
[taskInfo.delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
|
||||
{
|
||||
_QNSURLSessionDemuxTaskInfo * taskInfo;
|
||||
|
||||
taskInfo = [self taskInfoForTask:task];
|
||||
|
||||
// This is our last delegate callback so we remove our task info record.
|
||||
|
||||
@synchronized (self) {
|
||||
[self.taskInfoByTaskID removeObjectForKey:@(taskInfo.task.taskIdentifier)];
|
||||
}
|
||||
|
||||
// Call the delegate if required. In that case we invalidate the task info on the client thread
|
||||
// after calling the delegate, otherwise the client thread side of the -performBlock: code can
|
||||
// find itself with an invalidated task info.
|
||||
|
||||
if ([taskInfo.delegate respondsToSelector:@selector(URLSession:task:didCompleteWithError:)]) {
|
||||
[taskInfo performBlock:^{
|
||||
[taskInfo.delegate URLSession:session task:task didCompleteWithError:error];
|
||||
[taskInfo invalidate];
|
||||
}];
|
||||
} else {
|
||||
[taskInfo invalidate];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
|
||||
{
|
||||
_QNSURLSessionDemuxTaskInfo * taskInfo;
|
||||
|
||||
taskInfo = [self taskInfoForTask:dataTask];
|
||||
if ([taskInfo.delegate respondsToSelector:@selector(URLSession:dataTask:didReceiveResponse:completionHandler:)]) {
|
||||
[taskInfo performBlock:^{
|
||||
[taskInfo.delegate URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
|
||||
}];
|
||||
} else {
|
||||
completionHandler(NSURLSessionResponseAllow);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
|
||||
{
|
||||
_QNSURLSessionDemuxTaskInfo * taskInfo;
|
||||
|
||||
taskInfo = [self taskInfoForTask:dataTask];
|
||||
if ([taskInfo.delegate respondsToSelector:@selector(URLSession:dataTask:didBecomeDownloadTask:)]) {
|
||||
[taskInfo performBlock:^{
|
||||
[taskInfo.delegate URLSession:session dataTask:dataTask didBecomeDownloadTask:downloadTask];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
|
||||
{
|
||||
_QNSURLSessionDemuxTaskInfo * taskInfo;
|
||||
|
||||
taskInfo = [self taskInfoForTask:dataTask];
|
||||
if ([taskInfo.delegate respondsToSelector:@selector(URLSession:dataTask:didReceiveData:)]) {
|
||||
[taskInfo performBlock:^{
|
||||
[taskInfo.delegate URLSession:session dataTask:dataTask didReceiveData:data];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
|
||||
{
|
||||
_QNSURLSessionDemuxTaskInfo * taskInfo;
|
||||
|
||||
taskInfo = [self taskInfoForTask:dataTask];
|
||||
if ([taskInfo.delegate respondsToSelector:@selector(URLSession:dataTask:willCacheResponse:completionHandler:)]) {
|
||||
[taskInfo performBlock:^{
|
||||
[taskInfo.delegate URLSession:session dataTask:dataTask willCacheResponse:proposedResponse completionHandler:completionHandler];
|
||||
}];
|
||||
} else {
|
||||
completionHandler(proposedResponse);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
31
Pods/CocoaDebug/Sources/Logs/CocoaDebugTool.h
generated
@@ -1,31 +0,0 @@
|
||||
//
|
||||
// CocoaDebugTool.h
|
||||
// Example_Swift
|
||||
//
|
||||
// Created by man 5/8/19.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface CocoaDebugTool : NSObject
|
||||
|
||||
/// log with string,
|
||||
/// default color is white
|
||||
+ (void)logWithString:(NSString *)string;
|
||||
+ (void)logWithString:(NSString *)string color:(UIColor *)color;
|
||||
|
||||
/// log with JSON Data,
|
||||
/// default color is white,
|
||||
/// return string
|
||||
+ (NSString *)logWithJsonData:(NSData *)data;
|
||||
+ (NSString *)logWithJsonData:(NSData *)data color:(UIColor *)color;
|
||||
|
||||
/// log with Protobuf Data,
|
||||
/// default color is white,
|
||||
/// return string
|
||||
//+ (NSString *)logWithProtobufData:(NSData *)data className:(NSString *)className;
|
||||
//+ (NSString *)logWithProtobufData:(NSData *)data className:(NSString *)className color:(UIColor *)color;
|
||||
|
||||
@end
|
||||
103
Pods/CocoaDebug/Sources/Logs/CocoaDebugTool.m
generated
@@ -1,103 +0,0 @@
|
||||
//
|
||||
// CocoaDebugTool.m
|
||||
// Example_Swift
|
||||
//
|
||||
// Created by man 5/8/19.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CocoaDebugTool.h"
|
||||
#import "_OCLogHelper.h"
|
||||
#import "GPBMessage+CocoaDebug.h"
|
||||
//#import "GPBMessage.h"
|
||||
|
||||
@implementation CocoaDebugTool
|
||||
|
||||
#pragma mark - logWithString
|
||||
+ (void)logWithString:(NSString *)string {
|
||||
[self logWithString:string color:[UIColor colorWithRed:1 green:1 blue:1 alpha:1]];
|
||||
}
|
||||
|
||||
+ (void)logWithString:(NSString *)string color:(UIColor *)color {
|
||||
[self finalLogWithString:string type:CocoaDebugToolTypeNone color:color];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - logWithJsonData
|
||||
+ (NSString *)logWithJsonData:(NSData *)data {
|
||||
return [self logWithJsonData:data color:[UIColor colorWithRed:1 green:1 blue:1 alpha:1]];
|
||||
}
|
||||
|
||||
+ (NSString *)logWithJsonData:(NSData *)data color:(UIColor *)color {
|
||||
NSString *string = [self getPrettyJsonStringWithData:data] ? : @"NULL";
|
||||
return [self finalLogWithString:string type:CocoaDebugToolTypeJson color:color];
|
||||
}
|
||||
|
||||
|
||||
//#pragma mark - logWithProtobufData
|
||||
//+ (NSString *)logWithProtobufData:(NSData *)data className:(NSString *)className {
|
||||
// return [self logWithProtobufData:data className:className color:[UIColor colorWithRed:1 green:1 blue:1 alpha:1]];
|
||||
//}
|
||||
//
|
||||
//+ (NSString *)logWithProtobufData:(NSData *)data className:(NSString *)className color:(UIColor *)color {
|
||||
// NSString *string = [self parsingProtobufWithData:data className:className] ? : @"NULL";
|
||||
// return [self finalLogWithString:string type:CocoaDebugToolTypeProtobuf color:color];
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma mark - tool
|
||||
|
||||
+ (NSString *)getPrettyJsonStringWithJsonString:(NSString *)jsonString {
|
||||
return [self getPrettyJsonStringWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
}
|
||||
|
||||
+ (NSString *)getPrettyJsonStringWithData:(NSData *)data {
|
||||
if (!data) {return nil;}
|
||||
|
||||
//1.pretty json
|
||||
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
|
||||
if (!dict) {return nil;}
|
||||
|
||||
NSData *prettyData = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:nil];
|
||||
if (!prettyData) {return nil;}
|
||||
|
||||
NSString *prettyJsonString = [[NSString alloc] initWithData:prettyData encoding:NSUTF8StringEncoding];
|
||||
if (prettyJsonString) {
|
||||
return prettyJsonString;
|
||||
}
|
||||
|
||||
//2.protobuf
|
||||
// GPBMessage *message = [GPBMessage parseFromData:data error:nil];
|
||||
// if ([message serializedSize] > 0) {
|
||||
// return [message description];
|
||||
// }
|
||||
|
||||
//3.utf-8 string
|
||||
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
+ (NSString *)finalLogWithString:(NSString *)string type:(CocoaDebugToolType)type color:(UIColor *)color {
|
||||
[[_OCLogHelper shared] handleLogWithFile:@"XXX" function:@"XXX" line:1 message:string color:color type:type];
|
||||
return string;
|
||||
}
|
||||
|
||||
//解析Protobuf
|
||||
//+ (NSString *)parsingProtobufWithData:(NSData *)data className:(NSString *)className {
|
||||
// if (!data || !className) {return nil;}
|
||||
//
|
||||
// Class cls = NSClassFromString(className);
|
||||
// //protobuf
|
||||
// GPBMessage *obj = [cls parseFromData:data error:nil];
|
||||
// //HuiCao
|
||||
// NSString *jsonString = [obj _JSONStringWithIgnoreFields:nil];
|
||||
// if (!jsonString) {return nil;}
|
||||
//
|
||||
// NSString *prettyJsonString = [self getPrettyJsonStringWithJsonString:jsonString];
|
||||
// if (!prettyJsonString) {return nil;}
|
||||
//
|
||||
// return [prettyJsonString stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"];
|
||||
//}
|
||||
|
||||
@end
|
||||
68
Pods/CocoaDebug/Sources/Logs/LogCell.swift
generated
@@ -1,68 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class LogCell: UITableViewCell {
|
||||
|
||||
@IBOutlet weak var labelContent: CustomTextView!
|
||||
@IBOutlet weak var viewTypeLogColor: UIView!
|
||||
|
||||
var model: _OCLogModel? {
|
||||
didSet {
|
||||
guard let model = model else { return }
|
||||
|
||||
labelContent.text = nil
|
||||
labelContent.text = model.str
|
||||
labelContent.attributedText = model.attr
|
||||
|
||||
labelContent.textContainer.lineBreakMode = NSLineBreakMode.byCharWrapping
|
||||
labelContent.textContainer.lineFragmentPadding = 0
|
||||
labelContent.textContainerInset = .zero
|
||||
labelContent.isUserInteractionEnabled = false
|
||||
|
||||
//tag
|
||||
if model.isTag == true {
|
||||
self.contentView.backgroundColor = "#007aff".hexColor
|
||||
} else {
|
||||
//isSelected
|
||||
if model.isSelected == true {
|
||||
self.contentView.backgroundColor = "#222222".hexColor
|
||||
} else {
|
||||
self.contentView.backgroundColor = .black
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CustomTextView : UITextView {
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)!
|
||||
self.inputView = UIView.init(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
|
||||
}
|
||||
|
||||
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
|
||||
if action == #selector(selectAll) {
|
||||
if let range = selectedTextRange, range.start == beginningOfDocument, range.end == endOfDocument {
|
||||
return false
|
||||
}
|
||||
return !text.isEmpty
|
||||
}
|
||||
else if action == #selector(paste(_:)) {
|
||||
return false
|
||||
}
|
||||
else if action == #selector(cut(_:)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return super.canPerformAction(action, withSender: sender)
|
||||
}
|
||||
}
|
||||
700
Pods/CocoaDebug/Sources/Logs/LogViewController.swift
generated
@@ -1,700 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class LogViewController: UIViewController {
|
||||
|
||||
var reachEndDefault: Bool = true
|
||||
var reachEndRN: Bool = true
|
||||
var reachEndWeb: Bool = true
|
||||
|
||||
var firstInDefault: Bool = true
|
||||
var firstInRN: Bool = true
|
||||
var firstInWeb: Bool = true
|
||||
|
||||
var selectedSegmentIndex: Int = 0
|
||||
var selectedSegment_0: Bool = false
|
||||
var selectedSegment_1: Bool = false
|
||||
var selectedSegment_2: Bool = false
|
||||
|
||||
var defaultReloadDataFinish: Bool = true
|
||||
var rnReloadDataFinish: Bool = true
|
||||
var webReloadDataFinish: Bool = true
|
||||
|
||||
|
||||
@IBOutlet weak var segmentedControl: UISegmentedControl!
|
||||
@IBOutlet weak var deleteItem: UIBarButtonItem!
|
||||
|
||||
@IBOutlet weak var defaultTableView: UITableView!
|
||||
@IBOutlet weak var defaultSearchBar: UISearchBar!
|
||||
var defaultModels: [_OCLogModel] = [_OCLogModel]()
|
||||
var defaultCacheModels: Array<_OCLogModel>?
|
||||
var defaultSearchModels: Array<_OCLogModel>?
|
||||
|
||||
@IBOutlet weak var rnTableView: UITableView!
|
||||
@IBOutlet weak var rnSearchBar: UISearchBar!
|
||||
var rnModels: [_OCLogModel] = [_OCLogModel]()
|
||||
var rnCacheModels: Array<_OCLogModel>?
|
||||
var rnSearchModels: Array<_OCLogModel>?
|
||||
|
||||
@IBOutlet weak var webTableView: UITableView!
|
||||
@IBOutlet weak var webSearchBar: UISearchBar!
|
||||
var webModels: [_OCLogModel] = [_OCLogModel]()
|
||||
var webCacheModels: Array<_OCLogModel>?
|
||||
var webSearchModels: Array<_OCLogModel>?
|
||||
|
||||
|
||||
|
||||
//MARK: - tool
|
||||
//搜索逻辑
|
||||
func searchLogic(_ searchText: String = "") {
|
||||
|
||||
if selectedSegmentIndex == 0
|
||||
{
|
||||
guard let defaultCacheModels = defaultCacheModels else {return}
|
||||
defaultSearchModels = defaultCacheModels
|
||||
|
||||
if searchText == "" {
|
||||
defaultModels = defaultCacheModels
|
||||
} else {
|
||||
guard let defaultSearchModels = defaultSearchModels else {return}
|
||||
|
||||
for _ in defaultSearchModels {
|
||||
if let index = self.defaultSearchModels?.firstIndex(where: { (model) -> Bool in
|
||||
return !model.content.lowercased().contains(searchText.lowercased())//忽略大小写
|
||||
}) {
|
||||
self.defaultSearchModels?.remove(at: index)
|
||||
}
|
||||
}
|
||||
defaultModels = self.defaultSearchModels ?? []
|
||||
}
|
||||
}
|
||||
else if selectedSegmentIndex == 1
|
||||
{
|
||||
guard let rnCacheModels = rnCacheModels else {return}
|
||||
rnSearchModels = rnCacheModels
|
||||
|
||||
if searchText == "" {
|
||||
rnModels = rnCacheModels
|
||||
} else {
|
||||
guard let rnSearchModels = rnSearchModels else {return}
|
||||
|
||||
for _ in rnSearchModels {
|
||||
if let index = self.rnSearchModels?.firstIndex(where: { (model) -> Bool in
|
||||
return !model.content.lowercased().contains(searchText.lowercased())//忽略大小写
|
||||
}) {
|
||||
self.rnSearchModels?.remove(at: index)
|
||||
}
|
||||
}
|
||||
rnModels = self.rnSearchModels ?? []
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
guard let webCacheModels = webCacheModels else {return}
|
||||
webSearchModels = webCacheModels
|
||||
|
||||
if searchText == "" {
|
||||
webModels = webCacheModels
|
||||
} else {
|
||||
guard let webSearchModels = webSearchModels else {return}
|
||||
|
||||
for _ in webSearchModels {
|
||||
if let index = self.webSearchModels?.firstIndex(where: { (model) -> Bool in
|
||||
return !model.content.lowercased().contains(searchText.lowercased())//忽略大小写
|
||||
}) {
|
||||
self.webSearchModels?.remove(at: index)
|
||||
}
|
||||
}
|
||||
webModels = self.webSearchModels ?? []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - private
|
||||
func reloadLogs(needScrollToEnd: Bool = false, needReloadData: Bool = true) {
|
||||
|
||||
if selectedSegmentIndex == 0
|
||||
{
|
||||
if defaultReloadDataFinish == false {return}
|
||||
|
||||
if defaultSearchBar.isHidden != false ||
|
||||
rnSearchBar.isHidden != true ||
|
||||
webSearchBar.isHidden != true {
|
||||
defaultSearchBar.isHidden = false
|
||||
rnSearchBar.isHidden = true
|
||||
webSearchBar.isHidden = true
|
||||
}
|
||||
|
||||
if defaultTableView.isHidden != false ||
|
||||
rnTableView.isHidden != true ||
|
||||
webTableView.isHidden != true {
|
||||
defaultTableView.isHidden = false
|
||||
rnTableView.isHidden = true
|
||||
webTableView.isHidden = true
|
||||
}
|
||||
|
||||
if needReloadData == false && defaultModels.count > 0 {return}
|
||||
|
||||
if let arr = _OCLogStoreManager.shared()?.normalLogArray {
|
||||
defaultModels = arr as! [_OCLogModel]
|
||||
}
|
||||
|
||||
self.defaultCacheModels = self.defaultModels
|
||||
|
||||
self.searchLogic(CocoaDebugSettings.shared.logSearchWordNormal ?? "")
|
||||
|
||||
// dispatch_main_async_safe { [weak self] in
|
||||
self.defaultReloadDataFinish = false
|
||||
self.defaultTableView.reloadData {
|
||||
self.defaultReloadDataFinish = true
|
||||
}
|
||||
|
||||
if needScrollToEnd == false {return}
|
||||
|
||||
//table下滑到底部
|
||||
// guard let count = self.defaultModels.count else {return}
|
||||
if self.defaultModels.count > 0 {
|
||||
// guard let firstInDefault = self.firstInDefault else {return}
|
||||
self.defaultTableView.tableViewScrollToBottom(animated: !firstInDefault)
|
||||
self.firstInDefault = false
|
||||
}
|
||||
// }
|
||||
}
|
||||
else if selectedSegmentIndex == 1
|
||||
{
|
||||
if rnReloadDataFinish == false {return}
|
||||
|
||||
if defaultSearchBar.isHidden != true ||
|
||||
rnSearchBar.isHidden != false ||
|
||||
webSearchBar.isHidden != true {
|
||||
defaultSearchBar.isHidden = true
|
||||
rnSearchBar.isHidden = false
|
||||
webSearchBar.isHidden = true
|
||||
}
|
||||
|
||||
if defaultTableView.isHidden != true ||
|
||||
rnTableView.isHidden != false ||
|
||||
webTableView.isHidden != true {
|
||||
defaultTableView.isHidden = true
|
||||
rnTableView.isHidden = false
|
||||
webTableView.isHidden = true
|
||||
}
|
||||
|
||||
if needReloadData == false && rnModels.count > 0 {return}
|
||||
|
||||
if let arr = _OCLogStoreManager.shared()?.rnLogArray {
|
||||
rnModels = arr as! [_OCLogModel]
|
||||
}
|
||||
|
||||
self.rnCacheModels = self.rnModels
|
||||
|
||||
self.searchLogic(CocoaDebugSettings.shared.logSearchWordRN ?? "")
|
||||
|
||||
// dispatch_main_async_safe { [weak self] in
|
||||
self.rnReloadDataFinish = false
|
||||
self.rnTableView.reloadData {
|
||||
self.rnReloadDataFinish = true
|
||||
}
|
||||
|
||||
if needScrollToEnd == false {return}
|
||||
|
||||
//table下滑到底部
|
||||
// guard let count = self.rnModels.count else {return}
|
||||
if self.rnModels.count > 0 {
|
||||
// guard let firstInRN = self.firstInRN else {return}
|
||||
self.rnTableView.tableViewScrollToBottom(animated: !firstInRN)
|
||||
self.firstInRN = false
|
||||
}
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
if webReloadDataFinish == false {return}
|
||||
|
||||
if defaultSearchBar.isHidden != true ||
|
||||
rnSearchBar.isHidden != true ||
|
||||
webSearchBar.isHidden != false {
|
||||
defaultSearchBar.isHidden = true
|
||||
rnSearchBar.isHidden = true
|
||||
webSearchBar.isHidden = false
|
||||
}
|
||||
|
||||
if defaultTableView.isHidden != true ||
|
||||
rnTableView.isHidden != true ||
|
||||
webTableView.isHidden != false {
|
||||
defaultTableView.isHidden = true
|
||||
rnTableView.isHidden = true
|
||||
webTableView.isHidden = false
|
||||
}
|
||||
|
||||
if needReloadData == false && webModels.count > 0 {return}
|
||||
|
||||
if let arr = _OCLogStoreManager.shared().webLogArray {
|
||||
webModels = arr as! [_OCLogModel]
|
||||
}
|
||||
|
||||
self.webCacheModels = self.webModels
|
||||
|
||||
self.searchLogic(CocoaDebugSettings.shared.logSearchWordWeb ?? "")
|
||||
|
||||
// dispatch_main_async_safe { [weak self] in
|
||||
self.webReloadDataFinish = false
|
||||
self.webTableView.reloadData {
|
||||
self.webReloadDataFinish = true
|
||||
}
|
||||
|
||||
if needScrollToEnd == false {return}
|
||||
|
||||
//table下滑到底部
|
||||
// guard let count = self.webModels.count else {return}
|
||||
if self.webModels.count > 0 {
|
||||
// guard let firstInWeb = self.firstInWeb else {return}
|
||||
self.webTableView.tableViewScrollToBottom(animated: !firstInWeb)
|
||||
self.firstInWeb = false
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//MARK: - init
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
let tap = UITapGestureRecognizer.init(target: self, action: #selector(didTapView))
|
||||
tap.cancelsTouchesInView = false
|
||||
view.addGestureRecognizer(tap)
|
||||
|
||||
|
||||
deleteItem.tintColor = Color.mainGreen
|
||||
segmentedControl.tintColor = Color.mainGreen
|
||||
|
||||
if UIScreen.main.bounds.size.width == 320 {
|
||||
segmentedControl.setTitleTextAttributes([NSAttributedString.Key.font: UIFont.systemFont(ofSize: 11)], for: .normal)
|
||||
} else {
|
||||
segmentedControl.setTitleTextAttributes([NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13)], for: .normal)
|
||||
}
|
||||
|
||||
//notification
|
||||
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "refreshLogs_CocoaDebug"), object: nil, queue: OperationQueue.main) { [weak self] _ in
|
||||
self?.refreshLogs_notification()
|
||||
}
|
||||
|
||||
|
||||
defaultTableView.tableFooterView = UIView()
|
||||
defaultTableView.delegate = self
|
||||
defaultTableView.dataSource = self
|
||||
// defaultTableView.rowHeight = UITableViewAutomaticDimension
|
||||
defaultSearchBar.delegate = self
|
||||
defaultSearchBar.text = CocoaDebugSettings.shared.logSearchWordNormal
|
||||
defaultSearchBar.isHidden = true
|
||||
//抖动bug
|
||||
defaultTableView.estimatedRowHeight = 0
|
||||
defaultTableView.estimatedSectionHeaderHeight = 0
|
||||
defaultTableView.estimatedSectionFooterHeight = 0
|
||||
|
||||
|
||||
|
||||
rnTableView.tableFooterView = UIView()
|
||||
rnTableView.delegate = self
|
||||
rnTableView.dataSource = self
|
||||
// rnTableView.rowHeight = UITableViewAutomaticDimension
|
||||
rnSearchBar.delegate = self
|
||||
rnSearchBar.text = CocoaDebugSettings.shared.logSearchWordRN
|
||||
rnSearchBar.isHidden = true
|
||||
//抖动bug
|
||||
rnTableView.estimatedRowHeight = 0
|
||||
rnTableView.estimatedSectionHeaderHeight = 0
|
||||
rnTableView.estimatedSectionFooterHeight = 0
|
||||
|
||||
|
||||
|
||||
webTableView.tableFooterView = UIView()
|
||||
webTableView.delegate = self
|
||||
webTableView.dataSource = self
|
||||
// webTableView.rowHeight = UITableViewAutomaticDimension
|
||||
webSearchBar.delegate = self
|
||||
webSearchBar.text = CocoaDebugSettings.shared.logSearchWordWeb
|
||||
webSearchBar.isHidden = true
|
||||
//抖动bug
|
||||
webTableView.estimatedRowHeight = 0
|
||||
webTableView.estimatedSectionHeaderHeight = 0
|
||||
webTableView.estimatedSectionFooterHeight = 0
|
||||
|
||||
|
||||
|
||||
//segmentedControl
|
||||
selectedSegmentIndex = CocoaDebugSettings.shared.logSelectIndex
|
||||
segmentedControl.selectedSegmentIndex = selectedSegmentIndex
|
||||
|
||||
if selectedSegmentIndex == 0 {
|
||||
selectedSegment_0 = true
|
||||
} else if selectedSegmentIndex == 1 {
|
||||
selectedSegment_1 = true
|
||||
} else {
|
||||
selectedSegment_2 = true
|
||||
}
|
||||
|
||||
reloadLogs(needScrollToEnd: true, needReloadData: true)
|
||||
|
||||
|
||||
|
||||
//hide searchBar icon
|
||||
let textFieldInsideSearchBar = defaultSearchBar.value(forKey: "searchField") as! UITextField
|
||||
textFieldInsideSearchBar.leftViewMode = .never
|
||||
textFieldInsideSearchBar.leftView = nil
|
||||
textFieldInsideSearchBar.backgroundColor = .white
|
||||
textFieldInsideSearchBar.returnKeyType = .default
|
||||
|
||||
let textFieldInsideSearchBar2 = rnSearchBar.value(forKey: "searchField") as! UITextField
|
||||
textFieldInsideSearchBar2.leftViewMode = .never
|
||||
textFieldInsideSearchBar2.leftView = nil
|
||||
textFieldInsideSearchBar2.backgroundColor = .white
|
||||
textFieldInsideSearchBar2.returnKeyType = .default
|
||||
|
||||
let textFieldInsideSearchBar3 = webSearchBar.value(forKey: "searchField") as! UITextField
|
||||
textFieldInsideSearchBar3.leftViewMode = .never
|
||||
textFieldInsideSearchBar3.leftView = nil
|
||||
textFieldInsideSearchBar3.backgroundColor = .white
|
||||
textFieldInsideSearchBar3.returnKeyType = .default
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
defaultSearchBar.resignFirstResponder()
|
||||
rnSearchBar.resignFirstResponder()
|
||||
webSearchBar.resignFirstResponder()
|
||||
}
|
||||
|
||||
deinit {
|
||||
//notification
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
|
||||
//MARK: - target action
|
||||
@IBAction func didTapDown(_ sender: Any) {
|
||||
if selectedSegmentIndex == 0
|
||||
{
|
||||
defaultTableView.tableViewScrollToBottom(animated: true)
|
||||
defaultSearchBar.resignFirstResponder()
|
||||
reachEndDefault = true
|
||||
}
|
||||
else if selectedSegmentIndex == 1
|
||||
{
|
||||
rnTableView.tableViewScrollToBottom(animated: true)
|
||||
rnSearchBar.resignFirstResponder()
|
||||
reachEndRN = true
|
||||
}
|
||||
else
|
||||
{
|
||||
webTableView.tableViewScrollToBottom(animated: true)
|
||||
webSearchBar.resignFirstResponder()
|
||||
reachEndWeb = true
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func didTapUp(_ sender: Any) {
|
||||
if selectedSegmentIndex == 0
|
||||
{
|
||||
defaultTableView.tableViewScrollToHeader(animated: true)
|
||||
defaultSearchBar.resignFirstResponder()
|
||||
reachEndDefault = false
|
||||
}
|
||||
else if selectedSegmentIndex == 1
|
||||
{
|
||||
rnTableView.tableViewScrollToHeader(animated: true)
|
||||
rnSearchBar.resignFirstResponder()
|
||||
reachEndRN = false
|
||||
}
|
||||
else
|
||||
{
|
||||
webTableView.tableViewScrollToHeader(animated: true)
|
||||
webSearchBar.resignFirstResponder()
|
||||
reachEndWeb = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@IBAction func resetLogs(_ sender: Any) {
|
||||
if selectedSegmentIndex == 0
|
||||
{
|
||||
defaultModels = []
|
||||
defaultCacheModels = []
|
||||
// defaultSearchBar.text = nil
|
||||
defaultSearchBar.resignFirstResponder()
|
||||
// CocoaDebugSettings.shared.logSearchWordNormal = nil
|
||||
|
||||
_OCLogStoreManager.shared()?.resetNormalLogs()
|
||||
|
||||
// dispatch_main_async_safe { [weak self] in
|
||||
self.defaultTableView.reloadData()
|
||||
// }
|
||||
}
|
||||
else if selectedSegmentIndex == 1
|
||||
{
|
||||
rnModels = []
|
||||
rnCacheModels = []
|
||||
// rnSearchBar.text = nil
|
||||
rnSearchBar.resignFirstResponder()
|
||||
// CocoaDebugSettings.shared.logSearchWordRN = nil
|
||||
|
||||
_OCLogStoreManager.shared()?.resetRNLogs()
|
||||
|
||||
// dispatch_main_async_safe { [weak self] in
|
||||
self.rnTableView.reloadData()
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
webModels = []
|
||||
webCacheModels = []
|
||||
// webSearchBar.text = nil
|
||||
webSearchBar.resignFirstResponder()
|
||||
// CocoaDebugSettings.shared.logSearchWordWeb = nil
|
||||
|
||||
_OCLogStoreManager.shared().resetWebLogs()
|
||||
|
||||
// dispatch_main_async_safe { [weak self] in
|
||||
self.webTableView.reloadData()
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func segmentedControlAction(_ sender: UISegmentedControl) {
|
||||
selectedSegmentIndex = segmentedControl.selectedSegmentIndex
|
||||
CocoaDebugSettings.shared.logSelectIndex = selectedSegmentIndex
|
||||
|
||||
if selectedSegmentIndex == 0 {
|
||||
rnSearchBar.resignFirstResponder()
|
||||
webSearchBar.resignFirstResponder()
|
||||
} else if selectedSegmentIndex == 1 {
|
||||
defaultSearchBar.resignFirstResponder()
|
||||
webSearchBar.resignFirstResponder()
|
||||
} else {
|
||||
defaultSearchBar.resignFirstResponder()
|
||||
rnSearchBar.resignFirstResponder()
|
||||
}
|
||||
|
||||
if selectedSegmentIndex == 0 && selectedSegment_0 == false {
|
||||
selectedSegment_0 = true
|
||||
reloadLogs(needScrollToEnd: true, needReloadData: true)
|
||||
return
|
||||
}
|
||||
|
||||
if selectedSegmentIndex == 1 && selectedSegment_1 == false {
|
||||
selectedSegment_1 = true
|
||||
reloadLogs(needScrollToEnd: true, needReloadData: true)
|
||||
return
|
||||
}
|
||||
|
||||
if selectedSegmentIndex == 2 && selectedSegment_2 == false {
|
||||
selectedSegment_2 = true
|
||||
reloadLogs(needScrollToEnd: true, needReloadData: true)
|
||||
return
|
||||
}
|
||||
|
||||
reloadLogs(needScrollToEnd: false, needReloadData: false)
|
||||
}
|
||||
|
||||
@objc func didTapView() {
|
||||
if selectedSegmentIndex == 0 {
|
||||
defaultSearchBar.resignFirstResponder()
|
||||
} else if selectedSegmentIndex == 1 {
|
||||
rnSearchBar.resignFirstResponder()
|
||||
} else {
|
||||
webSearchBar.resignFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//MARK: - notification
|
||||
@objc func refreshLogs_notification() {
|
||||
if self.selectedSegmentIndex == 0 {
|
||||
self.reloadLogs(needScrollToEnd: self.reachEndDefault , needReloadData: true)
|
||||
} else if self.selectedSegmentIndex == 1 {
|
||||
self.reloadLogs(needScrollToEnd: self.reachEndRN , needReloadData: true)
|
||||
} else {
|
||||
self.reloadLogs(needScrollToEnd: self.reachEndWeb , needReloadData: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UITableViewDataSource
|
||||
extension LogViewController: UITableViewDataSource {
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if tableView == defaultTableView {
|
||||
return defaultModels.count
|
||||
} else if tableView == rnTableView {
|
||||
return rnModels.count
|
||||
} else {
|
||||
return webModels.count
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "LogCell", for: indexPath) as! LogCell
|
||||
|
||||
if tableView == defaultTableView
|
||||
{
|
||||
cell.model = defaultModels[indexPath.row]
|
||||
}
|
||||
else if tableView == rnTableView
|
||||
{
|
||||
cell.model = rnModels[indexPath.row]
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.model = webModels[indexPath.row]
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UITableViewDelegate
|
||||
extension LogViewController: UITableViewDelegate {
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
|
||||
var logTitleString = ""
|
||||
var models_: [_OCLogModel]?
|
||||
|
||||
if tableView == defaultTableView
|
||||
{
|
||||
defaultSearchBar.resignFirstResponder()
|
||||
reachEndDefault = false
|
||||
logTitleString = "Log"
|
||||
models_ = defaultModels
|
||||
}
|
||||
else if tableView == rnTableView
|
||||
{
|
||||
rnSearchBar.resignFirstResponder()
|
||||
reachEndRN = false
|
||||
logTitleString = "RN"
|
||||
models_ = rnModels
|
||||
}
|
||||
else
|
||||
{
|
||||
webSearchBar.resignFirstResponder()
|
||||
reachEndWeb = false
|
||||
logTitleString = "Web"
|
||||
models_ = webModels
|
||||
}
|
||||
|
||||
//
|
||||
guard let models = models_ else {return}
|
||||
|
||||
let vc = JsonViewController.instanceFromStoryBoard()
|
||||
vc.editType = .log
|
||||
vc.logTitleString = logTitleString
|
||||
vc.logModels = models
|
||||
vc.logModel = models[indexPath.row]
|
||||
|
||||
navigationController?.pushViewController(vc, animated: true)
|
||||
|
||||
vc.justCancelCallback = {
|
||||
tableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
|
||||
var model: _OCLogModel
|
||||
|
||||
if tableView == defaultTableView {
|
||||
model = defaultModels[indexPath.row]
|
||||
|
||||
} else if tableView == rnTableView {
|
||||
model = rnModels[indexPath.row]
|
||||
|
||||
} else {
|
||||
model = webModels[indexPath.row]
|
||||
}
|
||||
|
||||
if let height = model.str?.height(with: UIFont.boldSystemFont(ofSize: 12), constraintToWidth: UIScreen.main.bounds.size.width) {
|
||||
return height + 10;
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UIScrollViewDelegate
|
||||
extension LogViewController: UIScrollViewDelegate {
|
||||
|
||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
if scrollView == defaultTableView {
|
||||
defaultSearchBar.resignFirstResponder()
|
||||
reachEndDefault = false
|
||||
} else if scrollView == rnTableView {
|
||||
rnSearchBar.resignFirstResponder()
|
||||
reachEndRN = false
|
||||
} else {
|
||||
webSearchBar.resignFirstResponder()
|
||||
reachEndWeb = false
|
||||
}
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if (scrollView.contentOffset.y + 1) >= (scrollView.contentSize.height - scrollView.frame.size.height) {
|
||||
//bottom reached
|
||||
if scrollView == defaultTableView {
|
||||
reachEndDefault = true
|
||||
} else if scrollView == rnTableView {
|
||||
reachEndRN = true
|
||||
} else {
|
||||
reachEndWeb = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UISearchBarDelegate
|
||||
extension LogViewController: UISearchBarDelegate {
|
||||
|
||||
func searchBarSearchButtonClicked(_ searchBar: UISearchBar)
|
||||
{
|
||||
searchBar.resignFirstResponder()
|
||||
}
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
|
||||
{
|
||||
if searchBar == defaultSearchBar
|
||||
{
|
||||
CocoaDebugSettings.shared.logSearchWordNormal = searchText
|
||||
searchLogic(searchText)
|
||||
|
||||
// dispatch_main_async_safe { [weak self] in
|
||||
self.defaultTableView.reloadData()
|
||||
// }
|
||||
}
|
||||
else if searchBar == rnSearchBar
|
||||
{
|
||||
CocoaDebugSettings.shared.logSearchWordRN = searchText
|
||||
searchLogic(searchText)
|
||||
|
||||
// dispatch_main_async_safe { [weak self] in
|
||||
self.rnTableView.reloadData()
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
CocoaDebugSettings.shared.logSearchWordWeb = searchText
|
||||
searchLogic(searchText)
|
||||
|
||||
// dispatch_main_async_safe { [weak self] in
|
||||
self.webTableView.reloadData()
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
96
Pods/CocoaDebug/Sources/Logs/_NSLogHook.m
generated
@@ -1,96 +0,0 @@
|
||||
//
|
||||
// _NSLogHook.m
|
||||
// Example_Swift
|
||||
//
|
||||
// Created by man 7/26/19.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "_OCLogHelper.h"
|
||||
#import "_fishhook.h"
|
||||
//#import <React/RCTLog.h>
|
||||
//#import "RCTLog.h"
|
||||
|
||||
@interface _NSLogHook : NSObject
|
||||
|
||||
@end
|
||||
|
||||
@implementation _NSLogHook
|
||||
|
||||
static void (*_original_nslog)(NSString *format, ...);
|
||||
|
||||
#pragma mark - hooks
|
||||
void cocoadebug_nslog(NSString *format, ...)
|
||||
{
|
||||
if (![format isKindOfClass:[NSString class]]) {return;}
|
||||
|
||||
va_list vl;
|
||||
va_start(vl, format);
|
||||
NSString *str = [[NSString alloc] initWithFormat:format arguments:vl];
|
||||
|
||||
@try {
|
||||
_original_nslog(str);
|
||||
} @catch(NSException *exception) {
|
||||
|
||||
} @finally {
|
||||
|
||||
}
|
||||
|
||||
@try {
|
||||
[_OCLogHelper.shared handleLogWithFile:@"" function:@"" line:999999999 message:str color:[UIColor whiteColor] type:CocoaDebugToolTypeNone];
|
||||
} @catch(NSException *exception) {
|
||||
|
||||
} @finally {
|
||||
|
||||
}
|
||||
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
#pragma mark - load
|
||||
+ (void)load
|
||||
{
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"enableLogMonitoring_CocoaDebug"]) {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
struct rebinding nslog_rebinding = {"NSLog",cocoadebug_nslog,(void*)&_original_nslog};
|
||||
rebind_symbols((struct rebinding[1]){nslog_rebinding}, 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//#pragma mark - RN
|
||||
//void _RCTLogJavaScriptInternal(RCTLogLevel level, NSString *message)
|
||||
//{
|
||||
// if ([[NSUserDefaults standardUserDefaults] boolForKey:@"enableRNMonitoring_CocoaDebug"]) {return;}
|
||||
// if (![message isKindOfClass:[NSString class]]) {return;}
|
||||
//// if (level != RCTLogLevelError && level != RCTLogLevelInfo) {return;}
|
||||
//
|
||||
// NSString *levelStr = @"";
|
||||
//
|
||||
// switch (level) {
|
||||
//// case RCTLogLevelTrace:
|
||||
//// levelStr = @"[RCTLogTrace]";
|
||||
//// break;
|
||||
// case RCTLogLevelInfo:
|
||||
// levelStr = @"[RCTLogInfo]";
|
||||
// break;
|
||||
// case RCTLogLevelWarning:
|
||||
// levelStr = @"[RCTLogWarn]";
|
||||
// break;
|
||||
// case RCTLogLevelError:
|
||||
// levelStr = @"[RCTLogError]";
|
||||
// break;
|
||||
//// case RCTLogLevelFatal:
|
||||
//// levelStr = @"[RCTLogFatal]";
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// [_OCLogHelper.shared handleLogWithFile:[NSString stringWithFormat:@"%@\n", levelStr] function:@"" line:-1 message:message color:[UIColor whiteColor] type:CocoaDebugToolTypeRN];
|
||||
//}
|
||||
|
||||
@end
|
||||
|
||||
21
Pods/CocoaDebug/Sources/Logs/_OCLogHelper.h
generated
@@ -1,21 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "_OCLogModel.h"
|
||||
|
||||
@interface _OCLogHelper : NSObject
|
||||
|
||||
//@property (nonatomic, assign) BOOL enable;
|
||||
|
||||
+ (instancetype)shared;
|
||||
|
||||
- (void)handleLogWithFile:(NSString *)file function:(NSString *)function line:(NSInteger)line message:(NSString *)message color:(UIColor *)color type:(CocoaDebugToolType)type;
|
||||
|
||||
@end
|
||||
91
Pods/CocoaDebug/Sources/Logs/_OCLogHelper.m
generated
@@ -1,91 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_OCLogHelper.h"
|
||||
#import "_OCLogStoreManager.h"
|
||||
|
||||
@implementation _OCLogHelper
|
||||
|
||||
+ (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.enable = YES;
|
||||
// }
|
||||
// return self;
|
||||
//}
|
||||
|
||||
- (NSString *)parseFileInfo:(NSString *)file function:(NSString *)function line:(NSInteger)line
|
||||
{
|
||||
if (![file isKindOfClass:[NSString class]] || ![function isKindOfClass:[NSString class]]) {
|
||||
return @"\n";
|
||||
}
|
||||
|
||||
if ([file isEqualToString:@"XXX"] && [function isEqualToString:@"XXX"] && line == 1) {
|
||||
return @"XXX|XXX|1";
|
||||
}
|
||||
|
||||
if (line == 0) { //web
|
||||
NSString *fileName = [[file componentsSeparatedByString:@"/"] lastObject] ?: @"";
|
||||
return [NSString stringWithFormat:@"%@ %@\n", fileName, function];
|
||||
}
|
||||
|
||||
if (line == 999999999) { //nslog
|
||||
NSString *fileName = [[file componentsSeparatedByString:@"/"] lastObject] ?: @"";
|
||||
return [NSString stringWithFormat:@"%@ %@\n", fileName, function];
|
||||
}
|
||||
|
||||
if (line == -1) { //RN
|
||||
return file;
|
||||
}
|
||||
|
||||
NSString *fileName = [[file componentsSeparatedByString:@"/"] lastObject] ?: @"";
|
||||
return [NSString stringWithFormat:@"%@[%ld]%@\n", fileName, (long)line, function];
|
||||
}
|
||||
|
||||
- (void)handleLogWithFile:(NSString *)file function:(NSString *)function line:(NSInteger)line message:(NSString *)message color:(UIColor *)color type:(CocoaDebugToolType)type
|
||||
{
|
||||
// if (!self.enable && type != CocoaDebugToolTypeRN) {return;}
|
||||
if (!file || !function || !message || !color) {return;}
|
||||
|
||||
//1.
|
||||
NSString *fileInfo = [self parseFileInfo:file function:function line:line];
|
||||
|
||||
//2.
|
||||
_OCLogModel *newLog = [[_OCLogModel alloc] initWithContent:message color:color fileInfo:fileInfo isTag:NO type:type];
|
||||
|
||||
// if (line == 0 && ![fileInfo isEqualToString:@"XXX|XXX|1"]) {
|
||||
// newLog.logType = CocoaDebugLogType...
|
||||
// }
|
||||
|
||||
if (type == CocoaDebugToolTypeRN) {
|
||||
newLog.logType = CocoaDebugLogTypeRN;
|
||||
}
|
||||
|
||||
if ([file isEqualToString:@"[WKWebView]"]) {
|
||||
newLog.logType = CocoaDebugLogTypeWeb;
|
||||
}
|
||||
|
||||
[[_OCLogStoreManager shared] addLog:newLog];
|
||||
|
||||
//3.
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"refreshLogs_CocoaDebug" object:nil userInfo:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
44
Pods/CocoaDebug/Sources/Logs/_OCLogModel.h
generated
@@ -1,44 +0,0 @@
|
||||
//
|
||||
// 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(NSInteger, CocoaDebugLogType) {
|
||||
CocoaDebugLogTypeNormal = 0,
|
||||
CocoaDebugLogTypeRN,
|
||||
CocoaDebugLogTypeWeb
|
||||
};
|
||||
|
||||
typedef NS_ENUM (NSInteger, CocoaDebugToolType) {
|
||||
CocoaDebugToolTypeNone,
|
||||
CocoaDebugToolTypeRN,
|
||||
CocoaDebugToolTypeJson,
|
||||
CocoaDebugToolTypeProtobuf
|
||||
};
|
||||
|
||||
@interface _OCLogModel : NSObject
|
||||
|
||||
@property (nonatomic, copy) NSData *contentData;
|
||||
|
||||
@property (nonatomic, copy) NSString *Id;
|
||||
@property (nonatomic, copy) NSString *fileInfo;
|
||||
@property (nonatomic, copy) NSString *content;
|
||||
@property (nonatomic, strong) NSDate *date;
|
||||
@property (nonatomic, strong) UIColor *color;
|
||||
|
||||
@property (nonatomic, assign) BOOL isTag;
|
||||
@property (nonatomic, assign) BOOL isSelected;
|
||||
@property (nonatomic, copy) NSString *str;
|
||||
@property (nonatomic, copy) NSAttributedString *attr;
|
||||
|
||||
@property (nonatomic, assign) CocoaDebugLogType logType;
|
||||
|
||||
- (instancetype)initWithContent:(NSString *)content color:(UIColor *)color fileInfo:(NSString *)fileInfo isTag:(BOOL)isTag type:(CocoaDebugToolType)type;
|
||||
|
||||
@end
|
||||
110
Pods/CocoaDebug/Sources/Logs/_OCLogModel.m
generated
@@ -1,110 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_OCLogModel.h"
|
||||
#import "_OCLoggerFormat.h"
|
||||
#import "_NetworkHelper.h"
|
||||
|
||||
@implementation _OCLogModel
|
||||
|
||||
- (instancetype)initWithContent:(NSString *)content color:(UIColor *)color fileInfo:(NSString *)fileInfo isTag:(BOOL)isTag type:(CocoaDebugToolType)type
|
||||
{
|
||||
if (self = [super init]) {
|
||||
|
||||
if ([fileInfo isEqualToString:@"XXX|XXX|1"]) {
|
||||
if (type == CocoaDebugToolTypeProtobuf) {
|
||||
fileInfo = @"Protobuf\n";
|
||||
} else {
|
||||
fileInfo = @"\n";
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
if (type == CocoaDebugToolTypeNone) {
|
||||
if ([fileInfo isEqualToString:@" \n"]) {//nslog
|
||||
fileInfo = @"NSLog\n";
|
||||
} else if ([fileInfo isEqualToString:@"\n"]) {//color
|
||||
fileInfo = @"\n";
|
||||
}
|
||||
}
|
||||
|
||||
//RN (java script)
|
||||
if ([fileInfo isEqualToString:@"[RCTLogError]\n"]) {
|
||||
fileInfo = @"[error]\n";
|
||||
} else if ([fileInfo isEqualToString:@"[RCTLogInfo]\n"]) {
|
||||
fileInfo = @"[log]\n";
|
||||
}
|
||||
|
||||
//
|
||||
self.Id = [[NSUUID UUID] UUIDString];
|
||||
self.fileInfo = fileInfo;
|
||||
self.date = [NSDate date];
|
||||
self.color = color;
|
||||
self.isTag = isTag;
|
||||
|
||||
if ([content isKindOfClass:[NSString class]]) {
|
||||
self.contentData = [content dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
//避免日志数量过多导致卡顿
|
||||
if (content.length > 1000) {
|
||||
content = [content substringToIndex:1000];
|
||||
}
|
||||
self.content = content;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NSInteger startIndex = 0;
|
||||
NSInteger lenghtDate = 0;
|
||||
NSString *stringContent = @"";
|
||||
|
||||
stringContent = [stringContent stringByAppendingFormat:@"[%@]", [_OCLoggerFormat formatDate:self.date]];
|
||||
lenghtDate = [stringContent length];
|
||||
startIndex = [stringContent length];
|
||||
|
||||
if (self.fileInfo) {
|
||||
stringContent = [stringContent stringByAppendingFormat:@"%@%@", self.fileInfo, self.content];
|
||||
} else {
|
||||
stringContent = [stringContent stringByAppendingFormat:@"%@", self.content];
|
||||
}
|
||||
|
||||
NSMutableAttributedString *attstr = [[NSMutableAttributedString alloc] initWithString:stringContent];
|
||||
[attstr addAttribute:NSForegroundColorAttributeName value:self.color range:NSMakeRange(0, [stringContent length])];
|
||||
|
||||
NSRange range = NSMakeRange(0, lenghtDate);
|
||||
[attstr addAttribute:NSForegroundColorAttributeName value: [[_NetworkHelper shared] mainColor] range: range];
|
||||
[attstr addAttribute:NSFontAttributeName value: [UIFont boldSystemFontOfSize:12] range: range];
|
||||
|
||||
NSRange range2 = NSMakeRange(startIndex, self.fileInfo.length);
|
||||
|
||||
if ([self.fileInfo isEqualToString:@"[error]\n"]) {
|
||||
[attstr addAttribute: NSForegroundColorAttributeName value: [UIColor systemRedColor] range: range2];
|
||||
} else {
|
||||
[attstr addAttribute: NSForegroundColorAttributeName value: [UIColor systemGrayColor] range: range2];
|
||||
}
|
||||
|
||||
[attstr addAttribute: NSFontAttributeName value: [UIFont boldSystemFontOfSize:12] range: range2];
|
||||
|
||||
|
||||
//换行
|
||||
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
|
||||
paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping;
|
||||
|
||||
NSRange rang3 = NSMakeRange(0, attstr.length);
|
||||
[attstr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:rang3];
|
||||
|
||||
|
||||
//
|
||||
self.str = stringContent;
|
||||
self.attr = [attstr copy];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
28
Pods/CocoaDebug/Sources/Logs/_OCLogStoreManager.h
generated
@@ -1,28 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "_OCLogModel.h"
|
||||
|
||||
@interface _OCLogStoreManager : NSObject
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray<_OCLogModel *> *normalLogArray;
|
||||
@property (nonatomic, strong) NSMutableArray<_OCLogModel *> *rnLogArray;
|
||||
@property (nonatomic, strong) NSMutableArray<_OCLogModel *> *webLogArray;
|
||||
|
||||
+ (instancetype)shared;
|
||||
|
||||
- (void)addLog:(_OCLogModel *)log;
|
||||
- (void)removeLog:(_OCLogModel *)log;
|
||||
|
||||
- (void)resetNormalLogs;
|
||||
- (void)resetRNLogs;
|
||||
- (void)resetWebLogs;
|
||||
|
||||
@end
|
||||
|
||||
127
Pods/CocoaDebug/Sources/Logs/_OCLogStoreManager.m
generated
@@ -1,127 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_OCLogStoreManager.h"
|
||||
#import "_NetworkHelper.h"
|
||||
|
||||
@implementation _OCLogStoreManager
|
||||
|
||||
+ (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.normalLogArray = [NSMutableArray arrayWithCapacity:1000 + 100];
|
||||
self.rnLogArray = [NSMutableArray arrayWithCapacity:1000 + 100];
|
||||
self.webLogArray = [NSMutableArray arrayWithCapacity:1000 + 100];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addLog:(_OCLogModel *)log
|
||||
{
|
||||
if (![log.content isKindOfClass:[NSString class]]) {return;}
|
||||
|
||||
//log过滤, 忽略大小写
|
||||
for (NSString *prefixStr in [_NetworkHelper shared].onlyPrefixLogs) {
|
||||
if (![log.content hasPrefix:prefixStr]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
//log过滤, 忽略大小写
|
||||
for (NSString *prefixStr in [_NetworkHelper shared].ignoredPrefixLogs) {
|
||||
if ([log.content hasPrefix:prefixStr]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
if (log.logType == CocoaDebugLogTypeNormal)
|
||||
{
|
||||
//normal
|
||||
if ([self.normalLogArray count] >= 1000) {
|
||||
if (self.normalLogArray.count > 0) {
|
||||
[self.normalLogArray removeObjectAtIndex:0];
|
||||
}
|
||||
}
|
||||
|
||||
[self.normalLogArray addObject:log];
|
||||
}
|
||||
else if (log.logType == CocoaDebugLogTypeRN)
|
||||
{
|
||||
//rn
|
||||
if ([self.rnLogArray count] >= 1000) {
|
||||
if (self.rnLogArray.count > 0) {
|
||||
[self.rnLogArray removeObjectAtIndex:0];
|
||||
}
|
||||
}
|
||||
|
||||
[self.rnLogArray addObject:log];
|
||||
}
|
||||
else
|
||||
{
|
||||
//web
|
||||
if ([self.webLogArray count] >= 1000) {
|
||||
if (self.webLogArray.count > 0) {
|
||||
[self.webLogArray removeObjectAtIndex:0];
|
||||
}
|
||||
}
|
||||
|
||||
[self.webLogArray addObject:log];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeLog:(_OCLogModel *)log
|
||||
{
|
||||
if (log.logType == CocoaDebugLogTypeNormal)
|
||||
{
|
||||
//normal
|
||||
[self.normalLogArray removeObject:log];
|
||||
}
|
||||
else if (log.logType == CocoaDebugLogTypeNormal)
|
||||
{
|
||||
//rn
|
||||
[self.rnLogArray removeObject:log];
|
||||
}
|
||||
else
|
||||
{
|
||||
//web
|
||||
[self.webLogArray removeObject:log];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resetNormalLogs
|
||||
{
|
||||
[self.normalLogArray removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)resetRNLogs
|
||||
{
|
||||
[self.rnLogArray removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)resetWebLogs
|
||||
{
|
||||
[self.webLogArray removeAllObjects];
|
||||
}
|
||||
|
||||
@end
|
||||
15
Pods/CocoaDebug/Sources/Logs/_OCLoggerFormat.h
generated
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface _OCLoggerFormat : NSObject
|
||||
|
||||
+ (NSString *)formatDate:(NSDate *)date;
|
||||
|
||||
@end
|
||||
21
Pods/CocoaDebug/Sources/Logs/_OCLoggerFormat.m
generated
@@ -1,21 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_OCLoggerFormat.h"
|
||||
|
||||
@implementation _OCLoggerFormat
|
||||
|
||||
+ (NSString *)formatDate:(NSDate *)date
|
||||
{
|
||||
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
||||
formatter.timeZone = [NSTimeZone systemTimeZone];
|
||||
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
|
||||
return [formatter stringFromDate:date];
|
||||
}
|
||||
|
||||
@end
|
||||
20
Pods/CocoaDebug/Sources/Logs/_ObjcLog.h
generated
@@ -1,20 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface _ObjcLog : NSObject
|
||||
|
||||
+ (void)logWithFile:(const char *)file
|
||||
function:(const char *)function
|
||||
line:(NSUInteger)line
|
||||
color:(UIColor *)color
|
||||
message:(id)format, ...;
|
||||
|
||||
@end
|
||||
57
Pods/CocoaDebug/Sources/Logs/_ObjcLog.m
generated
@@ -1,57 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_ObjcLog.h"
|
||||
#import "_OCLogHelper.h"
|
||||
#import "NSObject+CocoaDebug.h"
|
||||
#import <JavaScriptCore/JavaScriptCore.h>
|
||||
|
||||
@implementation _ObjcLog
|
||||
|
||||
+ (void)logWithFile:(const char *)file
|
||||
function:(const char *)function
|
||||
line:(NSUInteger)line
|
||||
color:(UIColor *)color
|
||||
message:(id)format, ...
|
||||
{
|
||||
if (format)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
if ([format isKindOfClass:[NSString class]])
|
||||
{
|
||||
// NSLogv(format, args);
|
||||
[_OCLogHelper.shared handleLogWithFile:[NSString stringWithUTF8String:file] function:[NSString stringWithUTF8String:function] line:line message:[[NSString alloc] initWithFormat:format arguments:args] color:color type:CocoaDebugToolTypeNone];
|
||||
}
|
||||
else if ([format isKindOfClass:[JSValue class]])
|
||||
{
|
||||
id format_ = [format toString];
|
||||
if ([format_ isEqualToString:@"[object Object]"])
|
||||
{
|
||||
format_ = [format toDictionary];
|
||||
// NSLogv([NSString stringWithFormat:@"%@",format_], args);
|
||||
[_OCLogHelper.shared handleLogWithFile:[NSString stringWithUTF8String:file] function:[NSString stringWithUTF8String:function] line:line message:[NSString stringWithFormat:@"%@",format_] color:color type:CocoaDebugToolTypeNone];
|
||||
}
|
||||
else
|
||||
{
|
||||
// NSLogv([NSString stringWithFormat:@"%@",format], args);
|
||||
[_OCLogHelper.shared handleLogWithFile:[NSString stringWithUTF8String:file] function:[NSString stringWithUTF8String:function] line:line message:[NSString stringWithFormat:@"%@",format] color:color type:CocoaDebugToolTypeNone];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// NSLogv([NSString stringWithFormat:@"%@",format], args);
|
||||
[_OCLogHelper.shared handleLogWithFile:[NSString stringWithUTF8String:file] function:[NSString stringWithUTF8String:function] line:line message:[NSString stringWithFormat:@"%@",format] color:color type:CocoaDebugToolTypeNone];
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
49
Pods/CocoaDebug/Sources/Logs/_SwiftLogHelper.swift
generated
@@ -1,49 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class _SwiftLogHelper: NSObject {
|
||||
|
||||
var enable: Bool = true
|
||||
|
||||
@objc public static let shared = _SwiftLogHelper()
|
||||
private override init() {}
|
||||
|
||||
|
||||
fileprivate func parseFileInfo(file: String?, function: String?, line: Int?) -> String? {
|
||||
guard let file = file, let function = function, let line = line, let fileName = file.components(separatedBy: "/").last else {return nil}
|
||||
return "\(fileName)[\(line)]\(function)\n"
|
||||
}
|
||||
|
||||
|
||||
public func handleLog(file: String?, function: String?, line: Int?, message: Any..., color: UIColor?) {
|
||||
let stringContent = message.reduce("") { result, next -> String in
|
||||
return "\(result)\(result.count > 0 ? " " : "")\(next)"
|
||||
}
|
||||
commonHandleLog(file: file, function: function, line: (line ?? 0), message: stringContent, color: color)
|
||||
}
|
||||
|
||||
|
||||
private func commonHandleLog(file: String?, function: String?, line: Int, message: String, color: UIColor?) {
|
||||
guard enable else {
|
||||
return
|
||||
}
|
||||
|
||||
//1.
|
||||
let fileInfo = parseFileInfo(file: file, function: function, line: line)
|
||||
|
||||
//2.
|
||||
if let newLog = _OCLogModel.init(content: message, color: color, fileInfo: fileInfo, isTag: false, type: .none) {
|
||||
_OCLogStoreManager.shared().addLog(newLog)
|
||||
}
|
||||
|
||||
//3.
|
||||
NotificationCenter.default.post(name: NSNotification.Name("refreshLogs_CocoaDebug"), object: nil, userInfo: nil)
|
||||
}
|
||||
}
|
||||
113
Pods/CocoaDebug/Sources/Monitor/FPSCounter.swift
generated
@@ -1,113 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
/*
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public class FPSCounter: NSObject {
|
||||
|
||||
internal class DisplayLinkProxy: NSObject {
|
||||
|
||||
@objc weak var parentCounter: FPSCounter?
|
||||
|
||||
@objc func updateFromDisplayLink(_ displayLink: CADisplayLink) {
|
||||
parentCounter?.updateFromDisplayLink(displayLink)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
private var displayLink: CADisplayLink
|
||||
private var displayLinkProxy: DisplayLinkProxy
|
||||
|
||||
|
||||
public override init() {
|
||||
self.displayLinkProxy = DisplayLinkProxy()
|
||||
self.displayLink = CADisplayLink(
|
||||
target: self.displayLinkProxy,
|
||||
selector: #selector(DisplayLinkProxy.updateFromDisplayLink(_:))
|
||||
)
|
||||
|
||||
super.init()
|
||||
|
||||
self.displayLinkProxy.parentCounter = self
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.displayLink.invalidate()
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Configuration
|
||||
|
||||
public weak var delegate: FPSCounterDelegate?
|
||||
|
||||
@objc public var notificationDelay: TimeInterval = 1.0
|
||||
|
||||
|
||||
// MARK: - Tracking
|
||||
|
||||
private var runloop: RunLoop?
|
||||
private var mode: RunLoop.Mode?
|
||||
|
||||
|
||||
@objc public func startMonitoring(inRunLoop runloop: RunLoop = .main, mode: RunLoop.Mode = .common) {
|
||||
if CocoaDebugSettings.shared.enableFpsMonitoring == false {return}
|
||||
|
||||
self.stopMonitoring()
|
||||
|
||||
self.runloop = runloop
|
||||
self.mode = mode
|
||||
self.displayLink.add(to: runloop, forMode: mode)
|
||||
}
|
||||
|
||||
|
||||
@objc public func stopMonitoring() {
|
||||
guard let runloop = self.runloop, let mode = self.mode else { return }
|
||||
|
||||
self.displayLink.remove(from: runloop, forMode: mode)
|
||||
self.runloop = nil
|
||||
self.mode = nil
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Handling Frame Updates
|
||||
|
||||
private var lastNotificationTime: CFAbsoluteTime = 0.0
|
||||
private var numberOfFrames = 0
|
||||
|
||||
private func updateFromDisplayLink(_ displayLink: CADisplayLink) {
|
||||
if self.lastNotificationTime == 0.0 {
|
||||
self.lastNotificationTime = CFAbsoluteTimeGetCurrent()
|
||||
return
|
||||
}
|
||||
|
||||
self.numberOfFrames += 1
|
||||
|
||||
let currentTime = CFAbsoluteTimeGetCurrent()
|
||||
let elapsedTime = currentTime - self.lastNotificationTime
|
||||
|
||||
if elapsedTime >= self.notificationDelay {
|
||||
self.notifyUpdateForElapsedTime(elapsedTime)
|
||||
self.lastNotificationTime = 0.0
|
||||
self.numberOfFrames = 0
|
||||
}
|
||||
}
|
||||
|
||||
private func notifyUpdateForElapsedTime(_ elapsedTime: CFAbsoluteTime) {
|
||||
let fps = Int(round(Double(self.numberOfFrames) / elapsedTime))
|
||||
self.delegate?.fpsCounter(self, didUpdateFramesPerSecond: fps)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public protocol FPSCounterDelegate: NSObjectProtocol {
|
||||
func fpsCounter(_ counter: FPSCounter, didUpdateFramesPerSecond fps: Int)
|
||||
}
|
||||
*/
|
||||
25
Pods/CocoaDebug/Sources/Monitor/_BacktraceLogger.h
generated
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/*!
|
||||
* @brief 线程堆栈上下文输出
|
||||
*/
|
||||
@interface _BacktraceLogger : NSObject
|
||||
|
||||
+ (NSString *)cocoadebug_backtraceOfAllThread;
|
||||
+ (NSString *)cocoadebug_backtraceOfMainThread;
|
||||
+ (NSString *)cocoadebug_backtraceOfCurrentThread;
|
||||
+ (NSString *)cocoadebug_backtraceOfNSThread:(NSThread *)thread;
|
||||
|
||||
+ (void)cocoadebug_logMain;
|
||||
+ (void)cocoadebug_logCurrent;
|
||||
+ (void)cocoadebug_logAllThread;
|
||||
|
||||
@end
|
||||
420
Pods/CocoaDebug/Sources/Monitor/_BacktraceLogger.m
generated
@@ -1,420 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_BacktraceLogger.h"
|
||||
#import <mach/mach.h>
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/nlist.h>
|
||||
|
||||
#import "CocoaDebugTool.h"
|
||||
#import "_OCLogHelper.h"
|
||||
|
||||
|
||||
/*!
|
||||
* @brief 适配不同CPU的宏定义
|
||||
*
|
||||
* @thx 代码出自https://github.com/bestswifter/BSBacktraceLogger 加工修改
|
||||
*/
|
||||
#if defined(__arm64__)
|
||||
#define _DETAG_INSTRUCTION_ADDRESS(A) ((A) & ~(3UL))
|
||||
#define COCOADEBUG_THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT
|
||||
#define COCOADEBUG_THREAD_STATE ARM_THREAD_STATE64
|
||||
#define COCOADEBUG_FRAME_POINTER __fp
|
||||
#define COCOADEBUG_STACK_POINTER __sp
|
||||
#define COCOADEBUG_INSTRUCTION_ADDRESS __pc
|
||||
|
||||
#elif defined(__arm__)
|
||||
#define _DETAG_INSTRUCTION_ADDRESS(A) ((A) & ~(1UL))
|
||||
#define COCOADEBUG_THREAD_STATE_COUNT ARM_THREAD_STATE_COUNT
|
||||
#define COCOADEBUG_THREAD_STATE ARM_THREAD_STATE
|
||||
#define COCOADEBUG_FRAME_POINTER __r[7]
|
||||
#define COCOADEBUG_STACK_POINTER __sp
|
||||
#define COCOADEBUG_INSTRUCTION_ADDRESS __pc
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
#define _DETAG_INSTRUCTION_ADDRESS(A) (A)
|
||||
#define COCOADEBUG_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT
|
||||
#define COCOADEBUG_THREAD_STATE x86_THREAD_STATE64
|
||||
#define COCOADEBUG_FRAME_POINTER __rbp
|
||||
#define COCOADEBUG_STACK_POINTER __rsp
|
||||
#define COCOADEBUG_INSTRUCTION_ADDRESS __rip
|
||||
|
||||
#elif defined(__i386__)
|
||||
#define _DETAG_INSTRUCTION_ADDRESS(A) (A)
|
||||
#define COCOADEBUG_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT
|
||||
#define COCOADEBUG_THREAD_STATE x86_THREAD_STATE32
|
||||
#define COCOADEBUG_FRAME_POINTER __ebp
|
||||
#define COCOADEBUG_STACK_POINTER __esp
|
||||
#define COCOADEBUG_INSTRUCTION_ADDRESS __eip
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__LP64__)
|
||||
#define _TRACE_FMT "%-4d%-31s 0x%016lx %s + %lu"
|
||||
#define _POINTER_FMT "0x%016lx"
|
||||
#define _POINTER_SHORT_FMT "0x%lx"
|
||||
#define COCOADEBUG_NLIST struct nlist_64
|
||||
#else
|
||||
#define _TRACE_FMT "%-4d%-31s 0x%08lx %s + %lu"
|
||||
#define _POINTER_FMT "0x%08lx"
|
||||
#define _POINTER_SHORT_FMT "0x%lx"
|
||||
#define COCOADEBUG_NLIST struct nlist
|
||||
|
||||
#endif
|
||||
|
||||
#define _MAX_FRAME_NUMBER 30
|
||||
#define _LOG_SEPERATE printf("\n");
|
||||
#define _FAILED_UINT_PTR_ADDRESS 0
|
||||
#define _CALL_INSTRUCTION_FROM_RETURN_ADDRESS(A) (_DETAG_INSTRUCTION_ADDRESS((A)) - 1)
|
||||
|
||||
|
||||
typedef struct COCOADEBUGStackFrameEntry{
|
||||
const struct COCOADEBUGStackFrameEntry * const previous;
|
||||
const uintptr_t return_address;
|
||||
} COCOADEBUGStackFrameEntry;
|
||||
|
||||
static mach_port_t main_thread_id;
|
||||
|
||||
|
||||
|
||||
@implementation _BacktraceLogger
|
||||
|
||||
+ (void)load {
|
||||
main_thread_id = mach_thread_self();
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Public
|
||||
+ (NSString *)cocoadebug_backtraceOfAllThread {
|
||||
thread_act_array_t threads;
|
||||
mach_msg_type_number_t thread_count = 0;
|
||||
const task_t this_task = mach_task_self();
|
||||
|
||||
kern_return_t kr = task_threads(this_task, &threads, &thread_count);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
return @"Failed to get information of all threads";
|
||||
}
|
||||
NSMutableString * result = @"".mutableCopy;
|
||||
for (int idx = 0; idx < thread_count; idx++) {
|
||||
[result appendString: _cocoadebug_backtraceOfThread(threads[idx])];
|
||||
}
|
||||
return result.copy;
|
||||
}
|
||||
|
||||
+ (NSString *)cocoadebug_backtraceOfMainThread {
|
||||
return [self cocoadebug_backtraceOfNSThread: [NSThread mainThread]];
|
||||
}
|
||||
|
||||
+ (NSString *)cocoadebug_backtraceOfCurrentThread {
|
||||
return [self cocoadebug_backtraceOfNSThread: [NSThread currentThread]];
|
||||
}
|
||||
|
||||
+ (NSString *)cocoadebug_backtraceOfNSThread:(NSThread *)thread {
|
||||
return _cocoadebug_backtraceOfThread(cocoadebug_machThreadFromNSThread(thread));
|
||||
}
|
||||
|
||||
+ (void)cocoadebug_logMain {
|
||||
// _LOG_SEPERATE
|
||||
// NSLog(@"Detected UI Blocking %@", [self cocoadebug_backtraceOfMainThread]);
|
||||
// _LOG_SEPERATE
|
||||
|
||||
[CocoaDebugTool logWithString:[NSString stringWithFormat:@"\nDetected UI Blocking\n%@\n", [self cocoadebug_backtraceOfMainThread]] color:[UIColor redColor]];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"CocoaDebug_Detected_UI_Blocking" object:nil];
|
||||
}
|
||||
|
||||
+ (void)cocoadebug_logCurrent {
|
||||
_LOG_SEPERATE
|
||||
NSLog(@"%@", [self cocoadebug_backtraceOfCurrentThread]);
|
||||
_LOG_SEPERATE
|
||||
}
|
||||
|
||||
+ (void)cocoadebug_logAllThread {
|
||||
_LOG_SEPERATE
|
||||
NSLog(@"%@", [self cocoadebug_backtraceOfAllThread]);
|
||||
_LOG_SEPERATE
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Generate
|
||||
thread_t cocoadebug_machThreadFromNSThread(NSThread * nsthread) {
|
||||
char name[256];
|
||||
thread_act_array_t list;
|
||||
mach_msg_type_number_t count;
|
||||
task_threads(mach_task_self(), &list, &count);
|
||||
|
||||
NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
|
||||
NSString * originName = nsthread.name;
|
||||
[nsthread setName: [NSString stringWithFormat: @"%f", timeStamp]];
|
||||
|
||||
if ([nsthread isMainThread]) { return (thread_t)main_thread_id; }
|
||||
|
||||
for (int idx = 0; idx < count; idx++) {
|
||||
pthread_t pt = pthread_from_mach_thread_np(list[idx]);
|
||||
if ([nsthread isMainThread] && list[idx] == main_thread_id) { return list[idx]; }
|
||||
if (pt) {
|
||||
name[0] = '\0';
|
||||
pthread_getname_np(pt, name, sizeof(name));
|
||||
if (!strcmp(name, [nsthread name].UTF8String)) {
|
||||
[nsthread setName: originName];
|
||||
return list[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
[nsthread setName: originName];
|
||||
return mach_thread_self();
|
||||
}
|
||||
|
||||
NSString * _cocoadebug_backtraceOfThread(thread_t thread) {
|
||||
uintptr_t backtraceBuffer[_MAX_FRAME_NUMBER];
|
||||
int idx = 0;
|
||||
NSMutableString * result = [NSString stringWithFormat: @"Backtrace of Thread %u:\n======================================================================================\n", thread].mutableCopy;
|
||||
|
||||
_STRUCT_MCONTEXT machineContext;
|
||||
if (!cocoadebug_fillThreadStateIntoMachineContext(thread, &machineContext)) {
|
||||
return [NSString stringWithFormat: @"Failed to get information abount thread: %u", thread];
|
||||
}
|
||||
const uintptr_t instructionAddress = cocoadebug_mach_instructionAddress(&machineContext);
|
||||
backtraceBuffer[idx++] = instructionAddress;
|
||||
|
||||
uintptr_t linkRegister = cocoadebug_mach_linkRegister(&machineContext);
|
||||
if (linkRegister) {
|
||||
backtraceBuffer[idx++] = linkRegister;
|
||||
}
|
||||
if (instructionAddress == _FAILED_UINT_PTR_ADDRESS) { return @"Failed to get instruction address"; }
|
||||
|
||||
COCOADEBUGStackFrameEntry frame = { 0 };
|
||||
const uintptr_t framePtr = cocoadebug_mach_framePointer(&machineContext);
|
||||
if (framePtr == _FAILED_UINT_PTR_ADDRESS ||
|
||||
cocoadebug_mach_copyMem((void *)framePtr, &frame, sizeof(frame)) != KERN_SUCCESS) {
|
||||
return @"failed to get frame pointer";
|
||||
}
|
||||
|
||||
for (; idx < _MAX_FRAME_NUMBER; idx++) {
|
||||
backtraceBuffer[idx] = frame.return_address;
|
||||
if (backtraceBuffer[idx] == _FAILED_UINT_PTR_ADDRESS ||
|
||||
frame.previous == NULL ||
|
||||
cocoadebug_mach_copyMem(frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int backtraceLength = idx;
|
||||
Dl_info symbolicated[backtraceLength];
|
||||
cocoadebug_symbolicate(backtraceBuffer, symbolicated, backtraceLength, 0);
|
||||
for (int idx = 0; idx < backtraceLength; idx++) {
|
||||
[result appendFormat: @"%@", cocoadebug_logBacktraceEntry(idx, backtraceBuffer[idx], &symbolicated[idx])];
|
||||
}
|
||||
[result appendString: @"\n"];
|
||||
[result appendString: @"======================================================================================"];
|
||||
return result.copy;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - operate machine context
|
||||
bool cocoadebug_fillThreadStateIntoMachineContext(thread_t thread, _STRUCT_MCONTEXT * machineContext) {
|
||||
mach_msg_type_number_t state_count = COCOADEBUG_THREAD_STATE_COUNT;
|
||||
kern_return_t kr = thread_get_state(thread, COCOADEBUG_THREAD_STATE, (thread_state_t)&machineContext->__ss, &state_count);
|
||||
return (kr == KERN_SUCCESS);
|
||||
}
|
||||
|
||||
uintptr_t cocoadebug_mach_linkRegister(_STRUCT_MCONTEXT * const machineContext){
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
return _FAILED_UINT_PTR_ADDRESS;
|
||||
#else
|
||||
return machineContext->__ss.__lr;
|
||||
#endif
|
||||
}
|
||||
|
||||
uintptr_t cocoadebug_mach_framePointer(_STRUCT_MCONTEXT * const machineContext) {
|
||||
return machineContext->__ss.COCOADEBUG_FRAME_POINTER;
|
||||
}
|
||||
|
||||
uintptr_t cocoadebug_mach_instructionAddress(_STRUCT_MCONTEXT * const machineContext) {
|
||||
return machineContext->__ss.COCOADEBUG_INSTRUCTION_ADDRESS;
|
||||
}
|
||||
|
||||
kern_return_t cocoadebug_mach_copyMem(const void * src, const void * dst, const size_t numBytes) {
|
||||
vm_size_t bytesCopied = 0;
|
||||
return vm_read_overwrite(mach_task_self(), (vm_address_t)src, (vm_size_t)numBytes, (vm_address_t)dst, &bytesCopied);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - handle symbolicate
|
||||
void cocoadebug_symbolicate(const uintptr_t * const backtraceBuffer, Dl_info * const symbolsBuffer, const int numEntries, const int skippedEntries) {
|
||||
int idx = 0;
|
||||
if (!skippedEntries && idx < numEntries) {
|
||||
cocoadebug_dladdr(backtraceBuffer[idx], &symbolsBuffer[idx]);
|
||||
idx++;
|
||||
}
|
||||
|
||||
for (; idx < numEntries; idx++) {
|
||||
cocoadebug_dladdr(_CALL_INSTRUCTION_FROM_RETURN_ADDRESS(backtraceBuffer[idx]), &symbolsBuffer[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
bool cocoadebug_dladdr(const uintptr_t address, Dl_info * const info) {
|
||||
info->dli_fname = NULL;
|
||||
info->dli_fbase = NULL;
|
||||
info->dli_sname = NULL;
|
||||
info->dli_saddr = NULL;
|
||||
|
||||
const uint32_t idx = cocoadebug_imageIndexContainingAddress(address);
|
||||
if (idx == UINT_MAX) { return false; }
|
||||
|
||||
const struct mach_header * header = _dyld_get_image_header(idx);
|
||||
const uintptr_t imageVMAddressSlide = (uintptr_t)_dyld_get_image_vmaddr_slide(idx);
|
||||
const uintptr_t addressWithSlide = address - imageVMAddressSlide;
|
||||
const uintptr_t segmentBase = cocoadebug_segmentBaseOfImageIndex(idx) + imageVMAddressSlide;
|
||||
if (segmentBase == _FAILED_UINT_PTR_ADDRESS) { return false; }
|
||||
|
||||
info->dli_fbase = (void *)header;
|
||||
info->dli_fname = _dyld_get_image_name(idx);
|
||||
|
||||
const COCOADEBUG_NLIST * bestMatch = NULL;
|
||||
uintptr_t bestDistance = ULONG_MAX;
|
||||
uintptr_t cmdPtr = cocoadebug_firstCmdAfterHeader(header);
|
||||
if (cmdPtr == _FAILED_UINT_PTR_ADDRESS) { return false; }
|
||||
|
||||
for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) {
|
||||
const struct load_command * loadCmd = (struct load_command *)cmdPtr;
|
||||
if (loadCmd->cmd == LC_SYMTAB) {
|
||||
const struct symtab_command * symtabCmd = (struct symtab_command *)cmdPtr;
|
||||
const COCOADEBUG_NLIST * symbolTable = (COCOADEBUG_NLIST *)(segmentBase + symtabCmd->symoff);
|
||||
const uintptr_t stringTable = segmentBase + symtabCmd->stroff;
|
||||
|
||||
for (uint32_t iSym = 0; iSym < symtabCmd->nsyms; iSym++) {
|
||||
if (symbolTable[iSym].n_value == _FAILED_UINT_PTR_ADDRESS) { continue; }
|
||||
uintptr_t symbolBase = symbolTable[iSym].n_value;
|
||||
uintptr_t currentDistance = addressWithSlide - symbolBase;
|
||||
if ( (addressWithSlide >= symbolBase && currentDistance <= bestDistance) ) {
|
||||
bestMatch = symbolTable + iSym;
|
||||
bestDistance = currentDistance;
|
||||
}
|
||||
}
|
||||
if (bestMatch != NULL) {
|
||||
info->dli_saddr = (void *)(bestMatch->n_value + imageVMAddressSlide);
|
||||
info->dli_sname = (char *)((intptr_t)stringTable + (intptr_t)bestMatch->n_un.n_strx);
|
||||
if (*info->dli_sname == '_') {
|
||||
info->dli_sname++;
|
||||
}
|
||||
if (info->dli_saddr == info->dli_fbase && bestMatch->n_type == 3) {
|
||||
info->dli_sname = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
cmdPtr += loadCmd->cmdsize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uintptr_t cocoadebug_firstCmdAfterHeader(const struct mach_header * const header) {
|
||||
switch (header->magic) {
|
||||
case MH_MAGIC:
|
||||
case MH_CIGAM:
|
||||
return (uintptr_t)(header + 1);
|
||||
case MH_MAGIC_64:
|
||||
case MH_CIGAM_64:
|
||||
return (uintptr_t)(((struct mach_header_64*)header) + 1);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t cocoadebug_segmentBaseOfImageIndex(const uint32_t idx) {
|
||||
const struct mach_header * header = _dyld_get_image_header(idx);
|
||||
|
||||
uintptr_t cmdPtr = cocoadebug_firstCmdAfterHeader(header);
|
||||
if (cmdPtr == _FAILED_UINT_PTR_ADDRESS) { return _FAILED_UINT_PTR_ADDRESS; }
|
||||
for (uint32_t idx = 0; idx < header->ncmds; idx++) {
|
||||
const struct load_command * loadCmd = (struct load_command *)cmdPtr;
|
||||
if (loadCmd->cmd == LC_SEGMENT) {
|
||||
const struct segment_command * segCmd = (struct segment_command *)cmdPtr;
|
||||
if (strcmp(segCmd->segname, SEG_LINKEDIT) == 0) {
|
||||
return segCmd->vmaddr - segCmd->fileoff;
|
||||
}
|
||||
} else if (loadCmd->cmd == LC_SEGMENT_64) {
|
||||
const struct segment_command_64 * segCmd = (struct segment_command_64 *)cmdPtr;
|
||||
if (strcmp(segCmd->segname, SEG_LINKEDIT) == 0) {
|
||||
return segCmd->vmaddr - segCmd->fileoff;
|
||||
}
|
||||
}
|
||||
cmdPtr += loadCmd->cmdsize;
|
||||
}
|
||||
return _FAILED_UINT_PTR_ADDRESS;
|
||||
}
|
||||
|
||||
uint32_t cocoadebug_imageIndexContainingAddress(const uintptr_t address) {
|
||||
const uint32_t imageCount = _dyld_image_count();
|
||||
const struct mach_header * header = _FAILED_UINT_PTR_ADDRESS;
|
||||
|
||||
for (uint32_t iImg = 0; iImg < imageCount; iImg++) {
|
||||
header = _dyld_get_image_header(iImg);
|
||||
if (header != NULL) {
|
||||
uintptr_t addressWSlide = address - (uintptr_t)_dyld_get_image_vmaddr_slide(iImg);
|
||||
uintptr_t cmdPtr = cocoadebug_firstCmdAfterHeader(header);
|
||||
if (cmdPtr == _FAILED_UINT_PTR_ADDRESS) { continue; }
|
||||
|
||||
for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) {
|
||||
const struct load_command * loadCmd = (struct load_command *)cmdPtr;
|
||||
if (loadCmd->cmd == LC_SEGMENT) {
|
||||
const struct segment_command * segCmd = (struct segment_command *)cmdPtr;
|
||||
if (addressWSlide >= segCmd->vmaddr &&
|
||||
addressWSlide < segCmd->vmaddr + segCmd->vmsize) {
|
||||
return iImg;
|
||||
}
|
||||
} else if (loadCmd->cmd == LC_SEGMENT_64) {
|
||||
const struct segment_command_64 * segCmd = (struct segment_command_64 *)cmdPtr;
|
||||
if (addressWSlide >= segCmd->vmaddr &&
|
||||
addressWSlide < segCmd->vmaddr + segCmd->vmsize) {
|
||||
return iImg;
|
||||
}
|
||||
}
|
||||
cmdPtr += loadCmd->cmdsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - generate backtrace entry
|
||||
const char * cocoadebug_lastPathEntry(const char * const path) {
|
||||
if (path == NULL) { return NULL; }
|
||||
char * lastFile = strrchr(path, '/');
|
||||
return lastFile == NULL ? path: lastFile + 1;
|
||||
}
|
||||
|
||||
NSString * cocoadebug_logBacktraceEntry(const int entryNum, const uintptr_t address, const Dl_info * const dlInfo) {
|
||||
char faddrBuffer[20];
|
||||
char saddrBuffer[20];
|
||||
|
||||
const char * fname = cocoadebug_lastPathEntry(dlInfo->dli_fname);
|
||||
if (fname == NULL) {
|
||||
sprintf(faddrBuffer, _POINTER_FMT, (uintptr_t)dlInfo->dli_fbase);
|
||||
fname = faddrBuffer;
|
||||
}
|
||||
|
||||
uintptr_t offset = address - (uintptr_t)dlInfo->dli_saddr;
|
||||
const char * sname = dlInfo->dli_sname;
|
||||
if (sname == NULL) {
|
||||
sprintf(saddrBuffer, _POINTER_SHORT_FMT, (uintptr_t)dlInfo->dli_fbase);
|
||||
sname = saddrBuffer;
|
||||
offset = address - (uintptr_t)dlInfo->dli_fbase;
|
||||
}
|
||||
return [NSString stringWithFormat: @"%-30s 0x%08" PRIxPTR " %s + %lu\n", fname, (uintptr_t)address, sname, offset];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
18
Pods/CocoaDebug/Sources/Monitor/_DebugConsoleLabel.h
generated
@@ -1,18 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
/*
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface _DebugConsoleLabel : UILabel
|
||||
|
||||
- (void)updateLabelWithValue:(float)value;
|
||||
|
||||
- (NSAttributedString *)uiBlockingAttributedStringWith:(float)uiBlocking;
|
||||
|
||||
@end
|
||||
*/
|
||||
75
Pods/CocoaDebug/Sources/Monitor/_DebugConsoleLabel.m
generated
@@ -1,75 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
/*
|
||||
#import "_DebugConsoleLabel.h"
|
||||
|
||||
@interface _DebugConsoleLabel ()
|
||||
|
||||
@property (nonatomic, strong) UIFont *mainFont;
|
||||
@property (nonatomic, strong) UIFont *subFont;
|
||||
|
||||
@end
|
||||
|
||||
@implementation _DebugConsoleLabel
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self setDefault];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setDefault {
|
||||
self.textAlignment = NSTextAlignmentCenter;
|
||||
self.userInteractionEnabled = NO;
|
||||
self.adjustsFontSizeToFitWidth = YES;
|
||||
|
||||
self.mainFont = [UIFont fontWithName:@"Menlo" size:14];
|
||||
if (self.mainFont) {
|
||||
self.subFont = [UIFont fontWithName:@"Menlo" size:4];
|
||||
} else {
|
||||
self.mainFont = [UIFont fontWithName:@"Courier" size:14];
|
||||
self.subFont = [UIFont fontWithName:@"Courier" size:4];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateLabelWithValue:(float)value {
|
||||
self.attributedText = [self uiBlockingAttributedStringWith:value];
|
||||
}
|
||||
|
||||
#pragma mark - NSAttributedString
|
||||
|
||||
- (NSAttributedString *)uiBlockingAttributedStringWith:(float)uiBlocking {
|
||||
CGFloat progress = uiBlocking / 60.0;
|
||||
UIColor *color = [UIColor colorWithHue:0.27 * (progress - 0.2) saturation:1 brightness:0.9 alpha:1];
|
||||
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%d UIBlocking",(int)round(uiBlocking)]];
|
||||
[text addAttribute:NSForegroundColorAttributeName value:color range:NSMakeRange(0, text.length - 3)];
|
||||
[text addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(text.length - 3, 3)];
|
||||
[text addAttribute:NSFontAttributeName value:self.mainFont range:NSMakeRange(0, text.length)];
|
||||
[text addAttribute:NSFontAttributeName value:self.subFont range:NSMakeRange(text.length - 4, 1)];
|
||||
return text;
|
||||
}
|
||||
|
||||
#pragma mark - Color
|
||||
|
||||
- (UIColor*)getColorByPercent:(CGFloat)percent {
|
||||
NSInteger r = 0, g = 0, one = 255 + 255;
|
||||
if (percent < 0.5) {
|
||||
r = one * percent;
|
||||
g = 255;
|
||||
}
|
||||
if (percent >= 0.5) {
|
||||
g = 255 - ((percent - 0.5 ) * one) ;
|
||||
r = 255;
|
||||
}
|
||||
return [UIColor colorWithRed:r/255.0 green:g/255.0 blue:0 alpha:1];
|
||||
}
|
||||
|
||||
@end
|
||||
*/
|
||||
23
Pods/CocoaDebug/Sources/Monitor/_RunloopMonitor.h
generated
@@ -1,23 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface _RunloopMonitor : NSObject
|
||||
//@property (nonatomic, copy, class) NSString * version;
|
||||
+ (instancetype)shared;
|
||||
|
||||
- (void)beginMonitor;
|
||||
|
||||
- (void)endMonitor;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
147
Pods/CocoaDebug/Sources/Monitor/_RunloopMonitor.m
generated
@@ -1,147 +0,0 @@
|
||||
//
|
||||
// Example
|
||||
// man
|
||||
//
|
||||
// Created by man 11/11/2018.
|
||||
// Copyright © 2020 man. All rights reserved.
|
||||
//
|
||||
|
||||
#import "_RunloopMonitor.h"
|
||||
#import "_BacktraceLogger.h"
|
||||
|
||||
// 定义延迟时间 毫秒
|
||||
static int64_t const OUT_TIME = 100 * NSEC_PER_MSEC;
|
||||
// before wait 的超时时间
|
||||
static NSTimeInterval const WAIT_TIME = 0.5;
|
||||
|
||||
@interface _RunloopMonitor () {
|
||||
@public
|
||||
CFRunLoopObserverRef observer;
|
||||
CFRunLoopActivity currentActivity;
|
||||
dispatch_semaphore_t semaphore;
|
||||
BOOL isMonitoring;
|
||||
}
|
||||
@end
|
||||
|
||||
static void runloopObserverCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
|
||||
//_RunloopMonitor * monitor = (__bridge _RunloopMonitor*)info;
|
||||
[_RunloopMonitor shared]->currentActivity = activity;
|
||||
|
||||
// switch (activity) {
|
||||
// case kCFRunLoopEntry:
|
||||
// NSLog(@"##### %@", @"kCFRunLoopEntry");
|
||||
// break;
|
||||
// case kCFRunLoopBeforeTimers:
|
||||
// NSLog(@"##### %@", @"kCFRunLoopBeforeTimers");
|
||||
// break;
|
||||
// case kCFRunLoopBeforeSources:
|
||||
// NSLog(@"##### %@", @"kCFRunLoopBeforeSources");
|
||||
// break;
|
||||
// case kCFRunLoopBeforeWaiting:
|
||||
// NSLog(@"##### %@", @"kCFRunLoopBeforeWaiting");
|
||||
// break;
|
||||
// case kCFRunLoopAfterWaiting:
|
||||
// NSLog(@"##### %@", @"kCFRunLoopAfterWaiting");
|
||||
// break;
|
||||
// case kCFRunLoopExit:
|
||||
// NSLog(@"##### %@", @"kCFRunLoopExit");
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
|
||||
dispatch_semaphore_t sema = [_RunloopMonitor shared]->semaphore;
|
||||
dispatch_semaphore_signal(sema);
|
||||
}
|
||||
|
||||
@implementation _RunloopMonitor
|
||||
|
||||
+ (instancetype)shared {
|
||||
static id ins = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
ins = [[super allocWithZone:NSDefaultMallocZone()] init];
|
||||
});
|
||||
return ins;
|
||||
}
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
|
||||
return [self shared];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self endMonitor];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)beginMonitor {
|
||||
|
||||
if ([_RunloopMonitor shared]->isMonitoring) return;
|
||||
|
||||
[_RunloopMonitor shared]->isMonitoring = YES;
|
||||
|
||||
// 创建观察者
|
||||
CFRunLoopObserverContext context = {
|
||||
0,
|
||||
(__bridge void*)self,
|
||||
&CFRetain,
|
||||
&CFRelease,
|
||||
NULL
|
||||
};
|
||||
//static CFRunLoopObserverRef observer;
|
||||
observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &runloopObserverCallback, &context);
|
||||
|
||||
// 观察主线程
|
||||
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
|
||||
|
||||
// 在子线程中监控卡顿
|
||||
semaphore = dispatch_semaphore_create(0); //同步?
|
||||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
// 开启持续的loop来监控
|
||||
while ([_RunloopMonitor shared]->isMonitoring) {
|
||||
if ([_RunloopMonitor shared]->currentActivity == kCFRunLoopBeforeWaiting)
|
||||
{
|
||||
// 处理休眠前事件观测
|
||||
__block BOOL timeOut = YES;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
timeOut = NO; // timeOut任务
|
||||
});
|
||||
[NSThread sleepForTimeInterval:WAIT_TIME];
|
||||
// WAIT_TIME 时间后,如果 timeOut任务 任未执行, 则认为主线程前面的任务执行时间过长导致卡顿
|
||||
if (timeOut) {
|
||||
[_BacktraceLogger cocoadebug_logMain];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 处理 Timer,Source,唤醒后事件
|
||||
// 同步等待时间内,接收到信号result=0, 超时则继续往下执行并且result!=0
|
||||
long result = dispatch_semaphore_wait([_RunloopMonitor shared]->semaphore, dispatch_time(DISPATCH_TIME_NOW, OUT_TIME));
|
||||
if (result != 0) { // 超时
|
||||
if (![_RunloopMonitor shared]->observer) {
|
||||
[[_RunloopMonitor shared] endMonitor];
|
||||
continue;
|
||||
}
|
||||
if ([_RunloopMonitor shared]->currentActivity == kCFRunLoopBeforeSources ||
|
||||
[_RunloopMonitor shared]->currentActivity == kCFRunLoopAfterWaiting ||
|
||||
[_RunloopMonitor shared]->currentActivity == kCFRunLoopBeforeTimers) {
|
||||
|
||||
[_BacktraceLogger cocoadebug_logMain];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
- (void)endMonitor {
|
||||
if (!observer) return;
|
||||
if (!isMonitoring) return;
|
||||
isMonitoring = NO;
|
||||
|
||||
CFRunLoopRemoveObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
|
||||
CFRelease(observer);
|
||||
observer = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
154
Pods/CocoaDebug/Sources/Network/JsonViewController.swift
generated
@@ -1,154 +0,0 @@
|
||||
//
|
||||
// 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
@@ -1,157 +0,0 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
//
|
||||
// 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
@@ -1,104 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -1,523 +0,0 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
//
|
||||
// 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
@@ -1,27 +0,0 @@
|
||||
//
|
||||
// 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
@@ -1,86 +0,0 @@
|
||||
//
|
||||
// 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
@@ -1,41 +0,0 @@
|
||||
//
|
||||
// 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
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// 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
@@ -1,41 +0,0 @@
|
||||
//
|
||||
// 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
@@ -1,56 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
987
Pods/CocoaDebug/Sources/Resources/App.storyboard
generated
@@ -1,987 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="OYf-mT-x66">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Details-->
|
||||
<scene sceneID="Ln3-aw-eLd">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="CrashDetailViewController" title="Details" id="0sO-85-dbe" customClass="CrashDetailViewController" customModule="CocoaDebug" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" contentInsetAdjustmentBehavior="never" indicatorStyle="white" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="37c-ry-al7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="separatorColor" red="0.30192413930000001" green="0.30198186640000002" blue="0.30192047360000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<sections>
|
||||
<tableViewSection headerTitle="exception name" id="Cif-uf-RNE">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="30N-jJ-Luc">
|
||||
<rect key="frame" x="0.0" y="55.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="30N-jJ-Luc" id="FGx-b4-MmQ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="VfO-ya-iWG" customClass="CustomTextView" customModule="CocoaDebug" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="11" width="374" height="22"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="VfO-ya-iWG" firstAttribute="bottom" secondItem="FGx-b4-MmQ" secondAttribute="bottomMargin" id="6nV-u3-los"/>
|
||||
<constraint firstItem="VfO-ya-iWG" firstAttribute="top" secondItem="FGx-b4-MmQ" secondAttribute="topMargin" id="9y3-oD-vvT"/>
|
||||
<constraint firstItem="VfO-ya-iWG" firstAttribute="trailing" secondItem="FGx-b4-MmQ" secondAttribute="trailingMargin" id="FWp-ZX-gRK"/>
|
||||
<constraint firstItem="VfO-ya-iWG" firstAttribute="leading" secondItem="FGx-b4-MmQ" secondAttribute="leadingMargin" id="OsH-We-mwY"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="exception reason" id="MNB-IC-DKL">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="c8s-Q0-2Ti">
|
||||
<rect key="frame" x="0.0" y="155.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="c8s-Q0-2Ti" id="CCb-m3-M57">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="kbB-7c-HgE" customClass="CustomTextView" customModule="CocoaDebug" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="11" width="374" height="22"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="kbB-7c-HgE" firstAttribute="leading" secondItem="CCb-m3-M57" secondAttribute="leadingMargin" id="Dvy-Hq-elS"/>
|
||||
<constraint firstItem="kbB-7c-HgE" firstAttribute="trailing" secondItem="CCb-m3-M57" secondAttribute="trailingMargin" id="SKv-yR-q9i"/>
|
||||
<constraint firstItem="kbB-7c-HgE" firstAttribute="bottom" secondItem="CCb-m3-M57" secondAttribute="bottomMargin" id="gcR-FY-n3v"/>
|
||||
<constraint firstItem="kbB-7c-HgE" firstAttribute="top" secondItem="CCb-m3-M57" secondAttribute="topMargin" id="ibA-BZ-jAD"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="exception stack" id="RHd-9Y-ysy">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="2mC-pc-shD">
|
||||
<rect key="frame" x="0.0" y="255.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="2mC-pc-shD" id="No4-3n-ihh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="hdj-6x-4YO" customClass="CustomTextView" customModule="CocoaDebug" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="11" width="374" height="22"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="8"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="hdj-6x-4YO" firstAttribute="trailing" secondItem="No4-3n-ihh" secondAttribute="trailingMargin" id="EHj-tJ-s2b"/>
|
||||
<constraint firstItem="hdj-6x-4YO" firstAttribute="leading" secondItem="No4-3n-ihh" secondAttribute="leadingMargin" id="MEj-2K-MoH"/>
|
||||
<constraint firstItem="hdj-6x-4YO" firstAttribute="top" secondItem="No4-3n-ihh" secondAttribute="topMargin" id="OCL-Ih-ASc"/>
|
||||
<constraint firstItem="hdj-6x-4YO" firstAttribute="bottom" secondItem="No4-3n-ihh" secondAttribute="bottomMargin" id="aPL-jX-KXn"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" white="0.10484483510000001" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="0sO-85-dbe" id="eon-WA-9wM"/>
|
||||
<outlet property="delegate" destination="0sO-85-dbe" id="F77-uh-23h"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Details" id="uCh-Lu-9de">
|
||||
<barButtonItem key="backBarButtonItem" title=" " id="mEF-aY-qWg"/>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="naviItem" destination="uCh-Lu-9de" id="bP2-9u-ub4"/>
|
||||
<outlet property="textviewName" destination="VfO-ya-iWG" id="gry-vq-ffv"/>
|
||||
<outlet property="textviewReason" destination="kbB-7c-HgE" id="MUi-k9-l8S"/>
|
||||
<outlet property="textviewStackTraces" destination="hdj-6x-4YO" id="nSP-vx-AZH"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="JHR-73-MTd" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="710" y="3090"/>
|
||||
</scene>
|
||||
<!--Crash-->
|
||||
<scene sceneID="DuA-BW-Szs">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="CrashListViewController" title="Crash" hidesBottomBarWhenPushed="YES" id="wXt-DM-5Pz" customClass="CrashListViewController" customModule="CocoaDebug" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="adV-LP-Xiu">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="804"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<prototypes>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="CrashCell" id="Me6-d9-qI1" customClass="CrashCell" customModule="CocoaDebug" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="50" width="414" height="55"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Me6-d9-qI1" id="H71-A4-KNM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="383.5" height="55"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="qE4-AB-m3U" customClass="CustomTextView" customModule="CocoaDebug" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="11" width="355.5" height="33"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SIV-CH-DuD">
|
||||
<rect key="frame" x="20" y="54.5" width="396.5" height="0.5"/>
|
||||
<color key="backgroundColor" red="0.50190365309999996" green="0.50199371579999996" blue="0.50189799069999996" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="0.5" id="IVJ-gm-HZU"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="qE4-AB-m3U" firstAttribute="trailing" secondItem="H71-A4-KNM" secondAttribute="trailingMargin" id="4Zi-HB-Kby"/>
|
||||
<constraint firstItem="qE4-AB-m3U" firstAttribute="bottom" secondItem="H71-A4-KNM" secondAttribute="bottomMargin" id="9fA-VG-8ur"/>
|
||||
<constraint firstAttribute="bottom" secondItem="SIV-CH-DuD" secondAttribute="bottom" id="Krl-Nw-eGm"/>
|
||||
<constraint firstItem="qE4-AB-m3U" firstAttribute="leading" secondItem="H71-A4-KNM" secondAttribute="leadingMargin" id="Sws-VD-CUk"/>
|
||||
<constraint firstItem="qE4-AB-m3U" firstAttribute="top" secondItem="H71-A4-KNM" secondAttribute="topMargin" id="U6A-ju-LTb"/>
|
||||
<constraint firstItem="SIV-CH-DuD" firstAttribute="leading" secondItem="qE4-AB-m3U" secondAttribute="leading" id="eFj-Db-3UO"/>
|
||||
<constraint firstAttribute="trailing" secondItem="SIV-CH-DuD" secondAttribute="trailing" constant="-33" id="tUu-Hw-y9t"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<outlet property="textview" destination="qE4-AB-m3U" id="kPY-iq-8uM"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="wXt-DM-5Pz" id="Ear-nR-pXG"/>
|
||||
<outlet property="delegate" destination="wXt-DM-5Pz" id="8jk-jD-Puv"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" id="aWa-i7-7vr">
|
||||
<barButtonItem key="backBarButtonItem" title=" " id="2W9-oW-Eqa"/>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="naviItem" destination="aWa-i7-7vr" id="5Na-dp-CF2"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="vHE-gZ-5lN" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-279" y="3249"/>
|
||||
</scene>
|
||||
<!--Settings-->
|
||||
<scene sceneID="OGh-yx-4BT">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="IgnoredURLsViewController" title="Settings" hidesBottomBarWhenPushed="YES" id="if5-nq-YeY" customClass="IgnoredURLsViewController" customModule="CocoaDebug" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" contentInsetAdjustmentBehavior="never" indicatorStyle="white" dataMode="prototypes" style="grouped" separatorStyle="none" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="Wcd-XR-9Vs">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="804"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="CocoaDebugSettingsCell" id="sVC-dN-O65">
|
||||
<rect key="frame" x="0.0" y="55.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="sVC-dN-O65" id="ZmJ-2k-Qh8">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<sections/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="if5-nq-YeY" id="8WY-1w-Jmz"/>
|
||||
<outlet property="delegate" destination="if5-nq-YeY" id="WcR-7y-fwZ"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Settings" id="isR-TM-jaf">
|
||||
<barButtonItem key="backBarButtonItem" title=" " id="Kb7-EV-zlP"/>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="naviItem" destination="isR-TM-jaf" id="abw-ur-v0C"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dPT-gm-xVS" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1487" y="3090"/>
|
||||
</scene>
|
||||
<!--App-->
|
||||
<scene sceneID="PWc-Aa-Ldz">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="App" automaticallyAdjustsScrollViewInsets="NO" id="OYf-mT-x66" customClass="CocoaDebugNavigationController" customModule="CocoaDebug" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tabBarItem key="tabBarItem" title="App" image="_icon_file_type_app" selectedImage="_icon_file_type_app" id="bj0-w7-cGx"/>
|
||||
<toolbarItems/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" translucent="NO" id="LTO-vU-VsB">
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<color key="barTintColor" red="0.1215686277" green="0.1294117719" blue="0.1411764771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<textAttributes key="titleTextAttributes">
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</textAttributes>
|
||||
</navigationBar>
|
||||
<nil name="viewControllers"/>
|
||||
<connections>
|
||||
<segue destination="jF1-Ov-37R" kind="relationship" relationship="rootViewController" id="3DV-1o-p0n"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="y1v-lm-5AD" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-18" y="1721"/>
|
||||
</scene>
|
||||
<!--App-->
|
||||
<scene sceneID="bwZ-u7-a4K">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="AppInfoViewController" title="App" id="jF1-Ov-37R" customClass="AppInfoViewController" customModule="CocoaDebug" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" contentInsetAdjustmentBehavior="never" indicatorStyle="white" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="vkj-g7-DtV">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="804"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="separatorColor" red="0.30192413930000001" green="0.30198186640000002" blue="0.30192047360000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<sections>
|
||||
<tableViewSection headerTitle="Crash report" id="nsv-o2-ybJ">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="K6Q-tn-RD7">
|
||||
<rect key="frame" x="0.0" y="55.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="K6Q-tn-RD7" id="W7Y-jE-Ibv">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="crash" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="f9q-nw-Jdh">
|
||||
<rect key="frame" x="53" y="14" width="98" height="20.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="98" id="Qe4-kC-CQX"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="_icon_file_type_bugs.png" translatesAutoresizingMaskIntoConstraints="NO" id="6i2-3H-SU0">
|
||||
<rect key="frame" x="20" y="7" width="30" height="30"/>
|
||||
<color key="tintColor" red="0.74509805439999999" green="0.15686275059999999" blue="0.074509806930000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="F0w-m8-K6b"/>
|
||||
<constraint firstAttribute="width" constant="30" id="FwC-8C-rkR"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Y0Z-UK-UFZ">
|
||||
<rect key="frame" x="283" y="14" width="111" height="20.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="111" id="xV7-7C-4if"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" red="0.99823766950000004" green="0.38489317890000002" blue="0.2489312887" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="f9q-nw-Jdh" firstAttribute="top" secondItem="W7Y-jE-Ibv" secondAttribute="topMargin" constant="3" id="XPp-is-ZQa"/>
|
||||
<constraint firstItem="Y0Z-UK-UFZ" firstAttribute="top" secondItem="W7Y-jE-Ibv" secondAttribute="topMargin" constant="3" id="lRp-vS-UqO"/>
|
||||
<constraint firstItem="6i2-3H-SU0" firstAttribute="centerY" secondItem="W7Y-jE-Ibv" secondAttribute="centerY" id="o3w-Fp-G9N"/>
|
||||
<constraint firstItem="f9q-nw-Jdh" firstAttribute="leading" secondItem="W7Y-jE-Ibv" secondAttribute="leadingMargin" constant="33" id="oe3-Te-iJu"/>
|
||||
<constraint firstItem="6i2-3H-SU0" firstAttribute="leading" secondItem="W7Y-jE-Ibv" secondAttribute="leadingMargin" id="p70-F7-ufq"/>
|
||||
<constraint firstItem="Y0Z-UK-UFZ" firstAttribute="trailing" secondItem="W7Y-jE-Ibv" secondAttribute="trailingMargin" id="wcg-3V-MgZ"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<connections>
|
||||
<segue destination="wXt-DM-5Pz" kind="show" id="Zum-O8-9Re"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Application informations" id="Pcl-Ct-Ne4">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="AnD-nw-aqN">
|
||||
<rect key="frame" x="0.0" y="155.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="AnD-nw-aqN" id="yJa-v7-HTK">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="version" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EOJ-Ds-t9P">
|
||||
<rect key="frame" x="20" y="15" width="125" height="20.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="125" id="epH-HQ-i7p"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7Yi-0U-npR">
|
||||
<rect key="frame" x="153" y="14" width="241" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="7Yi-0U-npR" firstAttribute="leading" secondItem="EOJ-Ds-t9P" secondAttribute="trailing" constant="8" id="4jD-O5-N6W"/>
|
||||
<constraint firstItem="EOJ-Ds-t9P" firstAttribute="top" secondItem="yJa-v7-HTK" secondAttribute="topMargin" constant="4" id="N4v-2j-pcG"/>
|
||||
<constraint firstItem="EOJ-Ds-t9P" firstAttribute="leading" secondItem="yJa-v7-HTK" secondAttribute="leadingMargin" id="UiO-Yo-EzH"/>
|
||||
<constraint firstItem="7Yi-0U-npR" firstAttribute="trailing" secondItem="yJa-v7-HTK" secondAttribute="trailingMargin" id="XUc-ip-zBk"/>
|
||||
<constraint firstItem="7Yi-0U-npR" firstAttribute="top" secondItem="yJa-v7-HTK" secondAttribute="topMargin" constant="3" id="mwH-xj-Du9"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="ikd-Cj-i6S">
|
||||
<rect key="frame" x="0.0" y="199.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ikd-Cj-i6S" id="GuU-zo-g5U">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="build" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mfs-ey-WjO">
|
||||
<rect key="frame" x="20" y="15" width="125" height="20.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="125" id="kYs-ob-17X"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="N5q-Tu-CeT">
|
||||
<rect key="frame" x="153" y="14" width="241" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="N5q-Tu-CeT" firstAttribute="leading" secondItem="mfs-ey-WjO" secondAttribute="trailing" constant="8" id="49Q-xg-Lis"/>
|
||||
<constraint firstItem="N5q-Tu-CeT" firstAttribute="trailing" secondItem="GuU-zo-g5U" secondAttribute="trailingMargin" id="N6d-xe-KMe"/>
|
||||
<constraint firstItem="mfs-ey-WjO" firstAttribute="top" secondItem="GuU-zo-g5U" secondAttribute="topMargin" constant="4" id="OKE-Ny-Dst"/>
|
||||
<constraint firstItem="mfs-ey-WjO" firstAttribute="leading" secondItem="GuU-zo-g5U" secondAttribute="leadingMargin" id="T5R-Vf-jCs"/>
|
||||
<constraint firstItem="N5q-Tu-CeT" firstAttribute="top" secondItem="GuU-zo-g5U" secondAttribute="topMargin" constant="3" id="f5R-sK-6LM"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="1FN-s3-aSS">
|
||||
<rect key="frame" x="0.0" y="243.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1FN-s3-aSS" id="Qy1-wV-4Xx">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="bundle name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2H6-HZ-3M2">
|
||||
<rect key="frame" x="20" y="15" width="125" height="20.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="125" id="MrK-ea-kGO"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Unx-vn-qft">
|
||||
<rect key="frame" x="153" y="14" width="241" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="2H6-HZ-3M2" firstAttribute="leading" secondItem="Qy1-wV-4Xx" secondAttribute="leadingMargin" id="1Oa-vs-AV1"/>
|
||||
<constraint firstItem="2H6-HZ-3M2" firstAttribute="top" secondItem="Qy1-wV-4Xx" secondAttribute="topMargin" constant="4" id="Z5f-0z-0cf"/>
|
||||
<constraint firstItem="Unx-vn-qft" firstAttribute="trailing" secondItem="Qy1-wV-4Xx" secondAttribute="trailingMargin" id="c44-lt-EVn"/>
|
||||
<constraint firstItem="Unx-vn-qft" firstAttribute="leading" secondItem="2H6-HZ-3M2" secondAttribute="trailing" constant="8" id="nry-vD-SnN"/>
|
||||
<constraint firstItem="Unx-vn-qft" firstAttribute="top" secondItem="Qy1-wV-4Xx" secondAttribute="topMargin" constant="3" id="wVe-SW-j9g"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="9DO-nV-o0O">
|
||||
<rect key="frame" x="0.0" y="287.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="9DO-nV-o0O" id="P8v-Rp-4Nl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="bundle id" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jzy-ws-X9S">
|
||||
<rect key="frame" x="20" y="15" width="71" height="20.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="71" id="DdD-Fr-Zc1"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="1" translatesAutoresizingMaskIntoConstraints="NO" id="MBd-UL-t4v">
|
||||
<rect key="frame" x="99" y="14" width="295" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="MBd-UL-t4v" firstAttribute="trailing" secondItem="P8v-Rp-4Nl" secondAttribute="trailingMargin" id="CoJ-TU-ljL"/>
|
||||
<constraint firstItem="jzy-ws-X9S" firstAttribute="top" secondItem="P8v-Rp-4Nl" secondAttribute="topMargin" constant="4" id="LhO-Wb-nF6"/>
|
||||
<constraint firstItem="MBd-UL-t4v" firstAttribute="top" secondItem="P8v-Rp-4Nl" secondAttribute="topMargin" constant="3" id="atk-uX-OMK"/>
|
||||
<constraint firstItem="jzy-ws-X9S" firstAttribute="leading" secondItem="P8v-Rp-4Nl" secondAttribute="leadingMargin" id="ejP-Tv-hrW"/>
|
||||
<constraint firstItem="MBd-UL-t4v" firstAttribute="leading" secondItem="jzy-ws-X9S" secondAttribute="trailing" constant="8" id="fIU-cY-wUu"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="Rod-BJ-T4S">
|
||||
<rect key="frame" x="0.0" y="331.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Rod-BJ-T4S" id="g0b-6G-zHV">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="server" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XRg-Xd-q1c">
|
||||
<rect key="frame" x="20" y="15" width="48" height="20.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="48" id="zRr-Q0-5u2"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="1" translatesAutoresizingMaskIntoConstraints="NO" id="buG-N4-VY9">
|
||||
<rect key="frame" x="76" y="14" width="318" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="XRg-Xd-q1c" firstAttribute="leading" secondItem="g0b-6G-zHV" secondAttribute="leadingMargin" id="66O-ud-ekj"/>
|
||||
<constraint firstItem="buG-N4-VY9" firstAttribute="leading" secondItem="XRg-Xd-q1c" secondAttribute="trailing" constant="8" id="8lE-H5-FnD"/>
|
||||
<constraint firstItem="XRg-Xd-q1c" firstAttribute="top" secondItem="g0b-6G-zHV" secondAttribute="topMargin" constant="4" id="HHU-Rs-jMi"/>
|
||||
<constraint firstItem="buG-N4-VY9" firstAttribute="top" secondItem="g0b-6G-zHV" secondAttribute="topMargin" constant="3" id="qRK-PV-8KD"/>
|
||||
<constraint firstItem="buG-N4-VY9" firstAttribute="trailing" secondItem="g0b-6G-zHV" secondAttribute="trailingMargin" id="uCk-oh-IER"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Device" id="BvG-Vd-D0n">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="SVL-Ej-OzZ">
|
||||
<rect key="frame" x="0.0" y="431.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="SVL-Ej-OzZ" id="fLv-2F-0ym">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="screen resolution" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Tof-t8-LIb">
|
||||
<rect key="frame" x="20" y="15" width="143" height="20.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="143" id="FG8-vD-c7U"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Kz7-U6-YrS">
|
||||
<rect key="frame" x="171" y="14" width="223" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Kz7-U6-YrS" firstAttribute="leading" secondItem="Tof-t8-LIb" secondAttribute="trailing" constant="8" id="PBJ-ve-qeg"/>
|
||||
<constraint firstItem="Tof-t8-LIb" firstAttribute="top" secondItem="fLv-2F-0ym" secondAttribute="topMargin" constant="4" id="Xo1-0I-nWa"/>
|
||||
<constraint firstItem="Kz7-U6-YrS" firstAttribute="trailing" secondItem="fLv-2F-0ym" secondAttribute="trailingMargin" id="eXP-Ep-gOf"/>
|
||||
<constraint firstItem="Kz7-U6-YrS" firstAttribute="top" secondItem="fLv-2F-0ym" secondAttribute="topMargin" constant="3" id="qTl-mS-gUW"/>
|
||||
<constraint firstItem="Tof-t8-LIb" firstAttribute="leading" secondItem="fLv-2F-0ym" secondAttribute="leadingMargin" id="sOO-w3-2p2"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="Khf-ne-QHn">
|
||||
<rect key="frame" x="0.0" y="475.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Khf-ne-QHn" id="VHa-mO-V0x">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="device" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zzD-ax-75S">
|
||||
<rect key="frame" x="20" y="15" width="125" height="20.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="125" id="yZy-dL-nEc"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HVz-ej-w8c">
|
||||
<rect key="frame" x="153" y="14" width="241" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="HVz-ej-w8c" firstAttribute="trailing" secondItem="VHa-mO-V0x" secondAttribute="trailingMargin" id="0j3-kp-bnl"/>
|
||||
<constraint firstItem="zzD-ax-75S" firstAttribute="top" secondItem="VHa-mO-V0x" secondAttribute="topMargin" constant="4" id="5Hz-V5-8zP"/>
|
||||
<constraint firstItem="HVz-ej-w8c" firstAttribute="top" secondItem="VHa-mO-V0x" secondAttribute="topMargin" constant="3" id="GSJ-FY-Zg1"/>
|
||||
<constraint firstItem="HVz-ej-w8c" firstAttribute="leading" secondItem="zzD-ax-75S" secondAttribute="trailing" constant="8" id="eHS-s0-mJ9"/>
|
||||
<constraint firstItem="zzD-ax-75S" firstAttribute="leading" secondItem="VHa-mO-V0x" secondAttribute="leadingMargin" id="rmn-SJ-LgZ"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="WYS-Zq-jV2">
|
||||
<rect key="frame" x="0.0" y="519.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WYS-Zq-jV2" id="Fka-Mr-FRA">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="iOS version" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="G6d-X9-D60">
|
||||
<rect key="frame" x="20" y="15" width="125" height="20.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="125" id="dqa-oF-naU"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lQJ-Bz-A5f">
|
||||
<rect key="frame" x="153" y="14" width="241" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="lQJ-Bz-A5f" firstAttribute="top" secondItem="Fka-Mr-FRA" secondAttribute="topMargin" constant="3" id="8qL-Se-JGK"/>
|
||||
<constraint firstItem="lQJ-Bz-A5f" firstAttribute="trailing" secondItem="Fka-Mr-FRA" secondAttribute="trailingMargin" id="WED-Ob-rk8"/>
|
||||
<constraint firstItem="G6d-X9-D60" firstAttribute="leading" secondItem="Fka-Mr-FRA" secondAttribute="leadingMargin" id="jyk-wz-Hpo"/>
|
||||
<constraint firstItem="lQJ-Bz-A5f" firstAttribute="leading" secondItem="G6d-X9-D60" secondAttribute="trailing" constant="8" id="r8M-2w-RS2"/>
|
||||
<constraint firstItem="G6d-X9-D60" firstAttribute="top" secondItem="Fka-Mr-FRA" secondAttribute="topMargin" constant="4" id="wMw-X3-ijR"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="debug" id="iXh-NN-BVN" userLabel="Debug">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="JCN-FL-82K">
|
||||
<rect key="frame" x="0.0" y="619.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="JCN-FL-82K" id="iu1-kx-Q6n">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="slow animations" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lRE-aq-mNK">
|
||||
<rect key="frame" x="20" y="15" width="122" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qWu-gX-eiv">
|
||||
<rect key="frame" x="353" y="10" width="53" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="51" id="KJ8-oC-PDB"/>
|
||||
<constraint firstAttribute="height" constant="31" id="cET-57-0qa"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="lRE-aq-mNK" firstAttribute="centerY" secondItem="qWu-gX-eiv" secondAttribute="centerY" id="7HX-gc-Aj3"/>
|
||||
<constraint firstItem="lRE-aq-mNK" firstAttribute="leading" secondItem="iu1-kx-Q6n" secondAttribute="leadingMargin" id="EeJ-Im-Bsb"/>
|
||||
<constraint firstItem="lRE-aq-mNK" firstAttribute="top" secondItem="iu1-kx-Q6n" secondAttribute="topMargin" constant="4" id="Y58-5C-9by"/>
|
||||
<constraint firstAttribute="trailing" secondItem="qWu-gX-eiv" secondAttribute="trailing" constant="10" id="kBW-fe-TXu"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="monitor" id="RzW-bS-tRS" userLabel="Monitor">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="JML-Me-QmC">
|
||||
<rect key="frame" x="0.0" y="719.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="JML-Me-QmC" id="cb0-Fk-4Fh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Network" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="g2w-PI-4E7">
|
||||
<rect key="frame" x="20" y="15" width="64.5" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rnB-9b-x8i">
|
||||
<rect key="frame" x="353" y="10" width="53" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="31" id="LeT-67-2dt"/>
|
||||
<constraint firstAttribute="width" constant="51" id="hg8-FM-ebm"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="g2w-PI-4E7" firstAttribute="leading" secondItem="cb0-Fk-4Fh" secondAttribute="leadingMargin" id="7RI-lO-x6G"/>
|
||||
<constraint firstItem="g2w-PI-4E7" firstAttribute="top" secondItem="cb0-Fk-4Fh" secondAttribute="topMargin" constant="4" id="TgD-yI-yeE"/>
|
||||
<constraint firstAttribute="trailing" secondItem="rnB-9b-x8i" secondAttribute="trailing" constant="10" id="bmr-gW-EGp"/>
|
||||
<constraint firstItem="g2w-PI-4E7" firstAttribute="centerY" secondItem="rnB-9b-x8i" secondAttribute="centerY" id="pKO-TL-Kse"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="YVc-hL-cy8">
|
||||
<rect key="frame" x="0.0" y="763.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="YVc-hL-cy8" id="9hV-aV-OuE">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="App logs" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jOm-7W-o7b">
|
||||
<rect key="frame" x="20" y="15" width="67.5" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ssj-o9-QJs">
|
||||
<rect key="frame" x="353" y="10" width="53" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="51" id="fz1-Rz-nfr"/>
|
||||
<constraint firstAttribute="height" constant="31" id="tPG-6C-aFs"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="jOm-7W-o7b" firstAttribute="centerY" secondItem="Ssj-o9-QJs" secondAttribute="centerY" id="4Ar-wx-pQR"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Ssj-o9-QJs" secondAttribute="trailing" constant="10" id="5GL-Nz-FqV"/>
|
||||
<constraint firstItem="jOm-7W-o7b" firstAttribute="leading" secondItem="9hV-aV-OuE" secondAttribute="leadingMargin" id="5e1-hX-BW5"/>
|
||||
<constraint firstItem="jOm-7W-o7b" firstAttribute="top" secondItem="9hV-aV-OuE" secondAttribute="topMargin" constant="4" id="uzh-TJ-NBB"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="MGn-ZK-WVW">
|
||||
<rect key="frame" x="0.0" y="807.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="MGn-ZK-WVW" id="pUt-5Q-17Z">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="React Native logs" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XRi-K3-rg1">
|
||||
<rect key="frame" x="20" y="15" width="133" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="A7q-BC-KXI">
|
||||
<rect key="frame" x="353" y="10" width="53" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="31" id="Qrz-dL-2Xu"/>
|
||||
<constraint firstAttribute="width" constant="51" id="yhQ-9w-IMd"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="XRi-K3-rg1" firstAttribute="leading" secondItem="pUt-5Q-17Z" secondAttribute="leadingMargin" id="4NC-QV-Pmc"/>
|
||||
<constraint firstItem="XRi-K3-rg1" firstAttribute="top" secondItem="pUt-5Q-17Z" secondAttribute="topMargin" constant="4" id="Ipg-sM-KgQ"/>
|
||||
<constraint firstItem="XRi-K3-rg1" firstAttribute="centerY" secondItem="A7q-BC-KXI" secondAttribute="centerY" id="KvI-i6-Nxm"/>
|
||||
<constraint firstAttribute="trailing" secondItem="A7q-BC-KXI" secondAttribute="trailing" constant="10" id="gPH-Vf-flk"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="RNh-Ro-SRq">
|
||||
<rect key="frame" x="0.0" y="851.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="RNh-Ro-SRq" id="4ox-GS-19t">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="WKWebView logs" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8Lk-Ki-0bv">
|
||||
<rect key="frame" x="20" y="15" width="133.5" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="k9g-2G-DFt">
|
||||
<rect key="frame" x="353" y="10" width="53" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="51" id="5iH-nX-Sgf"/>
|
||||
<constraint firstAttribute="height" constant="31" id="Ohc-WB-zDW"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="8Lk-Ki-0bv" firstAttribute="leading" secondItem="4ox-GS-19t" secondAttribute="leadingMargin" id="0tf-R9-om5"/>
|
||||
<constraint firstItem="8Lk-Ki-0bv" firstAttribute="centerY" secondItem="k9g-2G-DFt" secondAttribute="centerY" id="CGU-v2-D0n"/>
|
||||
<constraint firstAttribute="trailing" secondItem="k9g-2G-DFt" secondAttribute="trailing" constant="10" id="iQF-CN-HhD"/>
|
||||
<constraint firstItem="8Lk-Ki-0bv" firstAttribute="top" secondItem="4ox-GS-19t" secondAttribute="topMargin" constant="4" id="kQU-ub-fhn"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="DtA-Am-Bxe">
|
||||
<rect key="frame" x="0.0" y="895.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="DtA-Am-Bxe" id="oPg-4h-3co">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="crash" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gt5-4n-jXv">
|
||||
<rect key="frame" x="20" y="15" width="42.5" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oYM-Zg-0TE">
|
||||
<rect key="frame" x="353" y="10" width="53" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="51" id="2EO-PA-UMz"/>
|
||||
<constraint firstAttribute="height" constant="31" id="okO-UR-jYC"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="oYM-Zg-0TE" secondAttribute="trailing" constant="10" id="08u-57-IDD"/>
|
||||
<constraint firstItem="gt5-4n-jXv" firstAttribute="centerY" secondItem="oYM-Zg-0TE" secondAttribute="centerY" id="3cn-Nu-rJ7"/>
|
||||
<constraint firstItem="gt5-4n-jXv" firstAttribute="top" secondItem="oPg-4h-3co" secondAttribute="topMargin" constant="4" id="AAt-vI-g1W"/>
|
||||
<constraint firstItem="gt5-4n-jXv" firstAttribute="leading" secondItem="oPg-4h-3co" secondAttribute="leadingMargin" id="Njy-ii-nDD"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="aJN-sv-Y5D">
|
||||
<rect key="frame" x="0.0" y="939.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="aJN-sv-Y5D" id="2uD-hr-bdX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="UI Blocking" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XqB-2b-S0G">
|
||||
<rect key="frame" x="20" y="15" width="86" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ihe-8P-ujT">
|
||||
<rect key="frame" x="353" y="10" width="53" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="51" id="33X-L4-zoh"/>
|
||||
<constraint firstAttribute="height" constant="31" id="6OA-N1-PtS"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="XqB-2b-S0G" firstAttribute="leading" secondItem="2uD-hr-bdX" secondAttribute="leadingMargin" id="6o8-Qd-tqz"/>
|
||||
<constraint firstItem="XqB-2b-S0G" firstAttribute="top" secondItem="2uD-hr-bdX" secondAttribute="topMargin" constant="4" id="CNw-2F-AYj"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Ihe-8P-ujT" secondAttribute="trailing" constant="10" id="DA1-V2-dhJ"/>
|
||||
<constraint firstItem="XqB-2b-S0G" firstAttribute="centerY" secondItem="Ihe-8P-ujT" secondAttribute="centerY" id="YVC-fk-VAK"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.199973762" green="0.2000150383" blue="0.19997116919999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="general" id="QhX-zS-obu" userLabel="General">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="aAa-Fa-vsR">
|
||||
<rect key="frame" x="0.0" y="1039.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="aAa-Fa-vsR" id="7jN-Rb-Eb3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="settings" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rqv-8H-UcW">
|
||||
<rect key="frame" x="16" y="14" width="110" height="20.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="110" id="PQV-zw-DIQ"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="rqv-8H-UcW" firstAttribute="leading" secondItem="7jN-Rb-Eb3" secondAttribute="leading" constant="16" id="RCG-xx-1HK"/>
|
||||
<constraint firstItem="rqv-8H-UcW" firstAttribute="top" secondItem="7jN-Rb-Eb3" secondAttribute="topMargin" constant="3" id="d2o-JF-Q4z"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.19997376203536987" green="0.20001503825187683" blue="0.19997116923332214" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<connections>
|
||||
<segue destination="if5-nq-YeY" kind="show" id="3DU-OB-maE"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="9nF-M0-YO4">
|
||||
<rect key="frame" x="0.0" y="1083.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="9nF-M0-YO4" id="gkt-zQ-QW1">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="about" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="g8c-ye-Bub">
|
||||
<rect key="frame" x="16" y="14" width="100" height="20.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="100" id="8du-Mf-oam"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="g8c-ye-Bub" firstAttribute="top" secondItem="gkt-zQ-QW1" secondAttribute="topMargin" constant="3" id="Hjx-B6-NhW"/>
|
||||
<constraint firstItem="g8c-ye-Bub" firstAttribute="leading" secondItem="gkt-zQ-QW1" secondAttribute="leading" constant="16" id="net-Dr-VM4"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.19997376203536987" green="0.20001503825187683" blue="0.19997116923332214" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<connections>
|
||||
<segue destination="FV0-KU-C1g" kind="show" id="uhi-v0-AVi"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="jF1-Ov-37R" id="JNM-E9-yLb"/>
|
||||
<outlet property="delegate" destination="jF1-Ov-37R" id="LiC-7u-XBE"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="App" id="nfh-qO-k6T">
|
||||
<barButtonItem key="backBarButtonItem" title=" " id="bL5-HO-27l"/>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="crashSwitch" destination="oYM-Zg-0TE" id="p6h-tY-LSZ"/>
|
||||
<outlet property="labelBuildNumber" destination="N5q-Tu-CeT" id="W9d-GI-HdH"/>
|
||||
<outlet property="labelBundleID" destination="MBd-UL-t4v" id="7aK-4x-XoF"/>
|
||||
<outlet property="labelBundleName" destination="Unx-vn-qft" id="dRB-fY-TyH"/>
|
||||
<outlet property="labelCrashCount" destination="Y0Z-UK-UFZ" id="1m4-p9-4gv"/>
|
||||
<outlet property="labelDeviceModel" destination="HVz-ej-w8c" id="eRv-iU-o6o"/>
|
||||
<outlet property="labelHtml" destination="8Lk-Ki-0bv" id="Rm8-HH-Faa"/>
|
||||
<outlet property="labelIOSVersion" destination="lQJ-Bz-A5f" id="QLN-M3-KPP"/>
|
||||
<outlet property="labelScreenResolution" destination="Kz7-U6-YrS" id="28a-cs-9Hu"/>
|
||||
<outlet property="labelVersionNumber" destination="7Yi-0U-npR" id="cIg-RG-FLI"/>
|
||||
<outlet property="labelserverURL" destination="buG-N4-VY9" id="W1a-e8-npR"/>
|
||||
<outlet property="logSwitch" destination="Ssj-o9-QJs" id="BWf-MV-I6J"/>
|
||||
<outlet property="naviItem" destination="nfh-qO-k6T" id="Hcv-Vg-JBZ"/>
|
||||
<outlet property="networkSwitch" destination="rnB-9b-x8i" id="yFO-ad-hxg"/>
|
||||
<outlet property="rnSwitch" destination="A7q-BC-KXI" id="CK3-u8-zrc"/>
|
||||
<outlet property="slowAnimationsSwitch" destination="qWu-gX-eiv" id="2gV-hu-Jtk"/>
|
||||
<outlet property="uiBlockingSwitch" destination="Ihe-8P-ujT" id="JTB-N6-HVv"/>
|
||||
<outlet property="webViewSwitch" destination="k9g-2G-DFt" id="96S-tY-w3r"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="RmQ-L8-DSv" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-18.399999999999999" y="2408.5457271364321"/>
|
||||
</scene>
|
||||
<!--About-->
|
||||
<scene sceneID="GVK-bH-MrX">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="AboutViewController" title="About" hidesBottomBarWhenPushed="YES" id="FV0-KU-C1g" customClass="AboutViewController" customModule="CocoaDebug" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" scrollEnabled="NO" contentInsetAdjustmentBehavior="never" indicatorStyle="white" dataMode="static" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="Vea-k2-6Ll">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="804"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<sections>
|
||||
<tableViewSection id="LX5-ww-cfA">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" rowHeight="120" id="RiH-dO-FGa">
|
||||
<rect key="frame" x="0.0" y="50" width="414" height="120"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="RiH-dO-FGa" id="ehQ-pU-nc1">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="120"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5W3-za-4wq">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="120"/>
|
||||
<gestureRecognizers/>
|
||||
<string key="text">Homepage:
|
||||
https://github.com/CocoaDebug
|
||||
|
||||
Author:
|
||||
CocoaDebug@gmail.com</string>
|
||||
<fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="15"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="5W3-za-4wq" secondAttribute="trailing" id="9yQ-hw-Wq4"/>
|
||||
<constraint firstItem="5W3-za-4wq" firstAttribute="leading" secondItem="ehQ-pU-nc1" secondAttribute="leading" id="PJJ-HW-Ckn"/>
|
||||
<constraint firstAttribute="bottom" secondItem="5W3-za-4wq" secondAttribute="bottom" id="fk1-Af-zDU"/>
|
||||
<constraint firstItem="5W3-za-4wq" firstAttribute="top" secondItem="ehQ-pU-nc1" secondAttribute="top" id="yf8-iQ-EJM"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" rowHeight="120" id="FtU-Vd-Jba">
|
||||
<rect key="frame" x="0.0" y="170" width="414" height="120"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="FtU-Vd-Jba" id="Pr4-FY-xdf">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="120"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lng-CP-TTU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="120"/>
|
||||
<gestureRecognizers/>
|
||||
<fontDescription key="fontDescription" name="Helvetica-BoldOblique" family="Helvetica" pointSize="12"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="lng-CP-TTU" firstAttribute="top" secondItem="Pr4-FY-xdf" secondAttribute="top" id="b50-Bp-SI1"/>
|
||||
<constraint firstAttribute="bottom" secondItem="lng-CP-TTU" secondAttribute="bottom" id="dSx-1r-ebT"/>
|
||||
<constraint firstItem="lng-CP-TTU" firstAttribute="leading" secondItem="Pr4-FY-xdf" secondAttribute="leading" id="sVe-Bg-MBL"/>
|
||||
<constraint firstAttribute="trailing" secondItem="lng-CP-TTU" secondAttribute="trailing" id="wap-js-2sj"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="FV0-KU-C1g" id="66o-yP-Y3N"/>
|
||||
<outlet property="delegate" destination="FV0-KU-C1g" id="72Y-kJ-MSf"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="About" id="miW-ow-i75">
|
||||
<barButtonItem key="backBarButtonItem" title=" " id="Qcc-2f-UGU"/>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="naviItem" destination="miW-ow-i75" id="Ohg-gp-LEr"/>
|
||||
<outlet property="versionLabel" destination="lng-CP-TTU" id="MPO-vR-80e"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="g3T-S0-LUh" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<tapGestureRecognizer id="Ltp-bc-bl2">
|
||||
<connections>
|
||||
<action selector="tapUrl:" destination="FV0-KU-C1g" id="xoE-RK-L3L"/>
|
||||
</connections>
|
||||
</tapGestureRecognizer>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="980" y="2358.170914542729"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="_icon_file_type_app" width="16" height="16"/>
|
||||
<image name="_icon_file_type_bugs.png" width="50" height="50"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
Before Width: | Height: | Size: 433 B |
|
Before Width: | Height: | Size: 673 B |
|
Before Width: | Height: | Size: 976 B |
|
Before Width: | Height: | Size: 451 B |
|
Before Width: | Height: | Size: 768 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 469 B |
|
Before Width: | Height: | Size: 717 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 498 B |
|
Before Width: | Height: | Size: 658 B |
|
Before Width: | Height: | Size: 903 B |
|
Before Width: | Height: | Size: 448 B |