Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88a52fb92c | ||
|
|
723f1665a7 | ||
|
|
4f92d3d58d | ||
|
|
05d06a4f31 |
@@ -9,6 +9,7 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
5404AEEB24A90717003B2F54 /* PrefsShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E67E4524A8B0FE0025D261 /* PrefsShared.swift */; };
|
5404AEEB24A90717003B2F54 /* PrefsShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E67E4524A8B0FE0025D261 /* PrefsShared.swift */; };
|
||||||
5404AEED24A95F3F003B2F54 /* SlideInAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5404AEEC24A95F3F003B2F54 /* SlideInAnimation.swift */; };
|
5404AEED24A95F3F003B2F54 /* SlideInAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5404AEEC24A95F3F003B2F54 /* SlideInAnimation.swift */; };
|
||||||
|
5404AEEF24ACC089003B2F54 /* VCAnalysisBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5404AEEE24ACC089003B2F54 /* VCAnalysisBar.swift */; };
|
||||||
540E6780242D2CF100871BBE /* VCRecordings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E677F242D2CF100871BBE /* VCRecordings.swift */; };
|
540E6780242D2CF100871BBE /* VCRecordings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E677F242D2CF100871BBE /* VCRecordings.swift */; };
|
||||||
540E67822433483D00871BBE /* VCEditRecording.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E67812433483D00871BBE /* VCEditRecording.swift */; };
|
540E67822433483D00871BBE /* VCEditRecording.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E67812433483D00871BBE /* VCEditRecording.swift */; };
|
||||||
540E67842433FAFE00871BBE /* TVCPreviousRecords.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E67832433FAFE00871BBE /* TVCPreviousRecords.swift */; };
|
540E67842433FAFE00871BBE /* TVCPreviousRecords.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E67832433FAFE00871BBE /* TVCPreviousRecords.swift */; };
|
||||||
@@ -176,6 +177,7 @@
|
|||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
5404AEEC24A95F3F003B2F54 /* SlideInAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlideInAnimation.swift; sourceTree = "<group>"; };
|
5404AEEC24A95F3F003B2F54 /* SlideInAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlideInAnimation.swift; sourceTree = "<group>"; };
|
||||||
|
5404AEEE24ACC089003B2F54 /* VCAnalysisBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCAnalysisBar.swift; sourceTree = "<group>"; };
|
||||||
540E677F242D2CF100871BBE /* VCRecordings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCRecordings.swift; sourceTree = "<group>"; };
|
540E677F242D2CF100871BBE /* VCRecordings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCRecordings.swift; sourceTree = "<group>"; };
|
||||||
540E67812433483D00871BBE /* VCEditRecording.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCEditRecording.swift; sourceTree = "<group>"; };
|
540E67812433483D00871BBE /* VCEditRecording.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCEditRecording.swift; sourceTree = "<group>"; };
|
||||||
540E67832433FAFE00871BBE /* TVCPreviousRecords.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVCPreviousRecords.swift; sourceTree = "<group>"; };
|
540E67832433FAFE00871BBE /* TVCPreviousRecords.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVCPreviousRecords.swift; sourceTree = "<group>"; };
|
||||||
@@ -347,7 +349,8 @@
|
|||||||
5412F8ED24571B8100A63D7A /* VCDateFilter.swift */,
|
5412F8ED24571B8100A63D7A /* VCDateFilter.swift */,
|
||||||
54953E6023E0D69A0054345C /* TVCHosts.swift */,
|
54953E6023E0D69A0054345C /* TVCHosts.swift */,
|
||||||
54953E6E23E44CD00054345C /* TVCHostDetails.swift */,
|
54953E6E23E44CD00054345C /* TVCHostDetails.swift */,
|
||||||
541FC47424A12CE9009154D8 /* Analytics */,
|
544F911F24A67EC5001D4B00 /* TVCOccurrenceContext.swift */,
|
||||||
|
541FC47424A12CE9009154D8 /* Analysis */,
|
||||||
);
|
);
|
||||||
path = Requests;
|
path = Requests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -415,13 +418,13 @@
|
|||||||
path = main;
|
path = main;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
541FC47424A12CE9009154D8 /* Analytics */ = {
|
541FC47424A12CE9009154D8 /* Analysis */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
5404AEEE24ACC089003B2F54 /* VCAnalysisBar.swift */,
|
||||||
541FC47724A1453F009154D8 /* VCCoOccurrence.swift */,
|
541FC47724A1453F009154D8 /* VCCoOccurrence.swift */,
|
||||||
544F911F24A67EC5001D4B00 /* TVCOccurrenceContext.swift */,
|
|
||||||
);
|
);
|
||||||
path = Analytics;
|
path = Analysis;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
542E2A9B24051F79001462DC /* media */ = {
|
542E2A9B24051F79001462DC /* media */ = {
|
||||||
@@ -860,6 +863,7 @@
|
|||||||
54E540F8247DB90F00F7C34A /* RecordingsDB.swift in Sources */,
|
54E540F8247DB90F00F7C34A /* RecordingsDB.swift in Sources */,
|
||||||
54E67E4F24A8E2910025D261 /* Equatable.swift in Sources */,
|
54E67E4F24A8E2910025D261 /* Equatable.swift in Sources */,
|
||||||
54E540F4247D3F2600F7C34A /* TestDataSource.swift in Sources */,
|
54E540F4247D3F2600F7C34A /* TestDataSource.swift in Sources */,
|
||||||
|
5404AEEF24ACC089003B2F54 /* VCAnalysisBar.swift in Sources */,
|
||||||
545DDDD424466D37003B6544 /* AutoLayout.swift in Sources */,
|
545DDDD424466D37003B6544 /* AutoLayout.swift in Sources */,
|
||||||
54B345AD241BBB00004C53CC /* DBExtensions.swift in Sources */,
|
54B345AD241BBB00004C53CC /* DBExtensions.swift in Sources */,
|
||||||
54E540F2247C423200F7C34A /* DomainFilter.swift in Sources */,
|
54E540F2247C423200F7C34A /* DomainFilter.swift in Sources */,
|
||||||
@@ -1157,7 +1161,7 @@
|
|||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = main/main.entitlements;
|
CODE_SIGN_ENTITLEMENTS = main/main.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 23;
|
CURRENT_PROJECT_VERSION = 24;
|
||||||
INFOPLIST_FILE = main/Info.plist;
|
INFOPLIST_FILE = main/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -1176,7 +1180,7 @@
|
|||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = main/main.entitlements;
|
CODE_SIGN_ENTITLEMENTS = main/main.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 23;
|
CURRENT_PROJECT_VERSION = 24;
|
||||||
INFOPLIST_FILE = main/Info.plist;
|
INFOPLIST_FILE = main/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -1195,7 +1199,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements;
|
CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 23;
|
CURRENT_PROJECT_VERSION = 24;
|
||||||
INFOPLIST_FILE = GlassVPN/Info.plist;
|
INFOPLIST_FILE = GlassVPN/Info.plist;
|
||||||
MARKETING_VERSION = 1.0.0;
|
MARKETING_VERSION = 1.0.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN";
|
PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN";
|
||||||
@@ -1213,7 +1217,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements;
|
CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 23;
|
CURRENT_PROJECT_VERSION = 24;
|
||||||
INFOPLIST_FILE = GlassVPN/Info.plist;
|
INFOPLIST_FILE = GlassVPN/Info.plist;
|
||||||
MARKETING_VERSION = 1.0.0;
|
MARKETING_VERSION = 1.0.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN";
|
PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN";
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -16,6 +16,8 @@ Your data belongs to you.
|
|||||||
Therefore, monitoring and analysis take place on your device only.
|
Therefore, monitoring and analysis take place on your device only.
|
||||||
The app does not share any data with us or any other third-party – unless you choose to.
|
The app does not share any data with us or any other third-party – unless you choose to.
|
||||||
|
|
||||||
|
Join [Testflight beta](https://testflight.apple.com/join/9jjaFeHO)
|
||||||
|
|
||||||
|
|
||||||
### How does it work?
|
### How does it work?
|
||||||
|
|
||||||
@@ -31,19 +33,23 @@ That means, AppCheck does not have to be active in the foreground all the time.
|
|||||||
- See history of previous connections
|
- See history of previous connections
|
||||||
- Block unwanted traffic based on domain names
|
- Block unwanted traffic based on domain names
|
||||||
- Record app specific activity<sup>1</sup>
|
- Record app specific activity<sup>1</sup>
|
||||||
- Apply logging filters
|
- Apply logging filters (block or ignore) and display filters (specific range or last x minutes)
|
||||||
|
- Sort results by time, name, or occurrence count
|
||||||
|
- Context Analysis
|
||||||
|
- What other domains occur often at the same time?
|
||||||
|
- What happened immediately before or after the action?
|
||||||
|
- Export results for custom analysis
|
||||||
|
|
||||||
**… and soon:**
|
**… and soon:**
|
||||||
|
|
||||||
- Alert Monitor & reminder
|
- Alert Monitor & reminder
|
||||||
- Occurrence Context Analysis
|
|
||||||
- Participate in privacy research
|
- Participate in privacy research
|
||||||
|
|
||||||
|
|
||||||
<sup>1</sup> Due to technical limitations, recording is not limited to any single application. Remember to force-quit all other applications before starting a recording.
|
<sup>1</sup> Due to technical limitations, recordings can not be restricted to a single application. Remember to force-quit all other applications before starting a recording.
|
||||||
|
|
||||||
|
|
||||||
## Research Project
|
## Research Project
|
||||||
|
|
||||||
*information will be added soon*
|
*information will be added soon™*
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 158 KiB |
@@ -344,9 +344,16 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<containerView key="tableHeaderView" opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kh4-PQ-hy6">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="49"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<connections>
|
||||||
|
<segue destination="1ba-SA-8sT" kind="embed" id="vf1-07-AS4"/>
|
||||||
|
</connections>
|
||||||
|
</containerView>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="HostCell" textLabel="Rnk-SP-UHm" detailTextLabel="ovQ-lJ-hWJ" style="IBUITableViewCellStyleSubtitle" id="uv0-9B-Zbb">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="HostCell" textLabel="Rnk-SP-UHm" detailTextLabel="ovQ-lJ-hWJ" style="IBUITableViewCellStyleSubtitle" id="uv0-9B-Zbb">
|
||||||
<rect key="frame" x="0.0" y="28" width="320" height="57.5"/>
|
<rect key="frame" x="0.0" y="77" width="320" height="57.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="uv0-9B-Zbb" id="6vH-Du-gCg">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="uv0-9B-Zbb" id="6vH-Du-gCg">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="293" height="57.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="293" height="57.5"/>
|
||||||
@@ -392,17 +399,13 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
<tabBar key="tableHeaderView" contentMode="scaleToFill" fixedFrame="YES" translucent="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1Jy-zg-CXR">
|
<containerView key="tableHeaderView" opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SxM-2c-aJb">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="49"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="49"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
<items>
|
|
||||||
<tabBarItem title="Co-Occurrence" image="intersection" id="KXh-kQ-rAF"/>
|
|
||||||
</items>
|
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="delegate" destination="h7Z-Qr-pJ5" id="qNN-nI-Kub"/>
|
<segue destination="1ba-SA-8sT" kind="embed" id="ueN-6L-cP7"/>
|
||||||
</connections>
|
</connections>
|
||||||
</tabBar>
|
</containerView>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="HostDetailCell" textLabel="J2P-mU-Vad" detailTextLabel="eWb-mX-udN" style="IBUITableViewCellStyleValue1" id="ZCA-Dz-i92">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="HostDetailCell" textLabel="J2P-mU-Vad" detailTextLabel="eWb-mX-udN" style="IBUITableViewCellStyleValue1" id="ZCA-Dz-i92">
|
||||||
<rect key="frame" x="0.0" y="77" width="320" height="43.5"/>
|
<rect key="frame" x="0.0" y="77" width="320" height="43.5"/>
|
||||||
@@ -438,25 +441,52 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</tableView>
|
</tableView>
|
||||||
<navigationItem key="navigationItem" title="Occurrences" prompt="com.domain.network.cdn" id="bys-2u-rHs"/>
|
<navigationItem key="navigationItem" title="Occurrences" prompt="com.domain.network.cdn" id="bys-2u-rHs"/>
|
||||||
<connections>
|
|
||||||
<outlet property="actionsBar" destination="1Jy-zg-CXR" id="7x3-Vy-i9C"/>
|
|
||||||
<segue destination="W5Q-oz-bFb" kind="modal" identifier="segueAnalysisCoOccurrence" id="ukY-Dy-AIA"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewController>
|
</tableViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="UxH-PH-KQy" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="UxH-PH-KQy" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="2100" y="-1250"/>
|
<point key="canvasLocation" x="2100" y="-1250"/>
|
||||||
</scene>
|
</scene>
|
||||||
|
<!--Analysis Bar-->
|
||||||
|
<scene sceneID="1qq-WD-Lqq">
|
||||||
|
<objects>
|
||||||
|
<viewController id="1ba-SA-8sT" customClass="VCAnalysisBar" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="qp6-er-N6U">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="49"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<tabBar contentMode="scaleToFill" translucent="NO" id="1Jy-zg-CXR">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="49"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<items>
|
||||||
|
<tabBarItem title="Co-Occurrence" image="intersection" id="KXh-kQ-rAF"/>
|
||||||
|
</items>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="1ba-SA-8sT" id="bRS-kh-dOv"/>
|
||||||
|
</connections>
|
||||||
|
</tabBar>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<viewLayoutGuide key="safeArea" id="dtz-KG-P4C"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="tabBar" destination="1Jy-zg-CXR" id="VTV-xq-Aou"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="XnK-B9-RSJ" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="1400" y="-1950"/>
|
||||||
|
</scene>
|
||||||
<!--Co Occurrence-->
|
<!--Co Occurrence-->
|
||||||
<scene sceneID="Gbm-AP-b72">
|
<scene sceneID="Gbm-AP-b72">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="W5Q-oz-bFb" customClass="VCCoOccurrence" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController storyboardIdentifier="IBCoOccurrence" id="W5Q-oz-bFb" customClass="VCCoOccurrence" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="f34-NO-d8f">
|
<view key="view" contentMode="scaleToFill" id="f34-NO-d8f">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="548"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<navigationBar contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rvt-nC-2Zr">
|
<navigationBar contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rvt-nC-2Zr">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="56"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||||
<items>
|
<items>
|
||||||
<navigationItem title="Co-Occurrence" id="csY-x8-Rpe">
|
<navigationItem title="Co-Occurrence" id="csY-x8-Rpe">
|
||||||
<barButtonItem key="leftBarButtonItem" systemItem="done" id="eg9-p3-Xas">
|
<barButtonItem key="leftBarButtonItem" systemItem="done" id="eg9-p3-Xas">
|
||||||
@@ -466,7 +496,7 @@
|
|||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
<barButtonItem key="rightBarButtonItem" id="bTi-7F-CFS">
|
<barButtonItem key="rightBarButtonItem" id="bTi-7F-CFS">
|
||||||
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="infoLight" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" id="kqK-SL-CxZ">
|
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="infoLight" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" id="kqK-SL-CxZ">
|
||||||
<rect key="frame" x="279" y="16" width="25" height="24"/>
|
<rect key="frame" x="279" y="10" width="25" height="24"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="showInfoScreen" destination="W5Q-oz-bFb" eventType="touchUpInside" id="TuI-R9-PNr"/>
|
<action selector="showInfoScreen" destination="W5Q-oz-bFb" eventType="touchUpInside" id="TuI-R9-PNr"/>
|
||||||
@@ -477,7 +507,7 @@
|
|||||||
</items>
|
</items>
|
||||||
</navigationBar>
|
</navigationBar>
|
||||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="PGb-pB-cfO">
|
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="PGb-pB-cfO">
|
||||||
<rect key="frame" x="0.0" y="56" width="320" height="492"/>
|
<rect key="frame" x="0.0" y="44" width="320" height="524"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
<segmentedControl key="tableHeaderView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" id="7ye-tU-pdo">
|
<segmentedControl key="tableHeaderView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" id="7ye-tU-pdo">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="32"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="32"/>
|
||||||
@@ -1376,6 +1406,7 @@ Duration: 60:00</string>
|
|||||||
</scenes>
|
</scenes>
|
||||||
<inferredMetricsTieBreakers>
|
<inferredMetricsTieBreakers>
|
||||||
<segue reference="EzT-Xq-wka"/>
|
<segue reference="EzT-Xq-wka"/>
|
||||||
|
<segue reference="ueN-6L-cP7"/>
|
||||||
</inferredMetricsTieBreakers>
|
</inferredMetricsTieBreakers>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="filter-clear" width="20" height="20"/>
|
<image name="filter-clear" width="20" height="20"/>
|
||||||
|
|||||||
@@ -33,6 +33,13 @@ class SearchBarManager: NSObject, UISearchResultsUpdating {
|
|||||||
if #available(iOS 11.0, *) {
|
if #available(iOS 11.0, *) {
|
||||||
tvc?.navigationItem.searchController = controller
|
tvc?.navigationItem.searchController = controller
|
||||||
} else {
|
} else {
|
||||||
|
let thv = tvc?.tableView.tableHeaderView
|
||||||
|
guard thv == nil || thv is UISearchBar else {
|
||||||
|
// Don't overwrite actions bar (co-occurrence, etc.)
|
||||||
|
// FIXME: find alternative or iOS 9-10 users can't search in hosts
|
||||||
|
tvc = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
controller.loadViewIfNeeded() // Fix: "Attempting to load the view of a view controller while it is deallocating"
|
controller.loadViewIfNeeded() // Fix: "Attempting to load the view of a view controller while it is deallocating"
|
||||||
tvc?.definesPresentationContext = true // make search bar disappear if user changes scene (eg. select cell)
|
tvc?.definesPresentationContext = true // make search bar disappear if user changes scene (eg. select cell)
|
||||||
//tvc?.tableView.backgroundView = UIView() // iOS 11+ bug: bright white background in dark mode
|
//tvc?.tableView.backgroundView = UIView() // iOS 11+ bug: bright white background in dark mode
|
||||||
@@ -42,7 +49,7 @@ class SearchBarManager: NSObject, UISearchResultsUpdating {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Search callback
|
/// Search callback
|
||||||
func updateSearchResults(for controller: UISearchController) {
|
internal func updateSearchResults(for controller: UISearchController) {
|
||||||
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(performSearch), object: nil)
|
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(performSearch), object: nil)
|
||||||
perform(#selector(performSearch), with: nil, afterDelay: 0.2)
|
perform(#selector(performSearch), with: nil, afterDelay: 0.2)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,18 +172,10 @@ private class StickyPresentationController: UIPresentationController {
|
|||||||
let preferred = presentedViewController.preferredContentSize
|
let preferred = presentedViewController.preferredContentSize
|
||||||
switch stickTo {
|
switch stickTo {
|
||||||
case .left, .right:
|
case .left, .right:
|
||||||
let fitted = target.systemLayoutSizeFitting(
|
let fitted = target.fittingSize(fixedHeight: full.height, preferredWidth: preferred.width)
|
||||||
CGSize(width: preferred.width, height: full.height),
|
|
||||||
withHorizontalFittingPriority: .fittingSizeLevel,
|
|
||||||
verticalFittingPriority: .required
|
|
||||||
)
|
|
||||||
return CGSize(width: min(fitted.width, full.width), height: full.height)
|
return CGSize(width: min(fitted.width, full.width), height: full.height)
|
||||||
case .top, .bottom:
|
case .top, .bottom:
|
||||||
let fitted = target.systemLayoutSizeFitting(
|
let fitted = target.fittingSize(fixedWidth: full.width, preferredHeight: preferred.height)
|
||||||
CGSize(width: full.width, height: preferred.height),
|
|
||||||
withHorizontalFittingPriority: .required,
|
|
||||||
verticalFittingPriority: .fittingSizeLevel
|
|
||||||
)
|
|
||||||
return CGSize(width: full.width, height: min(fitted.height, full.height))
|
return CGSize(width: full.width, height: min(fitted.height, full.height))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,8 +244,9 @@ extension SQLiteDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get sorted, unique list of `ts` with given `fqdn`.
|
/// Get sorted, unique list of `ts` with given `fqdn`.
|
||||||
func dnsLogsUniqTs(_ fqdn: String) -> [Timestamp]? {
|
func dnsLogsUniqTs(_ domain: String, isFQDN flag: Bool) -> [Timestamp]? {
|
||||||
try? run(sql: "SELECT DISTINCT ts FROM heap WHERE fqdn = ? ORDER BY ts;", bind: [BindText(fqdn)]) {
|
try? run(sql: "SELECT DISTINCT ts FROM heap WHERE \(flag ? "fqdn" : "domain") = ? ORDER BY ts;",
|
||||||
|
bind: [BindText(domain)]) {
|
||||||
allRows($0) { col_ts($0, 0) }
|
allRows($0) { col_ts($0, 0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,7 +258,7 @@ extension SQLiteDatabase {
|
|||||||
/// - dt: Search for `ts - dt <= X <= ts + dt`
|
/// - dt: Search for `ts - dt <= X <= ts + dt`
|
||||||
/// - fqdn: Rows matching this domain will be excluded from the result set.
|
/// - fqdn: Rows matching this domain will be excluded from the result set.
|
||||||
/// - Returns: List of tuples ordered by rank (ASC).
|
/// - Returns: List of tuples ordered by rank (ASC).
|
||||||
func contextAnalysis(coOccurrence times: [Timestamp], plusMinus dt: Timestamp, exclude fqdn: String) -> [ContextAnalysisResult]? {
|
func contextAnalysis(coOccurrence times: [Timestamp], plusMinus dt: Timestamp, exclude domain: String, isFQDN flag: Bool) -> [ContextAnalysisResult]? {
|
||||||
guard times.count > 0 else { return nil }
|
guard times.count > 0 else { return nil }
|
||||||
createFunction("fnDist") {
|
createFunction("fnDist") {
|
||||||
let x = $0.first as! Timestamp
|
let x = $0.first as! Timestamp
|
||||||
@@ -282,10 +283,10 @@ extension SQLiteDatabase {
|
|||||||
SELECT fqdn, count, avg, (\(fnRank)) rank FROM (
|
SELECT fqdn, count, avg, (\(fnRank)) rank FROM (
|
||||||
SELECT fqdn, COUNT(*) count, AVG(dist) avg FROM (
|
SELECT fqdn, COUNT(*) count, AVG(dist) avg FROM (
|
||||||
SELECT fqdn, fnDist(ts) dist FROM heap
|
SELECT fqdn, fnDist(ts) dist FROM heap
|
||||||
WHERE ts BETWEEN ? AND ? AND fqdn != ? AND dist <= ?
|
WHERE ts BETWEEN ? AND ? AND \(flag ? "fqdn" : "domain") != ? AND dist <= ?
|
||||||
) GROUP BY fqdn
|
) GROUP BY fqdn
|
||||||
) ORDER BY rank ASC LIMIT 99;
|
) ORDER BY rank ASC LIMIT 99;
|
||||||
""", bind: [BindInt64(dt), BindInt64(low), BindInt64(high), BindText(fqdn), BindInt64(dt)]) {
|
""", bind: [BindInt64(dt), BindInt64(low), BindInt64(high), BindText(domain), BindInt64(dt)]) {
|
||||||
allRows($0) {
|
allRows($0) {
|
||||||
(col_text($0, 0) ?? "", sqlite3_column_int($0, 1), sqlite3_column_double($0, 2), sqlite3_column_double($0, 3))
|
(col_text($0, 0) ?? "", sqlite3_column_int($0, 1), sqlite3_column_double($0, 2), sqlite3_column_double($0, 3))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,14 +43,6 @@ extension UITableView {
|
|||||||
func safeMoveRow(_ from: Int, to: Int) {
|
func safeMoveRow(_ from: Int, to: Int) {
|
||||||
isFrontmost ? moveRow(at: IndexPath(row: from), to: IndexPath(row: to)) : reloadData()
|
isFrontmost ? moveRow(at: IndexPath(row: from), to: IndexPath(row: to)) : reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recalculate and apply new `tableHeaderView` height.
|
|
||||||
func sizeHeaderToFit() {
|
|
||||||
if let head = tableHeaderView {
|
|
||||||
head.frame.size.height = head.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
|
|
||||||
tableHeaderView = head
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,18 @@ extension UIView {
|
|||||||
return UIImage(cgImage: image!.cgImage!)
|
return UIImage(cgImage: image!.cgImage!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find size that fits into frame with given `width` as precondition.
|
||||||
|
/// - Parameter preferredHeight:If unset, find smallest possible size.
|
||||||
|
func fittingSize(fixedWidth: CGFloat, preferredHeight: CGFloat = 0) -> CGSize {
|
||||||
|
systemLayoutSizeFitting(CGSize(width: fixedWidth, height: preferredHeight), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find size that fits into frame with given `height` as precondition.
|
||||||
|
/// - Parameter preferredWidth:If unset, find smallest possible size.
|
||||||
|
func fittingSize(fixedHeight: CGFloat, preferredWidth: CGFloat = 0) -> CGSize {
|
||||||
|
systemLayoutSizeFitting(CGSize(width: preferredWidth, height: fixedHeight), withHorizontalFittingPriority: .fittingSizeLevel, verticalFittingPriority: .required)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UIEdgeInsets {
|
extension UIEdgeInsets {
|
||||||
|
|||||||
60
main/Requests/Analysis/VCAnalysisBar.swift
Normal file
60
main/Requests/Analysis/VCAnalysisBar.swift
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import UIKit
|
||||||
|
|
||||||
|
protocol AnalysisBarDelegate {
|
||||||
|
func analysisBarWillOpenCoOccurrence() -> (domain: String, isFQDN: Bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
class VCAnalysisBar: UIViewController, UITabBarDelegate {
|
||||||
|
|
||||||
|
@IBOutlet private var tabBar: UITabBar!
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
if #available(iOS 10.0, *) {
|
||||||
|
tabBar.unselectedItemTintColor = .sysLink
|
||||||
|
}
|
||||||
|
super.viewDidLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func willMove(toParent parent: UIViewController?) {
|
||||||
|
super.willMove(toParent: parent)
|
||||||
|
let enabled = (parent as? AnalysisBarDelegate) != nil
|
||||||
|
for item in tabBar.items! { item.isEnabled = enabled }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Tab Bar Appearance
|
||||||
|
|
||||||
|
override func viewWillAppear(_: Bool) {
|
||||||
|
resizeTableViewHeader()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func traitCollectionDidChange(_: UITraitCollection?) {
|
||||||
|
resizeTableViewHeader()
|
||||||
|
}
|
||||||
|
|
||||||
|
func resizeTableViewHeader() {
|
||||||
|
guard let tableView = (parent as? UITableViewController)?.tableView,
|
||||||
|
let head = tableView.tableHeaderView else { return }
|
||||||
|
// Recalculate and apply new height. Otherwise tabBar won't compress
|
||||||
|
tabBar.sizeToFit()
|
||||||
|
head.frame.size.height = tabBar.frame.height
|
||||||
|
tableView.tableHeaderView = head
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Tab Bar Delegate
|
||||||
|
|
||||||
|
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
|
||||||
|
tabBar.selectedItem = nil
|
||||||
|
openCoOccurrence()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func openCoOccurrence() {
|
||||||
|
guard let delegate = parent as? AnalysisBarDelegate,
|
||||||
|
let vc = storyboard?.instantiateViewController(withIdentifier: "IBCoOccurrence") as? VCCoOccurrence else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let info = delegate.analysisBarWillOpenCoOccurrence()
|
||||||
|
vc.domainName = info.domain
|
||||||
|
vc.isFQDN = info.isFQDN
|
||||||
|
present(vc, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class VCCoOccurrence: UIViewController, UITableViewDataSource {
|
class VCCoOccurrence: UIViewController, UITableViewDataSource {
|
||||||
var fqdn: String!
|
var domainName: String!
|
||||||
|
var isFQDN: Bool!
|
||||||
private var dataSource: [ContextAnalysisResult] = []
|
private var dataSource: [ContextAnalysisResult] = []
|
||||||
|
|
||||||
@IBOutlet private var tableView: UITableView!
|
@IBOutlet private var tableView: UITableView!
|
||||||
@@ -30,14 +31,15 @@ class VCCoOccurrence: UIViewController, UITableViewDataSource {
|
|||||||
dataSource = [("Loading …", 0, 0, 0)]
|
dataSource = [("Loading …", 0, 0, 0)]
|
||||||
logMaxCount = 1
|
logMaxCount = 1
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
let domain = fqdn!
|
let domain = domainName!
|
||||||
|
let flag = isFQDN!
|
||||||
let time = Timestamp(selectedTime)
|
let time = Timestamp(selectedTime)
|
||||||
DispatchQueue.global().async { [weak self] in
|
DispatchQueue.global().async { [weak self] in
|
||||||
let temp: [ContextAnalysisResult]
|
let temp: [ContextAnalysisResult]
|
||||||
let total: Int32
|
let total: Int32
|
||||||
if let db = AppDB,
|
if let db = AppDB,
|
||||||
let times = db.dnsLogsUniqTs(domain), times.count > 0,
|
let times = db.dnsLogsUniqTs(domain, isFQDN: flag), times.count > 0,
|
||||||
let result = db.contextAnalysis(coOccurrence: times, plusMinus: time, exclude: domain),
|
let result = db.contextAnalysis(coOccurrence: times, plusMinus: time, exclude: domain, isFQDN: flag),
|
||||||
result.count > 0
|
result.count > 0
|
||||||
{
|
{
|
||||||
temp = result
|
temp = result
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class TVCHostDetails: UITableViewController, SyncUpdateDelegate, UITabBarDelegate {
|
class TVCHostDetails: UITableViewController, SyncUpdateDelegate, AnalysisBarDelegate {
|
||||||
|
|
||||||
@IBOutlet private var actionsBar: UITabBar!
|
|
||||||
|
|
||||||
public var fullDomain: String!
|
public var fullDomain: String!
|
||||||
private var dataSource: [GroupedTsOccurrence] = []
|
private var dataSource: [GroupedTsOccurrence] = []
|
||||||
// TODO: respect date reverse sort order
|
// TODO: respect date reverse sort order
|
||||||
@@ -14,9 +12,19 @@ class TVCHostDetails: UITableViewController, SyncUpdateDelegate, UITabBarDelegat
|
|||||||
sync.addObserver(self) // calls `syncUpdate(reset:)`
|
sync.addObserver(self) // calls `syncUpdate(reset:)`
|
||||||
if #available(iOS 10.0, *) {
|
if #available(iOS 10.0, *) {
|
||||||
sync.allowPullToRefresh(onTVC: self, forObserver: self)
|
sync.allowPullToRefresh(onTVC: self, forObserver: self)
|
||||||
actionsBar.unselectedItemTintColor = .sysLink
|
|
||||||
}
|
}
|
||||||
UIDevice.orientationDidChangeNotification.observe(call: #selector(didChangeOrientation), on: self)
|
}
|
||||||
|
|
||||||
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
|
if let index = tableView.indexPathForSelectedRow?.row {
|
||||||
|
let tvc = segue.destination as? TVCOccurrenceContext
|
||||||
|
tvc?.domain = fullDomain
|
||||||
|
tvc?.ts = dataSource[index].ts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func analysisBarWillOpenCoOccurrence() -> (domain: String, isFQDN: Bool) {
|
||||||
|
(fullDomain, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Table View Data Source
|
// MARK: - Table View Data Source
|
||||||
@@ -33,34 +41,6 @@ class TVCHostDetails: UITableViewController, SyncUpdateDelegate, UITabBarDelegat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #########################
|
|
||||||
// #
|
|
||||||
// # MARK: - Tab Bar
|
|
||||||
// #
|
|
||||||
// #########################
|
|
||||||
|
|
||||||
extension TVCHostDetails {
|
|
||||||
|
|
||||||
@objc private func didChangeOrientation(_ sender: Notification) {
|
|
||||||
tableView.sizeHeaderToFit() // otherwise TabBar won't compress
|
|
||||||
}
|
|
||||||
|
|
||||||
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
|
|
||||||
tabBar.selectedItem = nil
|
|
||||||
performSegue(withIdentifier: "segueAnalysisCoOccurrence", sender: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
|
||||||
if segue.identifier == "segueAnalysisCoOccurrence" {
|
|
||||||
(segue.destination as? VCCoOccurrence)?.fqdn = fullDomain
|
|
||||||
} else if let index = tableView.indexPathForSelectedRow?.row {
|
|
||||||
let tvc = segue.destination as? TVCOccurrenceContext
|
|
||||||
tvc?.domain = fullDomain
|
|
||||||
tvc?.ts = dataSource[index].ts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ################################
|
// ################################
|
||||||
// #
|
// #
|
||||||
// # MARK: - Partial Update
|
// # MARK: - Partial Update
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class TVCHosts: UITableViewController, GroupedDomainDataSourceDelegate {
|
class TVCHosts: UITableViewController, GroupedDomainDataSourceDelegate, AnalysisBarDelegate {
|
||||||
|
|
||||||
lazy var source = GroupedDomainDataSource(withParent: parentDomain)
|
lazy var source = GroupedDomainDataSource(withParent: parentDomain)
|
||||||
|
|
||||||
@@ -21,6 +21,10 @@ class TVCHosts: UITableViewController, GroupedDomainDataSourceDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func analysisBarWillOpenCoOccurrence() -> (domain: String, isFQDN: Bool) {
|
||||||
|
(parentDomain, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Table View Data Source
|
// MARK: - Table View Data Source
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user