diff --git a/.gitignore b/.gitignore
index dcd8c16b..25daaa64 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,65 +1,68 @@
-.DS_Store
-*.xcuserdatad
-
-# Created by https://www.gitignore.io/api/xcode,osx
-
-### OSX ###
-*.DS_Store
-.AppleDouble
-.LSOverride
-
-# Icon must end with two \r
+.DS_Store
+*.xcuserdatad
+
+# Created by https://www.gitignore.io/api/xcode,osx
+
+### OSX ###
+*.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
Icon
-
-# Thumbnails
-._*
-
-# Files that might appear in the root of a volume
-.DocumentRevisions-V100
-.fseventsd
-.Spotlight-V100
-.TemporaryItems
-.Trashes
-.VolumeIcon.icns
-.com.apple.timemachine.donotpresent
-
-# Directories potentially created on remote AFP share
-.AppleDB
-.AppleDesktop
-Network Trash Folder
-Temporary Items
-.apdisk
-
-### Xcode ###
-# Xcode
-#
-# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
-
-## Build generated
-build/
-DerivedData/
-
-## Various settings
-*.pbxuser
-!default.pbxuser
-*.mode1v3
-!default.mode1v3
-*.mode2v3
-!default.mode2v3
-*.perspectivev3
-!default.perspectivev3
-xcuserdata/
-
-## Other
-*.moved-aside
-*.xccheckout
-*.xcscmblueprint
-
-### Xcode Patch ###
-*.xcodeproj/*
-!*.xcodeproj/project.pbxproj
-!*.xcodeproj/xcshareddata/
-!*.xcworkspace/contents.xcworkspacedata
-/*.gcno
-
-# End of https://www.gitignore.io/api/xcode,osx
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### Xcode ###
+# Xcode
+#
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+## Build generated
+build/
+DerivedData/
+
+## Various settings
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata/
+
+## Other
+*.moved-aside
+*.xccheckout
+*.xcscmblueprint
+
+### Xcode Patch ###
+*.xcodeproj/*
+!*.xcodeproj/project.pbxproj
+!*.xcodeproj/xcshareddata/
+!*.xcworkspace/contents.xcworkspacedata
+/*.gcno
+
+# End of https://www.gitignore.io/api/xcode,osx
+
+Pods/
+*.xcworkspace/
diff --git a/README.md b/README.md
index ba2752d5..9ee42b14 100644
--- a/README.md
+++ b/README.md
@@ -130,6 +130,18 @@ NFX.sharedInstance().ignoreURL("the_url")
```
Tip: You can use the url of the host (for example "https://www.github.com") to ignore all paths of it
+## Alternative to Charles - use netfox_mac app
+
+No need to mess around with Charles Proxy, SSL configurations in order to connect to your iPhone or iPad. We have created netfox_mac app which can connect to all apps (iOS or mac) that are running in local network. It will be easier for your QA person / team to know what API calls are made on device or if one of them has failed.
+
+Basically any iOS app that wants to expose it's network data to other netfox_mac app for inspection should start discovery service using:
+
+```swift
+NFX.sharedInstance().startServer()
+```
+After that netfox_mac app can connect using the same UI as in the mac app.
+
+
## Features
- Search: You can easily search among requests via
@@ -144,8 +156,26 @@ Tip: You can use the url of the host (for example "https://www.github.com") to i
- Clear data within the app
- Statistics: Check cool things like average response time, total response size and more for your selected types of responses
- Info: Check your IP address, your app version and build number and other things within the app
+- Live updates between apps running netfox with NFX.sharedInstance.startServer() and netfox_mac application
+- Directory-like structure of requests
+- Conversion from JSON to Codable class
- More to come.. ;)
+### netfox_mac app working in parallel with iOS app
+
+
+If you want to use netfox with the netfox_mac application, you should start netfox in the following way:
+
+#### Swift
+
+NFX.sharedInstance().startServer()
+
+
+#### Obj-C
+
+[[NFX sharedInstance] startServer];
+
+
## Integrations
[Droar](https://github.com/myriadmobile/netfox-Droar): A modular, single-line installation debugging window.
@@ -159,6 +189,8 @@ Tip: You can use the url of the host (for example "https://www.github.com") to i
Special thanks to [tbaranes](https://github.com/tbaranes) and [vincedev](https://github.com/vincedev) for their contribution on OSX library!
+Special thanks to [Tapptitude](https://tapptitude.com) team for their contribution on netfox_mac app!
+
## Licence
All source code is licensed under [MIT License](https://github.com/kasketis/netfox/blob/master/LICENSE). Which means you could do virtually anything with the code. I will appreciate it very much if you keep an attribution where appropriate.
diff --git a/netfox.podspec b/netfox.podspec
index 7035b842..f4754739 100644
--- a/netfox.podspec
+++ b/netfox.podspec
@@ -16,7 +16,10 @@ DESC
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.11'
s.requires_arc = true
- s.source_files = "netfox/Core/*.{swift,h,m}"
- s.ios.source_files = "netfox/iOS/*.swift"
- s.osx.source_files = "netfox/OSX/*.{swift,xib}"
+ s.source_files = "netfox/Core/**/*.{swift,h,m}"
+ s.ios.source_files = "netfox/iOS/**/*.swift"
+ s.osx.source_files = "netfox/OSX/**/*.{swift,xib}"
+
+ # s.dependency "Swifter"
+
end
\ No newline at end of file
diff --git a/netfox.xcodeproj/project.pbxproj b/netfox.xcodeproj/project.pbxproj
old mode 100644
new mode 100755
index fef6a3c9..3656a5a5
--- a/netfox.xcodeproj/project.pbxproj
+++ b/netfox.xcodeproj/project.pbxproj
@@ -7,6 +7,17 @@
objects = {
/* Begin PBXBuildFile section */
+ 234DBCD61FA33BD90086CB79 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 234DBCD51FA33BD90086CB79 /* AppDelegate.swift */; };
+ 234DBCD81FA33BD90086CB79 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 234DBCD71FA33BD90086CB79 /* Assets.xcassets */; };
+ 234DBCDB1FA33BD90086CB79 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 234DBCD91FA33BD90086CB79 /* MainMenu.xib */; };
+ 234DBCE11FA33BFE0086CB79 /* netfox_osx.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E20FD2E91C6912D400DCFF61 /* netfox_osx.framework */; };
+ 23580CD120401E400009C40C /* netfox_osx.framework in Copy Netfox framework */ = {isa = PBXBuildFile; fileRef = E20FD2E91C6912D400DCFF61 /* netfox_osx.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 23ABD6D01FA9C40000CD6186 /* NFXServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23ABD6CF1FA9C40000CD6186 /* NFXServer.swift */; };
+ 23ABD6D11FA9C53F00CD6186 /* NFXServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23ABD6CF1FA9C40000CD6186 /* NFXServer.swift */; };
+ 23ABD6D61FA9E08C00CD6186 /* NFXNetServiceMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23ABD6D51FA9E08C00CD6186 /* NFXNetServiceMonitor.swift */; };
+ 23ABD6D71FA9E08C00CD6186 /* NFXNetServiceMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23ABD6D51FA9E08C00CD6186 /* NFXNetServiceMonitor.swift */; };
+ 23B61B592022FF1F00048320 /* NFXClientConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23B61B582022FF1F00048320 /* NFXClientConnection.swift */; };
+ 23B61B5A2022FF1F00048320 /* NFXClientConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23B61B582022FF1F00048320 /* NFXClientConnection.swift */; };
026274D9214C6BC400AE1BDF /* WKWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026274D8214C6BC400AE1BDF /* WKWebViewController.swift */; };
8201A39D204E3E3F00AB2C3D /* NFXAuthenticationChallengeSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8201A39C204E3E3F00AB2C3D /* NFXAuthenticationChallengeSender.swift */; };
8201A39E204E451900AB2C3D /* NFXAuthenticationChallengeSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8201A39C204E3E3F00AB2C3D /* NFXAuthenticationChallengeSender.swift */; };
@@ -17,6 +28,17 @@
8229AD6C1F8FB34300A9D613 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8229AD6B1F8FB34300A9D613 /* Assets.xcassets */; };
8229AD6F1F8FB34300A9D613 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8229AD6D1F8FB34300A9D613 /* LaunchScreen.storyboard */; };
8229AD771F8FB4B500A9D613 /* netfox_ios.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3BC020F1C09CDA000C17F3A /* netfox_ios.framework */; };
+ 8F98A4DE202992FE007B2BB1 /* NFXPathNodeListCell_OSX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F98A4D9202992A2007B2BB1 /* NFXPathNodeListCell_OSX.swift */; };
+ 8F98A4E12029944D007B2BB1 /* NFXPathNodeListCell_OSX.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8F98A4DF20299399007B2BB1 /* NFXPathNodeListCell_OSX.xib */; };
+ 8F98A4E3202994CB007B2BB1 /* NFXPathNodeListController_OSX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F98A4E2202994CB007B2BB1 /* NFXPathNodeListController_OSX.swift */; };
+ 8F9DEE9120331F3C00E76B7E /* NFXJson2Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FBFFB4C20331DD000DF9319 /* NFXJson2Codable.swift */; };
+ 8F9DEE942033391800E76B7E /* NFXJsonParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F9DEE932033391800E76B7E /* NFXJsonParser.swift */; };
+ 8F9DEE962033393800E76B7E /* NFXCodableClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F9DEE952033393800E76B7E /* NFXCodableClass.swift */; };
+ 8F9DEE982033398500E76B7E /* String+CamelCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F9DEE972033398500E76B7E /* String+CamelCase.swift */; };
+ 8FBC1A8B2028ACEB00ABDF22 /* NFXPathNodeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FBC1A8A2028ACEB00ABDF22 /* NFXPathNodeManager.swift */; };
+ 8FBC1A8E2028AF2A00ABDF22 /* NFXPathNodeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FBC1A8A2028ACEB00ABDF22 /* NFXPathNodeManager.swift */; };
+ 8FD42BA62028A2D20084211A /* NFXPathNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FD42BA52028A2D20084211A /* NFXPathNode.swift */; };
+ 8FD42BA72028A3BB0084211A /* NFXPathNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FD42BA52028A2D20084211A /* NFXPathNode.swift */; };
826C4E9E1F979AB3008B440C /* NFXLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 826C4E9C1F979AB3008B440C /* NFXLoader.h */; };
826C4E9F1F979AB3008B440C /* NFXLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 826C4E9D1F979AB3008B440C /* NFXLoader.m */; };
82F6E1031F8FD81C002B31BD /* TextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82F6E1021F8FD81C002B31BD /* TextViewController.swift */; };
@@ -75,6 +97,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
+ 234DBCE21FA33D980086CB79 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = B3BC02061C09CDA000C17F3A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = E20FD2E81C6912D400DCFF61;
+ remoteInfo = netfox_osx;
+ };
8229AD741F8FB37000A9D613 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B3BC02061C09CDA000C17F3A /* Project object */;
@@ -84,7 +113,30 @@
};
/* End PBXContainerItemProxy section */
+/* Begin PBXCopyFilesBuildPhase section */
+ 23580CD020401E2E0009C40C /* Copy Netfox framework */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 23580CD120401E400009C40C /* netfox_osx.framework in Copy Netfox framework */,
+ );
+ name = "Copy Netfox framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
/* Begin PBXFileReference section */
+ 234DBCD31FA33BD90086CB79 /* netfox_mac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = netfox_mac.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 234DBCD51FA33BD90086CB79 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 234DBCD71FA33BD90086CB79 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 234DBCDA1FA33BD90086CB79 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
+ 234DBCDC1FA33BD90086CB79 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 234DBCDD1FA33BD90086CB79 /* netfox_mac.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = netfox_mac.entitlements; sourceTree = ""; };
+ 23ABD6CF1FA9C40000CD6186 /* NFXServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFXServer.swift; sourceTree = ""; };
+ 23ABD6D51FA9E08C00CD6186 /* NFXNetServiceMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NFXNetServiceMonitor.swift; sourceTree = ""; };
+ 23B61B582022FF1F00048320 /* NFXClientConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFXClientConnection.swift; sourceTree = ""; };
026274D8214C6BC400AE1BDF /* WKWebViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKWebViewController.swift; sourceTree = ""; };
8201A39C204E3E3F00AB2C3D /* NFXAuthenticationChallengeSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFXAuthenticationChallengeSender.swift; sourceTree = ""; };
8229AD621F8FB34300A9D613 /* netfox_ios_demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = netfox_ios_demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -92,6 +144,15 @@
8229AD661F8FB34300A9D613 /* ImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewController.swift; sourceTree = ""; };
8229AD691F8FB34300A9D613 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
8229AD6B1F8FB34300A9D613 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 8F98A4D9202992A2007B2BB1 /* NFXPathNodeListCell_OSX.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFXPathNodeListCell_OSX.swift; sourceTree = ""; };
+ 8F98A4DF20299399007B2BB1 /* NFXPathNodeListCell_OSX.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NFXPathNodeListCell_OSX.xib; sourceTree = ""; };
+ 8F98A4E2202994CB007B2BB1 /* NFXPathNodeListController_OSX.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFXPathNodeListController_OSX.swift; sourceTree = ""; };
+ 8F9DEE932033391800E76B7E /* NFXJsonParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFXJsonParser.swift; sourceTree = ""; };
+ 8F9DEE952033393800E76B7E /* NFXCodableClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFXCodableClass.swift; sourceTree = ""; };
+ 8F9DEE972033398500E76B7E /* String+CamelCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+CamelCase.swift"; sourceTree = ""; };
+ 8FBC1A8A2028ACEB00ABDF22 /* NFXPathNodeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFXPathNodeManager.swift; sourceTree = ""; };
+ 8FBFFB4C20331DD000DF9319 /* NFXJson2Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFXJson2Codable.swift; sourceTree = ""; };
+ 8FD42BA52028A2D20084211A /* NFXPathNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFXPathNode.swift; sourceTree = ""; };
8229AD6E1F8FB34300A9D613 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
8229AD701F8FB34300A9D613 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
826C4E9C1F979AB3008B440C /* NFXLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NFXLoader.h; sourceTree = ""; };
@@ -137,6 +198,14 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 234DBCD01FA33BD90086CB79 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 234DBCE11FA33BFE0086CB79 /* netfox_osx.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8229AD5F1F8FB34300A9D613 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -162,6 +231,29 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 234DBCD41FA33BD90086CB79 /* netfox_mac */ = {
+ isa = PBXGroup;
+ children = (
+ 234DBCD51FA33BD90086CB79 /* AppDelegate.swift */,
+ 234DBCD71FA33BD90086CB79 /* Assets.xcassets */,
+ 234DBCD91FA33BD90086CB79 /* MainMenu.xib */,
+ 234DBCDC1FA33BD90086CB79 /* Info.plist */,
+ 234DBCDD1FA33BD90086CB79 /* netfox_mac.entitlements */,
+ );
+ path = netfox_mac;
+ sourceTree = "";
+ };
+ 8F9DEE92203338F700E76B7E /* Json2Codable */ = {
+ isa = PBXGroup;
+ children = (
+ 8F9DEE952033393800E76B7E /* NFXCodableClass.swift */,
+ 8FBFFB4C20331DD000DF9319 /* NFXJson2Codable.swift */,
+ 8F9DEE932033391800E76B7E /* NFXJsonParser.swift */,
+ 8F9DEE972033398500E76B7E /* String+CamelCase.swift */,
+ );
+ path = Json2Codable;
+ sourceTree = "";
+ };
8229AD631F8FB34300A9D613 /* netfox_ios_demo */ = {
isa = PBXGroup;
children = (
@@ -190,6 +282,7 @@
children = (
CEFE42B41C0DDBD8001A56E0 /* netfox */,
8229AD631F8FB34300A9D613 /* netfox_ios_demo */,
+ 234DBCD41FA33BD90086CB79 /* netfox_mac */,
B3BC02101C09CDA000C17F3A /* Products */,
8229AD761F8FB4B500A9D613 /* Frameworks */,
);
@@ -201,6 +294,7 @@
B3BC020F1C09CDA000C17F3A /* netfox_ios.framework */,
E20FD2E91C6912D400DCFF61 /* netfox_osx.framework */,
8229AD621F8FB34300A9D613 /* netfox_ios_demo.app */,
+ 234DBCD31FA33BD90086CB79 /* netfox_mac.app */,
);
name = Products;
sourceTree = "";
@@ -227,6 +321,12 @@
B3F8BA7E1C833ABC00F9FBEA /* NFXRawBodyDetailsController.swift */,
B3F8BA7F1C833ABC00F9FBEA /* NFXSettingsController.swift */,
B3F8BA801C833ABC00F9FBEA /* NFXStatisticsController.swift */,
+ 23ABD6CF1FA9C40000CD6186 /* NFXServer.swift */,
+ 23ABD6D51FA9E08C00CD6186 /* NFXNetServiceMonitor.swift */,
+ 23B61B582022FF1F00048320 /* NFXClientConnection.swift */,
+ 8FD42BA52028A2D20084211A /* NFXPathNode.swift */,
+ 8FBC1A8A2028ACEB00ABDF22 /* NFXPathNodeManager.swift */,
+ 8F9DEE92203338F700E76B7E /* Json2Codable */,
B3F8BA811C833ABC00F9FBEA /* NFXWindowController.swift */,
);
path = Core;
@@ -259,6 +359,9 @@
B3F8D6781C833B1700F9FBEA /* NFXResponseTypeCell_OSX.xib */,
B3F8D6791C833B1700F9FBEA /* NFXSettingsController_OSX.swift */,
B3F8D67A1C833B1700F9FBEA /* NFXStatisticsController_OSX.swift */,
+ 8F98A4D9202992A2007B2BB1 /* NFXPathNodeListCell_OSX.swift */,
+ 8F98A4DF20299399007B2BB1 /* NFXPathNodeListCell_OSX.xib */,
+ 8F98A4E2202994CB007B2BB1 /* NFXPathNodeListController_OSX.swift */,
);
path = OSX;
sourceTree = "";
@@ -294,6 +397,25 @@
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
+ 234DBCD21FA33BD90086CB79 /* netfox_mac */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 234DBCE01FA33BD90086CB79 /* Build configuration list for PBXNativeTarget "netfox_mac" */;
+ buildPhases = (
+ 234DBCCF1FA33BD90086CB79 /* Sources */,
+ 234DBCD01FA33BD90086CB79 /* Frameworks */,
+ 234DBCD11FA33BD90086CB79 /* Resources */,
+ 23580CD020401E2E0009C40C /* Copy Netfox framework */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 234DBCE31FA33D980086CB79 /* PBXTargetDependency */,
+ );
+ name = netfox_mac;
+ productName = netfox_mac;
+ productReference = 234DBCD31FA33BD90086CB79 /* netfox_mac.app */;
+ productType = "com.apple.product-type.application";
+ };
8229AD611F8FB34300A9D613 /* netfox_ios_demo */ = {
isa = PBXNativeTarget;
buildConfigurationList = 8229AD711F8FB34300A9D613 /* Build configuration list for PBXNativeTarget "netfox_ios_demo" */;
@@ -354,10 +476,14 @@
B3BC02061C09CDA000C17F3A /* Project object */ = {
isa = PBXProject;
attributes = {
- LastSwiftUpdateCheck = 0720;
+ LastSwiftUpdateCheck = 0900;
LastUpgradeCheck = 0930;
ORGANIZATIONNAME = kasketis;
TargetAttributes = {
+ 234DBCD21FA33BD90086CB79 = {
+ CreatedOnToolsVersion = 9.0.1;
+ ProvisioningStyle = Automatic;
+ };
8229AD611F8FB34300A9D613 = {
CreatedOnToolsVersion = 9.0;
ProvisioningStyle = Automatic;
@@ -372,7 +498,7 @@
};
};
};
- buildConfigurationList = B3BC02091C09CDA000C17F3A /* Build configuration list for PBXProject "netfox" */;
+ buildConfigurationList = B3BC02091C09CDA000C17F3A /* Build configuration list for PBXProject "netfox_ios" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
@@ -388,11 +514,21 @@
B3BC020E1C09CDA000C17F3A /* netfox_ios */,
E20FD2E81C6912D400DCFF61 /* netfox_osx */,
8229AD611F8FB34300A9D613 /* netfox_ios_demo */,
+ 234DBCD21FA33BD90086CB79 /* netfox_mac */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 234DBCD11FA33BD90086CB79 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 234DBCD81FA33BD90086CB79 /* Assets.xcassets in Resources */,
+ 234DBCDB1FA33BD90086CB79 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8229AD601F8FB34300A9D613 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -414,6 +550,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 8F98A4E12029944D007B2BB1 /* NFXPathNodeListCell_OSX.xib in Resources */,
B3F8D6821C833B1700F9FBEA /* NFXResponseTypeCell_OSX.xib in Resources */,
B3F8D67B1C833B1700F9FBEA /* NetfoxWindow.xib in Resources */,
B3F8D67F1C833B1700F9FBEA /* NFXListCell_OSX.xib in Resources */,
@@ -423,6 +560,14 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 234DBCCF1FA33BD90086CB79 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 234DBCD61FA33BD90086CB79 /* AppDelegate.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8229AD5E1F8FB34300A9D613 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -440,6 +585,7 @@
buildActionMask = 2147483647;
files = (
B3F8BA821C833ABC00F9FBEA /* NFX.swift in Sources */,
+ 8FD42BA62028A2D20084211A /* NFXPathNode.swift in Sources */,
B3F8BA8A1C833ABC00F9FBEA /* NFXGenericBodyDetailsController.swift in Sources */,
B3F8BA901C833ABC00F9FBEA /* NFXHTTPModel.swift in Sources */,
8201A39D204E3E3F00AB2C3D /* NFXAuthenticationChallengeSender.swift in Sources */,
@@ -448,8 +594,11 @@
B3F8BAB11C833AC700F9FBEA /* NFXSettingsController_iOS.swift in Sources */,
B3F8BAB01C833AC700F9FBEA /* NFXListController_iOS.swift in Sources */,
B3F8BA8E1C833ABC00F9FBEA /* NFXHelper.swift in Sources */,
+ 23ABD6D01FA9C40000CD6186 /* NFXServer.swift in Sources */,
B3F8BA861C833ABC00F9FBEA /* NFXConstants.swift in Sources */,
+ 23B61B592022FF1F00048320 /* NFXClientConnection.swift in Sources */,
B3F8BAAD1C833AC700F9FBEA /* NFXHelper_iOS.swift in Sources */,
+ 23ABD6D61FA9E08C00CD6186 /* NFXNetServiceMonitor.swift in Sources */,
B3F8BA9A1C833ABC00F9FBEA /* NFXProtocol.swift in Sources */,
B3F8BA941C833ABC00F9FBEA /* NFXImageBodyDetailsController.swift in Sources */,
B3F8BA9E1C833ABC00F9FBEA /* NFXSettingsController.swift in Sources */,
@@ -458,6 +607,7 @@
826C4E9F1F979AB3008B440C /* NFXLoader.m in Sources */,
B3F8BA8C1C833ABC00F9FBEA /* NFXGenericController.swift in Sources */,
B3F8BA921C833ABC00F9FBEA /* NFXHTTPModelManager.swift in Sources */,
+ 8FBC1A8B2028ACEB00ABDF22 /* NFXPathNodeManager.swift in Sources */,
B3F8BA881C833ABC00F9FBEA /* NFXDetailsController.swift in Sources */,
B3F8BA981C833ABC00F9FBEA /* NFXListController.swift in Sources */,
B3F8BAAE1C833AC700F9FBEA /* NFXInfoController_iOS.swift in Sources */,
@@ -481,21 +631,32 @@
B3F8BAA11C833ABC00F9FBEA /* NFXStatisticsController.swift in Sources */,
B3F8D67D1C833B1700F9FBEA /* NFXInfoController_OSX.swift in Sources */,
B3F8D6831C833B1700F9FBEA /* NFXSettingsController_OSX.swift in Sources */,
+ 8FBC1A8E2028AF2A00ABDF22 /* NFXPathNodeManager.swift in Sources */,
B3F8D67C1C833B1700F9FBEA /* NFXDetailsController_OSX.swift in Sources */,
B3F8BA8F1C833ABC00F9FBEA /* NFXHelper.swift in Sources */,
B3F8BA871C833ABC00F9FBEA /* NFXConstants.swift in Sources */,
+ 8F98A4E3202994CB007B2BB1 /* NFXPathNodeListController_OSX.swift in Sources */,
+ 8F9DEE962033393800E76B7E /* NFXCodableClass.swift in Sources */,
B3F8BA9B1C833ABC00F9FBEA /* NFXProtocol.swift in Sources */,
B3F8D6811C833B1700F9FBEA /* NFXResponseTypeCell_OSX.swift in Sources */,
+ 23B61B5A2022FF1F00048320 /* NFXClientConnection.swift in Sources */,
B3F8BA951C833ABC00F9FBEA /* NFXImageBodyDetailsController.swift in Sources */,
+ 8F9DEE9120331F3C00E76B7E /* NFXJson2Codable.swift in Sources */,
B3F8BA9F1C833ABC00F9FBEA /* NFXSettingsController.swift in Sources */,
B3F8BAA31C833ABC00F9FBEA /* NFXWindowController.swift in Sources */,
B3F8BA851C833ABC00F9FBEA /* NFXAssets.swift in Sources */,
+ 8F98A4DE202992FE007B2BB1 /* NFXPathNodeListCell_OSX.swift in Sources */,
+ 8F9DEE942033391800E76B7E /* NFXJsonParser.swift in Sources */,
B3F8D6841C833B1700F9FBEA /* NFXStatisticsController_OSX.swift in Sources */,
B3F8BA8D1C833ABC00F9FBEA /* NFXGenericController.swift in Sources */,
+ 23ABD6D71FA9E08C00CD6186 /* NFXNetServiceMonitor.swift in Sources */,
+ 8F9DEE982033398500E76B7E /* String+CamelCase.swift in Sources */,
B3F8BA931C833ABC00F9FBEA /* NFXHTTPModelManager.swift in Sources */,
B3F8BA891C833ABC00F9FBEA /* NFXDetailsController.swift in Sources */,
+ 23ABD6D11FA9C53F00CD6186 /* NFXServer.swift in Sources */,
B3F8BA991C833ABC00F9FBEA /* NFXListController.swift in Sources */,
B3F8BA9D1C833ABC00F9FBEA /* NFXRawBodyDetailsController.swift in Sources */,
+ 8FD42BA72028A3BB0084211A /* NFXPathNode.swift in Sources */,
B3F8D6801C833B1700F9FBEA /* NFXListController_OSX.swift in Sources */,
B3F8BA971C833ABC00F9FBEA /* NFXInfoController.swift in Sources */,
);
@@ -504,6 +665,11 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ 234DBCE31FA33D980086CB79 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = E20FD2E81C6912D400DCFF61 /* netfox_osx */;
+ targetProxy = 234DBCE21FA33D980086CB79 /* PBXContainerItemProxy */;
+ };
8229AD751F8FB37000A9D613 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = B3BC020E1C09CDA000C17F3A /* netfox_ios */;
@@ -512,6 +678,14 @@
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
+ 234DBCD91FA33BD90086CB79 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 234DBCDA1FA33BD90086CB79 /* Base */,
+ );
+ name = MainMenu.xib;
+ sourceTree = "";
+ };
8229AD681F8FB34300A9D613 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
@@ -531,6 +705,55 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
+ 234DBCDE1FA33BD90086CB79 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_ENTITLEMENTS = netfox_mac/netfox_mac.entitlements;
+ CODE_SIGN_IDENTITY = "-";
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ INFOPLIST_FILE = netfox_mac/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.tapptitude.netfox-mac";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_VERSION = 4.0;
+ };
+ name = Debug;
+ };
+ 234DBCDF1FA33BD90086CB79 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_ENTITLEMENTS = netfox_mac/netfox_mac.entitlements;
+ CODE_SIGN_IDENTITY = "-";
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ INFOPLIST_FILE = netfox_mac/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.tapptitude.netfox-mac";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ SWIFT_VERSION = 4.0;
+ };
+ name = Release;
+ };
8229AD721F8FB34300A9D613 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -795,6 +1018,15 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 234DBCE01FA33BD90086CB79 /* Build configuration list for PBXNativeTarget "netfox_mac" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 234DBCDE1FA33BD90086CB79 /* Debug */,
+ 234DBCDF1FA33BD90086CB79 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
8229AD711F8FB34300A9D613 /* Build configuration list for PBXNativeTarget "netfox_ios_demo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -804,7 +1036,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- B3BC02091C09CDA000C17F3A /* Build configuration list for PBXProject "netfox" */ = {
+ B3BC02091C09CDA000C17F3A /* Build configuration list for PBXProject "netfox_ios" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B3BC02151C09CDA000C17F3A /* Debug */,
diff --git a/netfox.xcodeproj/xcshareddata/xcschemes/netfox_ios.xcscheme b/netfox.xcodeproj/xcshareddata/xcschemes/netfox_ios.xcscheme
index c1d296c8..6bd7d213 100644
--- a/netfox.xcodeproj/xcshareddata/xcschemes/netfox_ios.xcscheme
+++ b/netfox.xcodeproj/xcshareddata/xcschemes/netfox_ios.xcscheme
@@ -1,6 +1,6 @@
@@ -36,6 +37,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/netfox.xcodeproj/xcshareddata/xcschemes/netfox_osx.xcscheme b/netfox.xcodeproj/xcshareddata/xcschemes/netfox_osx.xcscheme
index 964be81b..90ad5e4f 100644
--- a/netfox.xcodeproj/xcshareddata/xcschemes/netfox_osx.xcscheme
+++ b/netfox.xcodeproj/xcshareddata/xcschemes/netfox_osx.xcscheme
@@ -1,6 +1,6 @@
@@ -36,6 +37,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/netfox/Core/Json2Codable/NFXCodableClass.swift b/netfox/Core/Json2Codable/NFXCodableClass.swift
new file mode 100644
index 00000000..14203232
--- /dev/null
+++ b/netfox/Core/Json2Codable/NFXCodableClass.swift
@@ -0,0 +1,156 @@
+//
+// NFXCodableClass.swift
+// netfox_osx
+//
+// Created by Ștefan Suciu on 2/13/18.
+// Copyright © 2018 kasketis. All rights reserved.
+//
+
+import Foundation
+
+
+fileprivate class NFXCodableProperty: CustomStringConvertible {
+
+ var name: String
+ var type: String
+ var values: [AnyHashable]
+ var isOptional: Bool
+
+ init(name: String, type: String, isOptional: Bool, value: AnyHashable? = nil) {
+ self.name = name
+ self.type = type
+ self.isOptional = isOptional
+ self.values = value != nil ? [value!] : []
+ }
+
+ fileprivate func getName() -> String {
+ return name.camelCasedString
+ }
+
+ fileprivate func getType() -> String {
+ return type.camelCasedString.uppercaseFirstLetter().singular
+ }
+
+ fileprivate func getOptional() -> String {
+ return isOptional ? "?" : "!"
+ }
+
+ var description: String {
+ return "\tvar \(getName()): \(getType())\(getOptional())"
+ }
+
+ var codingKeyDescription: String {
+ return "\t\tcase \(getName()) = \"\(name)\""
+ }
+}
+
+
+fileprivate struct NFXCodableEnum: CustomStringConvertible {
+
+ var name: String
+ var type: String
+ var cases: [AnyHashable]
+
+ fileprivate func getName() -> String {
+ return name.camelCasedString.uppercaseFirstLetter().singular
+ }
+
+ fileprivate func getType() -> String {
+ return type.camelCasedString.uppercaseFirstLetter().singular
+ }
+
+ init(name: String, type: String, cases: [AnyHashable]) {
+ self.name = name
+ self.type = type
+ self.cases = cases
+ }
+
+ var description: String {
+ var str = "enum \(getName()): \(getType()), Codable {\n"
+
+ if type == "Int" {
+ str += cases.map{ "\n\tcase type\($0) = \($0)" }.joined()
+ } else {
+ str += cases.map{ "\n\tcase \($0) = \"\($0)\"" }.joined()
+ }
+
+ str += "\n}\n\n"
+ return str
+ }
+}
+
+
+class NFXCodableClass: CustomStringConvertible {
+
+ var className: String
+ fileprivate var properties: [NFXCodableProperty] = []
+ fileprivate var enums: [NFXCodableEnum] = []
+
+ init(className: String) {
+ self.className = className
+ }
+
+ fileprivate func getClassName(_ string: String) -> String {
+ return string.camelCasedString.uppercaseFirstLetter().singular
+ }
+
+ func addProperty(name: String, type: String, value: AnyHashable? = nil) {
+ if let property = properties.first(where: { $0.name == name }) {
+ if property.type.contains("Any") && !type.contains("Any") {
+ property.type = type
+ } else if type == "Any" {
+ property.isOptional = true
+ }
+ if let value = value {
+ property.values.append(value)
+ }
+ } else {
+ let property = NFXCodableProperty(name: name, type: type, isOptional: type == "Any", value: value)
+ properties.append(property)
+ }
+ }
+
+ func detectEnums() {
+ properties.filter{ !$0.name.contains("id") && ($0.type == "Int" || $0.type == "String") && !$0.values.isEmpty }
+ .forEach { (property: NFXCodableProperty) in
+ let uniqueValues = Array(Set(property.values))
+
+ // need a better decision mechanism
+ if Double(uniqueValues.count) / Double(property.values.count) < 0.5 {
+ let codableEnum = NFXCodableEnum(name: property.name, type: property.type, cases: uniqueValues)
+ enums.append(codableEnum)
+ property.type = property.name
+ }
+ }
+ }
+
+ var description: String {
+ return properties.isEmpty ? className : classDescription
+ }
+
+ fileprivate var classDescription: String {
+ var str = enumsDescription
+ str += "class \(getClassName(className)): Codable {\n\n"
+ str += properties.map{ $0.description }.joined(separator: "\n")
+ str += codingKeysDescription
+ str += "\n}\n\n"
+ return str
+ }
+
+ fileprivate var codingKeysDescription: String {
+ var str = ""
+
+ let filteredProperties = properties.filter{ $0.getName() != $0.name }
+ if !filteredProperties.isEmpty {
+ str += "\n\n\tenum CodingKeys: String, CodingKey {\n"
+ str += filteredProperties.map{ $0.codingKeyDescription }.joined(separator: "\n")
+ str += "\n\t}"
+ }
+
+ return str
+ }
+
+ fileprivate var enumsDescription: String {
+ return enums.map{ $0.description }.joined()
+ }
+}
diff --git a/netfox/Core/Json2Codable/NFXJson2Codable.swift b/netfox/Core/Json2Codable/NFXJson2Codable.swift
new file mode 100644
index 00000000..c617a814
--- /dev/null
+++ b/netfox/Core/Json2Codable/NFXJson2Codable.swift
@@ -0,0 +1,134 @@
+//
+// NFXJson2Codable.swift
+// netfox_ios
+//
+// Created by Ștefan Suciu on 2/13/18.
+// Copyright © 2018 kasketis. All rights reserved.
+//
+
+import Foundation
+
+public class NFXJson2Codable {
+
+ private let boolParser = NFXJsonParser()
+ private let intParser = NFXJsonParser()
+ private let doubleParser = NFXJsonParser()
+ private let stringParser = NFXJsonParser()
+ private let dictionaryParser = NFXJsonParser<[String: Any]>()
+ private let arrayParser = NFXJsonParser<[Any]>()
+ private let urlParser = NFXJsonParser()
+ private let dateParser = NFXJsonParser()
+
+ private var codableClasses: [NFXCodableClass] = []
+
+ func convertToCodable(name: String, from array: [Any]) -> NFXCodableClass {
+ if let item = array.first, dictionaryParser.canParse(item) {
+ return array.map{ convertToCodable(name: name, from: dictionaryParser.parse($0)) }.first!
+ }
+
+ return getCodableClass(name: convertToProperty(key: name, value: array.first!))
+ }
+
+ func convertToCodable(name: String, from dictionary: [String: Any]) -> NFXCodableClass {
+ let codableClass = getCodableClass(name: name)
+
+ dictionary.keys.forEach { key in
+ let type = convertToProperty(key: key, value: dictionary[key]!)
+ if type == "Int" || type == "String" {
+ codableClass.addProperty(name: key, type: type, value: dictionary[key]! as? AnyHashable)
+ } else {
+ codableClass.addProperty(name: key, type: type)
+ }
+ }
+
+ return codableClass
+ }
+
+ private func convertToProperty(key: String, value: Any) -> String {
+ if boolParser.canParse(value) {
+ return boolParser.getPropertyType()
+ }
+
+ if intParser.canParse(value) {
+ if dateParser.canParse(key: key, value: intParser.parse(value)) {
+ return dateParser.getPropertyType()
+ }
+
+ return intParser.getPropertyType()
+ }
+
+ if doubleParser.canParse(value) {
+ return doubleParser.getPropertyType()
+ }
+
+ if stringParser.canParse(value) {
+ let stringValue = stringParser.parse(value)
+
+ if urlParser.canParse(stringValue) {
+ return urlParser.getPropertyType()
+ }
+
+ if dateParser.canParse(stringValue) {
+ return dateParser.getPropertyType()
+ }
+
+ return stringParser.getPropertyType()
+ }
+
+ if dictionaryParser.canParse(value) {
+ let dictionary = dictionaryParser.parse(value)
+
+ if let _ = dictionary.first {
+ let _ = convertToCodable(name: key, from: dictionary)
+ return dictionaryParser.getPropertyType(name: key)
+ } else {
+ return "Any"
+ }
+ }
+
+ if arrayParser.canParse(value) {
+ let array = arrayParser.parse(value)
+
+ if let _ = array.first {
+ let codableClass = convertToCodable(name: key, from: array)
+ return "[\(codableClass.className)]"
+ } else {
+ return "[Any]"
+ }
+ }
+
+ return "Any"
+ }
+
+ private func getCodableClass(name: String) -> NFXCodableClass {
+ var codableClass: NFXCodableClass
+
+ if let foundClass = codableClasses.first(where: { $0.className == name }) {
+ codableClass = foundClass
+ } else {
+ codableClass = NFXCodableClass(className: name)
+ codableClasses.append(codableClass)
+ }
+
+ return codableClass
+ }
+
+ func getResourceName(from url: String?) -> String {
+ guard var url = url else {
+ return "ClassName"
+ }
+
+ url = String(url.split(separator: "?").first!)
+
+ var components = url.split(separator: "/")
+ if let _ = Int(components.last ?? "") {
+ components = Array(components.dropLast())
+ }
+
+ return String(components.last ?? "")
+ }
+
+ func printClasses() -> String {
+ return codableClasses.map{ $0.description }.joined()
+ }
+}
diff --git a/netfox/Core/Json2Codable/NFXJsonParser.swift b/netfox/Core/Json2Codable/NFXJsonParser.swift
new file mode 100644
index 00000000..c21fa6a4
--- /dev/null
+++ b/netfox/Core/Json2Codable/NFXJsonParser.swift
@@ -0,0 +1,71 @@
+//
+// NFXJsonParser.swift
+// netfox_osx
+//
+// Created by Ștefan Suciu on 2/13/18.
+// Copyright © 2018 kasketis. All rights reserved.
+//
+
+import Foundation
+
+
+class NFXJsonParser {
+
+ func canParse(_ value: Any) -> Bool {
+ return value is T
+ }
+
+ func parse(_ value: Any) -> T {
+ return value as! T
+ }
+
+ func getPropertyType() -> String {
+ return "\(T.self)"
+ }
+}
+
+extension NFXJsonParser where T == [String: Any] {
+
+ func getPropertyType(name: String) -> String {
+ return "\(name)"
+ }
+}
+
+extension NFXJsonParser where T == URL {
+
+ func canParse(_ value: Any) -> Bool {
+ let stringValue = value as! String
+ return stringValue.hasPrefix("https://") || stringValue.hasPrefix("http://")
+ }
+}
+
+extension NFXJsonParser where T == Date {
+
+ private var keys: [String] {
+ return ["_at", "date", "day"]
+ }
+
+ func canParse(_ value: String) -> Bool {
+ let dateFormats = ["yyyy-MM-dd HH:mm:ss.ZZZZZ", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ssZZZZZZ"]
+ let dateFormatter = DateFormatter()
+
+ dateFormatter.dateFormat = dateFormats.first!
+ let accumulator = dateFormatter.date(from: value) != nil
+
+ return dateFormats.dropFirst().map{
+ dateFormatter.dateFormat = $0
+ return dateFormatter.date(from: value) != nil
+ }.reduce(accumulator, { $0 || $1 })
+ }
+
+ func canParse(key: String, value: Int) -> Bool {
+ return keys.dropFirst().map{ key.contains($0) }.reduce(key.contains(keys.first!), { $0 || $1 })
+ }
+}
+
+extension NFXJsonParser where T == Bool {
+
+ func canParse(_ value: Any) -> Bool {
+ return String(describing: type(of: value)).contains("Boolean")
+ }
+}
diff --git a/netfox/Core/Json2Codable/String+CamelCase.swift b/netfox/Core/Json2Codable/String+CamelCase.swift
new file mode 100644
index 00000000..1354cb43
--- /dev/null
+++ b/netfox/Core/Json2Codable/String+CamelCase.swift
@@ -0,0 +1,60 @@
+//
+// String+CamelCase.swift
+// netfox_osx
+//
+// Created by Ștefan Suciu on 2/13/18.
+// Copyright © 2018 kasketis. All rights reserved.
+//
+
+import Foundation
+
+extension String {
+
+ func uppercaseFirstLetter() -> String {
+ if hasPrefix("[") && hasSuffix("]") {
+ let leadingParanthesisCount = components(separatedBy: "[").count - 1
+ let endingParanthesisCount = components(separatedBy: "]").count - 1
+ let startIndex = index(self.startIndex, offsetBy: leadingParanthesisCount)
+ let endIndex = index(self.endIndex, offsetBy: -endingParanthesisCount)
+ let stringWithoutParanthesis = self[startIndex.. String {
+ return prefix(1).lowercased() + dropFirst()
+ }
+
+ func replacingLastOccurences(of searchString: String, with replacementString: String) -> String {
+ if let range = self.range(of: searchString, options: [.backwards, .caseInsensitive]) {
+ return self.replacingCharacters(in: range, with: replacementString)
+ }
+
+ return self
+ }
+
+ var camelCasedString: String {
+ let splittedComponents = components(separatedBy: CharacterSet(charactersIn: "_- "))
+ return splittedComponents.first! + splittedComponents.dropFirst().map{ $0.uppercaseFirstLetter() }.joined()
+ }
+
+ var singular: String {
+ let paranthesisCount = components(separatedBy: "]").count - 1
+ let stringWithoutParanthesis = dropLast(paranthesisCount)
+
+ if stringWithoutParanthesis.hasSuffix("ies") {
+ return replacingLastOccurences(of: "ies", with: "y")
+ }
+
+ if stringWithoutParanthesis.hasSuffix("s") {
+ return stringWithoutParanthesis.dropLast() + String(repeating: "]", count: paranthesisCount)
+ }
+
+ return self
+ }
+}
diff --git a/netfox/Core/NFX.swift b/netfox/Core/NFX.swift
index 89e78d0c..51044fe7 100755
--- a/netfox/Core/NFX.swift
+++ b/netfox/Core/NFX.swift
@@ -6,6 +6,7 @@
//
import Foundation
+
#if os(OSX)
import Cocoa
#else
@@ -63,6 +64,7 @@ open class NFX: NSObject
fileprivate var ignoredURLs = [String]()
fileprivate var filters = [Bool]()
fileprivate var lastVisitDate: Date = Date()
+ var server: NFXServer?
internal var cacheStoragePolicy = URLCache.StoragePolicy.notAllowed
@objc open func start()
@@ -94,6 +96,20 @@ open class NFX: NSObject
#endif
}
+ @objc public func startServer() {
+ if !self.started {
+ self.start()
+ }
+
+ self.server = NFXServer()
+ self.server?.startServer()
+ }
+
+ @objc public func stopServer() {
+ self.server?.stopServer()
+ self.server = nil
+ }
+
fileprivate func showMessage(_ msg: String) {
print("netfox \(nfxVersion) - [https://github.com/kasketis/netfox]: \(msg)")
}
@@ -261,7 +277,8 @@ extension NFX {
navigationController.navigationBar.tintColor = UIColor.NFXOrangeColor()
navigationController.navigationBar.barTintColor = UIColor.NFXStarkWhiteColor()
#if !swift(>=4.0)
- navigationController.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.NFXOrangeColor()]
+
+ navigationController.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.NFXOrangeColor()]
#else
navigationController.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.NFXOrangeColor()]
#endif
@@ -285,6 +302,14 @@ extension NFX {
public func windowDidClose() {
self.presented = false
+
+ if Bundle.main.bundleIdentifier == "com.tapptitude.netfox-mac" {
+ #if !swift(>=4.0)
+ NSApplication.shared().terminate(self)
+ #else
+ NSApplication.shared.terminate(self)
+ #endif
+ }
}
private func setupNetfoxMenuItem() {
diff --git a/netfox/Core/NFXAssets.swift b/netfox/Core/NFXAssets.swift
index cff3403e..738e6f70 100644
--- a/netfox/Core/NFXAssets.swift
+++ b/netfox/Core/NFXAssets.swift
@@ -12,6 +12,13 @@ enum NFXAssetName {
case close
case info
case statistics
+ case cloud
+ case folder
+ case fileDownloading
+ case fileSuccess
+ case fileWarning
+ case fileUnauthorized
+ case serverError
}
class NFXAssets
@@ -24,6 +31,13 @@ class NFXAssets
case .close: return getCloseImageBase64()
case .info: return getInfoImageBase64()
case .statistics: return getStatisticsImageBase64()
+ case .cloud: return getCloudImageBase64()
+ case .folder: return getFolderImageBase64()
+ case .fileDownloading: return getFileDownloadingBase64()
+ case .fileSuccess: return getFileSuccessBase64()
+ case .fileWarning: return getFileWarningBase64()
+ case .fileUnauthorized: return getFileUnauthorizedBase64()
+ case .serverError: return getServerErrorBase64()
}
}()
@@ -50,4 +64,37 @@ class NFXAssets
return "iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAflBMVEUAAADsXijsXijsXijsXijtXSfsXijsXSjsXijsXijsXSbsXijtXSjsXijsXifsXijsXiftXifsXifsXCfsXifsXifsXifsXifsXSftXSfsWyjsXijsXifsXifsXijtXifsXiftXybsXSfsXijtXSftXifsWibsXijuYCfsXijgd1NoAAAAKXRSTlMANNuwVBbuh/CUA9UG+t/QvCz0D+jYy3RKOQrlxqmOgGUgv7VvVwxbHXzMYTYAAAEjSURBVDjL7ZRJcoMwEEVlEgkhMU8GzOgp+fe/YChXxQSqK27vvPBbQfdbSD1IkMSxYONb6/Nta+n4B4XWZFjgCQTCZMciCWfZEUyctzzTDH3GlgfgSMmTCuXWlREQUXILbIcz0UijiZC/AiC4/FXNgOBMXdCckI4jEPvLYLdId1Q1pgOi+dZZhDRZjlASpfO7MsXZv32OCNxbdh9gT9U5BoLqN1orlNI0HnRNNiUHvCUsj8htiJOkO/gdlauMi3sRiXabTSc0ULFnI2kvLzaiLyjn1SeLKsdzb92OoA7DmooLClMURnDxAI/ruphxua4+HDTP9qAyazMFj+VK4zhGMmwXqplXdV7URsF96Mr7tjywe6jr8ndV6P+Ru0KuNrHoVukfuacsKwHjtUEAAAAASUVORK5CYII="
}
+ class func getCloudImageBase64() -> String
+ {
+ return "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAAAA3RJREFUeJztmU9oFFccx7+/36yk8ZA0s6F1cyutlxxEsWJPtceGiNpDYlvqYUUNQjabSA4ehGGhQi8LmUlIGwRzESktVgUhx54KBQWhaA+i8SBoEMnCgn8S7O/nwQiym9ns252Z3aTvc/y9+b33fd+Z37w38wCLxWKxWCwWi8VisVgs/zco6g49z+N0Ov0VgEEAXwDYKSIfAmBmLgG4D+BvEblRKpX+LBQKErUGEyIzwPO8lOu6x1X1LDN/Uk+OiDwkop+Wl5cvFgqF11FpMSESA3zf7yeiSwD2NNjFbWY+Njo6ejcKPSY0bUAQBIMi8hszb2+mHxF54TjO0Vwud6NZTSY0ZUAQBIMArgFIRSMHr4nomyRNaNgA3/f7VfVms3d+HZ4z8/6kyqEhAzzPS6XT6ZsAdkes5x23ieigqh4F8LWI7GLmjwCIiDxl5n8ALKyurl6enJx81sxADRng+/4pIpprZuCNEBFl5o30vQQw1dHR8ePIyMiLRsYxNsDzPO7p6blf71KXEP+q6qF8Pv/ANJFNE1zXPdBmkweAfiL6a2Zm5jPTRGMDiOigaU5CfCwi14vFYqdJkrEBeLu9bVf6U6nUOZOERgzY2UBOYhDRxPT0dLre640NWPuwaWc6VfW7ei82MmBubm47M28z15Q4Q/W+C+oyQFXJ9/1jKysri83pSowvHcdZDILgB1WtudRvuA9Y2/X9DOBEZPISRFUvZDKZ08PDw/+t117zCVBVcl13Fpt08gBARCeXlpZmw9qdWsm9vb3fAzgfuark2TswMHBvYWHhTmVDaAkUi8VOx3EWmXlHvNqSQUQed3d3f5rNZl+9Hw8tAcdxhrfK5AGAmfvK5fJQVTwsgYiOxCupJRyuDNQy4PN4tSQPEe2rjIUaICKZeOW0hL7KQKgBzKzxamkJVWcQoQao6pN4tbSEx5WBWu+AW/FqaQlVc6r1BFyNV0vyEFHVnEIN6Orq+h3AlikDVX0kIlcq46EGZLPZVyJyJl5ZyUFE4/l8fqUyXvNjaHx8/FdV/SU+WckgIsHY2Ngf67Vt+D8gk8mMbmYTRCQolUoTYe11nwtMTU19C6DIzFWbiXZEVR8x80Qul6uq+/cxOhiZn5//oFwuD6nqEQB7AfS10S+yVbxd528R0VURubJezVssFovFYrFYLBaLxQK8AdiMC3Y6JoDbAAAAAElFTkSuQmCC"
+ }
+
+ class func getFolderImageBase64() -> String
+ {
+ return "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"
+ }
+
+ class func getFileDownloadingBase64() -> String
+ {
+ return "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"
+ }
+
+ class func getFileSuccessBase64() -> String
+ {
+ return "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"
+ }
+
+ class func getFileWarningBase64() -> String
+ {
+ return "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"
+ }
+
+ class func getFileUnauthorizedBase64() -> String
+ {
+ return "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"
+ }
+
+ class func getServerErrorBase64() -> String {
+ return "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAv1JREFUeJzt2rGLHkUcxvHPjodgYiVc7gQliUHBaGWtgmkk2Es0ighiOrFJZWMREPwPYhQFi+RAUWysIlhIElRsPFEJaDSIGm1MFETnNxZ3F16Tu/O93O7OqfOFgeXd993f8zw7s8zMvjQajUaj0Wg0/p90fV0ouB0PFu7FnbgVN+K6xMXgWyx2fIB3E2f7ql2NwkzwWOZ0oWykZc4Ejxdmavu4JoL9mS83anyVIL4I9tf2MzXB9swrmzW+ShAvF7bV9rcuhfnMx32bn2gfBXO1fa7KsvlNd/kpesLnWy6EwrbMJ0Obnwjhwy01HAqvjWV+IoRjtX2D4KGxzU+E8GrmVObnTGT+zPyQeT9zJLin9DiXuYrCTOZsrQCmDOlU8MAgAQRP1Da4kSFT+n5uZM7UNrbBEE4HN/ViPrhjYLFvBrPBjszbPYdwQx8BPDNkAMHsRK25nsM9uukAMieGDODKegMEfP80PtM65+6+5vS2AIUj03xvvQBu7klLFRL3FZ4Pdm/4x8GeTPybh8DE8yAyC8HOac0/mfltSPNjBjARxKXg4D+Zf25oIbUCWGnB4TXv/FgiagawHMKjK3W75eK3BZ+mPiYQU9JdsYhZLZShCH7tuCtxLi1/8OKY5muT2F54AbpgT1dhi3qyB5Sl4xizflA6diccGLPwhIDL216F+bHrJ7qOR1Jh39jFoXB0eQ0wX3iphoZgX5f5Pm21TciRCL7rMjmtPyX+zxL8kZBrC6lISfiptoqKXEj4rEbl4K3CjsJc8E4NDVhMHe/VqNxxKC3dgR87nq6k4WTC8RhxGvr3+pcPhtvbX4NYmngtpMRXiTfGFlA4tjIPiDpvgY4nzq0shnYFi2krvY8bkOBix97E+QQdX3ccqi1sLDqeSpy/6kRweMx1eY0WPLtuOsHBzKXaQvtumV+Ch6fqIsHOzImhN0ZHMp4zrwe3rDEc1qawCwdiacW4F7OJ66dKsRLB77hg6S95JzsWOr6pravRaDQajUaj0Wg0GluJvwDvfvlgh9X/mgAAAABJRU5ErkJggg=="
+ }
}
diff --git a/netfox/Core/NFXClientConnection.swift b/netfox/Core/NFXClientConnection.swift
new file mode 100644
index 00000000..34e1deeb
--- /dev/null
+++ b/netfox/Core/NFXClientConnection.swift
@@ -0,0 +1,221 @@
+//
+// NFXClientConnection.swift
+// netfox
+//
+// Created by Alexandru Tudose on 01/02/2018.
+// Copyright © 2018 kasketis. All rights reserved.
+//
+
+import Foundation
+
+class NFXClientConnection: NSObject {
+ let inputStream: InputStream
+ let outputStream: OutputStream
+
+ private var thread: Thread!
+ private var runLoop: CFRunLoop?
+
+ var _onClose: (() -> Void)?
+
+ init (inputStream: InputStream, outputStream: OutputStream) {
+ self.inputStream = inputStream
+ self.outputStream = outputStream
+ }
+
+ deinit {
+ inputStream.close()
+ outputStream.close()
+ }
+
+ func scheduleOnBackgroundRunLoop() {
+ let threadName = String(describing: self).components(separatedBy: .punctuationCharacters)[1]
+ thread = Thread(target: self, selector: #selector(startRunLoop), object: nil)
+ thread.name = "\(threadName)-\(UUID().uuidString)"
+ thread.start()
+ }
+
+ @objc func startRunLoop() {
+ scheduleOnRunLoop()
+ self.runLoop = CFRunLoopGetCurrent()
+ CFRunLoopRun()
+ }
+
+ @objc func stopRunLoop() {
+ self.thread?.cancel()
+ self.thread = nil
+ if let runLoop = runLoop {
+ CFRunLoopStop(runLoop)
+ }
+ }
+
+ @objc func scheduleOnRunLoop() {
+ inputStream.schedule(in: RunLoop.current, forMode: .defaultRunLoopMode)
+ outputStream.schedule(in: RunLoop.current, forMode: .defaultRunLoopMode)
+ inputStream.open()
+ outputStream.open()
+ }
+
+ var onClose: (() -> Void)? {
+ get { return _onClose }
+ set { _onClose = {[unowned self] in
+ self.inputStream.delegate = nil
+ self.stopRunLoop()
+ self.inputStream.close()
+ self.outputStream.close()
+ DispatchQueue.main.async {
+ newValue?()
+ }
+ }
+ }
+ }
+
+ static let serialQueue = DispatchQueue(label: "NFXClientConnection")
+ @objc func writeModel(_ model: NFXHTTPModel) {
+ NFXClientConnection.serialQueue.async {
+ let models = [ model.toJSON() ]
+ let jsonData = try! JSONSerialization.data(withJSONObject: models, options: [])
+ self.writeData(jsonData)
+ }
+ }
+
+ @objc func writeAllModels() {
+ NFXClientConnection.serialQueue.async {
+ let models = NFXHTTPModelManager.sharedInstance.getModels()
+ models.reversed().chunked(by: 20).forEach({ items in
+ print(items.count)
+ let models = items.map({ $0.toJSON() })
+ let jsonData = try! JSONSerialization.data(withJSONObject: models, options: [])
+ self.writeData(jsonData)
+ })
+ }
+ }
+
+ func writeData(_ data: Data) {
+ // write size of payload
+ let messageSize = Int32(data.count)
+ outputStream.write(toByteArrary(value: messageSize), maxLength: MemoryLayout.size(ofValue: messageSize))
+
+ // write payload
+ let bytes = [UInt8](data)
+ var bytesWritten = 0
+ while bytes.count > bytesWritten {
+ let count = bytes.count - bytesWritten
+ let writeCount = outputStream.write([UInt8](bytes[bytesWritten...bytes.count - 1]), maxLength: count)
+ if writeCount == -1 {
+ onClose?()
+ print(outputStream.streamError ?? "")
+ print("Netfox connection - An error occured while writing data")
+ return
+ }
+
+ bytesWritten += writeCount
+ }
+ }
+
+ func prepareForReadingStream() {
+ inputStream.delegate = self
+ }
+
+ func prepareForWritingStream() {
+ outputStream.delegate = self
+ }
+
+ var bufferData: Data = Data()
+ var toReadCount: Int = 0
+
+ func readData() {
+ if toReadCount == 0 {
+ var sizeBuffer: [UInt8] = [0,0,0,0]
+ inputStream.read(&sizeBuffer, maxLength: sizeBuffer.count)
+ let data = NSData(bytes: sizeBuffer, length: sizeBuffer.count)
+ toReadCount = Int(data.uint32.littleEndian)
+ bufferData = Data()
+ }
+
+ let bufferSize = min(toReadCount, 2048)
+ var buffer = Array(repeating: 0, count: bufferSize)
+ let readCount = inputStream.read(&buffer, maxLength: bufferSize)
+ bufferData.append(buffer, count: readCount)
+ toReadCount -= readCount
+ if toReadCount == 0 {
+ NFX.sharedInstance().addJSONModels(bufferData)
+ bufferData = Data()
+ }
+ }
+}
+
+
+extension NFXClientConnection: StreamDelegate {
+ public func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
+ guard let _ = aStream as? InputStream else {
+ return
+ }
+
+ switch eventCode {
+ case .hasBytesAvailable:
+ readData()
+ case .errorOccurred, .endEncountered:
+ print(eventCode)
+ onClose?()
+ case .openCompleted, .hasSpaceAvailable:
+ break
+ default:
+ break
+ }
+ }
+}
+
+extension NFX {
+ public func addJSONModels(_ data: Data) {
+ guard let json = try? JSONSerialization.jsonObject(with: data, options: []) else {
+ return
+ }
+
+ if let jsonModels = json as? [[String: Any]] {
+ let models: [NFXHTTPModel] = jsonModels.flatMap({
+ let model = NFXHTTPModel()
+ model.fromJSON(json: $0)
+ return model
+ })
+
+ models.reversed().forEach({ NFXHTTPModelManager.sharedInstance.add($0) })
+
+ DispatchQueue.main.async {
+ NotificationCenter.default.post(name: Notification.Name(rawValue: "NFXReloadData"), object: nil)
+ }
+ }
+ }
+}
+
+
+func toByteArrary(value: T) -> [UInt8] where T: FixedWidthInteger{
+ var bigEndian = value.littleEndian
+ let count = MemoryLayout.size
+ let bytePtr = withUnsafePointer(to: &bigEndian) {
+ $0.withMemoryRebound(to: UInt8.self, capacity: count) {
+ UnsafeBufferPointer(start: $0, count: count)
+ }
+ }
+
+ return Array(bytePtr)
+}
+
+extension NSData {
+ var uint32: UInt32 {
+ get {
+ var number: UInt32 = 0
+ self.getBytes(&number, length: MemoryLayout.size)
+ return number
+ }
+ }
+}
+
+
+extension Array {
+ func chunked(by chunkSize:Int) -> [[Element]] {
+ let groups = stride(from: 0, to: self.count, by: chunkSize).map {
+ Array(self[$0..<[$0 + chunkSize, self.count].min()!])
+ }
+ return groups
+ }
+}
diff --git a/netfox/Core/NFXGenericController.swift b/netfox/Core/NFXGenericController.swift
index 8249b555..04ec6aaf 100755
--- a/netfox/Core/NFXGenericController.swift
+++ b/netfox/Core/NFXGenericController.swift
@@ -45,8 +45,8 @@ class NFXGenericController: NFXViewController
for match in matchesBodyHeaders {
#if !swift(>=4.0)
- tempMutableString.addAttribute(NSFontAttributeName, value: NFXFont.NFXFontBold(size: 14), range: match.range)
- tempMutableString.addAttribute(NSForegroundColorAttributeName, value: NFXColor.NFXOrangeColor(), range: match.range)
+ tempMutableString.addAttribute(NSAttributedStringKey.font, value: NFXFont.NFXFontBold(size: 14), range: match.range)
+ tempMutableString.addAttribute(NSAttributedStringKey.foregroundColor, value: NFXColor.NFXOrangeColor(), range: match.range)
#else
tempMutableString.addAttribute(NSAttributedStringKey.font, value: NFXFont.NFXFontBold(size: 14), range: match.range)
tempMutableString.addAttribute(NSAttributedStringKey.foregroundColor, value: NFXColor.NFXOrangeColor(), range: match.range)
@@ -58,7 +58,7 @@ class NFXGenericController: NFXViewController
for match in matchesKeys {
#if !swift(>=4.0)
- tempMutableString.addAttribute(NSForegroundColorAttributeName, value: NFXColor.NFXBlackColor(), range: match.range)
+ tempMutableString.addAttribute(NSAttributedStringKey.foregroundColor, value: NFXColor.NFXBlackColor(), range: match.range)
#else
tempMutableString.addAttribute(NSAttributedStringKey.foregroundColor, value: NFXColor.NFXBlackColor(), range: match.range)
#endif
diff --git a/netfox/Core/NFXHTTPModel.swift b/netfox/Core/NFXHTTPModel.swift
index 9e223bd7..50f5907c 100755
--- a/netfox/Core/NFXHTTPModel.swift
+++ b/netfox/Core/NFXHTTPModel.swift
@@ -56,6 +56,7 @@ fileprivate func < (lhs: T?, rhs: T?) -> Bool {
self.requestTimeout = request.getNFXTimeout()
self.requestHeaders = request.getNFXHeaders()
self.requestType = requestHeaders?["Content-Type"] as! String?
+
self.requestCurl = request.getCurl()
}
@@ -344,3 +345,69 @@ fileprivate func < (lhs: T?, rhs: T?) -> Bool {
}
}
+
+
+/// allow serialization
+extension NFXHTTPModel {
+ func toJSON() -> [String: Any] {
+ var json: [String: Any] = [:]
+
+ json["requestURL"] = requestURL
+ json["requestMethod"] = requestMethod
+
+ json["requestCachePolicy"] = requestCachePolicy
+ json["requestDate"] = requestDate?.timeIntervalSince1970
+ json["requestTime"] = requestTime
+ json["requestTimeout"] = requestTimeout
+ json["requestHeaders"] = requestHeaders
+ json["requestBodyLength"] = requestBodyLength
+ json["requestType"] = requestType
+ json["responseStatus"] = responseStatus
+ json["responseType"] = responseType
+ json["responseDate"] = responseDate?.timeIntervalSince1970
+
+ json["responseTime"] = responseTime
+ json["responseHeaders"] = responseHeaders
+ json["responseBodyLength"] = responseBodyLength
+ json["timeInterval"] = timeInterval
+ json["randomHash"] = randomHash
+ json["shortType"] = shortType
+ json["noResponse"] = noResponse
+
+
+ json["requestBody"] = getRequestBody()
+ json["responseBody"] = getResponseBody()
+
+ return json
+ }
+
+ func fromJSON(json: [String: Any]) {
+ requestURL = json["requestURL"] as? String
+ requestMethod = json["requestMethod"] as? String
+
+ requestCachePolicy = json["requestCachePolicy"] as? String
+ requestDate = json["requestDate"].flatMap({ $0 as? TimeInterval }).flatMap({ Date(timeIntervalSince1970: $0) })
+ requestTime = json["requestTime"] as? String
+ requestTimeout = json["requestTimeout"] as? String
+ requestHeaders = json["requestHeaders"] as? [AnyHashable: Any]
+ requestBodyLength = json["requestBodyLength"] as? Int
+ requestType = json["requestType"] as? String
+ responseStatus = json["responseStatus"] as? Int
+ responseType = json["responseType"] as? String
+ responseDate = json["responseDate"].flatMap({ $0 as? TimeInterval }).flatMap({ Date(timeIntervalSince1970: $0) })
+
+ responseTime = json["responseTime"] as? String
+ responseHeaders = json["responseHeaders"] as? [AnyHashable: Any]
+ responseBodyLength = json["responseBodyLength"] as? Int
+ timeInterval = json["timeInterval"] as? Float
+ randomHash = json["randomHash"] as? NSString
+ shortType = json["shortType"] as! NSString
+ noResponse = json["noResponse"] as? Bool ?? true
+
+ let requestBody = json["requestBody"] as? String ?? ""
+ let responseBody = json["responseBody"] as? String ?? ""
+
+ saveRequestBodyData(requestBody.data(using: .utf8)!)
+ saveResponseBodyData(responseBody.data(using: .utf8)!)
+ }
+}
diff --git a/netfox/Core/NFXHTTPModelManager.swift b/netfox/Core/NFXHTTPModelManager.swift
index 0cb549da..bd4be6c6 100755
--- a/netfox/Core/NFXHTTPModelManager.swift
+++ b/netfox/Core/NFXHTTPModelManager.swift
@@ -20,6 +20,10 @@ final class NFXHTTPModelManager: NSObject
syncQueue.async {
self.models.insert(obj, at: 0)
NotificationCenter.default.post(name: NSNotification.Name.NFXAddedModel, object: obj)
+
+ #if os(OSX)
+ NFXPathNodeManager.sharedInstance.add(obj)
+ #endif
}
}
@@ -28,6 +32,10 @@ final class NFXHTTPModelManager: NSObject
syncQueue.async {
self.models.removeAll()
NotificationCenter.default.post(name: NSNotification.Name.NFXClearedModels, object: nil)
+
+ #if os(OSX)
+ NFXPathNodeManager.sharedInstance.clear()
+ #endif
}
}
diff --git a/netfox/Core/NFXHelper.swift b/netfox/Core/NFXHelper.swift
index 61091dc1..0f071a83 100644
--- a/netfox/Core/NFXHelper.swift
+++ b/netfox/Core/NFXHelper.swift
@@ -210,62 +210,89 @@ extension URLResponse
extension NFXImage
{
- class func NFXSettings() -> NFXImage
- {
- #if os (iOS)
- return UIImage(data: NFXAssets.getImage(NFXAssetName.settings), scale: 1.7)!
- #elseif os(OSX)
- return NSImage(data: NFXAssets.getImage(NFXAssetName.settings))!
- #endif
- }
+ static let settings: NFXImage = {
+ #if os (iOS)
+ return UIImage(data: NFXAssets.getImage(NFXAssetName.settings), scale: 1.7)!
+ #elseif os(OSX)
+ return NSImage(data: NFXAssets.getImage(NFXAssetName.settings))!
+ #endif
+ }()
- class func NFXClose() -> NFXImage
- {
- #if os (iOS)
- return UIImage(data: NFXAssets.getImage(NFXAssetName.close), scale: 1.7)!
- #elseif os(OSX)
- return NSImage(data: NFXAssets.getImage(NFXAssetName.close))!
- #endif
- }
-
- class func NFXInfo() -> NFXImage
- {
- #if os (iOS)
- return UIImage(data: NFXAssets.getImage(NFXAssetName.info), scale: 1.7)!
- #elseif os(OSX)
- return NSImage(data: NFXAssets.getImage(NFXAssetName.info))!
- #endif
- }
-
- class func NFXStatistics() -> NFXImage
- {
- #if os (iOS)
- return UIImage(data: NFXAssets.getImage(NFXAssetName.statistics), scale: 1.7)!
- #elseif os(OSX)
- return NSImage(data: NFXAssets.getImage(NFXAssetName.statistics))!
+ static let close: NFXImage = {
+ #if os (iOS)
+ return UIImage(data: NFXAssets.getImage(NFXAssetName.close), scale: 1.7)!
+ #elseif os(OSX)
+ return NSImage(data: NFXAssets.getImage(NFXAssetName.close))!
+ #endif
+ }()
+
+ static let info: NFXImage = {
+ #if os (iOS)
+ return UIImage(data: NFXAssets.getImage(NFXAssetName.info), scale: 1.7)!
+ #elseif os(OSX)
+ return NSImage(data: NFXAssets.getImage(NFXAssetName.info))!
+ #endif
+ }()
+
+ static let statistics: NFXImage = {
+ #if os (iOS)
+ return UIImage(data: NFXAssets.getImage(NFXAssetName.statistics), scale: 1.7)!
+ #elseif os(OSX)
+ return NSImage(data: NFXAssets.getImage(NFXAssetName.statistics))!
+ #endif
+ }()
+
+ #if os (OSX)
+ static let cloud: NFXImage = {
+ return NSImage(data: NFXAssets.getImage(NFXAssetName.cloud))!
+ }()
+
+ static let folder: NFXImage = {
+ return NSImage(data: NFXAssets.getImage(NFXAssetName.folder))!
+ }()
+
+ static let fileDownloading: NFXImage = {
+ return NSImage(data: NFXAssets.getImage(NFXAssetName.fileDownloading))!
+ }()
+
+ static let fileSuccess: NFXImage = {
+ return NSImage(data: NFXAssets.getImage(NFXAssetName.fileSuccess))!
+ }()
+
+ static let fileWarning: NFXImage = {
+ return NSImage(data: NFXAssets.getImage(NFXAssetName.fileWarning))!
+ }()
+
+ static let fileUnauthorized: NFXImage = {
+ return NSImage(data: NFXAssets.getImage(NFXAssetName.fileUnauthorized))!
+ }()
+
+ static let serverError: NFXImage = {
+ return NSImage(data: NFXAssets.getImage(NFXAssetName.serverError))!
+ }()
#endif
- }
}
extension InputStream {
- func readfully() -> Data {
- var result = Data()
- var buffer = [UInt8](repeating: 0, count: 4096)
-
- open()
- var amount = 0
- repeat {
- amount = read(&buffer, maxLength: buffer.count)
- if amount > 0 {
- result.append(buffer, count: amount)
- }
- } while amount > 0
-
- close()
-
- return result
- }
+ func readfully() -> Data {
+ var result = Data()
+ var buffer = [UInt8](repeating: 0, count: 4096)
+
+ open()
+
+ var amount = 0
+ repeat {
+ amount = read(&buffer, maxLength: buffer.count)
+ if amount > 0 {
+ result.append(buffer, count: amount)
+ }
+ } while amount > 0
+
+ close()
+
+ return result
+ }
}
extension Date
@@ -343,7 +370,12 @@ class NFXDebugInfo
let session = URLSession.shared
session.dataTask(with: req as URLRequest, completionHandler: { (data, response, error) in
do {
- let rawJsonData = try JSONSerialization.jsonObject(with: data!, options: [.allowFragments])
+ guard let data = data else {
+ completion("-")
+ return
+ }
+
+ let rawJsonData = try JSONSerialization.jsonObject(with: data, options: [.allowFragments])
if let ipAddress = (rawJsonData as AnyObject).value(forKey: "ip") {
completion(ipAddress as! String)
} else {
diff --git a/netfox/Core/NFXNetServiceMonitor.swift b/netfox/Core/NFXNetServiceMonitor.swift
new file mode 100644
index 00000000..73cab7c2
--- /dev/null
+++ b/netfox/Core/NFXNetServiceMonitor.swift
@@ -0,0 +1,148 @@
+//
+// NFXNetService.swift
+// netfox_mac
+//
+// Created by Alexandru Tudose on 01/11/2017.
+// Copyright © 2017 kasketis. All rights reserved.
+//
+
+import Foundation
+
+#if os(OSX)
+class NFXNetServiceMonitor: NSObject {
+
+ static let shared = NFXNetServiceMonitor()
+ class FoundService {
+ var service: NetService
+ var address: String
+ var canConnect = true
+
+ init(service: NetService, address: String) {
+ self.service = service
+ self.address = address
+ }
+
+ var name: String {
+ return service.name + " " + (service.hostName ?? "")
+ }
+ }
+
+ var foundServices: [FoundService] = []
+ var processingServices: [NetService] = []
+ let serviceBrowser = NetServiceBrowser()
+
+ var clients: [NFXClientConnection] = []
+
+ func browseForAvailableNFXServices() {
+ windowController?.popupButton.removeAllItems()
+
+ serviceBrowser.delegate = self
+ serviceBrowser.searchForServices(ofType: NFXServer.Options.bonjourServiceType, inDomain: "")
+ }
+
+ func foundServer(address: String, service: NetService) {
+ let foundService = FoundService(service: service, address: address)
+ if let index = foundServices.index(where: { $0.name == foundService.name }) {
+ foundServices[index] = foundService
+ let popupItem = windowController?.popupButton.item(at: index)
+ popupItem?.isEnabled = true
+
+ let isSelected = windowController?.popupButton.indexOfSelectedItem == index
+ if isSelected {
+ fetchServiceContent(service: service)
+ }
+ } else {
+ if foundServices.isEmpty {
+ fetchServiceContent(service: service)
+ }
+
+ foundServices.append(foundService)
+ windowController?.popupButton.addItem(withTitle: foundService.name )
+ print(" ", windowController!.popupButton.itemTitles)
+ }
+ }
+
+ func removeService(_ service: NetService) {
+ if let index = foundServices.index(where: { $0.service === service }) {
+ let item = foundServices[index]
+ item.canConnect = false
+ let popupItem = windowController?.popupButton.item(at: index)
+ popupItem?.isEnabled = false
+ }
+ }
+
+ var windowController: NFXWindowController? {
+ return NFX.sharedInstance().windowController
+ }
+
+ func fetchServiceContent(service: NetService) {
+ NFXHTTPModelManager.sharedInstance.clear()
+ NotificationCenter.default.post(name: Notification.Name(rawValue: "NFXReloadData"), object: nil)
+
+ var inputStream: InputStream?
+ var outputStream: OutputStream?
+ let didOpen = service.getInputStream(&inputStream, outputStream: &outputStream)
+ if didOpen {
+ let client = NFXClientConnection(inputStream: inputStream!, outputStream: outputStream!)
+ client.scheduleOnBackgroundRunLoop()
+ client.prepareForReadingStream()
+ clients.forEach({ $0.stopRunLoop() })
+ clients = [client]
+ client.onClose = { [unowned self] in
+ self.clients = []
+ self.removeService(service)
+ }
+ }
+ }
+}
+
+
+extension NFXNetServiceMonitor: NetServiceBrowserDelegate {
+ func netServiceBrowser(_ browser: NetServiceBrowser, didFind service: NetService, moreComing: Bool) {
+ service.delegate = self
+ service.resolve(withTimeout: 0)
+ processingServices.append(service)
+ }
+
+ func netServiceBrowserDidStopSearch(_ browser: NetServiceBrowser) {
+ print(#function)
+ }
+}
+
+extension NFXNetServiceMonitor: NetServiceDelegate {
+ func netServiceDidResolveAddress(_ sender: NetService) {
+ print("Found service: \(sender.hostName ?? "") \(sender)")
+
+ let addresses = sender.addresses?.flatMap({ (data: Data) -> String? in
+ let nsData = data as NSData
+ let inetAddress: sockaddr_in = nsData.castToCPointer()
+ if inetAddress.sin_family == __uint8_t(AF_INET) {
+ return String(cString: inet_ntoa(inetAddress.sin_addr), encoding: .ascii)
+ } else {
+ return nil
+ }
+ })
+
+ if let address = addresses?.first {
+ self.foundServer(address: address, service: sender)
+ }
+ }
+
+ func netService(_ sender: NetService, didNotResolve errorDict: [String : NSNumber]) {
+ print("failed to resove service \(sender): \(errorDict)")
+ }
+}
+
+
+
+extension NSData {
+ func castToCPointer() -> T {
+ let mem = UnsafeMutablePointer.allocate(capacity: MemoryLayout.size)
+ self.getBytes(mem, length: MemoryLayout.size)
+ return mem.move()
+ }
+}
+
+
+
+#endif
diff --git a/netfox/Core/NFXPathNode.swift b/netfox/Core/NFXPathNode.swift
new file mode 100644
index 00000000..ae28822d
--- /dev/null
+++ b/netfox/Core/NFXPathNode.swift
@@ -0,0 +1,72 @@
+//
+// NFXPathNode.swift
+// netfox_ios
+//
+// Created by Ștefan Suciu on 2/5/18.
+// Copyright © 2018 kasketis. All rights reserved.
+//
+
+import Foundation
+
+#if os(OSX)
+
+class NFXPathNode {
+
+ var name: String
+ var children: [NFXPathNode]
+ weak var parent: NFXPathNode?
+ var httpModel: NFXHTTPModel?
+ var isExpanded: Bool
+
+ init(name: String) {
+ self.name = name
+ self.children = []
+ self.isExpanded = true
+ }
+
+ func insert(_ node: NFXPathNode) {
+ children.append(node)
+ node.parent = self
+ }
+
+ func find(_ node: NFXPathNode) -> NFXPathNode? {
+ if name == node.name && httpModel == nil {
+ return self
+ }
+
+ return children.flatMap{ $0.find(node) }.first
+ }
+
+ func depth() -> Int {
+ if parent == nil {
+ return 0
+ }
+
+ return parent!.depth() + 1
+ }
+
+ func findLeaves() -> [NFXHTTPModel] {
+ if children.isEmpty {
+ return httpModel != nil ? [httpModel!] : []
+ }
+
+ return children.map{ $0.findLeaves() }.reduce([], +)
+ }
+
+ func toArray() -> [NFXPathNode] {
+ if !isExpanded {
+ return [self]
+ }
+
+ return [self] + children.map{ $0.toArray() }.reduce([], +)
+ }
+
+ func printTree(_ level: Int = 0) {
+ print(String(repeating: " ", count: level) + "\(name) -> \(httpModel?.requestURL ?? "")")
+ for child in children {
+ child.printTree(level + 4)
+ }
+ }
+}
+
+#endif
diff --git a/netfox/Core/NFXPathNodeManager.swift b/netfox/Core/NFXPathNodeManager.swift
new file mode 100644
index 00000000..b05b321e
--- /dev/null
+++ b/netfox/Core/NFXPathNodeManager.swift
@@ -0,0 +1,65 @@
+//
+// NFXPathNodeManager.swift
+// netfox_ios
+//
+// Created by Ștefan Suciu on 2/5/18.
+// Copyright © 2018 kasketis. All rights reserved.
+//
+
+import Foundation
+
+#if os(OSX)
+
+final class NFXPathNodeManager {
+
+ static let sharedInstance = NFXPathNodeManager()
+ fileprivate var rootNode = NFXPathNode(name: "root")
+
+ func add(_ arr: [NFXHTTPModel]) {
+ arr.forEach{ add($0) }
+ }
+
+ func add(_ obj: NFXHTTPModel) {
+ guard let nodes = obj.requestURL?.split(separator: "/").dropFirst().map({ NFXPathNode(name: String($0)) }) else {
+ return
+ }
+
+ let nodesWithoutLast = nodes.dropLast()
+ var previousNode = rootNode
+ for node in nodesWithoutLast {
+ if let foundNode = previousNode.find(node) {
+ previousNode = foundNode
+ } else {
+ previousNode.insert(node)
+ previousNode = node
+ }
+ }
+ let resourceNode = NFXPathNode(name: nodes.last?.name ?? "-")
+ resourceNode.httpModel = obj
+ previousNode.insert(resourceNode)
+ }
+
+ func update(_ obj: NFXHTTPModel) {
+ guard let name = obj.requestURL?.split(separator: "/").last else {
+ return
+ }
+
+ let node = NFXPathNode(name: String(name))
+ rootNode.find(node)?.httpModel = obj
+ }
+
+ func clear() {
+ rootNode.children = []
+ }
+
+ func getHttpModels() -> [NFXHTTPModel] {
+ return rootNode.findLeaves()
+ }
+
+ func getTableModels() -> [NFXPathNode] {
+ var models = rootNode.toArray()
+ models.remove(at: 0)
+ return models
+ }
+}
+#endif
diff --git a/netfox/Core/NFXProtocol.swift b/netfox/Core/NFXProtocol.swift
index 60ed3dc8..fbe3d452 100755
--- a/netfox/Core/NFXProtocol.swift
+++ b/netfox/Core/NFXProtocol.swift
@@ -111,6 +111,7 @@ open class NFXProtocol: URLProtocol
{
if (self.model != nil) {
NFXHTTPModelManager.sharedInstance.add(self.model!)
+ NFX.sharedInstance().server?.broadcastModel(self.model!)
}
NotificationCenter.default.post(name: Notification.Name.NFXReloadData, object: nil)
diff --git a/netfox/Core/NFXServer.swift b/netfox/Core/NFXServer.swift
new file mode 100644
index 00000000..f39396dd
--- /dev/null
+++ b/netfox/Core/NFXServer.swift
@@ -0,0 +1,82 @@
+//
+// NFXServer.swift
+// netfox_ios
+//
+// Created by Alexandru Tudose on 01/11/2017.
+// Copyright © 2017 kasketis. All rights reserved.
+//
+
+import Foundation
+
+public class NFXServer: NSObject {
+ public struct Options {
+ public static let bonjourServiceType = "_NFX._tcp."
+ public static let port: UInt16 = 12222
+ }
+
+ var netService: NetService?
+ var port: UInt16 = Options.port
+ var numberOfRetries = 0
+ var connectedClients: [NFXClientConnection] = []
+
+ public func startServer() {
+ publishHttpService()
+ #if os(iOS)
+ NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: .UIApplicationDidBecomeActive, object: nil)
+ #endif
+ }
+
+ public func stopServer() {
+ netService?.stop()
+ netService = nil
+ NotificationCenter.default.removeObserver(self)
+ }
+
+ func publishHttpService() {
+ let bundleIdentifier = Bundle.main.bundleIdentifier ?? ""
+ let netService = NetService(domain: "", type: NFXServer.Options.bonjourServiceType, name: bundleIdentifier, port: Int32(port))
+ netService.delegate = self
+ netService.publish(options: [.listenForConnections])
+ self.netService = netService
+ }
+
+ func broadcastModel(_ model: NFXHTTPModel) {
+ connectedClients.forEach({ $0.writeModel(model) })
+ }
+
+ @objc func applicationDidBecomeActive() {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
+ if self.connectedClients.isEmpty {
+ self.stopServer()
+
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
+ self.startServer()
+ }
+ }
+ }
+ }
+}
+
+extension NFXServer: NetServiceDelegate {
+ public func netServiceDidPublish(_ sender: NetService) {
+
+ }
+
+ public func netService(_ sender: NetService, didNotPublish errorDict: [String : NSNumber]) {
+ print("failed to publish http service: \(errorDict)")
+ }
+
+ public func netService(_ sender: NetService, didAcceptConnectionWith inputStream: InputStream, outputStream: OutputStream) {
+ let client = NFXClientConnection(inputStream: inputStream, outputStream: outputStream)
+ client.scheduleOnBackgroundRunLoop()
+ connectedClients.append(client)
+ client.prepareForWritingStream()
+ client.writeAllModels()
+ client.onClose = { [unowned self] in
+ if let index = self.connectedClients.index(of: client) {
+ self.connectedClients.remove(at: index)
+ }
+ }
+ }
+}
+
diff --git a/netfox/Core/NFXWindowController.swift b/netfox/Core/NFXWindowController.swift
index aa4788e4..18091956 100644
--- a/netfox/Core/NFXWindowController.swift
+++ b/netfox/Core/NFXWindowController.swift
@@ -19,9 +19,12 @@ class NFXWindowController: NSWindowController, NSWindowDelegate, NFXWindowContro
@IBOutlet var infoButton: NSButton!
@IBOutlet var statisticsButton: NSButton!
+ @IBOutlet var popupButton: NSPopUpButton!
@IBOutlet var listView: NSView!
@IBOutlet var detailsView: NSView!
- @IBOutlet var listViewController: NFXListController_OSX!
+ @IBOutlet var tableView: NSTableView!
+ @IBOutlet var listViewController: NFXPathNodeListController_OSX!
+ @IBOutlet var structuredListViewController: NFXPathNodeListController_OSX!
@IBOutlet var detailsViewController: NFXDetailsController_OSX!
@IBOutlet var settingsPopover: NSPopover!
@@ -33,6 +36,7 @@ class NFXWindowController: NSWindowController, NSWindowDelegate, NFXWindowContro
@IBOutlet var infoViewController: NFXInfoController_OSX!
@IBOutlet var infoView: NSView!
+ @IBOutlet var segmentedControl: NSSegmentedControl!
@IBOutlet var statisticsViewController: NFXStatisticsController_OSX!
@IBOutlet var statisticsView: NSView!
@@ -44,10 +48,11 @@ class NFXWindowController: NSWindowController, NSWindowDelegate, NFXWindowContro
infoButton.image = NSImage(data: NFXAssets.getImage(.info))
statisticsButton.image = NSImage(data: NFXAssets.getImage(.statistics))
- listViewController.view = listView
listViewController.delegate = self
detailsViewController.view = detailsView
+ structuredListViewController.delegate = self
+
settingsViewController.view = settingsView
infoViewController.view = infoView
statisticsViewController.view = statisticsView
@@ -59,6 +64,8 @@ class NFXWindowController: NSWindowController, NSWindowDelegate, NFXWindowContro
override func windowDidLoad() {
super.windowDidLoad()
self.window?.delegate = self
+
+ NFXNetServiceMonitor.shared.browseForAvailableNFXServices()
}
// MARK: NSWindowDelegate
@@ -71,18 +78,42 @@ class NFXWindowController: NSWindowController, NSWindowDelegate, NFXWindowContro
// MARK: Actions
- @IBAction func settingsClicked(sender: AnyObject?) {
+ @IBAction func settingsClicked(_ sender: AnyObject?) {
settingsPopover.show(relativeTo: NSZeroRect, of: settingsButton, preferredEdge: NSRectEdge.maxY)
}
- @IBAction func infoClicked(sender: AnyObject?) {
+ @IBAction func infoClicked(_ sender: AnyObject?) {
infoPopover.show(relativeTo: NSZeroRect, of: infoButton, preferredEdge: NSRectEdge.maxY)
}
- @IBAction func statisticsClicked(sender: AnyObject?) {
+ @IBAction func statisticsClicked(_ sender: AnyObject?) {
statisticsPopover.show(relativeTo: NSZeroRect, of: statisticsButton, preferredEdge: NSRectEdge.maxY)
}
-
+
+ @IBAction func hostClicked(_ sender: Any) {
+ let foundService = NFXNetServiceMonitor.shared.foundServices[popupButton.indexOfSelectedItem]
+ NFXNetServiceMonitor.shared.fetchServiceContent(service: foundService.service)
+ }
+
+ @IBAction func segmentedAction(_ sender: Any) {
+ switch segmentedControl.selectedSegment {
+ case 0:
+ listViewController.searchField.delegate = listViewController
+ listViewController.tableView = tableView
+ tableView.delegate = listViewController
+ tableView.dataSource = listViewController
+ tableView.reloadData()
+ case 1:
+ structuredListViewController.searchField.delegate = structuredListViewController
+ structuredListViewController.tableView = tableView
+ tableView.delegate = structuredListViewController
+ tableView.dataSource = structuredListViewController
+ tableView.reloadData()
+ default:
+ abort()
+ }
+ }
+
}
extension NFXWindowController {
diff --git a/netfox/OSX/NFXDetailsController_OSX.swift b/netfox/OSX/NFXDetailsController_OSX.swift
index d7fffd87..5c10697a 100644
--- a/netfox/OSX/NFXDetailsController_OSX.swift
+++ b/netfox/OSX/NFXDetailsController_OSX.swift
@@ -16,6 +16,7 @@ class NFXDetailsController_OSX: NFXDetailsController {
@IBOutlet var textViewBodyRequest: NSTextView!
@IBOutlet var textViewResponse: NSTextView!
@IBOutlet var textViewBodyResponse: NSTextView!
+ @IBOutlet var textViewCodable: NSTextView!
override func viewDidLoad() {
super.viewDidLoad()
@@ -35,15 +36,41 @@ class NFXDetailsController_OSX: NFXDetailsController {
self.textViewBodyRequest.textStorage?.setAttributedString(bodyRequest)
self.textViewResponse.textStorage?.setAttributedString(self.getResponseStringFromObject(model))
- let bodyResponse: NSAttributedString
+ var bodyResponse: NSAttributedString
if model.responseBodyLength == 0 {
bodyResponse = self.formatNFXString(String(self.getResponseBodyStringFooter(model)))
} else {
bodyResponse = self.formatNFXString(String(model.getResponseBody()))
}
self.textViewBodyResponse.textStorage?.setAttributedString(bodyResponse)
+
+ guard model.responseBodyLength != 0 else {
+ bodyResponse = self.formatNFXString(String(self.getResponseBodyStringFooter(model)))
+ self.textViewCodable.textStorage?.setAttributedString(bodyResponse)
+ return
+ }
+
+ do {
+ let str = model.getResponseBody() as String
+ let data = str.data(using: .utf8)!
+ let converter = NFXJson2Codable()
+ if let dictionary = try JSONSerialization.jsonObject(with: data) as? [String: Any] {
+ let _ = converter.convertToCodable(
+ name: converter.getResourceName(from: model.requestURL),
+ from: dictionary
+ )
+ self.textViewCodable.textStorage?.setAttributedString(NSAttributedString(string: converter.printClasses()))
+ } else if let array = try JSONSerialization.jsonObject(with: data) as? [Any], !array.isEmpty {
+ let _ = converter.convertToCodable(
+ name: converter.getResourceName(from: model.requestURL),
+ from: array
+ )
+ self.textViewCodable.textStorage?.setAttributedString(NSAttributedString(string: converter.printClasses()))
+ }
+ } catch {
+ self.textViewCodable.textStorage?.setAttributedString(NSAttributedString(string: "Something went wrong with decoding. :("))
+ }
}
-
}
#endif
diff --git a/netfox/OSX/NFXListCell_OSX.xib b/netfox/OSX/NFXListCell_OSX.xib
old mode 100644
new mode 100755
index f124b830..66a9cae9
--- a/netfox/OSX/NFXListCell_OSX.xib
+++ b/netfox/OSX/NFXListCell_OSX.xib
@@ -1,15 +1,16 @@
-
-
+
+
-
+
+
-
-
+
+
@@ -18,7 +19,7 @@
-
+
@@ -26,7 +27,7 @@
-
+
@@ -52,20 +53,21 @@
-
+
-
+
-
+
-
+
+ Multiline Label Multiline Label Multiline Label Multiline Label Multiline Label Multiline Label Multiline Label Multiline Label Multiline Label Multiline Label
-
+
@@ -73,7 +75,7 @@
-
+
@@ -82,7 +84,7 @@
-
+
@@ -94,7 +96,7 @@
-
+
@@ -120,7 +122,7 @@
-
+
diff --git a/netfox/OSX/NFXListController_OSX.swift b/netfox/OSX/NFXListController_OSX.swift
old mode 100644
new mode 100755
index 2e907f65..cbe142ca
--- a/netfox/OSX/NFXListController_OSX.swift
+++ b/netfox/OSX/NFXListController_OSX.swift
@@ -24,10 +24,11 @@ class NFXListController_OSX: NFXListController, NSTableViewDelegate, NSTableView
// MARK: View Life Cycle
override func awakeFromNib() {
+ let bundle = Bundle(for: type(of: self))
#if !swift(>=4.0)
- tableView.register(NSNib(nibNamed: cellIdentifier, bundle: nil), forIdentifier: cellIdentifier)
+ tableView.register(NSNib(nibNamed: cellIdentifier, bundle: bundle), forIdentifier: cellIdentifier)
#else
- tableView.register(NSNib(nibNamed: NSNib.Name(rawValue: cellIdentifier), bundle: nil), forIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier))
+ tableView.register(NSNib(nibNamed: NSNib.Name(rawValue: cellIdentifier), bundle: bundle), forIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier))
#endif
searchField.delegate = self
@@ -57,7 +58,7 @@ class NFXListController_OSX: NFXListController, NSTableViewDelegate, NSTableView
reloadTableViewData()
}
- func controlTextDidChange(obj: NSNotification) {
+ @objc override func controlTextDidChange(_ obj: Notification) {
guard let searchField = obj.object as? NSSearchField else {
return
}
@@ -68,7 +69,7 @@ class NFXListController_OSX: NFXListController, NSTableViewDelegate, NSTableView
// MARK: UITableViewDataSource
- func numberOfRowsInTableView(tableView: NSTableView) -> Int {
+ func numberOfRows(in tableView: NSTableView) -> Int {
if (self.isSearchControllerActive) {
return self.filteredTableData.count
} else {
diff --git a/netfox/OSX/NFXPathNodeListCell_OSX.swift b/netfox/OSX/NFXPathNodeListCell_OSX.swift
new file mode 100644
index 00000000..e9d1a3d8
--- /dev/null
+++ b/netfox/OSX/NFXPathNodeListCell_OSX.swift
@@ -0,0 +1,99 @@
+//
+// NFXPathNodeListCell_OSX.swift
+// netfox_ios
+//
+// Created by Ștefan Suciu on 2/6/18.
+// Copyright © 2018 kasketis. All rights reserved.
+//
+
+#if os(OSX)
+
+import Cocoa
+
+let cloudImage = NFXImage.cloud
+let folderImage = NFXImage.folder
+let fileDownloadingImage = NFXImage.fileDownloading
+let fileSuccessImage = NFXImage.fileSuccess
+let fileWarningImage = NFXImage.fileWarning
+let fileUnauthorizedImage = NFXImage.fileUnauthorized
+let serverErrorImage = NFXImage.serverError
+
+class NFXPathNodeListCell_OSX: NSTableCellView {
+
+ @IBOutlet var statusView: NSView!
+ @IBOutlet var URLLabel: NSTextField!
+ @IBOutlet var _imageView: NSImageView!
+
+ @IBOutlet var circleView: NSView!
+ @IBOutlet weak var leadingConstraint: NSLayoutConstraint!
+
+ let padding: CGFloat = 5
+
+ // MARK: Life cycle
+
+ override func awakeFromNib() {
+ layer?.backgroundColor = NFXColor.clear.cgColor
+
+ circleView.layer?.backgroundColor = NSColor.NFXGray44Color().cgColor
+ circleView.layer?.cornerRadius = 4
+ circleView.alphaValue = 0.2
+ URLLabel.font = NSFont.NFXFont(size: 12)
+ }
+
+ func isNew() {
+ circleView.isHidden = false
+ }
+
+ func isOld() {
+ circleView.isHidden = true
+ }
+
+ func configForObject(obj: NFXPathNode) {
+ leadingConstraint.constant = CGFloat(obj.depth()) * 16.0
+ URLLabel.stringValue = obj.name
+
+ if let httpModel = obj.httpModel {
+ configForObject(obj: httpModel)
+ } else if obj.parent?.parent == nil {
+ _imageView.image = cloudImage
+ } else {
+ _imageView.image = folderImage
+ }
+ }
+
+ func configForObject(obj: NFXHTTPModel) {
+ setStatus(status: obj.responseStatus)
+ isNewBasedOnDate(responseDate: obj.responseDate as NSDate? ?? NSDate())
+ }
+
+ func setStatus(status: Int?) {
+ guard let status = status else {
+ _imageView.image = fileWarningImage
+ return
+ }
+
+ if status >= 200 && status < 300 {
+ _imageView.image = fileSuccessImage
+ } else if status >= 400 && status < 500 {
+ if status == 403 {
+ _imageView.image = fileUnauthorizedImage
+ } else {
+ _imageView.image = fileWarningImage
+ }
+ } else if status >= 500 && status < 600 {
+ _imageView.image = serverErrorImage
+ } else {
+ _imageView.image = fileDownloadingImage
+ }
+ }
+
+ func isNewBasedOnDate(responseDate: NSDate) {
+ if responseDate.isGreaterThan(NFX.sharedInstance().getLastVisitDate()) {
+ isNew()
+ } else {
+ isOld()
+ }
+ }
+}
+
+#endif
diff --git a/netfox/OSX/NFXPathNodeListCell_OSX.xib b/netfox/OSX/NFXPathNodeListCell_OSX.xib
new file mode 100755
index 00000000..11dc8833
--- /dev/null
+++ b/netfox/OSX/NFXPathNodeListCell_OSX.xib
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Multiline Label Multiline Label Multiline Label Multiline Label Multiline Label Multiline Label Multiline Label Multiline Label Multiline Label Multiline Label
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/netfox/OSX/NFXPathNodeListController_OSX.swift b/netfox/OSX/NFXPathNodeListController_OSX.swift
new file mode 100644
index 00000000..fdc3749f
--- /dev/null
+++ b/netfox/OSX/NFXPathNodeListController_OSX.swift
@@ -0,0 +1,144 @@
+//
+// NFXPathNodeListController_OSX.swift
+// netfox_osx
+//
+// Created by Ștefan Suciu on 2/6/18.
+// Copyright © 2018 kasketis. All rights reserved.
+//
+
+#if os(OSX)
+
+import Cocoa
+
+class NFXPathNodeListController_OSX: NFXListController, NSTableViewDelegate, NSTableViewDataSource, NSSearchFieldDelegate {
+
+ // MARK: Properties
+
+ @IBOutlet var searchField: NSTextField!
+ @IBOutlet var tableView: NSTableView!
+
+ var isSearchControllerActive: Bool = false
+ var delegate: NFXWindowControllerDelegate?
+
+ private let cellIdentifier = "NFXPathNodeListCell_OSX"
+
+ fileprivate let modelManager = NFXPathNodeManager.sharedInstance
+ fileprivate var pathNodeTableData: [NFXPathNode] = []
+
+ // MARK: View Life Cycle
+
+ override func awakeFromNib() {
+ let bundle = Bundle(for: type(of: self))
+ if Bundle.main.bundleIdentifier == "com.tapptitude.netfox-mac" {
+ #if !swift(>=4.0)
+ tableView.register(NSNib(nibNamed: cellIdentifier, bundle: bundle), forIdentifier: cellIdentifier)
+ #else
+ tableView.register(NSNib(nibNamed: NSNib.Name(rawValue: cellIdentifier), bundle: bundle), forIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier))
+ #endif
+ }
+ searchField.delegate = self
+
+ NotificationCenter.default.addObserver(self, selector: #selector(NFXListController.reloadTableViewData), name: .NFXReloadData, object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(NFXPathNodeListController_OSX.deactivateSearchController), name: .NFXDeactivateSearch, object: nil)
+
+ pathNodeTableData = modelManager.getTableModels()
+ }
+
+ // MARK: Notifications
+
+ override func reloadTableViewData() {
+ DispatchQueue.main.async {
+ if self.searchField.stringValue.isEmpty {
+ self.pathNodeTableData = self.modelManager.getTableModels()
+ } else {
+ let filtered = self.filter(models: self.modelManager.getHttpModels(), searchString: self.searchField.stringValue)
+ let model = NFXPathNodeManager()
+ model.add(filtered)
+ self.pathNodeTableData = model.getTableModels()
+ }
+
+ self.tableView.reloadData()
+ }
+ }
+
+ @objc func deactivateSearchController() {
+ isSearchControllerActive = false
+ }
+
+ // MARK: Search
+
+ func filter(models: [NFXHTTPModel], searchString: String) -> [NFXHTTPModel] {
+ let predicateURL = NSPredicate(format: "requestURL contains[cd] '\(searchString)'")
+ let predicateMethod = NSPredicate(format: "requestMethod contains[cd] '\(searchString)'")
+ let predicateType = NSPredicate(format: "responseType contains[cd] '\(searchString)'")
+ let predicates = [predicateURL, predicateMethod, predicateType]
+ let searchPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: predicates)
+
+ let array = (models as NSArray).filtered(using: searchPredicate)
+ return array as! [NFXHTTPModel]
+ }
+
+ func updateSearchResultsForSearchController() {
+ updateSearchResultsForSearchControllerWithString(searchField.stringValue)
+ reloadTableViewData()
+ }
+
+ @objc override func controlTextDidChange(_ obj: Notification) {
+ guard let searchField = obj.object as? NSSearchField else {
+ return
+ }
+
+ isSearchControllerActive = searchField.stringValue.count > 0
+ updateSearchResultsForSearchController()
+ }
+
+ // MARK: UITableViewDataSource
+
+ func numberOfRows(in tableView: NSTableView) -> Int {
+ return pathNodeTableData.count
+ }
+
+ func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
+ #if !swift(>=4.0)
+ guard let cell = tableView.make(withIdentifier: cellIdentifier, owner: nil) as? NFXPathNodeListCell_OSX else {
+ return nil
+ } #else
+ guard let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: nil) as? NFXPathNodeListCell_OSX else {
+ return nil
+ } #endif
+
+ let obj = pathNodeTableData[row]
+ cell.configForObject(obj: obj)
+
+ return cell
+ }
+
+ // MARK: NSTableViewDelegate
+
+ func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
+ return 20
+ }
+
+ func tableViewSelectionDidChange(_ notification: Notification) {
+ guard tableView.selectedRow >= 0 else {
+ return
+ }
+
+ if let httpModel = pathNodeTableData[tableView.selectedRow].httpModel {
+ delegate?.httpModelSelectedDidChange(model: httpModel)
+ } else {
+ let node = pathNodeTableData[tableView.selectedRow]
+ if !node.isExpanded {
+ node.isExpanded = true
+ pathNodeTableData.insert(contentsOf: node.children, at: tableView.selectedRow + 1)
+ reloadTableViewData()
+ } else {
+ node.isExpanded = false
+ reloadTableViewData()
+ }
+ }
+ }
+}
+
+#endif
+
diff --git a/netfox/OSX/NFXResponseTypeCell_OSX.xib b/netfox/OSX/NFXResponseTypeCell_OSX.xib
index 239171a2..78de7b31 100644
--- a/netfox/OSX/NFXResponseTypeCell_OSX.xib
+++ b/netfox/OSX/NFXResponseTypeCell_OSX.xib
@@ -1,8 +1,9 @@
-
-
+
+
-
+
+
diff --git a/netfox/OSX/NFXSettingsController_OSX.swift b/netfox/OSX/NFXSettingsController_OSX.swift
old mode 100644
new mode 100755
index b8901a94..74f69ec5
--- a/netfox/OSX/NFXSettingsController_OSX.swift
+++ b/netfox/OSX/NFXSettingsController_OSX.swift
@@ -25,11 +25,12 @@ class NFXSettingsController_OSX: NFXSettingsController, NSTableViewDataSource, N
nfxVersionLabel.stringValue = nfxVersionString
nfxURLButton.title = nfxURL
+ let bundle = Bundle(for: type(of:self))
#if !swift(>=4.0)
- responseTypesTableView.register(NSNib(nibNamed: cellIdentifier, bundle: nil), forIdentifier: cellIdentifier)
+ responseTypesTableView.register(NSNib(nibNamed: cellIdentifier, bundle: bundle), forIdentifier: cellIdentifier)
#else
- responseTypesTableView.register(NSNib(nibNamed: NSNib.Name(rawValue: cellIdentifier), bundle: nil), forIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier))
+ responseTypesTableView.register(NSNib(nibNamed: NSNib.Name(rawValue: cellIdentifier), bundle: bundle), forIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier))
#endif
reloadTableData()
@@ -42,7 +43,7 @@ class NFXSettingsController_OSX: NFXSettingsController, NSTableViewDataSource, N
// MARK: Actions
- @IBAction func loggingButtonClicked(sender: NSButton) {
+ @IBAction func loggingButtonClicked(_ sender: NSButton) {
var senderStateOn: Bool
#if !swift(>=4.0)
senderStateOn = sender.state == NSControlStateValueOn
@@ -57,12 +58,13 @@ class NFXSettingsController_OSX: NFXSettingsController, NSTableViewDataSource, N
}
}
- @IBAction func clearDataClicked(sender: AnyObject?) {
+ @IBAction func clearDataClicked(_ sender: AnyObject?) {
NFX.sharedInstance().clearOldData()
NotificationCenter.default.post(name: NSNotification.Name.NFXReloadData, object: nil)
}
- @IBAction func nfxURLButtonClicked(sender: NSButton) {
+
+ @IBAction func nfxURLButtonClicked(_ sender: NSButton) {
#if !swift(>=4.0)
NSWorkspace.shared().open(NSURL(string: nfxURL)! as URL)
#else
@@ -70,7 +72,7 @@ class NFXSettingsController_OSX: NFXSettingsController, NSTableViewDataSource, N
#endif
}
- @IBAction func toggleResponseTypeClicked(sender: NSButton) {
+ @IBAction func toggleResponseTypeClicked(_ sender: NSButton) {
filters[sender.tag] = !filters[sender.tag]
NFX.sharedInstance().cacheFilters(filters)
NotificationCenter.default.post(name: NSNotification.Name.NFXReloadData, object: nil)
@@ -84,7 +86,7 @@ class NFXSettingsController_OSX: NFXSettingsController, NSTableViewDataSource, N
// MARK: Table View Delegate and DataSource
- func numberOfRowsInTableView(tableView: NSTableView) -> Int {
+ func numberOfRows(in tableView: NSTableView) -> Int {
return tableData.count
}
@@ -108,7 +110,7 @@ class NFXSettingsController_OSX: NFXSettingsController, NSTableViewDataSource, N
#endif
cell.activeCheckbox.tag = row
cell.activeCheckbox.target = self
- cell.activeCheckbox.action = #selector(toggleResponseTypeClicked(sender:))
+ cell.activeCheckbox.action = #selector(toggleResponseTypeClicked(_:))
return cell
}
diff --git a/netfox/OSX/NetfoxWindow.xib b/netfox/OSX/NetfoxWindow.xib
old mode 100644
new mode 100755
index 77305834..bf3e9ece
--- a/netfox/OSX/NetfoxWindow.xib
+++ b/netfox/OSX/NetfoxWindow.xib
@@ -1,8 +1,9 @@
-
-
+
+
-
+
+
@@ -15,6 +16,8 @@
+
+
@@ -23,6 +26,8 @@
+
+
@@ -30,22 +35,40 @@
-
-
+
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -97,26 +120,39 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
@@ -130,11 +166,12 @@
-
+
+
@@ -155,7 +192,6 @@
-
@@ -167,58 +203,61 @@
-
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
-
@@ -228,7 +267,7 @@
-
+
@@ -249,18 +288,16 @@
-
-
+
+
-
-
+
+
-
-
@@ -291,18 +328,16 @@
-
-
+
+
-
-
+
+
-
-
@@ -327,24 +362,22 @@
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
@@ -354,7 +387,7 @@
-
+
@@ -369,24 +402,22 @@
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
@@ -396,7 +427,7 @@
-
+
@@ -409,6 +440,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -433,18 +504,25 @@
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
@@ -486,13 +564,13 @@
-
+
-
+
@@ -511,6 +589,7 @@
+
@@ -531,7 +610,6 @@
-
@@ -637,18 +715,16 @@
-
+
-
-
+
+
-
-
@@ -693,18 +769,16 @@
-
+
-
-
+
+
-
-
diff --git a/netfox/iOS/NFXListController_iOS.swift b/netfox/iOS/NFXListController_iOS.swift
index 0aaf70e8..258cb7fb 100755
--- a/netfox/iOS/NFXListController_iOS.swift
+++ b/netfox/iOS/NFXListController_iOS.swift
@@ -38,9 +38,9 @@ class NFXListController_iOS: NFXListController, UITableViewDelegate, UITableView
self.tableView.register(NFXListCell.self, forCellReuseIdentifier: NSStringFromClass(NFXListCell.self))
- self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage.NFXClose(), style: .plain, target: self, action: #selector(NFXListController_iOS.closeButtonPressed))
+ self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: NFXImage.close, style: .plain, target: self, action: #selector(NFXListController_iOS.closeButtonPressed))
- self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage.NFXSettings(), style: .plain, target: self, action: #selector(NFXListController_iOS.settingsButtonPressed))
+ self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: NFXImage.settings, style: .plain, target: self, action: #selector(NFXListController_iOS.settingsButtonPressed))
self.searchController = UISearchController(searchResultsController: nil)
self.searchController.searchResultsUpdater = self
diff --git a/netfox/iOS/NFXSettingsController_iOS.swift b/netfox/iOS/NFXSettingsController_iOS.swift
index 65bc4c41..5dda2cf8 100644
--- a/netfox/iOS/NFXSettingsController_iOS.swift
+++ b/netfox/iOS/NFXSettingsController_iOS.swift
@@ -31,7 +31,7 @@ class NFXSettingsController_iOS: NFXSettingsController, UITableViewDelegate, UIT
self.extendedLayoutIncludesOpaqueBars = false
self.automaticallyAdjustsScrollViewInsets = false
- self.navigationItem.rightBarButtonItems = [UIBarButtonItem(image: UIImage.NFXStatistics(), style: .plain, target: self, action: #selector(NFXSettingsController_iOS.statisticsButtonPressed)), UIBarButtonItem(image: UIImage.NFXInfo(), style: .plain, target: self, action: #selector(NFXSettingsController_iOS.infoButtonPressed))]
+ self.navigationItem.rightBarButtonItems = [UIBarButtonItem(image: NFXImage.statistics, style: .plain, target: self, action: #selector(NFXSettingsController_iOS.statisticsButtonPressed)), UIBarButtonItem(image: NFXImage.info, style: .plain, target: self, action: #selector(NFXSettingsController_iOS.infoButtonPressed))]
self.tableView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height - 60)
self.tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
diff --git a/netfox_ios_demo/Assets.xcassets/AppIcon.appiconset/Contents.json b/netfox_ios_demo/Assets.xcassets/AppIcon.appiconset/Contents.json
index 1d060ed2..d8db8d65 100644
--- a/netfox_ios_demo/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/netfox_ios_demo/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -84,6 +84,11 @@
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "size" : "1024x1024",
+ "scale" : "1x"
}
],
"info" : {
diff --git a/netfox_mac/AppDelegate.swift b/netfox_mac/AppDelegate.swift
new file mode 100644
index 00000000..17ea1c1f
--- /dev/null
+++ b/netfox_mac/AppDelegate.swift
@@ -0,0 +1,30 @@
+//
+// AppDelegate.swift
+// netfox_mac
+//
+// Created by Alexandru Tudose on 27/10/2017.
+// Copyright © 2017 kasketis. All rights reserved.
+//
+
+import Cocoa
+import netfox_osx
+
+@NSApplicationMain
+class AppDelegate: NSObject, NSApplicationDelegate {
+ @IBOutlet weak var window: NSWindow!
+
+ func applicationWillFinishLaunching(_ aNotification: Notification) {
+
+ }
+
+ func applicationDidFinishLaunching(_ aNotification: Notification) {
+ NFX.sharedInstance().start()
+ NFX.sharedInstance().show()
+ }
+
+ func applicationWillTerminate(_ aNotification: Notification) {
+ }
+}
+
+
+
diff --git a/netfox_mac/Assets.xcassets/AppIcon.appiconset/Contents.json b/netfox_mac/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..08cac0f1
--- /dev/null
+++ b/netfox_mac/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,63 @@
+{
+ "images" : [
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "netfox_128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "netfox_256-1.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "netfox_256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "netfox_512-1.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "netfox_512.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_128.png b/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_128.png
new file mode 100644
index 00000000..da578aeb
Binary files /dev/null and b/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_128.png differ
diff --git a/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_256-1.png b/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_256-1.png
new file mode 100644
index 00000000..cfa1c081
Binary files /dev/null and b/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_256-1.png differ
diff --git a/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_256.png b/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_256.png
new file mode 100644
index 00000000..cfa1c081
Binary files /dev/null and b/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_256.png differ
diff --git a/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_512-1.png b/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_512-1.png
new file mode 100644
index 00000000..8077c579
Binary files /dev/null and b/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_512-1.png differ
diff --git a/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_512.png b/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_512.png
new file mode 100644
index 00000000..8077c579
Binary files /dev/null and b/netfox_mac/Assets.xcassets/AppIcon.appiconset/netfox_512.png differ
diff --git a/netfox_mac/Base.lproj/MainMenu.xib b/netfox_mac/Base.lproj/MainMenu.xib
new file mode 100644
index 00000000..d5b10970
--- /dev/null
+++ b/netfox_mac/Base.lproj/MainMenu.xib
@@ -0,0 +1,678 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Default
+
+
+
+
+
+
+ Left to Right
+
+
+
+
+
+
+ Right to Left
+
+
+
+
+
+
+
+
+
+
+ Default
+
+
+
+
+
+
+ Left to Right
+
+
+
+
+
+
+ Right to Left
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/netfox_mac/Info.plist b/netfox_mac/Info.plist
new file mode 100644
index 00000000..3b9aa32c
--- /dev/null
+++ b/netfox_mac/Info.plist
@@ -0,0 +1,37 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+ LSMinimumSystemVersion
+ $(MACOSX_DEPLOYMENT_TARGET)
+ NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+
+
+ NSHumanReadableCopyright
+ Copyright © 2017 kasketis. All rights reserved.
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/netfox_mac/netfox_mac.entitlements b/netfox_mac/netfox_mac.entitlements
new file mode 100644
index 00000000..625af03d
--- /dev/null
+++ b/netfox_mac/netfox_mac.entitlements
@@ -0,0 +1,12 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.files.user-selected.read-only
+
+ com.apple.security.network.client
+
+
+
diff --git a/netfox_mac_demo.gif b/netfox_mac_demo.gif
new file mode 100644
index 00000000..c91c62bc
Binary files /dev/null and b/netfox_mac_demo.gif differ