This commit is contained in:
DDIsFriend
2023-09-07 17:09:41 +08:00
parent 5aa7ce5659
commit c853b73127
663 changed files with 57056 additions and 24723 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -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 */,
);

View File

@@ -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>

Binary file not shown.

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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{

View File

@@ -141,8 +141,11 @@ extension AppDelegate : JPUSHRegisterDelegate {
func jpushNotificationAuthorization(_ status: JPAuthorizationStatus, withInfo info: [AnyHashable : Any]?) {
DDLog(message:"通知授权状态\(status)")
USER.regId = JPUSHService.registrationID()
JPUSHService.registrationIDCompletionHandler { errorCode, registrationID in
USER.regId = registrationID
DDLog(message:"registrationID:\(JPUSHService.registrationID())")
}
if status != .statusAuthorized {
NOTIAUTHTOOL.requestNotiAuthRelay.accept(nil)

View File

@@ -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()
}

View File

@@ -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)

View File

@@ -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!
}
}

View File

@@ -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{

Binary file not shown.

View 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"

View File

@@ -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{

View File

@@ -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|

View File

@@ -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

View File

@@ -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 | [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/CocoaDebug.svg)](https://img.shields.io/cocoapods/v/CocoaDebug.svg) |
| Platform | ![Platform](https://img.shields.io/badge/platforms-iOS%2012.0+-blue.svg) |
| Languages | ![Languages](https://img.shields.io/badge/languages-Swift%20%7C%20ObjC-blue.svg) |
<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 emails “To” field when share via email. Default value is **nil**.
- `emailCcRecipients` - Set the initial recipients to include in the emails “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)

View File

@@ -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()
}
}

View File

@@ -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)
}
}
}

View File

@@ -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
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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)
// }
}
}
}

View File

@@ -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)
}
}

View File

@@ -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 []
}
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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"

View File

@@ -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 emails 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 emails 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)
}

View File

@@ -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

View File

@@ -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

View File

@@ -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")
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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()
// }
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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)
}
*/

View File

@@ -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

View File

@@ -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

View File

@@ -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
*/

View File

@@ -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
*/

View File

@@ -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

View File

@@ -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

View File

@@ -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
}
}
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}
}

View File

@@ -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
// }
// }
// }
// }
}
}

View File

@@ -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)
}
}
}
}

View File

@@ -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()
// }
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 B

Some files were not shown because too many files have changed in this diff Show More