diff --git a/.bartycrouch.toml b/.bartycrouch.toml
new file mode 100644
index 0000000..334dc53
--- /dev/null
+++ b/.bartycrouch.toml
@@ -0,0 +1,34 @@
+[update]
+tasks = ["interfaces", "code", "transform", "normalize"]
+
+[update.interfaces]
+paths = ["."]
+defaultToBase = false
+ignoreEmptyStrings = false
+unstripped = false
+
+[update.code]
+codePaths = ["."]
+localizablePaths = ["."]
+defaultToKeys = true
+unstripped = false
+plistArguments = true
+
+# [update.transform]
+# codePaths = ["."]
+# localizablePaths = ["."]
+# transformer = "foundation"
+# supportedLanguageEnumPath = "."
+# typeName = "BartyCrouch"
+# translateMethodName = "translate"
+
+[update.normalize]
+paths = ["."]
+sourceLocale = "en"
+harmonizeWithSource = false
+sortByKeys = false
+
+[lint]
+paths = ["."]
+duplicateKeys = true
+emptyValues = true
diff --git a/AccessLevel.swift b/AccessLevel.swift
new file mode 100644
index 0000000..0e013f8
--- /dev/null
+++ b/AccessLevel.swift
@@ -0,0 +1,20 @@
+//
+// AccessLevel.swift
+// Lockdown
+//
+// Created by Aliaksandr Dvoineu on 4.05.23.
+// Copyright © 2023 Confirmed Inc. All rights reserved.
+//
+
+import Foundation
+
+enum AccessLevel: Int, CaseIterable {
+ case basic
+ case advanced
+ case anonymous
+ case universal
+
+ func hasFeature() -> Bool {
+ return true
+ }
+}
diff --git a/Assets.xcassets/AppIcon.appiconset/100.png b/Assets.xcassets/AppIcon.appiconset/100.png
deleted file mode 100644
index d92e384..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/100.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/114.png b/Assets.xcassets/AppIcon.appiconset/114.png
deleted file mode 100644
index 72bc77f..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/114.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/120-1.png b/Assets.xcassets/AppIcon.appiconset/120-1.png
deleted file mode 100644
index 62d7174..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/120-1.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/120.png b/Assets.xcassets/AppIcon.appiconset/120.png
deleted file mode 100644
index 62d7174..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/120.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/144.png b/Assets.xcassets/AppIcon.appiconset/144.png
deleted file mode 100644
index e2820cb..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/144.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/152.png b/Assets.xcassets/AppIcon.appiconset/152.png
deleted file mode 100644
index 5032512..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/152.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/167.png b/Assets.xcassets/AppIcon.appiconset/167.png
deleted file mode 100644
index 348eb94..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/167.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/180.png b/Assets.xcassets/AppIcon.appiconset/180.png
deleted file mode 100644
index 2088293..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/180.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/20.png b/Assets.xcassets/AppIcon.appiconset/20.png
deleted file mode 100644
index b9886a7..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/20.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/29-1.png b/Assets.xcassets/AppIcon.appiconset/29-1.png
deleted file mode 100644
index 18f66f6..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/29-1.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/29.png b/Assets.xcassets/AppIcon.appiconset/29.png
deleted file mode 100644
index 18f66f6..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/29.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/40-1.png b/Assets.xcassets/AppIcon.appiconset/40-1.png
deleted file mode 100644
index 2015aee..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/40-1.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/40-2.png b/Assets.xcassets/AppIcon.appiconset/40-2.png
deleted file mode 100644
index 2015aee..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/40-2.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/40.png b/Assets.xcassets/AppIcon.appiconset/40.png
deleted file mode 100644
index 2015aee..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/40.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/50.png b/Assets.xcassets/AppIcon.appiconset/50.png
deleted file mode 100644
index 8f7c6a1..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/50.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/57.png b/Assets.xcassets/AppIcon.appiconset/57.png
deleted file mode 100644
index 6b744e3..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/57.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/58-1.png b/Assets.xcassets/AppIcon.appiconset/58-1.png
deleted file mode 100644
index eb6d7eb..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/58-1.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/58.png b/Assets.xcassets/AppIcon.appiconset/58.png
deleted file mode 100644
index eb6d7eb..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/58.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/60.png b/Assets.xcassets/AppIcon.appiconset/60.png
deleted file mode 100644
index 051ffab..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/60.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/72.png b/Assets.xcassets/AppIcon.appiconset/72.png
deleted file mode 100644
index dc66e6c..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/72.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/76.png b/Assets.xcassets/AppIcon.appiconset/76.png
deleted file mode 100644
index 5abd222..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/76.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/80-1.png b/Assets.xcassets/AppIcon.appiconset/80-1.png
deleted file mode 100644
index b156cc8..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/80-1.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/80.png b/Assets.xcassets/AppIcon.appiconset/80.png
deleted file mode 100644
index b156cc8..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/80.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/87.png b/Assets.xcassets/AppIcon.appiconset/87.png
deleted file mode 100644
index 43c9d8d..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/87.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/Contents.json b/Assets.xcassets/AppIcon.appiconset/Contents.json
index 7a83853..28d9cc7 100644
--- a/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -1,158 +1,158 @@
{
"images" : [
{
- "size" : "20x20",
+ "filename" : "notification-icon@2x.png",
"idiom" : "iphone",
- "filename" : "40.png",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "20x20"
},
{
- "size" : "20x20",
+ "filename" : "notification-icon@3x.png",
"idiom" : "iphone",
- "filename" : "60.png",
- "scale" : "3x"
+ "scale" : "3x",
+ "size" : "20x20"
},
{
- "size" : "29x29",
+ "filename" : "icon-small.png",
"idiom" : "iphone",
- "filename" : "29.png",
- "scale" : "1x"
+ "scale" : "1x",
+ "size" : "29x29"
},
{
- "size" : "29x29",
+ "filename" : "icon-small@2x.png",
"idiom" : "iphone",
- "filename" : "58.png",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "29x29"
},
{
- "size" : "29x29",
+ "filename" : "icon-small@3x.png",
"idiom" : "iphone",
- "filename" : "87.png",
- "scale" : "3x"
+ "scale" : "3x",
+ "size" : "29x29"
},
{
- "size" : "40x40",
+ "filename" : "icon-40@2x.png",
"idiom" : "iphone",
- "filename" : "80.png",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "40x40"
},
{
- "size" : "40x40",
+ "filename" : "icon-40@3x.png",
"idiom" : "iphone",
- "filename" : "120.png",
- "scale" : "3x"
+ "scale" : "3x",
+ "size" : "40x40"
},
{
- "size" : "57x57",
+ "filename" : "icon.png",
"idiom" : "iphone",
- "filename" : "57.png",
- "scale" : "1x"
+ "scale" : "1x",
+ "size" : "57x57"
},
{
- "size" : "57x57",
+ "filename" : "icon@2x.png",
"idiom" : "iphone",
- "filename" : "114.png",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "57x57"
},
{
- "size" : "60x60",
+ "filename" : "icon-60@2x.png",
"idiom" : "iphone",
- "filename" : "120-1.png",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "60x60"
},
{
- "size" : "60x60",
+ "filename" : "icon-60@3x.png",
"idiom" : "iphone",
- "filename" : "180.png",
- "scale" : "3x"
+ "scale" : "3x",
+ "size" : "60x60"
},
{
- "size" : "20x20",
+ "filename" : "notification-icon~ipad.png",
"idiom" : "ipad",
- "filename" : "20.png",
- "scale" : "1x"
+ "scale" : "1x",
+ "size" : "20x20"
},
{
- "size" : "20x20",
+ "filename" : "notification-icon~ipad@2x.png",
"idiom" : "ipad",
- "filename" : "40-1.png",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "20x20"
},
{
- "size" : "29x29",
+ "filename" : "icon-small.png",
"idiom" : "ipad",
- "filename" : "29-1.png",
- "scale" : "1x"
+ "scale" : "1x",
+ "size" : "29x29"
},
{
- "size" : "29x29",
+ "filename" : "icon-small@2x.png",
"idiom" : "ipad",
- "filename" : "58-1.png",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "29x29"
},
{
- "size" : "40x40",
+ "filename" : "icon-40.png",
"idiom" : "ipad",
- "filename" : "40-2.png",
- "scale" : "1x"
+ "scale" : "1x",
+ "size" : "40x40"
},
{
- "size" : "40x40",
+ "filename" : "icon-40@2x.png",
"idiom" : "ipad",
- "filename" : "80-1.png",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "40x40"
},
{
- "size" : "50x50",
+ "filename" : "icon-small-50.png",
"idiom" : "ipad",
- "filename" : "50.png",
- "scale" : "1x"
+ "scale" : "1x",
+ "size" : "50x50"
},
{
- "size" : "50x50",
+ "filename" : "icon-small-50@2x.png",
"idiom" : "ipad",
- "filename" : "100.png",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "50x50"
},
{
- "size" : "72x72",
+ "filename" : "icon-72.png",
"idiom" : "ipad",
- "filename" : "72.png",
- "scale" : "1x"
+ "scale" : "1x",
+ "size" : "72x72"
},
{
- "size" : "72x72",
+ "filename" : "icon-72@2x.png",
"idiom" : "ipad",
- "filename" : "144.png",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "72x72"
},
{
- "size" : "76x76",
+ "filename" : "icon-76.png",
"idiom" : "ipad",
- "filename" : "76.png",
- "scale" : "1x"
+ "scale" : "1x",
+ "size" : "76x76"
},
{
- "size" : "76x76",
+ "filename" : "icon-76@2x.png",
"idiom" : "ipad",
- "filename" : "152.png",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "76x76"
},
{
- "size" : "83.5x83.5",
+ "filename" : "icon-83.5@2x.png",
"idiom" : "ipad",
- "filename" : "167.png",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "83.5x83.5"
},
{
- "size" : "1024x1024",
+ "filename" : "ios-marketing.png",
"idiom" : "ios-marketing",
- "filename" : "Icon-1024.png",
- "scale" : "1x"
+ "scale" : "1x",
+ "size" : "1024x1024"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/Assets.xcassets/AppIcon.appiconset/Icon-1024.png b/Assets.xcassets/AppIcon.appiconset/Icon-1024.png
deleted file mode 100644
index 4247180..0000000
Binary files a/Assets.xcassets/AppIcon.appiconset/Icon-1024.png and /dev/null differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-40.png b/Assets.xcassets/AppIcon.appiconset/icon-40.png
new file mode 100644
index 0000000..df37be7
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-40.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
new file mode 100644
index 0000000..e32b637
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
new file mode 100644
index 0000000..cc0ab7e
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
new file mode 100644
index 0000000..cc0ab7e
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
new file mode 100644
index 0000000..805fcc8
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-72.png b/Assets.xcassets/AppIcon.appiconset/icon-72.png
new file mode 100644
index 0000000..b4c5244
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-72.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png b/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png
new file mode 100644
index 0000000..ef55962
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-76.png b/Assets.xcassets/AppIcon.appiconset/icon-76.png
new file mode 100644
index 0000000..beb0e66
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-76.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
new file mode 100644
index 0000000..e20ae82
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
new file mode 100644
index 0000000..a829af4
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-small-50.png b/Assets.xcassets/AppIcon.appiconset/icon-small-50.png
new file mode 100644
index 0000000..766512b
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-small-50.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-small-50@2x.png b/Assets.xcassets/AppIcon.appiconset/icon-small-50@2x.png
new file mode 100644
index 0000000..9b5f1b4
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-small-50@2x.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-small.png b/Assets.xcassets/AppIcon.appiconset/icon-small.png
new file mode 100644
index 0000000..a234b36
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-small.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-small@2x.png b/Assets.xcassets/AppIcon.appiconset/icon-small@2x.png
new file mode 100644
index 0000000..c26bed0
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-small@2x.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon-small@3x.png b/Assets.xcassets/AppIcon.appiconset/icon-small@3x.png
new file mode 100644
index 0000000..173686b
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon-small@3x.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon.png b/Assets.xcassets/AppIcon.appiconset/icon.png
new file mode 100644
index 0000000..91e2ffe
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/icon@2x.png b/Assets.xcassets/AppIcon.appiconset/icon@2x.png
new file mode 100644
index 0000000..4eb75dd
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/icon@2x.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/ios-marketing.png b/Assets.xcassets/AppIcon.appiconset/ios-marketing.png
new file mode 100644
index 0000000..8a0250e
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/ios-marketing.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/notification-icon@2x.png b/Assets.xcassets/AppIcon.appiconset/notification-icon@2x.png
new file mode 100644
index 0000000..df37be7
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/notification-icon@2x.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/notification-icon@3x.png b/Assets.xcassets/AppIcon.appiconset/notification-icon@3x.png
new file mode 100644
index 0000000..d77b643
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/notification-icon@3x.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad.png b/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad.png
new file mode 100644
index 0000000..be9e6b9
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad.png differ
diff --git a/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad@2x.png b/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad@2x.png
new file mode 100644
index 0000000..df37be7
Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad@2x.png differ
diff --git a/Assets.xcassets/Block Lists/Advanced Lists/Contents.json b/Assets.xcassets/Block Lists/Advanced Lists/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Assets.xcassets/Block Lists/Advanced Lists/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Block Lists/Advanced Lists/icn-junes-journey.imageset/Contents.json b/Assets.xcassets/Block Lists/Advanced Lists/icn-junes-journey.imageset/Contents.json
new file mode 100644
index 0000000..b63ef64
--- /dev/null
+++ b/Assets.xcassets/Block Lists/Advanced Lists/icn-junes-journey.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn-junes-journey.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Block Lists/Advanced Lists/icn-junes-journey.imageset/icn-junes-journey.pdf b/Assets.xcassets/Block Lists/Advanced Lists/icn-junes-journey.imageset/icn-junes-journey.pdf
new file mode 100644
index 0000000..adf0c2a
Binary files /dev/null and b/Assets.xcassets/Block Lists/Advanced Lists/icn-junes-journey.imageset/icn-junes-journey.pdf differ
diff --git a/Assets.xcassets/Block Lists/Advanced Lists/icn_advanced_analytics.imageset/Contents.json b/Assets.xcassets/Block Lists/Advanced Lists/icn_advanced_analytics.imageset/Contents.json
new file mode 100644
index 0000000..94a2b43
--- /dev/null
+++ b/Assets.xcassets/Block Lists/Advanced Lists/icn_advanced_analytics.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_advanced_analytics.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Block Lists/Advanced Lists/icn_advanced_analytics.imageset/icn_advanced_analytics.pdf b/Assets.xcassets/Block Lists/Advanced Lists/icn_advanced_analytics.imageset/icn_advanced_analytics.pdf
new file mode 100644
index 0000000..20c3b8f
Binary files /dev/null and b/Assets.xcassets/Block Lists/Advanced Lists/icn_advanced_analytics.imageset/icn_advanced_analytics.pdf differ
diff --git a/Assets.xcassets/Block Lists/Advanced Lists/icn_advanced_gaming.imageset/Contents.json b/Assets.xcassets/Block Lists/Advanced Lists/icn_advanced_gaming.imageset/Contents.json
new file mode 100644
index 0000000..268276f
--- /dev/null
+++ b/Assets.xcassets/Block Lists/Advanced Lists/icn_advanced_gaming.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_advanced_gaming.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Block Lists/Advanced Lists/icn_advanced_gaming.imageset/icn_advanced_gaming.pdf b/Assets.xcassets/Block Lists/Advanced Lists/icn_advanced_gaming.imageset/icn_advanced_gaming.pdf
new file mode 100644
index 0000000..350bc42
Binary files /dev/null and b/Assets.xcassets/Block Lists/Advanced Lists/icn_advanced_gaming.imageset/icn_advanced_gaming.pdf differ
diff --git a/Assets.xcassets/Block Lists/Advanced Lists/icn_ifunny.imageset/Contents.json b/Assets.xcassets/Block Lists/Advanced Lists/icn_ifunny.imageset/Contents.json
new file mode 100644
index 0000000..c89b3f9
--- /dev/null
+++ b/Assets.xcassets/Block Lists/Advanced Lists/icn_ifunny.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_ifunny.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Block Lists/Advanced Lists/icn_ifunny.imageset/icn_ifunny.pdf b/Assets.xcassets/Block Lists/Advanced Lists/icn_ifunny.imageset/icn_ifunny.pdf
new file mode 100644
index 0000000..6002b89
Binary files /dev/null and b/Assets.xcassets/Block Lists/Advanced Lists/icn_ifunny.imageset/icn_ifunny.pdf differ
diff --git a/Assets.xcassets/Block Lists/Advanced Lists/icn_scams.imageset/Contents.json b/Assets.xcassets/Block Lists/Advanced Lists/icn_scams.imageset/Contents.json
new file mode 100644
index 0000000..41b559a
--- /dev/null
+++ b/Assets.xcassets/Block Lists/Advanced Lists/icn_scams.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_scams.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Block Lists/Advanced Lists/icn_scams.imageset/icn_scams.pdf b/Assets.xcassets/Block Lists/Advanced Lists/icn_scams.imageset/icn_scams.pdf
new file mode 100644
index 0000000..7bfe4a6
Binary files /dev/null and b/Assets.xcassets/Block Lists/Advanced Lists/icn_scams.imageset/icn_scams.pdf differ
diff --git a/Assets.xcassets/Block Lists/Advanced Lists/icn_tiktok.imageset/Contents.json b/Assets.xcassets/Block Lists/Advanced Lists/icn_tiktok.imageset/Contents.json
new file mode 100644
index 0000000..34e6f76
--- /dev/null
+++ b/Assets.xcassets/Block Lists/Advanced Lists/icn_tiktok.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_tiktok.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Block Lists/Advanced Lists/icn_tiktok.imageset/icn_tiktok.pdf b/Assets.xcassets/Block Lists/Advanced Lists/icn_tiktok.imageset/icn_tiktok.pdf
new file mode 100644
index 0000000..08f8622
Binary files /dev/null and b/Assets.xcassets/Block Lists/Advanced Lists/icn_tiktok.imageset/icn_tiktok.pdf differ
diff --git a/Assets.xcassets/Block Lists/Contents.json b/Assets.xcassets/Block Lists/Contents.json
index da4a164..73c0059 100644
--- a/Assets.xcassets/Block Lists/Contents.json
+++ b/Assets.xcassets/Block Lists/Contents.json
@@ -1,6 +1,6 @@
{
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/Assets.xcassets/Block Lists/ads_icon.imageset/Contents.json b/Assets.xcassets/Block Lists/ads_icon.imageset/Contents.json
new file mode 100644
index 0000000..3ae77e6
--- /dev/null
+++ b/Assets.xcassets/Block Lists/ads_icon.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "ads.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "ads-1.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "ads-2.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Block Lists/ads_icon.imageset/ads-1.png b/Assets.xcassets/Block Lists/ads_icon.imageset/ads-1.png
new file mode 100644
index 0000000..0d9c199
Binary files /dev/null and b/Assets.xcassets/Block Lists/ads_icon.imageset/ads-1.png differ
diff --git a/Assets.xcassets/Block Lists/ads_icon.imageset/ads-2.png b/Assets.xcassets/Block Lists/ads_icon.imageset/ads-2.png
new file mode 100644
index 0000000..0d9c199
Binary files /dev/null and b/Assets.xcassets/Block Lists/ads_icon.imageset/ads-2.png differ
diff --git a/Assets.xcassets/Block Lists/ads_icon.imageset/ads.png b/Assets.xcassets/Block Lists/ads_icon.imageset/ads.png
new file mode 100644
index 0000000..0d9c199
Binary files /dev/null and b/Assets.xcassets/Block Lists/ads_icon.imageset/ads.png differ
diff --git a/Assets.xcassets/Block Lists/amazon_icon.imageset/Contents.json b/Assets.xcassets/Block Lists/amazon_icon.imageset/Contents.json
new file mode 100644
index 0000000..01e1bfa
--- /dev/null
+++ b/Assets.xcassets/Block Lists/amazon_icon.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "pngegg.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "pngegg-1.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "pngegg-2.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Block Lists/amazon_icon.imageset/pngegg-1.png b/Assets.xcassets/Block Lists/amazon_icon.imageset/pngegg-1.png
new file mode 100644
index 0000000..420ad0e
Binary files /dev/null and b/Assets.xcassets/Block Lists/amazon_icon.imageset/pngegg-1.png differ
diff --git a/Assets.xcassets/Block Lists/amazon_icon.imageset/pngegg-2.png b/Assets.xcassets/Block Lists/amazon_icon.imageset/pngegg-2.png
new file mode 100644
index 0000000..420ad0e
Binary files /dev/null and b/Assets.xcassets/Block Lists/amazon_icon.imageset/pngegg-2.png differ
diff --git a/Assets.xcassets/Block Lists/amazon_icon.imageset/pngegg.png b/Assets.xcassets/Block Lists/amazon_icon.imageset/pngegg.png
new file mode 100644
index 0000000..420ad0e
Binary files /dev/null and b/Assets.xcassets/Block Lists/amazon_icon.imageset/pngegg.png differ
diff --git a/Assets.xcassets/Block Lists/facebook_white_icon.imageset/Contents.json b/Assets.xcassets/Block Lists/facebook_white_icon.imageset/Contents.json
index 6f0edd2..32f8535 100644
--- a/Assets.xcassets/Block Lists/facebook_white_icon.imageset/Contents.json
+++ b/Assets.xcassets/Block Lists/facebook_white_icon.imageset/Contents.json
@@ -2,7 +2,7 @@
"images" : [
{
"idiom" : "universal",
- "filename" : "Logos-Facebook-icon.png",
+ "filename" : "facebook_white_icon.png",
"scale" : "1x"
},
{
diff --git a/Assets.xcassets/Block Lists/facebook_white_icon.imageset/Logos-Facebook-icon.png b/Assets.xcassets/Block Lists/facebook_white_icon.imageset/Logos-Facebook-icon.png
deleted file mode 100644
index d7db7af..0000000
Binary files a/Assets.xcassets/Block Lists/facebook_white_icon.imageset/Logos-Facebook-icon.png and /dev/null differ
diff --git a/Assets.xcassets/Block Lists/facebook_white_icon.imageset/facebook_white_icon.png b/Assets.xcassets/Block Lists/facebook_white_icon.imageset/facebook_white_icon.png
new file mode 100644
index 0000000..16262e0
Binary files /dev/null and b/Assets.xcassets/Block Lists/facebook_white_icon.imageset/facebook_white_icon.png differ
diff --git a/Assets.xcassets/power_button.imageset/Contents.json b/Assets.xcassets/Block Lists/game_ads_icon.imageset/Contents.json
similarity index 69%
rename from Assets.xcassets/power_button.imageset/Contents.json
rename to Assets.xcassets/Block Lists/game_ads_icon.imageset/Contents.json
index ce9a723..7c88ab0 100644
--- a/Assets.xcassets/power_button.imageset/Contents.json
+++ b/Assets.xcassets/Block Lists/game_ads_icon.imageset/Contents.json
@@ -2,17 +2,17 @@
"images" : [
{
"idiom" : "universal",
- "filename" : "power-button.png",
+ "filename" : "game_ads.png",
"scale" : "1x"
},
{
"idiom" : "universal",
- "filename" : "power-button-1.png",
+ "filename" : "game_ads_icon-1.png",
"scale" : "2x"
},
{
"idiom" : "universal",
- "filename" : "power-button-2.png",
+ "filename" : "game_ads_icon-2.png",
"scale" : "3x"
}
],
diff --git a/Assets.xcassets/Block Lists/game_ads_icon.imageset/game_ads.png b/Assets.xcassets/Block Lists/game_ads_icon.imageset/game_ads.png
new file mode 100644
index 0000000..b8ae567
Binary files /dev/null and b/Assets.xcassets/Block Lists/game_ads_icon.imageset/game_ads.png differ
diff --git a/Assets.xcassets/Block Lists/game_ads_icon.imageset/game_ads_icon-1.png b/Assets.xcassets/Block Lists/game_ads_icon.imageset/game_ads_icon-1.png
new file mode 100644
index 0000000..b8ae567
Binary files /dev/null and b/Assets.xcassets/Block Lists/game_ads_icon.imageset/game_ads_icon-1.png differ
diff --git a/Assets.xcassets/Block Lists/game_ads_icon.imageset/game_ads_icon-2.png b/Assets.xcassets/Block Lists/game_ads_icon.imageset/game_ads_icon-2.png
new file mode 100644
index 0000000..b8ae567
Binary files /dev/null and b/Assets.xcassets/Block Lists/game_ads_icon.imageset/game_ads_icon-2.png differ
diff --git a/Assets.xcassets/safari.imageset/Contents.json b/Assets.xcassets/Block Lists/google_icon.imageset/Contents.json
similarity index 72%
rename from Assets.xcassets/safari.imageset/Contents.json
rename to Assets.xcassets/Block Lists/google_icon.imageset/Contents.json
index e9db547..afbe6f7 100644
--- a/Assets.xcassets/safari.imageset/Contents.json
+++ b/Assets.xcassets/Block Lists/google_icon.imageset/Contents.json
@@ -2,17 +2,17 @@
"images" : [
{
"idiom" : "universal",
- "filename" : "safari.png",
+ "filename" : "google-1.png",
"scale" : "1x"
},
{
"idiom" : "universal",
- "filename" : "safari-1.png",
+ "filename" : "google-2.png",
"scale" : "2x"
},
{
"idiom" : "universal",
- "filename" : "safari-2.png",
+ "filename" : "google.png",
"scale" : "3x"
}
],
diff --git a/Assets.xcassets/Block Lists/google_icon.imageset/google-1.png b/Assets.xcassets/Block Lists/google_icon.imageset/google-1.png
new file mode 100644
index 0000000..35e7062
Binary files /dev/null and b/Assets.xcassets/Block Lists/google_icon.imageset/google-1.png differ
diff --git a/Assets.xcassets/Block Lists/google_icon.imageset/google-2.png b/Assets.xcassets/Block Lists/google_icon.imageset/google-2.png
new file mode 100644
index 0000000..35e7062
Binary files /dev/null and b/Assets.xcassets/Block Lists/google_icon.imageset/google-2.png differ
diff --git a/Assets.xcassets/Block Lists/google_icon.imageset/google.png b/Assets.xcassets/Block Lists/google_icon.imageset/google.png
new file mode 100644
index 0000000..35e7062
Binary files /dev/null and b/Assets.xcassets/Block Lists/google_icon.imageset/google.png differ
diff --git a/Assets.xcassets/Block Lists/marketing_icon.imageset/Contents.json b/Assets.xcassets/Block Lists/marketing_icon.imageset/Contents.json
index a13fb58..30e09a5 100644
--- a/Assets.xcassets/Block Lists/marketing_icon.imageset/Contents.json
+++ b/Assets.xcassets/Block Lists/marketing_icon.imageset/Contents.json
@@ -2,7 +2,7 @@
"images" : [
{
"idiom" : "universal",
- "filename" : "noun_Magnifying Glass_251109.png",
+ "filename" : "marketing.png",
"scale" : "1x"
},
{
diff --git a/Assets.xcassets/Block Lists/marketing_icon.imageset/marketing.png b/Assets.xcassets/Block Lists/marketing_icon.imageset/marketing.png
new file mode 100644
index 0000000..f065691
Binary files /dev/null and b/Assets.xcassets/Block Lists/marketing_icon.imageset/marketing.png differ
diff --git a/Assets.xcassets/Block Lists/marketing_icon.imageset/noun_Magnifying Glass_251109.png b/Assets.xcassets/Block Lists/marketing_icon.imageset/noun_Magnifying Glass_251109.png
deleted file mode 100644
index 354ffeb..0000000
Binary files a/Assets.xcassets/Block Lists/marketing_icon.imageset/noun_Magnifying Glass_251109.png and /dev/null differ
diff --git a/Assets.xcassets/Block Lists/ransomware_icon.imageset/Contents.json b/Assets.xcassets/Block Lists/ransomware_icon.imageset/Contents.json
index 5a786cb..8072c85 100644
--- a/Assets.xcassets/Block Lists/ransomware_icon.imageset/Contents.json
+++ b/Assets.xcassets/Block Lists/ransomware_icon.imageset/Contents.json
@@ -2,7 +2,7 @@
"images" : [
{
"idiom" : "universal",
- "filename" : "ransomware-2.png",
+ "filename" : "ransomware.png",
"scale" : "1x"
},
{
diff --git a/Assets.xcassets/Block Lists/ransomware_icon.imageset/ransomware-2.png b/Assets.xcassets/Block Lists/ransomware_icon.imageset/ransomware.png
similarity index 100%
rename from Assets.xcassets/Block Lists/ransomware_icon.imageset/ransomware-2.png
rename to Assets.xcassets/Block Lists/ransomware_icon.imageset/ransomware.png
diff --git a/Assets.xcassets/Block Lists/reporting_icon.imageset/Contents.json b/Assets.xcassets/Block Lists/reporting_icon.imageset/Contents.json
new file mode 100644
index 0000000..803383c
--- /dev/null
+++ b/Assets.xcassets/Block Lists/reporting_icon.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "report.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "report-1.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "report-2.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Block Lists/reporting_icon.imageset/report-1.png b/Assets.xcassets/Block Lists/reporting_icon.imageset/report-1.png
new file mode 100644
index 0000000..1a2deff
Binary files /dev/null and b/Assets.xcassets/Block Lists/reporting_icon.imageset/report-1.png differ
diff --git a/Assets.xcassets/Block Lists/reporting_icon.imageset/report-2.png b/Assets.xcassets/Block Lists/reporting_icon.imageset/report-2.png
new file mode 100644
index 0000000..1a2deff
Binary files /dev/null and b/Assets.xcassets/Block Lists/reporting_icon.imageset/report-2.png differ
diff --git a/Assets.xcassets/Block Lists/reporting_icon.imageset/report.png b/Assets.xcassets/Block Lists/reporting_icon.imageset/report.png
new file mode 100644
index 0000000..1a2deff
Binary files /dev/null and b/Assets.xcassets/Block Lists/reporting_icon.imageset/report.png differ
diff --git a/Assets.xcassets/menu.imageset/Contents.json b/Assets.xcassets/Block Lists/snapchat_analytics_icon.imageset/Contents.json
similarity index 69%
rename from Assets.xcassets/menu.imageset/Contents.json
rename to Assets.xcassets/Block Lists/snapchat_analytics_icon.imageset/Contents.json
index 292b849..0a10d3c 100644
--- a/Assets.xcassets/menu.imageset/Contents.json
+++ b/Assets.xcassets/Block Lists/snapchat_analytics_icon.imageset/Contents.json
@@ -2,17 +2,17 @@
"images" : [
{
"idiom" : "universal",
- "filename" : "menu-button.png",
+ "filename" : "ghost_1f47b.png",
"scale" : "1x"
},
{
"idiom" : "universal",
- "filename" : "menu-button-1.png",
+ "filename" : "ghost_1f47b-1.png",
"scale" : "2x"
},
{
"idiom" : "universal",
- "filename" : "menu-button-2.png",
+ "filename" : "ghost_1f47b-2.png",
"scale" : "3x"
}
],
diff --git a/Assets.xcassets/Block Lists/snapchat_analytics_icon.imageset/ghost_1f47b-1.png b/Assets.xcassets/Block Lists/snapchat_analytics_icon.imageset/ghost_1f47b-1.png
new file mode 100644
index 0000000..c936ed8
Binary files /dev/null and b/Assets.xcassets/Block Lists/snapchat_analytics_icon.imageset/ghost_1f47b-1.png differ
diff --git a/Assets.xcassets/Block Lists/snapchat_analytics_icon.imageset/ghost_1f47b-2.png b/Assets.xcassets/Block Lists/snapchat_analytics_icon.imageset/ghost_1f47b-2.png
new file mode 100644
index 0000000..c936ed8
Binary files /dev/null and b/Assets.xcassets/Block Lists/snapchat_analytics_icon.imageset/ghost_1f47b-2.png differ
diff --git a/Assets.xcassets/Block Lists/snapchat_analytics_icon.imageset/ghost_1f47b.png b/Assets.xcassets/Block Lists/snapchat_analytics_icon.imageset/ghost_1f47b.png
new file mode 100644
index 0000000..c936ed8
Binary files /dev/null and b/Assets.xcassets/Block Lists/snapchat_analytics_icon.imageset/ghost_1f47b.png differ
diff --git a/Assets.xcassets/Block Lists/user_data_icon.imageset/Contents.json b/Assets.xcassets/Block Lists/user_data_icon.imageset/Contents.json
new file mode 100644
index 0000000..36425c7
--- /dev/null
+++ b/Assets.xcassets/Block Lists/user_data_icon.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "user-data-icon.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "user-data-icon-1.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "user-data-icon-2.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Block Lists/user_data_icon.imageset/user-data-icon-1.png b/Assets.xcassets/Block Lists/user_data_icon.imageset/user-data-icon-1.png
new file mode 100644
index 0000000..6e82dc6
Binary files /dev/null and b/Assets.xcassets/Block Lists/user_data_icon.imageset/user-data-icon-1.png differ
diff --git a/Assets.xcassets/Block Lists/user_data_icon.imageset/user-data-icon-2.png b/Assets.xcassets/Block Lists/user_data_icon.imageset/user-data-icon-2.png
new file mode 100644
index 0000000..6e82dc6
Binary files /dev/null and b/Assets.xcassets/Block Lists/user_data_icon.imageset/user-data-icon-2.png differ
diff --git a/Assets.xcassets/Block Lists/user_data_icon.imageset/user-data-icon.png b/Assets.xcassets/Block Lists/user_data_icon.imageset/user-data-icon.png
new file mode 100644
index 0000000..6e82dc6
Binary files /dev/null and b/Assets.xcassets/Block Lists/user_data_icon.imageset/user-data-icon.png differ
diff --git a/Assets.xcassets/Configure Blocking/Contents.json b/Assets.xcassets/Configure Blocking/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Assets.xcassets/Configure Blocking/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Configure Blocking/icn_create_list.imageset/Contents.json b/Assets.xcassets/Configure Blocking/icn_create_list.imageset/Contents.json
new file mode 100644
index 0000000..f36680e
--- /dev/null
+++ b/Assets.xcassets/Configure Blocking/icn_create_list.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_create_list.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Configure Blocking/icn_create_list.imageset/icn_create_list.pdf b/Assets.xcassets/Configure Blocking/icn_create_list.imageset/icn_create_list.pdf
new file mode 100644
index 0000000..4f5144e
Binary files /dev/null and b/Assets.xcassets/Configure Blocking/icn_create_list.imageset/icn_create_list.pdf differ
diff --git a/Assets.xcassets/Configure Blocking/icn_csv_file.imageset/Contents.json b/Assets.xcassets/Configure Blocking/icn_csv_file.imageset/Contents.json
new file mode 100644
index 0000000..0d49867
--- /dev/null
+++ b/Assets.xcassets/Configure Blocking/icn_csv_file.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_csv_file.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Configure Blocking/icn_csv_file.imageset/icn_csv_file.pdf b/Assets.xcassets/Configure Blocking/icn_csv_file.imageset/icn_csv_file.pdf
new file mode 100644
index 0000000..888e023
Binary files /dev/null and b/Assets.xcassets/Configure Blocking/icn_csv_file.imageset/icn_csv_file.pdf differ
diff --git a/Assets.xcassets/Configure Blocking/icn_edit.imageset/Contents.json b/Assets.xcassets/Configure Blocking/icn_edit.imageset/Contents.json
new file mode 100644
index 0000000..a833796
--- /dev/null
+++ b/Assets.xcassets/Configure Blocking/icn_edit.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_edit.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Configure Blocking/icn_edit.imageset/icn_edit.pdf b/Assets.xcassets/Configure Blocking/icn_edit.imageset/icn_edit.pdf
new file mode 100644
index 0000000..cf55e13
Binary files /dev/null and b/Assets.xcassets/Configure Blocking/icn_edit.imageset/icn_edit.pdf differ
diff --git a/Assets.xcassets/Configure Blocking/icn_export_folder.imageset/Contents.json b/Assets.xcassets/Configure Blocking/icn_export_folder.imageset/Contents.json
new file mode 100644
index 0000000..0ee3fd3
--- /dev/null
+++ b/Assets.xcassets/Configure Blocking/icn_export_folder.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_export_folder.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Configure Blocking/icn_export_folder.imageset/icn_export_folder.pdf b/Assets.xcassets/Configure Blocking/icn_export_folder.imageset/icn_export_folder.pdf
new file mode 100644
index 0000000..dc1563a
Binary files /dev/null and b/Assets.xcassets/Configure Blocking/icn_export_folder.imageset/icn_export_folder.pdf differ
diff --git a/Assets.xcassets/Configure Blocking/icn_import_list.imageset/Contents.json b/Assets.xcassets/Configure Blocking/icn_import_list.imageset/Contents.json
new file mode 100644
index 0000000..39d43d8
--- /dev/null
+++ b/Assets.xcassets/Configure Blocking/icn_import_list.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_import_list.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Configure Blocking/icn_import_list.imageset/icn_import_list.pdf b/Assets.xcassets/Configure Blocking/icn_import_list.imageset/icn_import_list.pdf
new file mode 100644
index 0000000..b461e4b
Binary files /dev/null and b/Assets.xcassets/Configure Blocking/icn_import_list.imageset/icn_import_list.pdf differ
diff --git a/Assets.xcassets/Configure Blocking/icn_list_lock.imageset/Contents.json b/Assets.xcassets/Configure Blocking/icn_list_lock.imageset/Contents.json
new file mode 100644
index 0000000..b4129a9
--- /dev/null
+++ b/Assets.xcassets/Configure Blocking/icn_list_lock.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_list_lock.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Configure Blocking/icn_list_lock.imageset/icn_list_lock.pdf b/Assets.xcassets/Configure Blocking/icn_list_lock.imageset/icn_list_lock.pdf
new file mode 100644
index 0000000..e391f4a
Binary files /dev/null and b/Assets.xcassets/Configure Blocking/icn_list_lock.imageset/icn_list_lock.pdf differ
diff --git a/Assets.xcassets/Configure Blocking/icn_trash.imageset/Contents.json b/Assets.xcassets/Configure Blocking/icn_trash.imageset/Contents.json
new file mode 100644
index 0000000..795b1b0
--- /dev/null
+++ b/Assets.xcassets/Configure Blocking/icn_trash.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_trash.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Configure Blocking/icn_trash.imageset/icn_trash.pdf b/Assets.xcassets/Configure Blocking/icn_trash.imageset/icn_trash.pdf
new file mode 100644
index 0000000..1951a0c
Binary files /dev/null and b/Assets.xcassets/Configure Blocking/icn_trash.imageset/icn_trash.pdf differ
diff --git a/Assets.xcassets/Confirmed Blue.colorset/Contents.json b/Assets.xcassets/Confirmed Blue.colorset/Contents.json
new file mode 100644
index 0000000..82be4f6
--- /dev/null
+++ b/Assets.xcassets/Confirmed Blue.colorset/Contents.json
@@ -0,0 +1,20 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xE7",
+ "green" : "0xAC",
+ "red" : "0x00"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Contents.json b/Assets.xcassets/Contents.json
index da4a164..73c0059 100644
--- a/Assets.xcassets/Contents.json
+++ b/Assets.xcassets/Contents.json
@@ -1,6 +1,6 @@
{
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/Assets.xcassets/Firewall/Contents.json b/Assets.xcassets/Firewall/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Assets.xcassets/Firewall/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Firewall/Subscription Plans/Contents.json b/Assets.xcassets/Firewall/Subscription Plans/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Assets.xcassets/Firewall/Subscription Plans/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Firewall/Subscription Plans/advanced_active.imageset/Contents.json b/Assets.xcassets/Firewall/Subscription Plans/advanced_active.imageset/Contents.json
new file mode 100644
index 0000000..c1dba7d
--- /dev/null
+++ b/Assets.xcassets/Firewall/Subscription Plans/advanced_active.imageset/Contents.json
@@ -0,0 +1,25 @@
+{
+ "images" : [
+ {
+ "filename" : "advanced.pdf",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "advanced_black_active.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/Subscription Plans/advanced_active.imageset/advanced.pdf b/Assets.xcassets/Firewall/Subscription Plans/advanced_active.imageset/advanced.pdf
new file mode 100644
index 0000000..73ef19c
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/advanced_active.imageset/advanced.pdf differ
diff --git a/Assets.xcassets/Firewall/Subscription Plans/advanced_active.imageset/advanced_black_active.pdf b/Assets.xcassets/Firewall/Subscription Plans/advanced_active.imageset/advanced_black_active.pdf
new file mode 100644
index 0000000..7c7ce87
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/advanced_active.imageset/advanced_black_active.pdf differ
diff --git a/Assets.xcassets/Firewall/Subscription Plans/advanced_nonactive.imageset/Contents.json b/Assets.xcassets/Firewall/Subscription Plans/advanced_nonactive.imageset/Contents.json
new file mode 100644
index 0000000..c0f94b5
--- /dev/null
+++ b/Assets.xcassets/Firewall/Subscription Plans/advanced_nonactive.imageset/Contents.json
@@ -0,0 +1,25 @@
+{
+ "images" : [
+ {
+ "filename" : "advanced_nonactive.pdf",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "advanced_black_nonactive.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/Subscription Plans/advanced_nonactive.imageset/advanced_black_nonactive.pdf b/Assets.xcassets/Firewall/Subscription Plans/advanced_nonactive.imageset/advanced_black_nonactive.pdf
new file mode 100644
index 0000000..92c557a
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/advanced_nonactive.imageset/advanced_black_nonactive.pdf differ
diff --git a/Assets.xcassets/Firewall/Subscription Plans/advanced_nonactive.imageset/advanced_nonactive.pdf b/Assets.xcassets/Firewall/Subscription Plans/advanced_nonactive.imageset/advanced_nonactive.pdf
new file mode 100644
index 0000000..c9166b7
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/advanced_nonactive.imageset/advanced_nonactive.pdf differ
diff --git a/Assets.xcassets/Firewall/Subscription Plans/anonymous_active.imageset/Contents.json b/Assets.xcassets/Firewall/Subscription Plans/anonymous_active.imageset/Contents.json
new file mode 100644
index 0000000..879bfa8
--- /dev/null
+++ b/Assets.xcassets/Firewall/Subscription Plans/anonymous_active.imageset/Contents.json
@@ -0,0 +1,25 @@
+{
+ "images" : [
+ {
+ "filename" : "anonymous_active.pdf",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "anonymous_black_active.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/Subscription Plans/anonymous_active.imageset/anonymous_active.pdf b/Assets.xcassets/Firewall/Subscription Plans/anonymous_active.imageset/anonymous_active.pdf
new file mode 100644
index 0000000..998de32
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/anonymous_active.imageset/anonymous_active.pdf differ
diff --git a/Assets.xcassets/Firewall/Subscription Plans/anonymous_active.imageset/anonymous_black_active.pdf b/Assets.xcassets/Firewall/Subscription Plans/anonymous_active.imageset/anonymous_black_active.pdf
new file mode 100644
index 0000000..7c7ce87
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/anonymous_active.imageset/anonymous_black_active.pdf differ
diff --git a/Assets.xcassets/Firewall/Subscription Plans/anonymous_nonactive.imageset/Contents.json b/Assets.xcassets/Firewall/Subscription Plans/anonymous_nonactive.imageset/Contents.json
new file mode 100644
index 0000000..e8d440e
--- /dev/null
+++ b/Assets.xcassets/Firewall/Subscription Plans/anonymous_nonactive.imageset/Contents.json
@@ -0,0 +1,25 @@
+{
+ "images" : [
+ {
+ "filename" : "anonymous.pdf",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "anonymous_black_nonactive.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/Subscription Plans/anonymous_nonactive.imageset/anonymous.pdf b/Assets.xcassets/Firewall/Subscription Plans/anonymous_nonactive.imageset/anonymous.pdf
new file mode 100644
index 0000000..75f424a
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/anonymous_nonactive.imageset/anonymous.pdf differ
diff --git a/Assets.xcassets/Firewall/Subscription Plans/anonymous_nonactive.imageset/anonymous_black_nonactive.pdf b/Assets.xcassets/Firewall/Subscription Plans/anonymous_nonactive.imageset/anonymous_black_nonactive.pdf
new file mode 100644
index 0000000..975d6c6
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/anonymous_nonactive.imageset/anonymous_black_nonactive.pdf differ
diff --git a/Assets.xcassets/Firewall/Subscription Plans/basic_active.imageset/Contents.json b/Assets.xcassets/Firewall/Subscription Plans/basic_active.imageset/Contents.json
new file mode 100644
index 0000000..a4935dd
--- /dev/null
+++ b/Assets.xcassets/Firewall/Subscription Plans/basic_active.imageset/Contents.json
@@ -0,0 +1,25 @@
+{
+ "images" : [
+ {
+ "filename" : "basic.pdf",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "basic_black.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/Subscription Plans/basic_active.imageset/basic.pdf b/Assets.xcassets/Firewall/Subscription Plans/basic_active.imageset/basic.pdf
new file mode 100644
index 0000000..baba656
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/basic_active.imageset/basic.pdf differ
diff --git a/Assets.xcassets/Firewall/Subscription Plans/basic_active.imageset/basic_black.pdf b/Assets.xcassets/Firewall/Subscription Plans/basic_active.imageset/basic_black.pdf
new file mode 100644
index 0000000..fe4f364
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/basic_active.imageset/basic_black.pdf differ
diff --git a/Assets.xcassets/Firewall/Subscription Plans/universal_active.imageset/Contents.json b/Assets.xcassets/Firewall/Subscription Plans/universal_active.imageset/Contents.json
new file mode 100644
index 0000000..ce61930
--- /dev/null
+++ b/Assets.xcassets/Firewall/Subscription Plans/universal_active.imageset/Contents.json
@@ -0,0 +1,25 @@
+{
+ "images" : [
+ {
+ "filename" : "universal_active.pdf",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "universal_black_active.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/Subscription Plans/universal_active.imageset/universal_active.pdf b/Assets.xcassets/Firewall/Subscription Plans/universal_active.imageset/universal_active.pdf
new file mode 100644
index 0000000..b48ccfc
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/universal_active.imageset/universal_active.pdf differ
diff --git a/Assets.xcassets/Firewall/Subscription Plans/universal_active.imageset/universal_black_active.pdf b/Assets.xcassets/Firewall/Subscription Plans/universal_active.imageset/universal_black_active.pdf
new file mode 100644
index 0000000..8001959
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/universal_active.imageset/universal_black_active.pdf differ
diff --git a/Assets.xcassets/Firewall/Subscription Plans/universal_nonactive.imageset/Contents.json b/Assets.xcassets/Firewall/Subscription Plans/universal_nonactive.imageset/Contents.json
new file mode 100644
index 0000000..e62cc47
--- /dev/null
+++ b/Assets.xcassets/Firewall/Subscription Plans/universal_nonactive.imageset/Contents.json
@@ -0,0 +1,25 @@
+{
+ "images" : [
+ {
+ "filename" : "universal.pdf",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "universal_black_nonactive.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/Subscription Plans/universal_nonactive.imageset/universal.pdf b/Assets.xcassets/Firewall/Subscription Plans/universal_nonactive.imageset/universal.pdf
new file mode 100644
index 0000000..a2ce090
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/universal_nonactive.imageset/universal.pdf differ
diff --git a/Assets.xcassets/Firewall/Subscription Plans/universal_nonactive.imageset/universal_black_nonactive.pdf b/Assets.xcassets/Firewall/Subscription Plans/universal_nonactive.imageset/universal_black_nonactive.pdf
new file mode 100644
index 0000000..8bd98ac
Binary files /dev/null and b/Assets.xcassets/Firewall/Subscription Plans/universal_nonactive.imageset/universal_black_nonactive.pdf differ
diff --git a/Assets.xcassets/Firewall/firewall-off-image.imageset/Contents.json b/Assets.xcassets/Firewall/firewall-off-image.imageset/Contents.json
new file mode 100644
index 0000000..f7b6750
--- /dev/null
+++ b/Assets.xcassets/Firewall/firewall-off-image.imageset/Contents.json
@@ -0,0 +1,25 @@
+{
+ "images" : [
+ {
+ "filename" : "off-image.pdf",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "firewall-off-image-black.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/firewall-off-image.imageset/firewall-off-image-black.pdf b/Assets.xcassets/Firewall/firewall-off-image.imageset/firewall-off-image-black.pdf
new file mode 100644
index 0000000..4ff5945
Binary files /dev/null and b/Assets.xcassets/Firewall/firewall-off-image.imageset/firewall-off-image-black.pdf differ
diff --git a/Assets.xcassets/Firewall/firewall-off-image.imageset/off-image.pdf b/Assets.xcassets/Firewall/firewall-off-image.imageset/off-image.pdf
new file mode 100644
index 0000000..198918a
Binary files /dev/null and b/Assets.xcassets/Firewall/firewall-off-image.imageset/off-image.pdf differ
diff --git a/Assets.xcassets/Firewall/firewall-on-image.imageset/Contents.json b/Assets.xcassets/Firewall/firewall-on-image.imageset/Contents.json
new file mode 100644
index 0000000..263ba15
--- /dev/null
+++ b/Assets.xcassets/Firewall/firewall-on-image.imageset/Contents.json
@@ -0,0 +1,25 @@
+{
+ "images" : [
+ {
+ "filename" : "on-image.pdf",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "firewall-on-image-black.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/firewall-on-image.imageset/firewall-on-image-black.pdf b/Assets.xcassets/Firewall/firewall-on-image.imageset/firewall-on-image-black.pdf
new file mode 100644
index 0000000..f1f893e
Binary files /dev/null and b/Assets.xcassets/Firewall/firewall-on-image.imageset/firewall-on-image-black.pdf differ
diff --git a/Assets.xcassets/Firewall/firewall-on-image.imageset/on-image.pdf b/Assets.xcassets/Firewall/firewall-on-image.imageset/on-image.pdf
new file mode 100644
index 0000000..3e195a6
Binary files /dev/null and b/Assets.xcassets/Firewall/firewall-on-image.imageset/on-image.pdf differ
diff --git a/Assets.xcassets/Firewall/icn_clickbait_trackers.imageset/Contents.json b/Assets.xcassets/Firewall/icn_clickbait_trackers.imageset/Contents.json
new file mode 100644
index 0000000..12fcb9b
--- /dev/null
+++ b/Assets.xcassets/Firewall/icn_clickbait_trackers.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_clickbait_trackers.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/icn_clickbait_trackers.imageset/icn_clickbait_trackers.pdf b/Assets.xcassets/Firewall/icn_clickbait_trackers.imageset/icn_clickbait_trackers.pdf
new file mode 100644
index 0000000..a4db3f9
Binary files /dev/null and b/Assets.xcassets/Firewall/icn_clickbait_trackers.imageset/icn_clickbait_trackers.pdf differ
diff --git a/Assets.xcassets/Firewall/icn_data_trackers.imageset/Contents.json b/Assets.xcassets/Firewall/icn_data_trackers.imageset/Contents.json
new file mode 100644
index 0000000..83f28eb
--- /dev/null
+++ b/Assets.xcassets/Firewall/icn_data_trackers.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_data_trackers.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/icn_data_trackers.imageset/icn_data_trackers.pdf b/Assets.xcassets/Firewall/icn_data_trackers.imageset/icn_data_trackers.pdf
new file mode 100644
index 0000000..ab53b36
Binary files /dev/null and b/Assets.xcassets/Firewall/icn_data_trackers.imageset/icn_data_trackers.pdf differ
diff --git a/Assets.xcassets/Firewall/icn_email_trackers.imageset/Contents.json b/Assets.xcassets/Firewall/icn_email_trackers.imageset/Contents.json
new file mode 100644
index 0000000..7f661cf
--- /dev/null
+++ b/Assets.xcassets/Firewall/icn_email_trackers.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_email_trackers.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/icn_email_trackers.imageset/icn_email_trackers.pdf b/Assets.xcassets/Firewall/icn_email_trackers.imageset/icn_email_trackers.pdf
new file mode 100644
index 0000000..fe9ff8b
Binary files /dev/null and b/Assets.xcassets/Firewall/icn_email_trackers.imageset/icn_email_trackers.pdf differ
diff --git a/Assets.xcassets/Firewall/icn_facebook_trackers.imageset/Contents.json b/Assets.xcassets/Firewall/icn_facebook_trackers.imageset/Contents.json
new file mode 100644
index 0000000..a37ecd0
--- /dev/null
+++ b/Assets.xcassets/Firewall/icn_facebook_trackers.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_facebook_trackers.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/icn_facebook_trackers.imageset/icn_facebook_trackers.pdf b/Assets.xcassets/Firewall/icn_facebook_trackers.imageset/icn_facebook_trackers.pdf
new file mode 100644
index 0000000..f914962
Binary files /dev/null and b/Assets.xcassets/Firewall/icn_facebook_trackers.imageset/icn_facebook_trackers.pdf differ
diff --git a/Assets.xcassets/Firewall/icn_game_marketing.imageset/Contents.json b/Assets.xcassets/Firewall/icn_game_marketing.imageset/Contents.json
new file mode 100644
index 0000000..19787c4
--- /dev/null
+++ b/Assets.xcassets/Firewall/icn_game_marketing.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_game_marketing.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/icn_game_marketing.imageset/icn_game_marketing.pdf b/Assets.xcassets/Firewall/icn_game_marketing.imageset/icn_game_marketing.pdf
new file mode 100644
index 0000000..87d6134
Binary files /dev/null and b/Assets.xcassets/Firewall/icn_game_marketing.imageset/icn_game_marketing.pdf differ
diff --git a/Assets.xcassets/Firewall/icn_lock.imageset/Contents.json b/Assets.xcassets/Firewall/icn_lock.imageset/Contents.json
new file mode 100644
index 0000000..960edaa
--- /dev/null
+++ b/Assets.xcassets/Firewall/icn_lock.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_lock.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/icn_lock.imageset/icn_lock.pdf b/Assets.xcassets/Firewall/icn_lock.imageset/icn_lock.pdf
new file mode 100644
index 0000000..bd2a190
Binary files /dev/null and b/Assets.xcassets/Firewall/icn_lock.imageset/icn_lock.pdf differ
diff --git a/Assets.xcassets/Firewall/icn_marketing_trackers.imageset/Contents.json b/Assets.xcassets/Firewall/icn_marketing_trackers.imageset/Contents.json
new file mode 100644
index 0000000..d95e161
--- /dev/null
+++ b/Assets.xcassets/Firewall/icn_marketing_trackers.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_marketing.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/icn_marketing_trackers.imageset/icn_marketing.pdf b/Assets.xcassets/Firewall/icn_marketing_trackers.imageset/icn_marketing.pdf
new file mode 100644
index 0000000..c676776
Binary files /dev/null and b/Assets.xcassets/Firewall/icn_marketing_trackers.imageset/icn_marketing.pdf differ
diff --git a/Assets.xcassets/Firewall/saveDiscount.imageset/Contents.json b/Assets.xcassets/Firewall/saveDiscount.imageset/Contents.json
new file mode 100644
index 0000000..3c85e33
--- /dev/null
+++ b/Assets.xcassets/Firewall/saveDiscount.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "Status indicator.png",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Firewall/saveDiscount.imageset/Status indicator.png b/Assets.xcassets/Firewall/saveDiscount.imageset/Status indicator.png
new file mode 100644
index 0000000..d3ff5be
Binary files /dev/null and b/Assets.xcassets/Firewall/saveDiscount.imageset/Status indicator.png differ
diff --git a/Assets.xcassets/Icon-Alt/Contents.json b/Assets.xcassets/Icon-Alt/Contents.json
index da4a164..73c0059 100644
--- a/Assets.xcassets/Icon-Alt/Contents.json
+++ b/Assets.xcassets/Icon-Alt/Contents.json
@@ -1,6 +1,6 @@
{
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/Assets.xcassets/Onboarding/Contents.json b/Assets.xcassets/Onboarding/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Assets.xcassets/Onboarding/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Onboarding/onboardingCheckmark.imageset/Contents.json b/Assets.xcassets/Onboarding/onboardingCheckmark.imageset/Contents.json
new file mode 100644
index 0000000..c6f97a0
--- /dev/null
+++ b/Assets.xcassets/Onboarding/onboardingCheckmark.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "Vector.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Onboarding/onboardingCheckmark.imageset/Vector.pdf b/Assets.xcassets/Onboarding/onboardingCheckmark.imageset/Vector.pdf
new file mode 100644
index 0000000..1add02b
Binary files /dev/null and b/Assets.xcassets/Onboarding/onboardingCheckmark.imageset/Vector.pdf differ
diff --git a/Assets.xcassets/Onboarding/onboardingPaywall.imageset/71.jpg b/Assets.xcassets/Onboarding/onboardingPaywall.imageset/71.jpg
new file mode 100644
index 0000000..3a2e550
Binary files /dev/null and b/Assets.xcassets/Onboarding/onboardingPaywall.imageset/71.jpg differ
diff --git a/Assets.xcassets/Onboarding/onboardingPaywall.imageset/71@2x.jpg b/Assets.xcassets/Onboarding/onboardingPaywall.imageset/71@2x.jpg
new file mode 100644
index 0000000..f004cb3
Binary files /dev/null and b/Assets.xcassets/Onboarding/onboardingPaywall.imageset/71@2x.jpg differ
diff --git a/Assets.xcassets/Onboarding/onboardingPaywall.imageset/71@3x.jpg b/Assets.xcassets/Onboarding/onboardingPaywall.imageset/71@3x.jpg
new file mode 100644
index 0000000..6a30c70
Binary files /dev/null and b/Assets.xcassets/Onboarding/onboardingPaywall.imageset/71@3x.jpg differ
diff --git a/Assets.xcassets/Onboarding/onboardingPaywall.imageset/Contents.json b/Assets.xcassets/Onboarding/onboardingPaywall.imageset/Contents.json
new file mode 100644
index 0000000..c67239f
--- /dev/null
+++ b/Assets.xcassets/Onboarding/onboardingPaywall.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "71.jpg",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "71@2x.jpg",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "71@3x.jpg",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Onboarding/onboardingStep1.imageset/72.jpg b/Assets.xcassets/Onboarding/onboardingStep1.imageset/72.jpg
new file mode 100644
index 0000000..904ab2a
Binary files /dev/null and b/Assets.xcassets/Onboarding/onboardingStep1.imageset/72.jpg differ
diff --git a/Assets.xcassets/Onboarding/onboardingStep1.imageset/72@2x.jpg b/Assets.xcassets/Onboarding/onboardingStep1.imageset/72@2x.jpg
new file mode 100644
index 0000000..6b5759f
Binary files /dev/null and b/Assets.xcassets/Onboarding/onboardingStep1.imageset/72@2x.jpg differ
diff --git a/Assets.xcassets/Onboarding/onboardingStep1.imageset/72@3x.jpg b/Assets.xcassets/Onboarding/onboardingStep1.imageset/72@3x.jpg
new file mode 100644
index 0000000..796c3f0
Binary files /dev/null and b/Assets.xcassets/Onboarding/onboardingStep1.imageset/72@3x.jpg differ
diff --git a/Assets.xcassets/Onboarding/onboardingStep1.imageset/Contents.json b/Assets.xcassets/Onboarding/onboardingStep1.imageset/Contents.json
new file mode 100644
index 0000000..e495ce7
--- /dev/null
+++ b/Assets.xcassets/Onboarding/onboardingStep1.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "72.jpg",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "72@2x.jpg",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "72@3x.jpg",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Onboarding/onboardingStep2.imageset/73.jpg b/Assets.xcassets/Onboarding/onboardingStep2.imageset/73.jpg
new file mode 100644
index 0000000..7dff8de
Binary files /dev/null and b/Assets.xcassets/Onboarding/onboardingStep2.imageset/73.jpg differ
diff --git a/Assets.xcassets/Onboarding/onboardingStep2.imageset/73@2x.jpg b/Assets.xcassets/Onboarding/onboardingStep2.imageset/73@2x.jpg
new file mode 100644
index 0000000..bdc9c73
Binary files /dev/null and b/Assets.xcassets/Onboarding/onboardingStep2.imageset/73@2x.jpg differ
diff --git a/Assets.xcassets/Onboarding/onboardingStep2.imageset/73@3x.jpg b/Assets.xcassets/Onboarding/onboardingStep2.imageset/73@3x.jpg
new file mode 100644
index 0000000..bef30ef
Binary files /dev/null and b/Assets.xcassets/Onboarding/onboardingStep2.imageset/73@3x.jpg differ
diff --git a/Assets.xcassets/Onboarding/onboardingStep2.imageset/Contents.json b/Assets.xcassets/Onboarding/onboardingStep2.imageset/Contents.json
new file mode 100644
index 0000000..c2eb643
--- /dev/null
+++ b/Assets.xcassets/Onboarding/onboardingStep2.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "73.jpg",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "73@2x.jpg",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "73@3x.jpg",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Onboarding/splashBackground.imageset/Contents.json b/Assets.xcassets/Onboarding/splashBackground.imageset/Contents.json
new file mode 100644
index 0000000..00d7fd1
--- /dev/null
+++ b/Assets.xcassets/Onboarding/splashBackground.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "splashBackground.jpg",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "splashBackground@2x.jpg",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "splashBackground@3x.jpg",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Onboarding/splashBackground.imageset/splashBackground.jpg b/Assets.xcassets/Onboarding/splashBackground.imageset/splashBackground.jpg
new file mode 100644
index 0000000..f659bd9
Binary files /dev/null and b/Assets.xcassets/Onboarding/splashBackground.imageset/splashBackground.jpg differ
diff --git a/Assets.xcassets/Onboarding/splashBackground.imageset/splashBackground@2x.jpg b/Assets.xcassets/Onboarding/splashBackground.imageset/splashBackground@2x.jpg
new file mode 100644
index 0000000..1599514
Binary files /dev/null and b/Assets.xcassets/Onboarding/splashBackground.imageset/splashBackground@2x.jpg differ
diff --git a/Assets.xcassets/Onboarding/splashBackground.imageset/splashBackground@3x.jpg b/Assets.xcassets/Onboarding/splashBackground.imageset/splashBackground@3x.jpg
new file mode 100644
index 0000000..19fb101
Binary files /dev/null and b/Assets.xcassets/Onboarding/splashBackground.imageset/splashBackground@3x.jpg differ
diff --git a/Assets.xcassets/Panel Background.colorset/Contents.json b/Assets.xcassets/Panel Background.colorset/Contents.json
new file mode 100644
index 0000000..dc38378
--- /dev/null
+++ b/Assets.xcassets/Panel Background.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ },
+ "colors" : [
+ {
+ "idiom" : "universal",
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "red" : "1.000",
+ "alpha" : "1.000",
+ "blue" : "1.000",
+ "green" : "1.000"
+ }
+ }
+ },
+ {
+ "idiom" : "universal",
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "red" : "0x1C",
+ "alpha" : "1.000",
+ "blue" : "0x1E",
+ "green" : "0x1C"
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Assets.xcassets/Panel Secondary Background.colorset/Contents.json b/Assets.xcassets/Panel Secondary Background.colorset/Contents.json
new file mode 100644
index 0000000..ec3e8d4
--- /dev/null
+++ b/Assets.xcassets/Panel Secondary Background.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "1.000",
+ "green" : "1.000",
+ "red" : "1.000"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "display-p3",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.196",
+ "green" : "0.183",
+ "red" : "0.182"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/Contents.json b/Assets.xcassets/Paywall/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Assets.xcassets/Paywall/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/Feadback/Contents.json b/Assets.xcassets/Paywall/Feadback/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Assets.xcassets/Paywall/Feadback/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/Feadback/feedback-checkmark.imageset/Contents.json b/Assets.xcassets/Paywall/Feadback/feedback-checkmark.imageset/Contents.json
new file mode 100644
index 0000000..0f1e1b9
--- /dev/null
+++ b/Assets.xcassets/Paywall/Feadback/feedback-checkmark.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "Vector.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Paywall/Feadback/feedback-checkmark.imageset/Vector.pdf b/Assets.xcassets/Paywall/Feadback/feedback-checkmark.imageset/Vector.pdf
new file mode 100644
index 0000000..92cbdd8
Binary files /dev/null and b/Assets.xcassets/Paywall/Feadback/feedback-checkmark.imageset/Vector.pdf differ
diff --git a/Assets.xcassets/Paywall/Feadback/feedback-paywall-arrow.imageset/Arrow Ramp Right.svg b/Assets.xcassets/Paywall/Feadback/feedback-paywall-arrow.imageset/Arrow Ramp Right.svg
new file mode 100644
index 0000000..2c3a6f0
--- /dev/null
+++ b/Assets.xcassets/Paywall/Feadback/feedback-paywall-arrow.imageset/Arrow Ramp Right.svg
@@ -0,0 +1,3 @@
+
diff --git a/Assets.xcassets/Paywall/Feadback/feedback-paywall-arrow.imageset/Contents.json b/Assets.xcassets/Paywall/Feadback/feedback-paywall-arrow.imageset/Contents.json
new file mode 100644
index 0000000..266c3f4
--- /dev/null
+++ b/Assets.xcassets/Paywall/Feadback/feedback-paywall-arrow.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "Arrow Ramp Right.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Paywall/Feadback/feedback-paywall-banner.imageset/AdobeStock_768605328 1.png b/Assets.xcassets/Paywall/Feadback/feedback-paywall-banner.imageset/AdobeStock_768605328 1.png
new file mode 100644
index 0000000..e2a75cd
Binary files /dev/null and b/Assets.xcassets/Paywall/Feadback/feedback-paywall-banner.imageset/AdobeStock_768605328 1.png differ
diff --git a/Assets.xcassets/Paywall/Feadback/feedback-paywall-banner.imageset/Contents.json b/Assets.xcassets/Paywall/Feadback/feedback-paywall-banner.imageset/Contents.json
new file mode 100644
index 0000000..7ddb1d5
--- /dev/null
+++ b/Assets.xcassets/Paywall/Feadback/feedback-paywall-banner.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "AdobeStock_768605328 1.png",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/Feadback/feedback-promo.imageset/AdobeStock_776091887 2 (1).png b/Assets.xcassets/Paywall/Feadback/feedback-promo.imageset/AdobeStock_776091887 2 (1).png
new file mode 100644
index 0000000..d33850c
Binary files /dev/null and b/Assets.xcassets/Paywall/Feadback/feedback-promo.imageset/AdobeStock_776091887 2 (1).png differ
diff --git a/Assets.xcassets/Paywall/Feadback/feedback-promo.imageset/Contents.json b/Assets.xcassets/Paywall/Feadback/feedback-promo.imageset/Contents.json
new file mode 100644
index 0000000..86fb01e
--- /dev/null
+++ b/Assets.xcassets/Paywall/Feadback/feedback-promo.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "AdobeStock_776091887 2 (1).png",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/Feadback/feedback.imageset/AdobeStock_811446718 1.png b/Assets.xcassets/Paywall/Feadback/feedback.imageset/AdobeStock_811446718 1.png
new file mode 100644
index 0000000..e28a096
Binary files /dev/null and b/Assets.xcassets/Paywall/Feadback/feedback.imageset/AdobeStock_811446718 1.png differ
diff --git a/Assets.xcassets/Paywall/Feadback/feedback.imageset/Contents.json b/Assets.xcassets/Paywall/Feadback/feedback.imageset/Contents.json
new file mode 100644
index 0000000..201b4d7
--- /dev/null
+++ b/Assets.xcassets/Paywall/Feadback/feedback.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "AdobeStock_811446718 1.png",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/VPNPaywall/Checkbox.imageset/Checkbox.pdf b/Assets.xcassets/Paywall/VPNPaywall/Checkbox.imageset/Checkbox.pdf
new file mode 100644
index 0000000..ff4c009
Binary files /dev/null and b/Assets.xcassets/Paywall/VPNPaywall/Checkbox.imageset/Checkbox.pdf differ
diff --git a/Assets.xcassets/Paywall/VPNPaywall/Checkbox.imageset/Contents.json b/Assets.xcassets/Paywall/VPNPaywall/Checkbox.imageset/Contents.json
new file mode 100644
index 0000000..7e26153
--- /dev/null
+++ b/Assets.xcassets/Paywall/VPNPaywall/Checkbox.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "Checkbox.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Paywall/VPNPaywall/Contents.json b/Assets.xcassets/Paywall/VPNPaywall/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Assets.xcassets/Paywall/VPNPaywall/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/VPNPaywall/discount.imageset/Contents.json b/Assets.xcassets/Paywall/VPNPaywall/discount.imageset/Contents.json
new file mode 100644
index 0000000..a667482
--- /dev/null
+++ b/Assets.xcassets/Paywall/VPNPaywall/discount.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "Label.png",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/VPNPaywall/discount.imageset/Label.png b/Assets.xcassets/Paywall/VPNPaywall/discount.imageset/Label.png
new file mode 100644
index 0000000..abf51dd
Binary files /dev/null and b/Assets.xcassets/Paywall/VPNPaywall/discount.imageset/Label.png differ
diff --git a/Assets.xcassets/Paywall/VPNPaywall/fill-1.imageset/Contents.json b/Assets.xcassets/Paywall/VPNPaywall/fill-1.imageset/Contents.json
new file mode 100644
index 0000000..60d74e9
--- /dev/null
+++ b/Assets.xcassets/Paywall/VPNPaywall/fill-1.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "fill-1.png",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Paywall/VPNPaywall/fill-1.imageset/fill-1.png b/Assets.xcassets/Paywall/VPNPaywall/fill-1.imageset/fill-1.png
new file mode 100644
index 0000000..4c6ee53
Binary files /dev/null and b/Assets.xcassets/Paywall/VPNPaywall/fill-1.imageset/fill-1.png differ
diff --git a/Assets.xcassets/Paywall/VPNPaywall/fill-2.imageset/fill-2.png b/Assets.xcassets/Paywall/VPNPaywall/fill-2.imageset/fill-2.png
new file mode 100644
index 0000000..61b4dc8
Binary files /dev/null and b/Assets.xcassets/Paywall/VPNPaywall/fill-2.imageset/fill-2.png differ
diff --git a/Assets.xcassets/Paywall/VPNPaywall/grey-ellipse-1.imageset/Contents.json b/Assets.xcassets/Paywall/VPNPaywall/grey-ellipse-1.imageset/Contents.json
new file mode 100644
index 0000000..00cd7e0
--- /dev/null
+++ b/Assets.xcassets/Paywall/VPNPaywall/grey-ellipse-1.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "grey-ellipse-1.png",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Paywall/VPNPaywall/grey-ellipse-1.imageset/grey-ellipse-1.png b/Assets.xcassets/Paywall/VPNPaywall/grey-ellipse-1.imageset/grey-ellipse-1.png
new file mode 100644
index 0000000..0263b5b
Binary files /dev/null and b/Assets.xcassets/Paywall/VPNPaywall/grey-ellipse-1.imageset/grey-ellipse-1.png differ
diff --git a/Assets.xcassets/Paywall/banner_70_percent.imageset/70% 1.svg b/Assets.xcassets/Paywall/banner_70_percent.imageset/70% 1.svg
new file mode 100644
index 0000000..c44aac7
--- /dev/null
+++ b/Assets.xcassets/Paywall/banner_70_percent.imageset/70% 1.svg
@@ -0,0 +1,9 @@
+
diff --git a/Assets.xcassets/Paywall/banner_70_percent.imageset/Contents.json b/Assets.xcassets/Paywall/banner_70_percent.imageset/Contents.json
new file mode 100644
index 0000000..0b1b2b5
--- /dev/null
+++ b/Assets.xcassets/Paywall/banner_70_percent.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "70% 1.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/bg_paywall_onetime.imageset/Contents.json b/Assets.xcassets/Paywall/bg_paywall_onetime.imageset/Contents.json
new file mode 100644
index 0000000..b4fa5a2
--- /dev/null
+++ b/Assets.xcassets/Paywall/bg_paywall_onetime.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "Group 1484762.jpg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/bg_paywall_onetime.imageset/Group 1484762.jpg b/Assets.xcassets/Paywall/bg_paywall_onetime.imageset/Group 1484762.jpg
new file mode 100644
index 0000000..cafe2cd
Binary files /dev/null and b/Assets.xcassets/Paywall/bg_paywall_onetime.imageset/Group 1484762.jpg differ
diff --git a/Assets.xcassets/Paywall/bg_paywall_onetime_ss.imageset/Contents.json b/Assets.xcassets/Paywall/bg_paywall_onetime_ss.imageset/Contents.json
new file mode 100644
index 0000000..72725e1
--- /dev/null
+++ b/Assets.xcassets/Paywall/bg_paywall_onetime_ss.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "bg_paywall_onetime_ss.jpg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/bg_paywall_onetime_ss.imageset/bg_paywall_onetime_ss.jpg b/Assets.xcassets/Paywall/bg_paywall_onetime_ss.imageset/bg_paywall_onetime_ss.jpg
new file mode 100644
index 0000000..c829398
Binary files /dev/null and b/Assets.xcassets/Paywall/bg_paywall_onetime_ss.imageset/bg_paywall_onetime_ss.jpg differ
diff --git a/Assets.xcassets/Paywall/december_banner.imageset/AdobeStock_863702591.png b/Assets.xcassets/Paywall/december_banner.imageset/AdobeStock_863702591.png
new file mode 100644
index 0000000..fdb4461
Binary files /dev/null and b/Assets.xcassets/Paywall/december_banner.imageset/AdobeStock_863702591.png differ
diff --git a/Assets.xcassets/Paywall/december_banner.imageset/AdobeStock_863702591@2x.png b/Assets.xcassets/Paywall/december_banner.imageset/AdobeStock_863702591@2x.png
new file mode 100644
index 0000000..3da9b8b
Binary files /dev/null and b/Assets.xcassets/Paywall/december_banner.imageset/AdobeStock_863702591@2x.png differ
diff --git a/Assets.xcassets/Paywall/december_banner.imageset/AdobeStock_863702591@3x.png b/Assets.xcassets/Paywall/december_banner.imageset/AdobeStock_863702591@3x.png
new file mode 100644
index 0000000..ced070b
Binary files /dev/null and b/Assets.xcassets/Paywall/december_banner.imageset/AdobeStock_863702591@3x.png differ
diff --git a/Assets.xcassets/Paywall/december_banner.imageset/Contents.json b/Assets.xcassets/Paywall/december_banner.imageset/Contents.json
new file mode 100644
index 0000000..9196947
--- /dev/null
+++ b/Assets.xcassets/Paywall/december_banner.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "AdobeStock_863702591.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "AdobeStock_863702591@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "AdobeStock_863702591@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/shield_checkmark.imageset/Contents.json b/Assets.xcassets/Paywall/shield_checkmark.imageset/Contents.json
new file mode 100644
index 0000000..54ecd17
--- /dev/null
+++ b/Assets.xcassets/Paywall/shield_checkmark.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "shield_checkmark.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/shield_checkmark.imageset/shield_checkmark.pdf b/Assets.xcassets/Paywall/shield_checkmark.imageset/shield_checkmark.pdf
new file mode 100644
index 0000000..aa3f864
Binary files /dev/null and b/Assets.xcassets/Paywall/shield_checkmark.imageset/shield_checkmark.pdf differ
diff --git a/Assets.xcassets/Paywall/special_offer_2025.imageset/Contents.json b/Assets.xcassets/Paywall/special_offer_2025.imageset/Contents.json
new file mode 100644
index 0000000..fd46bac
--- /dev/null
+++ b/Assets.xcassets/Paywall/special_offer_2025.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "special_offer_2025.png",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/special_offer_2025.imageset/special_offer_2025.png b/Assets.xcassets/Paywall/special_offer_2025.imageset/special_offer_2025.png
new file mode 100644
index 0000000..9646049
Binary files /dev/null and b/Assets.xcassets/Paywall/special_offer_2025.imageset/special_offer_2025.png differ
diff --git a/Assets.xcassets/Paywall/special_offer_stars.imageset/Contents.json b/Assets.xcassets/Paywall/special_offer_stars.imageset/Contents.json
new file mode 100644
index 0000000..638045a
--- /dev/null
+++ b/Assets.xcassets/Paywall/special_offer_stars.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "special_offer_stars.png",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Paywall/special_offer_stars.imageset/special_offer_stars.png b/Assets.xcassets/Paywall/special_offer_stars.imageset/special_offer_stars.png
new file mode 100644
index 0000000..c0ec1ff
Binary files /dev/null and b/Assets.xcassets/Paywall/special_offer_stars.imageset/special_offer_stars.png differ
diff --git a/Assets.xcassets/Power Button Shadow Color.colorset/Contents.json b/Assets.xcassets/Power Button Shadow Color.colorset/Contents.json
new file mode 100644
index 0000000..742124e
--- /dev/null
+++ b/Assets.xcassets/Power Button Shadow Color.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "0.250",
+ "blue" : "0.000",
+ "green" : "0.000",
+ "red" : "0.000"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.000",
+ "green" : "0.000",
+ "red" : "0.000"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Questionnaire/Contents.json b/Assets.xcassets/Questionnaire/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Assets.xcassets/Questionnaire/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Questionnaire/selectedRadioSwitcher.imageset/Contents.json b/Assets.xcassets/Questionnaire/selectedRadioSwitcher.imageset/Contents.json
new file mode 100644
index 0000000..d7cb567
--- /dev/null
+++ b/Assets.xcassets/Questionnaire/selectedRadioSwitcher.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "Group 16680.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Questionnaire/selectedRadioSwitcher.imageset/Group 16680.pdf b/Assets.xcassets/Questionnaire/selectedRadioSwitcher.imageset/Group 16680.pdf
new file mode 100644
index 0000000..a269c73
Binary files /dev/null and b/Assets.xcassets/Questionnaire/selectedRadioSwitcher.imageset/Group 16680.pdf differ
diff --git a/Assets.xcassets/Questionnaire/unselectedRadioSwitcher.imageset/Contents.json b/Assets.xcassets/Questionnaire/unselectedRadioSwitcher.imageset/Contents.json
new file mode 100644
index 0000000..ef9000c
--- /dev/null
+++ b/Assets.xcassets/Questionnaire/unselectedRadioSwitcher.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "Group 16682.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/Questionnaire/unselectedRadioSwitcher.imageset/Group 16682.pdf b/Assets.xcassets/Questionnaire/unselectedRadioSwitcher.imageset/Group 16682.pdf
new file mode 100644
index 0000000..f8aa97d
Binary files /dev/null and b/Assets.xcassets/Questionnaire/unselectedRadioSwitcher.imageset/Group 16682.pdf differ
diff --git a/Assets.xcassets/SheldIcon.imageset/Contents.json b/Assets.xcassets/SheldIcon.imageset/Contents.json
new file mode 100644
index 0000000..8ea1e07
--- /dev/null
+++ b/Assets.xcassets/SheldIcon.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "Tab Bar Icons.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/SheldIcon.imageset/Tab Bar Icons.pdf b/Assets.xcassets/SheldIcon.imageset/Tab Bar Icons.pdf
new file mode 100644
index 0000000..b5735a2
Binary files /dev/null and b/Assets.xcassets/SheldIcon.imageset/Tab Bar Icons.pdf differ
diff --git a/Assets.xcassets/VPN/Contents.json b/Assets.xcassets/VPN/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Assets.xcassets/VPN/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/VPN/icn_activity.imageset/Contents.json b/Assets.xcassets/VPN/icn_activity.imageset/Contents.json
new file mode 100644
index 0000000..fb8d664
--- /dev/null
+++ b/Assets.xcassets/VPN/icn_activity.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_activity.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/VPN/icn_activity.imageset/icn_activity.pdf b/Assets.xcassets/VPN/icn_activity.imageset/icn_activity.pdf
new file mode 100644
index 0000000..291a4b6
Binary files /dev/null and b/Assets.xcassets/VPN/icn_activity.imageset/icn_activity.pdf differ
diff --git a/Assets.xcassets/VPN/icn_checkmark.imageset/Contents.json b/Assets.xcassets/VPN/icn_checkmark.imageset/Contents.json
new file mode 100644
index 0000000..8c449dc
--- /dev/null
+++ b/Assets.xcassets/VPN/icn_checkmark.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_checkmark.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/VPN/icn_checkmark.imageset/icn_checkmark.pdf b/Assets.xcassets/VPN/icn_checkmark.imageset/icn_checkmark.pdf
new file mode 100644
index 0000000..8e29a35
Binary files /dev/null and b/Assets.xcassets/VPN/icn_checkmark.imageset/icn_checkmark.pdf differ
diff --git a/Assets.xcassets/VPN/icn_checkmark_bold.imageset/Contents.json b/Assets.xcassets/VPN/icn_checkmark_bold.imageset/Contents.json
new file mode 100644
index 0000000..ef446fd
--- /dev/null
+++ b/Assets.xcassets/VPN/icn_checkmark_bold.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_checkmark_bold.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/VPN/icn_checkmark_bold.imageset/icn_checkmark_bold.pdf b/Assets.xcassets/VPN/icn_checkmark_bold.imageset/icn_checkmark_bold.pdf
new file mode 100644
index 0000000..3521ca7
Binary files /dev/null and b/Assets.xcassets/VPN/icn_checkmark_bold.imageset/icn_checkmark_bold.pdf differ
diff --git a/Assets.xcassets/VPN/icn_configure_blocking.imageset/Contents.json b/Assets.xcassets/VPN/icn_configure_blocking.imageset/Contents.json
new file mode 100644
index 0000000..536528d
--- /dev/null
+++ b/Assets.xcassets/VPN/icn_configure_blocking.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_configure_blocking.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/VPN/icn_configure_blocking.imageset/icn_configure_blocking.pdf b/Assets.xcassets/VPN/icn_configure_blocking.imageset/icn_configure_blocking.pdf
new file mode 100644
index 0000000..74659c5
Binary files /dev/null and b/Assets.xcassets/VPN/icn_configure_blocking.imageset/icn_configure_blocking.pdf differ
diff --git a/Assets.xcassets/VPN/icn_globe.imageset/Contents.json b/Assets.xcassets/VPN/icn_globe.imageset/Contents.json
new file mode 100644
index 0000000..9f943b4
--- /dev/null
+++ b/Assets.xcassets/VPN/icn_globe.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_globe.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/VPN/icn_globe.imageset/icn_globe.pdf b/Assets.xcassets/VPN/icn_globe.imageset/icn_globe.pdf
new file mode 100644
index 0000000..b1c8e86
Binary files /dev/null and b/Assets.xcassets/VPN/icn_globe.imageset/icn_globe.pdf differ
diff --git a/Assets.xcassets/VPN/icn_import.imageset/Contents.json b/Assets.xcassets/VPN/icn_import.imageset/Contents.json
new file mode 100644
index 0000000..9da8012
--- /dev/null
+++ b/Assets.xcassets/VPN/icn_import.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_import.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/VPN/icn_import.imageset/icn_import.pdf b/Assets.xcassets/VPN/icn_import.imageset/icn_import.pdf
new file mode 100644
index 0000000..eaedc35
Binary files /dev/null and b/Assets.xcassets/VPN/icn_import.imageset/icn_import.pdf differ
diff --git a/Assets.xcassets/VPN/icn_personalized_blocking.imageset/Contents.json b/Assets.xcassets/VPN/icn_personalized_blocking.imageset/Contents.json
new file mode 100644
index 0000000..7edd911
--- /dev/null
+++ b/Assets.xcassets/VPN/icn_personalized_blocking.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_personalized_blocking.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/VPN/icn_personalized_blocking.imageset/icn_personalized_blocking.pdf b/Assets.xcassets/VPN/icn_personalized_blocking.imageset/icn_personalized_blocking.pdf
new file mode 100644
index 0000000..3954829
Binary files /dev/null and b/Assets.xcassets/VPN/icn_personalized_blocking.imageset/icn_personalized_blocking.pdf differ
diff --git a/Assets.xcassets/VPN/icn_whitelist.imageset/Contents.json b/Assets.xcassets/VPN/icn_whitelist.imageset/Contents.json
new file mode 100644
index 0000000..55f6c3a
--- /dev/null
+++ b/Assets.xcassets/VPN/icn_whitelist.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_whitelists.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/VPN/icn_whitelist.imageset/icn_whitelists.pdf b/Assets.xcassets/VPN/icn_whitelist.imageset/icn_whitelists.pdf
new file mode 100644
index 0000000..e94a02f
Binary files /dev/null and b/Assets.xcassets/VPN/icn_whitelist.imageset/icn_whitelists.pdf differ
diff --git a/Assets.xcassets/VPN/vpn-off-image.imageset/Contents.json b/Assets.xcassets/VPN/vpn-off-image.imageset/Contents.json
new file mode 100644
index 0000000..6655359
--- /dev/null
+++ b/Assets.xcassets/VPN/vpn-off-image.imageset/Contents.json
@@ -0,0 +1,25 @@
+{
+ "images" : [
+ {
+ "filename" : "vpn-off-image.pdf",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "vpn-off-image-black.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/VPN/vpn-off-image.imageset/vpn-off-image-black.pdf b/Assets.xcassets/VPN/vpn-off-image.imageset/vpn-off-image-black.pdf
new file mode 100644
index 0000000..2a80ba3
Binary files /dev/null and b/Assets.xcassets/VPN/vpn-off-image.imageset/vpn-off-image-black.pdf differ
diff --git a/Assets.xcassets/VPN/vpn-off-image.imageset/vpn-off-image.pdf b/Assets.xcassets/VPN/vpn-off-image.imageset/vpn-off-image.pdf
new file mode 100644
index 0000000..41f0363
Binary files /dev/null and b/Assets.xcassets/VPN/vpn-off-image.imageset/vpn-off-image.pdf differ
diff --git a/Assets.xcassets/VPN/vpn-on-image.imageset/Contents.json b/Assets.xcassets/VPN/vpn-on-image.imageset/Contents.json
new file mode 100644
index 0000000..26e3b26
--- /dev/null
+++ b/Assets.xcassets/VPN/vpn-on-image.imageset/Contents.json
@@ -0,0 +1,25 @@
+{
+ "images" : [
+ {
+ "filename" : "vpn-on-image.pdf",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "vpn-on-image-black.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/VPN/vpn-on-image.imageset/vpn-on-image-black.pdf b/Assets.xcassets/VPN/vpn-on-image.imageset/vpn-on-image-black.pdf
new file mode 100644
index 0000000..da5f46e
Binary files /dev/null and b/Assets.xcassets/VPN/vpn-on-image.imageset/vpn-on-image-black.pdf differ
diff --git a/Assets.xcassets/VPN/vpn-on-image.imageset/vpn-on-image.pdf b/Assets.xcassets/VPN/vpn-on-image.imageset/vpn-on-image.pdf
new file mode 100644
index 0000000..49163e5
Binary files /dev/null and b/Assets.xcassets/VPN/vpn-on-image.imageset/vpn-on-image.pdf differ
diff --git a/Assets.xcassets/VPNIcon.imageset/Contents.json b/Assets.xcassets/VPNIcon.imageset/Contents.json
new file mode 100644
index 0000000..7d29a35
--- /dev/null
+++ b/Assets.xcassets/VPNIcon.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "VPN.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/VPNIcon.imageset/VPN.pdf b/Assets.xcassets/VPNIcon.imageset/VPN.pdf
new file mode 100644
index 0000000..946db71
Binary files /dev/null and b/Assets.xcassets/VPNIcon.imageset/VPN.pdf differ
diff --git a/Assets.xcassets/checkmark.imageset/checkmark-1.png b/Assets.xcassets/checkmark.imageset/checkmark-1.png
deleted file mode 100644
index 113cb3c..0000000
Binary files a/Assets.xcassets/checkmark.imageset/checkmark-1.png and /dev/null differ
diff --git a/Assets.xcassets/checkmark.imageset/checkmark-2.png b/Assets.xcassets/checkmark.imageset/checkmark-2.png
deleted file mode 100644
index 113cb3c..0000000
Binary files a/Assets.xcassets/checkmark.imageset/checkmark-2.png and /dev/null differ
diff --git a/Assets.xcassets/checkmark.imageset/checkmark.png b/Assets.xcassets/checkmark.imageset/checkmark.png
deleted file mode 100644
index 113cb3c..0000000
Binary files a/Assets.xcassets/checkmark.imageset/checkmark.png and /dev/null differ
diff --git a/Assets.xcassets/globe.imageset/Contents.json b/Assets.xcassets/globe.imageset/Contents.json
new file mode 100644
index 0000000..053d53f
--- /dev/null
+++ b/Assets.xcassets/globe.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "earth-globe-americas_1f30e.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "earth-globe-americas_1f30e-1.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "earth-globe-americas_1f30e-2.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Assets.xcassets/globe.imageset/earth-globe-americas_1f30e-1.png b/Assets.xcassets/globe.imageset/earth-globe-americas_1f30e-1.png
new file mode 100644
index 0000000..51b5b4f
Binary files /dev/null and b/Assets.xcassets/globe.imageset/earth-globe-americas_1f30e-1.png differ
diff --git a/Assets.xcassets/globe.imageset/earth-globe-americas_1f30e-2.png b/Assets.xcassets/globe.imageset/earth-globe-americas_1f30e-2.png
new file mode 100644
index 0000000..51b5b4f
Binary files /dev/null and b/Assets.xcassets/globe.imageset/earth-globe-americas_1f30e-2.png differ
diff --git a/Assets.xcassets/globe.imageset/earth-globe-americas_1f30e.png b/Assets.xcassets/globe.imageset/earth-globe-americas_1f30e.png
new file mode 100644
index 0000000..51b5b4f
Binary files /dev/null and b/Assets.xcassets/globe.imageset/earth-globe-americas_1f30e.png differ
diff --git a/Assets.xcassets/icn_close_filled.imageset/Contents.json b/Assets.xcassets/icn_close_filled.imageset/Contents.json
new file mode 100644
index 0000000..b0a790e
--- /dev/null
+++ b/Assets.xcassets/icn_close_filled.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_close_filled.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/icn_close_filled.imageset/icn_close_filled.pdf b/Assets.xcassets/icn_close_filled.imageset/icn_close_filled.pdf
new file mode 100644
index 0000000..db7617f
Binary files /dev/null and b/Assets.xcassets/icn_close_filled.imageset/icn_close_filled.pdf differ
diff --git a/Assets.xcassets/icn_configuration.imageset/Contents.json b/Assets.xcassets/icn_configuration.imageset/Contents.json
new file mode 100644
index 0000000..be0da65
--- /dev/null
+++ b/Assets.xcassets/icn_configuration.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_configuration.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/icn_configuration.imageset/icn_configuration.pdf b/Assets.xcassets/icn_configuration.imageset/icn_configuration.pdf
new file mode 100644
index 0000000..ba7b23b
Binary files /dev/null and b/Assets.xcassets/icn_configuration.imageset/icn_configuration.pdf differ
diff --git a/Assets.xcassets/icn_firewall.imageset/Contents.json b/Assets.xcassets/icn_firewall.imageset/Contents.json
new file mode 100644
index 0000000..aae1cd5
--- /dev/null
+++ b/Assets.xcassets/icn_firewall.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_firewall.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/icn_firewall.imageset/icn_firewall.pdf b/Assets.xcassets/icn_firewall.imageset/icn_firewall.pdf
new file mode 100644
index 0000000..47fa338
Binary files /dev/null and b/Assets.xcassets/icn_firewall.imageset/icn_firewall.pdf differ
diff --git a/Assets.xcassets/icn_vpn.imageset/Contents.json b/Assets.xcassets/icn_vpn.imageset/Contents.json
new file mode 100644
index 0000000..00e105c
--- /dev/null
+++ b/Assets.xcassets/icn_vpn.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icn_vpn.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/icn_vpn.imageset/icn_vpn.pdf b/Assets.xcassets/icn_vpn.imageset/icn_vpn.pdf
new file mode 100644
index 0000000..117a703
Binary files /dev/null and b/Assets.xcassets/icn_vpn.imageset/icn_vpn.pdf differ
diff --git a/Assets.xcassets/ios-marketing.imageset/Contents.json b/Assets.xcassets/ios-marketing.imageset/Contents.json
index 86e4d18..6d39844 100644
--- a/Assets.xcassets/ios-marketing.imageset/Contents.json
+++ b/Assets.xcassets/ios-marketing.imageset/Contents.json
@@ -1,23 +1,23 @@
{
"images" : [
{
+ "filename" : "icon_512x512@2x.png",
"idiom" : "universal",
- "filename" : "Icon-1024.png",
"scale" : "1x"
},
{
+ "filename" : "icon_512x512@2x-1.png",
"idiom" : "universal",
- "filename" : "Icon-1025.png",
"scale" : "2x"
},
{
+ "filename" : "icon_512x512@2x-2.png",
"idiom" : "universal",
- "filename" : "Icon-1026.png",
"scale" : "3x"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/Assets.xcassets/ios-marketing.imageset/Icon-1024.png b/Assets.xcassets/ios-marketing.imageset/Icon-1024.png
deleted file mode 100644
index 6174ae3..0000000
Binary files a/Assets.xcassets/ios-marketing.imageset/Icon-1024.png and /dev/null differ
diff --git a/Assets.xcassets/ios-marketing.imageset/Icon-1025.png b/Assets.xcassets/ios-marketing.imageset/Icon-1025.png
deleted file mode 100644
index 6174ae3..0000000
Binary files a/Assets.xcassets/ios-marketing.imageset/Icon-1025.png and /dev/null differ
diff --git a/Assets.xcassets/ios-marketing.imageset/Icon-1026.png b/Assets.xcassets/ios-marketing.imageset/Icon-1026.png
deleted file mode 100644
index 6174ae3..0000000
Binary files a/Assets.xcassets/ios-marketing.imageset/Icon-1026.png and /dev/null differ
diff --git a/Assets.xcassets/ios-marketing.imageset/icon_512x512@2x-1.png b/Assets.xcassets/ios-marketing.imageset/icon_512x512@2x-1.png
new file mode 100644
index 0000000..a0608c3
Binary files /dev/null and b/Assets.xcassets/ios-marketing.imageset/icon_512x512@2x-1.png differ
diff --git a/Assets.xcassets/ios-marketing.imageset/icon_512x512@2x-2.png b/Assets.xcassets/ios-marketing.imageset/icon_512x512@2x-2.png
new file mode 100644
index 0000000..a0608c3
Binary files /dev/null and b/Assets.xcassets/ios-marketing.imageset/icon_512x512@2x-2.png differ
diff --git a/Assets.xcassets/ios-marketing.imageset/icon_512x512@2x.png b/Assets.xcassets/ios-marketing.imageset/icon_512x512@2x.png
new file mode 100644
index 0000000..a0608c3
Binary files /dev/null and b/Assets.xcassets/ios-marketing.imageset/icon_512x512@2x.png differ
diff --git a/Assets.xcassets/lightBlue.colorset/Contents.json b/Assets.xcassets/lightBlue.colorset/Contents.json
new file mode 100644
index 0000000..8048731
--- /dev/null
+++ b/Assets.xcassets/lightBlue.colorset/Contents.json
@@ -0,0 +1,20 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "display-p3",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.984",
+ "green" : "0.952",
+ "red" : "0.875"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/lockdown_icon.imageset/Contents.json b/Assets.xcassets/lockdown_icon.imageset/Contents.json
index fe848b5..6d39844 100644
--- a/Assets.xcassets/lockdown_icon.imageset/Contents.json
+++ b/Assets.xcassets/lockdown_icon.imageset/Contents.json
@@ -1,23 +1,23 @@
{
"images" : [
{
+ "filename" : "icon_512x512@2x.png",
"idiom" : "universal",
- "filename" : "Icon-512.png",
"scale" : "1x"
},
{
+ "filename" : "icon_512x512@2x-1.png",
"idiom" : "universal",
- "filename" : "Icon-513.png",
"scale" : "2x"
},
{
+ "filename" : "icon_512x512@2x-2.png",
"idiom" : "universal",
- "filename" : "Icon-514.png",
"scale" : "3x"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/Assets.xcassets/lockdown_icon.imageset/Icon-512.png b/Assets.xcassets/lockdown_icon.imageset/Icon-512.png
deleted file mode 100644
index 9e843a6..0000000
Binary files a/Assets.xcassets/lockdown_icon.imageset/Icon-512.png and /dev/null differ
diff --git a/Assets.xcassets/lockdown_icon.imageset/Icon-513.png b/Assets.xcassets/lockdown_icon.imageset/Icon-513.png
deleted file mode 100644
index 9e843a6..0000000
Binary files a/Assets.xcassets/lockdown_icon.imageset/Icon-513.png and /dev/null differ
diff --git a/Assets.xcassets/lockdown_icon.imageset/Icon-514.png b/Assets.xcassets/lockdown_icon.imageset/Icon-514.png
deleted file mode 100644
index 9e843a6..0000000
Binary files a/Assets.xcassets/lockdown_icon.imageset/Icon-514.png and /dev/null differ
diff --git a/Assets.xcassets/lockdown_icon.imageset/icon_512x512@2x-1.png b/Assets.xcassets/lockdown_icon.imageset/icon_512x512@2x-1.png
new file mode 100644
index 0000000..a0608c3
Binary files /dev/null and b/Assets.xcassets/lockdown_icon.imageset/icon_512x512@2x-1.png differ
diff --git a/Assets.xcassets/lockdown_icon.imageset/icon_512x512@2x-2.png b/Assets.xcassets/lockdown_icon.imageset/icon_512x512@2x-2.png
new file mode 100644
index 0000000..a0608c3
Binary files /dev/null and b/Assets.xcassets/lockdown_icon.imageset/icon_512x512@2x-2.png differ
diff --git a/Assets.xcassets/lockdown_icon.imageset/icon_512x512@2x.png b/Assets.xcassets/lockdown_icon.imageset/icon_512x512@2x.png
new file mode 100644
index 0000000..a0608c3
Binary files /dev/null and b/Assets.xcassets/lockdown_icon.imageset/icon_512x512@2x.png differ
diff --git a/Assets.xcassets/lockdown_icon_appstore.imageset/1024-1.png b/Assets.xcassets/lockdown_icon_appstore.imageset/1024-1.png
deleted file mode 100644
index 23a35d5..0000000
Binary files a/Assets.xcassets/lockdown_icon_appstore.imageset/1024-1.png and /dev/null differ
diff --git a/Assets.xcassets/lockdown_icon_appstore.imageset/1024-2.png b/Assets.xcassets/lockdown_icon_appstore.imageset/1024-2.png
deleted file mode 100644
index 23a35d5..0000000
Binary files a/Assets.xcassets/lockdown_icon_appstore.imageset/1024-2.png and /dev/null differ
diff --git a/Assets.xcassets/lockdown_icon_appstore.imageset/1024.png b/Assets.xcassets/lockdown_icon_appstore.imageset/1024.png
deleted file mode 100644
index 23a35d5..0000000
Binary files a/Assets.xcassets/lockdown_icon_appstore.imageset/1024.png and /dev/null differ
diff --git a/Assets.xcassets/lockdown_icon_appstore.imageset/Contents.json b/Assets.xcassets/lockdown_icon_appstore.imageset/Contents.json
index 5c10c1b..e43a222 100644
--- a/Assets.xcassets/lockdown_icon_appstore.imageset/Contents.json
+++ b/Assets.xcassets/lockdown_icon_appstore.imageset/Contents.json
@@ -1,23 +1,23 @@
{
"images" : [
{
+ "filename" : "ios-marketing.png",
"idiom" : "universal",
- "filename" : "1024.png",
"scale" : "1x"
},
{
+ "filename" : "ios-marketing-1.png",
"idiom" : "universal",
- "filename" : "1024-1.png",
"scale" : "2x"
},
{
+ "filename" : "ios-marketing-2.png",
"idiom" : "universal",
- "filename" : "1024-2.png",
"scale" : "3x"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/Assets.xcassets/lockdown_icon_appstore.imageset/ios-marketing-1.png b/Assets.xcassets/lockdown_icon_appstore.imageset/ios-marketing-1.png
new file mode 100644
index 0000000..8a0250e
Binary files /dev/null and b/Assets.xcassets/lockdown_icon_appstore.imageset/ios-marketing-1.png differ
diff --git a/Assets.xcassets/lockdown_icon_appstore.imageset/ios-marketing-2.png b/Assets.xcassets/lockdown_icon_appstore.imageset/ios-marketing-2.png
new file mode 100644
index 0000000..8a0250e
Binary files /dev/null and b/Assets.xcassets/lockdown_icon_appstore.imageset/ios-marketing-2.png differ
diff --git a/Assets.xcassets/lockdown_icon_appstore.imageset/ios-marketing.png b/Assets.xcassets/lockdown_icon_appstore.imageset/ios-marketing.png
new file mode 100644
index 0000000..8a0250e
Binary files /dev/null and b/Assets.xcassets/lockdown_icon_appstore.imageset/ios-marketing.png differ
diff --git a/Assets.xcassets/menu.imageset/menu-button-1.png b/Assets.xcassets/menu.imageset/menu-button-1.png
deleted file mode 100644
index 371c3f1..0000000
Binary files a/Assets.xcassets/menu.imageset/menu-button-1.png and /dev/null differ
diff --git a/Assets.xcassets/menu.imageset/menu-button-2.png b/Assets.xcassets/menu.imageset/menu-button-2.png
deleted file mode 100644
index 371c3f1..0000000
Binary files a/Assets.xcassets/menu.imageset/menu-button-2.png and /dev/null differ
diff --git a/Assets.xcassets/menu.imageset/menu-button.png b/Assets.xcassets/menu.imageset/menu-button.png
deleted file mode 100644
index 371c3f1..0000000
Binary files a/Assets.xcassets/menu.imageset/menu-button.png and /dev/null differ
diff --git a/Assets.xcassets/message-circle.imageset/Contents.json b/Assets.xcassets/message-circle.imageset/Contents.json
new file mode 100644
index 0000000..991d9d8
--- /dev/null
+++ b/Assets.xcassets/message-circle.imageset/Contents.json
@@ -0,0 +1,16 @@
+{
+ "images" : [
+ {
+ "filename" : "iconUpdated.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true,
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/Assets.xcassets/message-circle.imageset/iconUpdated.pdf b/Assets.xcassets/message-circle.imageset/iconUpdated.pdf
new file mode 100644
index 0000000..57ab9da
Binary files /dev/null and b/Assets.xcassets/message-circle.imageset/iconUpdated.pdf differ
diff --git a/Assets.xcassets/notification_example.imageset/Contents.json b/Assets.xcassets/notification_example.imageset/Contents.json
new file mode 100644
index 0000000..812609e
--- /dev/null
+++ b/Assets.xcassets/notification_example.imageset/Contents.json
@@ -0,0 +1,56 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "IMG_0005.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "IMG_0004.png",
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "IMG_0005-1.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "IMG_0004-1.png",
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "IMG_0005-2.png",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "IMG_0004-2.png",
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Assets.xcassets/notification_example.imageset/IMG_0004-1.png b/Assets.xcassets/notification_example.imageset/IMG_0004-1.png
new file mode 100644
index 0000000..b571c9e
Binary files /dev/null and b/Assets.xcassets/notification_example.imageset/IMG_0004-1.png differ
diff --git a/Assets.xcassets/notification_example.imageset/IMG_0004-2.png b/Assets.xcassets/notification_example.imageset/IMG_0004-2.png
new file mode 100644
index 0000000..b571c9e
Binary files /dev/null and b/Assets.xcassets/notification_example.imageset/IMG_0004-2.png differ
diff --git a/Assets.xcassets/notification_example.imageset/IMG_0004.png b/Assets.xcassets/notification_example.imageset/IMG_0004.png
new file mode 100644
index 0000000..b571c9e
Binary files /dev/null and b/Assets.xcassets/notification_example.imageset/IMG_0004.png differ
diff --git a/Assets.xcassets/notification_example.imageset/IMG_0005-1.png b/Assets.xcassets/notification_example.imageset/IMG_0005-1.png
new file mode 100644
index 0000000..d29c36b
Binary files /dev/null and b/Assets.xcassets/notification_example.imageset/IMG_0005-1.png differ
diff --git a/Assets.xcassets/notification_example.imageset/IMG_0005-2.png b/Assets.xcassets/notification_example.imageset/IMG_0005-2.png
new file mode 100644
index 0000000..d29c36b
Binary files /dev/null and b/Assets.xcassets/notification_example.imageset/IMG_0005-2.png differ
diff --git a/Assets.xcassets/notification_example.imageset/IMG_0005.png b/Assets.xcassets/notification_example.imageset/IMG_0005.png
new file mode 100644
index 0000000..d29c36b
Binary files /dev/null and b/Assets.xcassets/notification_example.imageset/IMG_0005.png differ
diff --git a/Assets.xcassets/power.imageset/Contents.json b/Assets.xcassets/power.imageset/Contents.json
new file mode 100644
index 0000000..ad2ec5f
--- /dev/null
+++ b/Assets.xcassets/power.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+ "images" : [
+ {
+ "filename" : "power.pdf",
+ "idiom" : "universal",
+ "language-direction" : "left-to-right"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "compression-type" : "lossless",
+ "preserves-vector-representation" : true,
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/Assets.xcassets/power.imageset/power.pdf b/Assets.xcassets/power.imageset/power.pdf
new file mode 100644
index 0000000..6f539f6
Binary files /dev/null and b/Assets.xcassets/power.imageset/power.pdf differ
diff --git a/Assets.xcassets/power_button.imageset/power-button-1.png b/Assets.xcassets/power_button.imageset/power-button-1.png
deleted file mode 100644
index 2b44cb7..0000000
Binary files a/Assets.xcassets/power_button.imageset/power-button-1.png and /dev/null differ
diff --git a/Assets.xcassets/power_button.imageset/power-button-2.png b/Assets.xcassets/power_button.imageset/power-button-2.png
deleted file mode 100644
index 2b44cb7..0000000
Binary files a/Assets.xcassets/power_button.imageset/power-button-2.png and /dev/null differ
diff --git a/Assets.xcassets/power_button.imageset/power-button.png b/Assets.xcassets/power_button.imageset/power-button.png
deleted file mode 100644
index 2b44cb7..0000000
Binary files a/Assets.xcassets/power_button.imageset/power-button.png and /dev/null differ
diff --git a/Assets.xcassets/safari.imageset/safari-1.png b/Assets.xcassets/safari.imageset/safari-1.png
deleted file mode 100644
index 3e891c0..0000000
Binary files a/Assets.xcassets/safari.imageset/safari-1.png and /dev/null differ
diff --git a/Assets.xcassets/safari.imageset/safari-2.png b/Assets.xcassets/safari.imageset/safari-2.png
deleted file mode 100644
index 3e891c0..0000000
Binary files a/Assets.xcassets/safari.imageset/safari-2.png and /dev/null differ
diff --git a/Assets.xcassets/safari.imageset/safari.png b/Assets.xcassets/safari.imageset/safari.png
deleted file mode 100644
index 3e891c0..0000000
Binary files a/Assets.xcassets/safari.imageset/safari.png and /dev/null differ
diff --git a/Assets.xcassets/checkmark.imageset/Contents.json b/Assets.xcassets/share.imageset/Contents.json
similarity index 70%
rename from Assets.xcassets/checkmark.imageset/Contents.json
rename to Assets.xcassets/share.imageset/Contents.json
index d9d5f9b..bfbe1c1 100644
--- a/Assets.xcassets/checkmark.imageset/Contents.json
+++ b/Assets.xcassets/share.imageset/Contents.json
@@ -2,17 +2,15 @@
"images" : [
{
"idiom" : "universal",
- "filename" : "checkmark.png",
+ "filename" : "share.png",
"scale" : "1x"
},
{
"idiom" : "universal",
- "filename" : "checkmark-1.png",
"scale" : "2x"
},
{
"idiom" : "universal",
- "filename" : "checkmark-2.png",
"scale" : "3x"
}
],
diff --git a/Assets.xcassets/share.imageset/share.png b/Assets.xcassets/share.imageset/share.png
new file mode 100644
index 0000000..3f83fa6
Binary files /dev/null and b/Assets.xcassets/share.imageset/share.png differ
diff --git a/Assets.xcassets/tableCellBackground.colorset/Contents.json b/Assets.xcassets/tableCellBackground.colorset/Contents.json
new file mode 100644
index 0000000..9c65033
--- /dev/null
+++ b/Assets.xcassets/tableCellBackground.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xF6",
+ "green" : "0xF6",
+ "red" : "0xF6"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x6A",
+ "green" : "0x68",
+ "red" : "0x64"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/tableCellSelectedBackground.colorset/Contents.json b/Assets.xcassets/tableCellSelectedBackground.colorset/Contents.json
new file mode 100644
index 0000000..cd71989
--- /dev/null
+++ b/Assets.xcassets/tableCellSelectedBackground.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xFC",
+ "green" : "0xF4",
+ "red" : "0xDB"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x73",
+ "green" : "0x56",
+ "red" : "0x00"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Assets.xcassets/Block Lists/website_icon.imageset/Contents.json b/Assets.xcassets/website_icon.imageset/Contents.json
similarity index 100%
rename from Assets.xcassets/Block Lists/website_icon.imageset/Contents.json
rename to Assets.xcassets/website_icon.imageset/Contents.json
diff --git a/Assets.xcassets/Block Lists/website_icon.imageset/website_icon.png b/Assets.xcassets/website_icon.imageset/website_icon.png
similarity index 100%
rename from Assets.xcassets/Block Lists/website_icon.imageset/website_icon.png
rename to Assets.xcassets/website_icon.imageset/website_icon.png
diff --git a/Assets.xcassets/welcome-image.imageset/Contents.json b/Assets.xcassets/welcome-image.imageset/Contents.json
new file mode 100644
index 0000000..a6dbe74
--- /dev/null
+++ b/Assets.xcassets/welcome-image.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "welcome-image.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/Assets.xcassets/welcome-image.imageset/welcome-image.pdf b/Assets.xcassets/welcome-image.imageset/welcome-image.pdf
new file mode 100644
index 0000000..25a6993
Binary files /dev/null and b/Assets.xcassets/welcome-image.imageset/welcome-image.pdf differ
diff --git a/Assets.xcassets/whyTrustImage.imageset/whyTrustLockdown-1.png b/Assets.xcassets/whyTrustImage.imageset/whyTrustLockdown-1.png
index 4190aa9..f31ba04 100644
Binary files a/Assets.xcassets/whyTrustImage.imageset/whyTrustLockdown-1.png and b/Assets.xcassets/whyTrustImage.imageset/whyTrustLockdown-1.png differ
diff --git a/Assets.xcassets/whyTrustImage.imageset/whyTrustLockdown-2.png b/Assets.xcassets/whyTrustImage.imageset/whyTrustLockdown-2.png
index 4190aa9..f31ba04 100644
Binary files a/Assets.xcassets/whyTrustImage.imageset/whyTrustLockdown-2.png and b/Assets.xcassets/whyTrustImage.imageset/whyTrustLockdown-2.png differ
diff --git a/Assets.xcassets/whyTrustImage.imageset/whyTrustLockdown.png b/Assets.xcassets/whyTrustImage.imageset/whyTrustLockdown.png
index 4190aa9..f31ba04 100644
Binary files a/Assets.xcassets/whyTrustImage.imageset/whyTrustLockdown.png and b/Assets.xcassets/whyTrustImage.imageset/whyTrustLockdown.png differ
diff --git a/BlockDayLog.swift b/BlockDayLog.swift
new file mode 100644
index 0000000..af8bc59
--- /dev/null
+++ b/BlockDayLog.swift
@@ -0,0 +1,89 @@
+//
+// BlockDayLog.swift
+// Lockdown
+//
+// Created by Oleg Dreyman on 22.05.2020.
+// Copyright © 2020 Confirmed Inc. All rights reserved.
+//
+
+import Foundation
+
+final class BlockDayLog {
+
+ static let shared = BlockDayLog()
+
+ static let dateFormatter: DateFormatter = {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "h:mm a_"
+ return formatter
+ }()
+
+ private let processingQueue = DispatchQueue(label: "LockdownBlockDayLogQueue")
+
+ private static let kIsBlockLogDisabled = "LockdownIsBlockLogDisabled"
+
+ private static let userDefaultsKey = "LockdownDayLogs"
+ private static let maxSize = 5000
+ private static let maxReduction = 4500
+
+ private init() { }
+
+ var isDisabled: Bool {
+ return defaults.bool(forKey: BlockDayLog.kIsBlockLogDisabled)
+ }
+
+ var isEnabled: Bool {
+ return !isDisabled
+ }
+
+ private var _dayLog: [Any]? {
+ #if DEBUG
+ dispatchPrecondition(condition: .onQueue(processingQueue))
+ #endif
+ return defaults.array(forKey: BlockDayLog.userDefaultsKey)
+ }
+
+ var strings: [String]? {
+ return processingQueue.sync {
+ self._dayLog as? [String]
+ }
+ }
+
+ func clear() {
+ processingQueue.async {
+ defaults.set([], forKey: BlockDayLog.userDefaultsKey)
+ }
+ }
+
+ func disable(shouldClear: Bool) {
+ defaults.set(true, forKey: BlockDayLog.kIsBlockLogDisabled)
+ if shouldClear {
+ clear()
+ }
+ }
+
+ func enable() {
+ defaults.set(false, forKey: BlockDayLog.kIsBlockLogDisabled)
+ }
+
+ func append(host: String, date: Date) {
+ guard isDisabled == false else {
+ // block log is disabled
+ return
+ }
+
+ processingQueue.async {
+ let logString = BlockDayLog.dateFormatter.string(from: date) + host
+ // reduce log size if it's over the maxSize
+ if var dayLog = self._dayLog {
+ if dayLog.count > BlockDayLog.maxSize {
+ dayLog = dayLog.suffix(BlockDayLog.maxReduction)
+ }
+ dayLog.append(logString)
+ defaults.set(dayLog, forKey: BlockDayLog.userDefaultsKey)
+ } else {
+ defaults.set([logString], forKey: BlockDayLog.userDefaultsKey)
+ }
+ }
+ }
+}
diff --git a/Cartfile b/Cartfile
index 4f39665..5c46a67 100755
--- a/Cartfile
+++ b/Cartfile
@@ -1 +1 @@
-github "zhuhaow/NEKit"
+github "https://github.com/confirmedcode/NEKit.git" "master"
\ No newline at end of file
diff --git a/Cartfile.resolved b/Cartfile.resolved
index 03e759f..82fa6da 100644
--- a/Cartfile.resolved
+++ b/Cartfile.resolved
@@ -1,8 +1,4 @@
-github "CocoaLumberjack/CocoaLumberjack" "3.5.3"
-github "behrang/YamlSwift" "3.4.3"
-github "lexrus/MMDB-Swift" "0.3.0"
-github "robbiehanson/CocoaAsyncSocket" "7.6.3"
-github "zhuhaow/NEKit" "0.14.0"
+github "CocoaLumberjack/CocoaLumberjack" "3.8.5"
+github "confirmedcode/CocoaAsyncSocket" "0cf7f247f8a5aef88da91c461612167d7382f47d"
+github "confirmedcode/NEKit" "ed44edffd80af18c0695cf03f0f96d85781d93e3"
github "zhuhaow/Resolver" "0.2.0"
-github "zhuhaow/Sodium-framework" "v1.0.10.1"
-github "zhuhaow/tun2socks" "0.7.0"
diff --git a/Client.swift b/Client.swift
index 86f88c1..f62444b 100644
--- a/Client.swift
+++ b/Client.swift
@@ -22,7 +22,9 @@ let kApiCodeEmailAlreadyUsed = 40
let kApiCodeReceiptAlreadyUsed = 48
let kApiCodeInvalidAuth = 401
let kApiCodeTooManyRequests = 999
+let kApiCodeSandboxReceiptNotAllowed = 9925
let kApiCodeUnknownError = 99999
+let kApiCodeNegativeError = -1
class Client {
@@ -34,9 +36,10 @@ class Client {
clearCookies()
return getReceipt(forceRefresh: forceRefresh)
.then { receipt -> Promise<(data: Data, response: URLResponse)> in
- let parameters = [
+ let parameters:[String : Any] = [
"authtype": "ios",
- "authreceipt": receipt
+ "authreceipt": receipt,
+ "lockdown": true
]
return URLSession.shared.dataTask(.promise,
with: try makePostRequest(urlString: mainURL + "/signin",
@@ -54,12 +57,148 @@ class Client {
}
}
}
+
+ static func signInWithEmail(email: String, password: String) throws -> Promise {
+ DDLogInfo("API CALL: test signIn with email")
+ URLCache.shared.removeAllCachedResponses()
+ clearCookies()
+ return firstly { () -> Promise<(data: Data, response: URLResponse)> in
+ let parameters:[String : Any] = [
+ "email" : email,
+ "password" : password,
+ "lockdown": true
+ ]
+ return URLSession.shared.dataTask(.promise, with: try makePostRequest(urlString: mainURL + "/signin", parameters: parameters))
+ }
+ .map { data, response -> SignIn in
+ try self.validateApiResponse(data: data, response: response)
+ let resp = response as! HTTPURLResponse // already validated the type in validateApiResponse
+ DDLogInfo("Got signin (with email) response with headers: \(resp.allHeaderFields)")
+ return try JSONDecoder().decode(SignIn.self, from: data)
+ }
+ }
+
+ static func resendConfirmCode(email: String) throws -> Promise {
+ DDLogInfo("API CALL: resendConfirmCode")
+ return firstly { () -> Promise<(data: Data, response: URLResponse)> in
+ let parameters:[String : Any] = [
+ "email" : email,
+ "lockdown": true
+ ]
+ return URLSession.shared.dataTask(.promise, with: try makePostRequest(urlString: mainURL + "/resend-confirm-code", parameters: parameters))
+ }
+ .map { data, response -> Bool in
+ if let httpResponse = response as? HTTPURLResponse {
+ DDLogInfo("API RESULT: resend-confirm-code: \(httpResponse.statusCode)")
+ if httpResponse.statusCode < 400 {
+ return true
+ }
+ return false
+ }
+ DDLogInfo("API RESULT: error - resend-confirm-code: not HTTPURLResponse")
+ return false
+ }
+ }
+
+ static func subscriptionEvent(forceRefresh: Bool = false) throws -> Promise {
+ DDLogInfo("API CALL: subscription-event")
+ return getReceipt(forceRefresh: forceRefresh)
+ .then { receipt -> Promise<(data: Data, response: URLResponse)> in
+ let parameters:[String : Any] = [
+ "authtype": "ios",
+ "authreceipt": receipt,
+ "lockdown": true
+ ]
+ return URLSession.shared.dataTask(.promise, with: try makePostRequest(urlString: mainURL + "/subscription-event", parameters: parameters))
+ }
+ .map { data, response -> SubscriptionEvent in
+ try self.validateApiResponse(data: data, response: response)
+ let subscriptionEvent = try JSONDecoder().decode(SubscriptionEvent.self, from: data)
+ DDLogInfo("API RESULT: subscriptionEvent: \(subscriptionEvent)")
+ return subscriptionEvent
+ }
+ .recover { error -> Promise in
+ DDLogInfo("Recovering from subscription-event error: \(error)")
+ return .value(SubscriptionEvent(message: "Recovery"))
+ }
+ }
+
+ static func activeSubscriptions() throws -> Promise<[Subscription]> {
+ DDLogInfo("API CALL: active-subscriptions")
+ return firstly {
+ URLSession.shared.dataTask(.promise, with: try makePostRequest(urlString: mainURL + "/active-subscriptions", parameters: [:]))
+ }.map { data, response -> [Subscription] in
+ try self.validateApiResponse(data: data, response: response)
+ let decoder = JSONDecoder()
+ let formatter = DateFormatter()
+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
+ decoder.dateDecodingStrategy = .formatted(formatter)
+ var subscriptions = try decoder.decode([Subscription].self, from: data)
+ DDLogInfo("API RESULT: active-subscriptions: \(subscriptions)")
+ // sort subscriptions with highest tier at the top
+ subscriptions.sort(by: { (sub1: Subscription, sub2: Subscription) -> Bool in
+ let p1 = Subscription.PlanType.precedence(p: sub1.planType)
+ let p2 = Subscription.PlanType.precedence(p: sub2.planType)
+ return p1 <= p2
+ })
+ DDLogInfo("API RESULT: sorted-active-subscriptions: \(subscriptions)")
+ return subscriptions
+ }
+ }
+
+ // For creating email account only - not signing up with IAP receipt
+ static func signup(email: String, password: String) throws -> Promise {
+ DDLogInfo("API CALL: signup")
+ return firstly { () -> Promise<(data: Data, response: URLResponse)> in
+ let parameters:[String : Any] = [
+ "email" : email,
+ "password" : password,
+ "lockdown": true
+ ]
+ return URLSession.shared.dataTask(.promise, with: try makePostRequest(urlString: mainURL + "/signup", parameters: parameters))
+ }
+ .map { data, response -> Signup in
+ try self.validateApiResponse(data: data, response: response)
+ let signup = try JSONDecoder().decode(Signup.self, from: data)
+ DDLogInfo("API RESULT: signup: \(signup)")
+ return signup
+ }
+ }
+
+ static func forgotPassword(email: String) throws -> Promise {
+ DDLogInfo("API CALL: forgot-password")
+ return firstly { () -> Promise<(data: Data, response: URLResponse)> in
+ let parameters:[String : Any] = [
+ "email" : email,
+ "lockdown": true
+ ]
+ return URLSession.shared.dataTask(.promise, with: try makePostRequest(urlString: mainURL + "/forgot-password", parameters: parameters))
+ }
+ .map { data, response -> Bool in
+ if let httpResponse = response as? HTTPURLResponse {
+ DDLogInfo("API RESULT: forgot-password: \(httpResponse.statusCode)")
+ if httpResponse.statusCode < 400 {
+ return true
+ }
+ if let error = try? JSONDecoder().decode(ApiError.self, from: data) {
+ throw error
+ }
+ throw ApiError(
+ code: httpResponse.statusCode,
+ message: "Unknown error"
+ )
+ }
+ DDLogInfo("API RESULT: error - forgot-password: not HTTPURLResponse")
+ return false
+ }
+ }
static func getKey() throws -> Promise {
DDLogInfo("API CALL: getKey")
return firstly { () -> Promise<(data: Data, response: URLResponse)> in
- let parameters = [
- "platform" : "ios"
+ let parameters:[String : Any] = [
+ "platform" : "ios",
+ "lockdown": true
]
return URLSession.shared.dataTask(.promise, with: try makePostRequest(urlString: mainURL + "/get-key", parameters: parameters))
}
@@ -98,16 +237,10 @@ class Client {
}
}
- static func getBlockedDomainTest(connectionSuccessHandler: @escaping () -> Void, connectionFailedHandler: @escaping (_ error: Error?) -> Void) -> PMKFinalizer {
+ static func getBlockedDomainTest() -> Promise {
return firstly {
URLSession.shared.dataTask(.promise, with: try Client.makeGetRequest(urlString: "https://\(testFirewallDomain)"))
- }
- .done { _ in
- connectionSuccessHandler()
- }
- .catch { error in
- connectionFailedHandler(error)
- }
+ }.asVoid()
}
// MARK: - Request Makers
@@ -127,7 +260,7 @@ class Client {
}
static func makePostRequest(urlString: String, parameters: [String: Any]) throws -> URLRequest {
- DDLogInfo("makePostRequest: \(urlString), parameters: \(parameters)")
+ DDLogInfo("makePostRequest: \(urlString)")//", parameters: \(parameters)")
if let url = URL(string: urlString) {
var rq = URLRequest(url: url)
rq.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData
@@ -144,7 +277,7 @@ class Client {
// MARK: - Util
- private static func getReceipt(forceRefresh: Bool) -> Promise {
+ static func getReceipt(forceRefresh: Bool) -> Promise {
DDLogInfo("fetch and set latest receipt")
return Promise { seal in
SwiftyStoreKit.fetchReceipt(forceRefresh: forceRefresh) { result in
@@ -198,7 +331,7 @@ class Client {
return hasValidCookie
}
- private static func clearCookies() {
+ static func clearCookies() {
DDLogInfo("clearing cookies")
var cookiesToDelete:[HTTPCookie] = []
if let cookies = HTTPCookieStorage.shared.cookies {
@@ -244,5 +377,4 @@ class Client {
throw "Invalid URL Response received"
}
}
-
}
diff --git a/ClientModels.swift b/ClientModels.swift
index bb80356..7784880 100644
--- a/ClientModels.swift
+++ b/ClientModels.swift
@@ -21,12 +21,122 @@ struct GetKey: Codable {
let b64: String
}
+struct SubscriptionEvent: Codable {
+ let message: String
+}
+
+enum pt {
+ case advancedMonthly
+ case advancedAnnual
+ case anonymousMonthly
+ case anonymousAnnual
+ case universalMonthly
+ case universalAnnual
+ case universalWeekly
+}
+
+struct Subscription: Codable {
+ let planType: PlanType
+ let receiptId: String
+ let expirationDate: Date
+ let expirationDateString: String
+ let expirationDateMs: Int
+ let cancellationDate: Date?
+ let cancellationDateString: String?
+ let cancellationDateMs: Int?
+
+ struct PlanType: RawRepresentable, RawValueCodable, Hashable {
+ let rawValue: String
+ init(rawValue: String) {
+ self.rawValue = rawValue
+ }
+
+ static func precedence(p: PlanType) -> Int {
+ switch p {
+ case Self.universalAnnual:
+ return 0
+ case Self.universalMonthly:
+ return 1
+ case Self.anonymousAnnual:
+ return 2
+ case Self.anonymousWeekly:
+ return 3
+ case Self.advancedAnnual:
+ return 4
+ case Self.advancedMonthly:
+ return 5
+ case Self.anonymousMonthly:
+ return 6
+ default:
+ return 7
+ }
+ }
+
+ static let advancedMonthly = PlanType(rawValue: "ios-fw-monthly")
+ static let advancedAnnual = PlanType(rawValue: "ios-fw-annual")
+ static let anonymousMonthly = PlanType(rawValue: "ios-monthly")
+ static let anonymousAnnual = PlanType(rawValue: "ios-annual")
+ static let universalMonthly = PlanType(rawValue: "all-monthly")
+ static let universalAnnual = PlanType(rawValue: "all-annual")
+ static let anonymousWeekly = PlanType(rawValue: "ios-weekly")
+
+ var isAdvanced: Bool {
+ self == Self.advancedAnnual || self == Self.advancedMonthly
+ }
+
+ var isAnonymous: Bool {
+ self == Self.anonymousAnnual || self == Self.anonymousMonthly || self == Self.anonymousWeekly
+ }
+
+ var isUniversal: Bool {
+ self == Self.universalAnnual || self == Self.universalMonthly
+ }
+ }
+
+ func isSameType(_ subscrition: Subscription) -> Bool {
+ self.planType.isAdvanced == subscrition.planType.isAdvanced ||
+ self.planType.isAnonymous == subscrition.planType.isAnonymous ||
+ self.planType.isUniversal == subscrition.planType.isUniversal
+ }
+}
+
struct SignIn: Codable {
let code: Int
let message: String
}
+struct Signup: Codable {
+ let code: Int
+ let message: String
+}
+
struct ApiError: Codable, Error {
let code: Int
let message: String
}
+
+// MARK: - Helpers
+
+public enum RawValueCodableError: Error {
+ case wrongRawValue
+}
+
+public protocol RawValueCodable: RawRepresentable, Codable {
+}
+
+public extension RawValueCodable where RawValue: Codable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.singleValueContainer()
+ let rawValue = try container.decode(RawValue.self)
+ if let value = Self.init(rawValue: rawValue) {
+ self = value
+ } else {
+ throw RawValueCodableError.wrongRawValue
+ }
+ }
+
+ func encode(to encoder: Encoder) throws {
+ var container = encoder.singleValueContainer()
+ try container.encode(rawValue)
+ }
+}
diff --git a/ConfirmedTunnel/PacketTunnelProvider.swift b/ConfirmedTunnel/PacketTunnelProvider.swift
deleted file mode 100644
index 425d95b..0000000
--- a/ConfirmedTunnel/PacketTunnelProvider.swift
+++ /dev/null
@@ -1,72 +0,0 @@
-//
-// PacketTunnelProvider.swift
-// ConfirmedTunnel
-//
-// Created by Rahul Dewan on 3/29/18.
-// Copyright © 2018 Trust Software. All rights reserved.
-//
-
-import NetworkExtension
-
-class PacketTunnelProvider: NEPacketTunnelProvider {
-
- override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
- // Add code here to start the process of connecting the tunnel.
-
- var settings = NEPacketTunnelNetworkSettings.init(tunnelRemoteAddress: "192.0.2.2")
-
- var ipv4Settings = NEIPv4Settings.init(addresses: ["192.0.2.1"], subnetMasks: ["255.255.255.0"])
- var route = NEIPv4Route.init(destinationAddress: "10.0.0.0", subnetMask: "104.25.112.26")
-
- var excluded = NEIPv4Route.default()// NEIPv4Route.init(destinationAddress: "255.255.255.0", subnetMask: "255.255.255.0")
-
- ipv4Settings.includedRoutes = [route];
- ipv4Settings.excludedRoutes = [excluded]
- //ipv4Settings.includedRoutes = @[[NEIPv4Route defaultRoute]];
- settings.ipv4Settings = ipv4Settings;
-
-
- //settings.IPv4Settings = ipv4Settings;
- settings.mtu = NSNumber.init(value: 1600)
- var proxySettings = NEProxySettings.init()
-
- var proxyServerPort = 3838;
- var proxyServerName = "localhost";
-
- proxySettings.httpEnabled = true;
- proxySettings.httpServer = NEProxyServer.init(address: proxyServerName, port: proxyServerPort)
- proxySettings.httpsEnabled = true;
- proxySettings.httpsServer = NEProxyServer.init(address: proxyServerName, port: proxyServerPort)
- proxySettings.excludeSimpleHostnames = true;
- proxySettings.exceptionList = ["*.ipchicken.com", "www.ipchicken.com"];
- proxySettings.matchDomains = ["*.google.com", "*.hulu.com"];
-
-
- settings.proxySettings = proxySettings;
-
- self.setTunnelNetworkSettings(settings, completionHandler: { error in
- completionHandler(nil)
- })
- }
-
- override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
- // Add code here to start the process of stopping the tunnel.
- completionHandler()
- }
-
- override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
- // Add code here to handle the message.
- if let handler = completionHandler {
- handler(messageData)
- }
- }
-
- override func sleep(completionHandler: @escaping () -> Void) {
- // Add code here to get ready to sleep.
- completionHandler()
- }
-
- override func wake() {
- // Add code here to wake up.
- }
-}
diff --git a/Defaults.swift b/Defaults.swift
new file mode 100644
index 0000000..74d4cba
--- /dev/null
+++ b/Defaults.swift
@@ -0,0 +1,55 @@
+//
+// Defaults.swift
+// Lockdown
+//
+// Created by Radu Lazar on 08.08.2024.
+// Copyright © 2024 Confirmed Inc. All rights reserved.
+//
+
+import Foundation
+
+//MARK: Metrics
+let kDayMetrics = "LockdownDayMetrics"
+let kWeekMetrics = "LockdownWeekMetrics"
+let kTotalMetrics = "LockdownTotalMetrics"
+let kTotalEnabledMetrics = "LockdownTotalEnabledMetrics"
+let kTotalDisabledMetrics = "LockdownTotalDisabledMetrics"
+
+let kActiveDay = "LockdownActiveDay"
+let kActiveWeek = "LockdownActiveWeek"
+
+//MARK: Firewall utils
+
+let kLockdownBlockedDomains = "lockdown_domains"
+let kUserBlockedDomains = "lockdown_domains_user"
+let kUserBlockedLists = "lockdown_lists_user"
+
+//MARK: Whitelist
+
+let kLockdownWhitelistedDomains = "whitelisted_domains"
+let kUserWhitelistedDomains = "whitelisted_domains_user"
+
+//MARK: Latest Knowledge
+
+let kLatestKnowledgeIsFirewallEnabled = "kLatestKnowledgeIsFirewallEnabled"
+let kLatestKnowledgeIsVPNEnabled = "kLatestKnowledgeIsVPNEnabled"
+
+// MARK: - VPN Region
+
+let kSavedVPNRegionServerPrefix = "vpn_region_server_prefix"
+
+//MARK: Others
+
+let kAPICredentialsConfirmed = "APICredentialsConfirmed"
+
+let kUserWantsFirewallEnabled = "user_wants_firewall_enabled"
+let kUserWantsVPNEnabled = "user_wants_vpn_enabled"
+
+let kAllowNotificationsAfterDate = "LockdownAllowNotificationsAfter"
+
+let kLockdownNotificationsEnergySavingCounter = "LockdownNotificationsEnergySavingCounter"
+
+let kAppActivateTime = "AppActivateTime"
+let kOneTimeOfferShown = "OneTimeOfferShown"
+let kSpecialOfferTimeDidReset = "SpecialOfferTimeDidReset_27_11_2024"
+let kVersionOfLastRun = "VersionOfLastRun"
diff --git a/Dnscryptproxy.xcframework/Info.plist b/Dnscryptproxy.xcframework/Info.plist
new file mode 100644
index 0000000..6d46073
--- /dev/null
+++ b/Dnscryptproxy.xcframework/Info.plist
@@ -0,0 +1,40 @@
+
+
+
+
+ AvailableLibraries
+
+
+ LibraryIdentifier
+ ios-arm64
+ LibraryPath
+ Dnscryptproxy.framework
+ SupportedArchitectures
+
+ arm64
+
+ SupportedPlatform
+ ios
+
+
+ LibraryIdentifier
+ ios-arm64_x86_64-simulator
+ LibraryPath
+ Dnscryptproxy.framework
+ SupportedArchitectures
+
+ arm64
+ x86_64
+
+ SupportedPlatform
+ ios
+ SupportedPlatformVariant
+ simulator
+
+
+ CFBundlePackageType
+ XFWK
+ XCFrameworkFormatVersion
+ 1.0
+
+
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Dnscryptproxy b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Dnscryptproxy
new file mode 120000
index 0000000..64546e2
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Dnscryptproxy
@@ -0,0 +1 @@
+Versions/Current/Dnscryptproxy
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Headers b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Headers
new file mode 120000
index 0000000..a177d2a
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Headers
@@ -0,0 +1 @@
+Versions/Current/Headers
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Modules b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Modules
new file mode 120000
index 0000000..5736f31
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Modules
@@ -0,0 +1 @@
+Versions/Current/Modules
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Resources b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Resources
new file mode 120000
index 0000000..953ee36
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Resources
@@ -0,0 +1 @@
+Versions/Current/Resources
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Dnscryptproxy b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Dnscryptproxy
new file mode 100644
index 0000000..b5c435d
Binary files /dev/null and b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Dnscryptproxy differ
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Headers/Dnscryptproxy.h b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Headers/Dnscryptproxy.h
new file mode 100644
index 0000000..96b2bd0
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Headers/Dnscryptproxy.h
@@ -0,0 +1,13 @@
+
+// Objective-C API for talking to the following Go packages
+//
+// github.com/jedisct1/dnscrypt-proxy/dnscrypt-proxy/ios
+//
+// File is generated by gomobile bind. Do not edit.
+#ifndef __Dnscryptproxy_FRAMEWORK_H__
+#define __Dnscryptproxy_FRAMEWORK_H__
+
+#include "Dnscryptproxy.objc.h"
+#include "Universe.objc.h"
+
+#endif
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Headers/Dnscryptproxy.objc.h b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Headers/Dnscryptproxy.objc.h
new file mode 100644
index 0000000..a3de93d
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Headers/Dnscryptproxy.objc.h
@@ -0,0 +1,81 @@
+// Objective-C API for talking to github.com/jedisct1/dnscrypt-proxy/dnscrypt-proxy/ios Go package.
+// gobind -lang=objc github.com/jedisct1/dnscrypt-proxy/dnscrypt-proxy/ios
+//
+// File is generated by gobind. Do not edit.
+
+#ifndef __Dnscryptproxy_H__
+#define __Dnscryptproxy_H__
+
+@import Foundation;
+#include "ref.h"
+#include "Universe.objc.h"
+
+
+@class DnscryptproxyApp;
+@class DnscryptproxyConfig;
+@protocol DnscryptproxyCloakCallback;
+@class DnscryptproxyCloakCallback;
+
+@protocol DnscryptproxyCloakCallback
+- (void)proxyReady;
+@end
+
+@interface DnscryptproxyApp : NSObject {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (nonnull instancetype)init;
+- (void)closeIdleConnections;
+- (void)logCritical:(NSString* _Nullable)s;
+- (void)logDebug:(NSString* _Nullable)s;
+- (void)logError:(NSString* _Nullable)s;
+- (void)logFatal:(NSString* _Nullable)s;
+- (void)logInfo:(NSString* _Nullable)s;
+- (void)logNotice:(NSString* _Nullable)s;
+- (void)logWarn:(NSString* _Nullable)s;
+- (long)refreshServersInfo;
+- (void)run:(id _Nullable)cloakCallback;
+- (BOOL)stop:(NSError* _Nullable* _Nullable)error;
+@end
+
+@interface DnscryptproxyConfig : NSObject {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (nullable instancetype)init;
+- (NSString* _Nonnull)listServers:(NSError* _Nullable* _Nullable)error;
+- (BOOL)loadJson:(NSString* _Nullable)cfg error:(NSError* _Nullable* _Nullable)error;
+- (BOOL)loadToml:(NSString* _Nullable)cfg error:(NSError* _Nullable* _Nullable)error;
+- (NSString* _Nonnull)toJson:(NSError* _Nullable* _Nullable)error;
+- (NSString* _Nonnull)toToml:(NSError* _Nullable* _Nullable)error;
+@end
+
+FOUNDATION_EXPORT NSString* _Nonnull const DnscryptproxyAppVersion;
+
+FOUNDATION_EXPORT DnscryptproxyConfig* _Nullable DnscryptproxyDefaultConfig(void);
+
+FOUNDATION_EXPORT BOOL DnscryptproxyFillIpBlacklistTrees(NSString* _Nullable filepath, NSError* _Nullable* _Nullable error);
+
+FOUNDATION_EXPORT BOOL DnscryptproxyFillPatternlistTrees(NSString* _Nullable filepath, NSError* _Nullable* _Nullable error);
+
+FOUNDATION_EXPORT DnscryptproxyApp* _Nullable DnscryptproxyMain(NSString* _Nullable configFile);
+
+FOUNDATION_EXPORT DnscryptproxyConfig* _Nullable DnscryptproxyNewConfig(void);
+
+FOUNDATION_EXPORT BOOL DnscryptproxyPrefetchSourceURLCloak(long timeout_int, BOOL useIPv4, BOOL useIPv6, NSString* _Nullable fallbackResolver, BOOL ignoreSystemDNS, NSString* _Nullable url, NSString* _Nullable cacheFile, NSString* _Nullable minisignKey, NSError* _Nullable* _Nullable error);
+
+FOUNDATION_EXPORT void DnscryptproxyRefreshServersInfoCloak(DnscryptproxyApp* _Nullable app);
+
+@class DnscryptproxyCloakCallback;
+
+@interface DnscryptproxyCloakCallback : NSObject {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (void)proxyReady;
+@end
+
+#endif
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Headers/Universe.objc.h b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Headers/Universe.objc.h
new file mode 100644
index 0000000..019e750
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Headers/Universe.objc.h
@@ -0,0 +1,29 @@
+// Objective-C API for talking to Go package.
+// gobind -lang=objc
+//
+// File is generated by gobind. Do not edit.
+
+#ifndef __Universe_H__
+#define __Universe_H__
+
+@import Foundation;
+#include "ref.h"
+
+@protocol Universeerror;
+@class Universeerror;
+
+@protocol Universeerror
+- (NSString* _Nonnull)error;
+@end
+
+@class Universeerror;
+
+@interface Universeerror : NSError {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (NSString* _Nonnull)error;
+@end
+
+#endif
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Headers/ref.h b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Headers/ref.h
new file mode 100644
index 0000000..b8036a4
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Headers/ref.h
@@ -0,0 +1,35 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#ifndef __GO_REF_HDR__
+#define __GO_REF_HDR__
+
+#include
+
+// GoSeqRef is an object tagged with an integer for passing back and
+// forth across the language boundary. A GoSeqRef may represent either
+// an instance of a Go object, or an Objective-C object passed to Go.
+// The explicit allocation of a GoSeqRef is used to pin a Go object
+// when it is passed to Objective-C. The Go seq package maintains a
+// reference to the Go object in a map keyed by the refnum along with
+// a reference count. When the reference count reaches zero, the Go
+// seq package will clear the corresponding entry in the map.
+@interface GoSeqRef : NSObject {
+}
+@property(readonly) int32_t refnum;
+@property(strong) id obj; // NULL when representing a Go object.
+
+// new GoSeqRef object to proxy a Go object. The refnum must be
+// provided from Go side.
+- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj;
+
+- (int32_t)incNum;
+
+@end
+
+@protocol goSeqRefInterface
+-(GoSeqRef*) _ref;
+@end
+
+#endif
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Modules/module.modulemap b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Modules/module.modulemap
new file mode 100644
index 0000000..8fa78da
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Modules/module.modulemap
@@ -0,0 +1,8 @@
+framework module "Dnscryptproxy" {
+ header "ref.h"
+ header "Dnscryptproxy.objc.h"
+ header "Universe.objc.h"
+ header "Dnscryptproxy.h"
+
+ export *
+}
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Resources/Info.plist b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Resources/Info.plist
new file mode 100644
index 0000000..0d1a4b8
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/A/Resources/Info.plist
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/Current b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/Current
new file mode 120000
index 0000000..8c7e5a6
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy-old.framework/Versions/Current
@@ -0,0 +1 @@
+A
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Dnscryptproxy b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Dnscryptproxy
new file mode 120000
index 0000000..64546e2
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Dnscryptproxy
@@ -0,0 +1 @@
+Versions/Current/Dnscryptproxy
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Headers b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Headers
new file mode 120000
index 0000000..a177d2a
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Headers
@@ -0,0 +1 @@
+Versions/Current/Headers
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Modules b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Modules
new file mode 120000
index 0000000..5736f31
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Modules
@@ -0,0 +1 @@
+Versions/Current/Modules
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Resources b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Resources
new file mode 120000
index 0000000..953ee36
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Resources
@@ -0,0 +1 @@
+Versions/Current/Resources
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Dnscryptproxy b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Dnscryptproxy
new file mode 100644
index 0000000..cdf2275
Binary files /dev/null and b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Dnscryptproxy differ
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Dnscryptproxy-not-working b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Dnscryptproxy-not-working
new file mode 100644
index 0000000..7cf7d92
Binary files /dev/null and b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Dnscryptproxy-not-working differ
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Headers/Dnscryptproxy.h b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Headers/Dnscryptproxy.h
new file mode 100644
index 0000000..4d0dca9
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Headers/Dnscryptproxy.h
@@ -0,0 +1,13 @@
+
+// Objective-C API for talking to the following Go packages
+//
+// github.com/DNSCrypt/dnscrypt-proxy/dnscrypt-proxy/ios
+//
+// File is generated by gomobile bind. Do not edit.
+#ifndef __Dnscryptproxy_FRAMEWORK_H__
+#define __Dnscryptproxy_FRAMEWORK_H__
+
+#include "Dnscryptproxy.objc.h"
+#include "Universe.objc.h"
+
+#endif
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Headers/Dnscryptproxy.objc.h b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Headers/Dnscryptproxy.objc.h
new file mode 100644
index 0000000..7bd31f6
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Headers/Dnscryptproxy.objc.h
@@ -0,0 +1,81 @@
+// Objective-C API for talking to github.com/DNSCrypt/dnscrypt-proxy/dnscrypt-proxy/ios Go package.
+// gobind -lang=objc github.com/DNSCrypt/dnscrypt-proxy/dnscrypt-proxy/ios
+//
+// File is generated by gobind. Do not edit.
+
+#ifndef __Dnscryptproxy_H__
+#define __Dnscryptproxy_H__
+
+@import Foundation;
+#include "ref.h"
+#include "Universe.objc.h"
+
+
+@class DnscryptproxyApp;
+@class DnscryptproxyConfig;
+@protocol DnscryptproxyCloakCallback;
+@class DnscryptproxyCloakCallback;
+
+@protocol DnscryptproxyCloakCallback
+- (void)proxyReady;
+@end
+
+@interface DnscryptproxyApp : NSObject {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (nonnull instancetype)init;
+- (void)closeIdleConnections;
+- (void)logCritical:(NSString* _Nullable)s;
+- (void)logDebug:(NSString* _Nullable)s;
+- (void)logError:(NSString* _Nullable)s;
+- (void)logFatal:(NSString* _Nullable)s;
+- (void)logInfo:(NSString* _Nullable)s;
+- (void)logNotice:(NSString* _Nullable)s;
+- (void)logWarn:(NSString* _Nullable)s;
+- (long)refreshServersInfo;
+- (void)run:(id _Nullable)cloakCallback;
+- (BOOL)stop:(NSError* _Nullable* _Nullable)error;
+@end
+
+@interface DnscryptproxyConfig : NSObject {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (nullable instancetype)init;
+- (NSString* _Nonnull)listServers:(NSError* _Nullable* _Nullable)error;
+- (BOOL)loadJson:(NSString* _Nullable)cfg error:(NSError* _Nullable* _Nullable)error;
+- (BOOL)loadToml:(NSString* _Nullable)cfg error:(NSError* _Nullable* _Nullable)error;
+- (NSString* _Nonnull)toJson:(NSError* _Nullable* _Nullable)error;
+- (NSString* _Nonnull)toToml:(NSError* _Nullable* _Nullable)error;
+@end
+
+FOUNDATION_EXPORT NSString* _Nonnull const DnscryptproxyAppVersion;
+
+FOUNDATION_EXPORT DnscryptproxyConfig* _Nullable DnscryptproxyDefaultConfig(void);
+
+FOUNDATION_EXPORT BOOL DnscryptproxyFillIpBlacklistTrees(NSString* _Nullable filepath, NSError* _Nullable* _Nullable error);
+
+FOUNDATION_EXPORT BOOL DnscryptproxyFillPatternlistTrees(NSString* _Nullable filepath, NSError* _Nullable* _Nullable error);
+
+FOUNDATION_EXPORT DnscryptproxyApp* _Nullable DnscryptproxyMain(NSString* _Nullable configFile);
+
+FOUNDATION_EXPORT DnscryptproxyConfig* _Nullable DnscryptproxyNewConfig(void);
+
+FOUNDATION_EXPORT BOOL DnscryptproxyPrefetchSourceURLCloak(long timeout_int, BOOL useIPv4, BOOL useIPv6, NSString* _Nullable fallbackResolver, BOOL ignoreSystemDNS, NSString* _Nullable url, NSString* _Nullable cacheFile, NSString* _Nullable minisignKey, NSError* _Nullable* _Nullable error);
+
+FOUNDATION_EXPORT void DnscryptproxyRefreshServersInfoCloak(DnscryptproxyApp* _Nullable app);
+
+@class DnscryptproxyCloakCallback;
+
+@interface DnscryptproxyCloakCallback : NSObject {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (void)proxyReady;
+@end
+
+#endif
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Headers/Universe.objc.h b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Headers/Universe.objc.h
new file mode 100644
index 0000000..019e750
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Headers/Universe.objc.h
@@ -0,0 +1,29 @@
+// Objective-C API for talking to Go package.
+// gobind -lang=objc
+//
+// File is generated by gobind. Do not edit.
+
+#ifndef __Universe_H__
+#define __Universe_H__
+
+@import Foundation;
+#include "ref.h"
+
+@protocol Universeerror;
+@class Universeerror;
+
+@protocol Universeerror
+- (NSString* _Nonnull)error;
+@end
+
+@class Universeerror;
+
+@interface Universeerror : NSError {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (NSString* _Nonnull)error;
+@end
+
+#endif
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Headers/ref.h b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Headers/ref.h
new file mode 100644
index 0000000..b8036a4
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Headers/ref.h
@@ -0,0 +1,35 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#ifndef __GO_REF_HDR__
+#define __GO_REF_HDR__
+
+#include
+
+// GoSeqRef is an object tagged with an integer for passing back and
+// forth across the language boundary. A GoSeqRef may represent either
+// an instance of a Go object, or an Objective-C object passed to Go.
+// The explicit allocation of a GoSeqRef is used to pin a Go object
+// when it is passed to Objective-C. The Go seq package maintains a
+// reference to the Go object in a map keyed by the refnum along with
+// a reference count. When the reference count reaches zero, the Go
+// seq package will clear the corresponding entry in the map.
+@interface GoSeqRef : NSObject {
+}
+@property(readonly) int32_t refnum;
+@property(strong) id obj; // NULL when representing a Go object.
+
+// new GoSeqRef object to proxy a Go object. The refnum must be
+// provided from Go side.
+- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj;
+
+- (int32_t)incNum;
+
+@end
+
+@protocol goSeqRefInterface
+-(GoSeqRef*) _ref;
+@end
+
+#endif
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Modules/module.modulemap b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Modules/module.modulemap
new file mode 100644
index 0000000..8fa78da
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Modules/module.modulemap
@@ -0,0 +1,8 @@
+framework module "Dnscryptproxy" {
+ header "ref.h"
+ header "Dnscryptproxy.objc.h"
+ header "Universe.objc.h"
+ header "Dnscryptproxy.h"
+
+ export *
+}
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Resources/Info.plist b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Resources/Info.plist
new file mode 100644
index 0000000..0d1a4b8
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/A/Resources/Info.plist
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/Current b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/Current
new file mode 120000
index 0000000..8c7e5a6
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64/Dnscryptproxy.framework/Versions/Current
@@ -0,0 +1 @@
+A
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Dnscryptproxy b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Dnscryptproxy
new file mode 120000
index 0000000..64546e2
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Dnscryptproxy
@@ -0,0 +1 @@
+Versions/Current/Dnscryptproxy
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Headers b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Headers
new file mode 120000
index 0000000..a177d2a
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Headers
@@ -0,0 +1 @@
+Versions/Current/Headers
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Modules b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Modules
new file mode 120000
index 0000000..5736f31
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Modules
@@ -0,0 +1 @@
+Versions/Current/Modules
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Resources b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Resources
new file mode 120000
index 0000000..953ee36
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Resources
@@ -0,0 +1 @@
+Versions/Current/Resources
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Dnscryptproxy b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Dnscryptproxy
new file mode 100644
index 0000000..e646ecf
Binary files /dev/null and b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Dnscryptproxy differ
diff --git a/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Headers/Dnscryptproxy.h b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Headers/Dnscryptproxy.h
new file mode 100644
index 0000000..96b2bd0
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Headers/Dnscryptproxy.h
@@ -0,0 +1,13 @@
+
+// Objective-C API for talking to the following Go packages
+//
+// github.com/jedisct1/dnscrypt-proxy/dnscrypt-proxy/ios
+//
+// File is generated by gomobile bind. Do not edit.
+#ifndef __Dnscryptproxy_FRAMEWORK_H__
+#define __Dnscryptproxy_FRAMEWORK_H__
+
+#include "Dnscryptproxy.objc.h"
+#include "Universe.objc.h"
+
+#endif
diff --git a/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Headers/Dnscryptproxy.objc.h b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Headers/Dnscryptproxy.objc.h
new file mode 100644
index 0000000..a3de93d
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Headers/Dnscryptproxy.objc.h
@@ -0,0 +1,81 @@
+// Objective-C API for talking to github.com/jedisct1/dnscrypt-proxy/dnscrypt-proxy/ios Go package.
+// gobind -lang=objc github.com/jedisct1/dnscrypt-proxy/dnscrypt-proxy/ios
+//
+// File is generated by gobind. Do not edit.
+
+#ifndef __Dnscryptproxy_H__
+#define __Dnscryptproxy_H__
+
+@import Foundation;
+#include "ref.h"
+#include "Universe.objc.h"
+
+
+@class DnscryptproxyApp;
+@class DnscryptproxyConfig;
+@protocol DnscryptproxyCloakCallback;
+@class DnscryptproxyCloakCallback;
+
+@protocol DnscryptproxyCloakCallback
+- (void)proxyReady;
+@end
+
+@interface DnscryptproxyApp : NSObject {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (nonnull instancetype)init;
+- (void)closeIdleConnections;
+- (void)logCritical:(NSString* _Nullable)s;
+- (void)logDebug:(NSString* _Nullable)s;
+- (void)logError:(NSString* _Nullable)s;
+- (void)logFatal:(NSString* _Nullable)s;
+- (void)logInfo:(NSString* _Nullable)s;
+- (void)logNotice:(NSString* _Nullable)s;
+- (void)logWarn:(NSString* _Nullable)s;
+- (long)refreshServersInfo;
+- (void)run:(id _Nullable)cloakCallback;
+- (BOOL)stop:(NSError* _Nullable* _Nullable)error;
+@end
+
+@interface DnscryptproxyConfig : NSObject {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (nullable instancetype)init;
+- (NSString* _Nonnull)listServers:(NSError* _Nullable* _Nullable)error;
+- (BOOL)loadJson:(NSString* _Nullable)cfg error:(NSError* _Nullable* _Nullable)error;
+- (BOOL)loadToml:(NSString* _Nullable)cfg error:(NSError* _Nullable* _Nullable)error;
+- (NSString* _Nonnull)toJson:(NSError* _Nullable* _Nullable)error;
+- (NSString* _Nonnull)toToml:(NSError* _Nullable* _Nullable)error;
+@end
+
+FOUNDATION_EXPORT NSString* _Nonnull const DnscryptproxyAppVersion;
+
+FOUNDATION_EXPORT DnscryptproxyConfig* _Nullable DnscryptproxyDefaultConfig(void);
+
+FOUNDATION_EXPORT BOOL DnscryptproxyFillIpBlacklistTrees(NSString* _Nullable filepath, NSError* _Nullable* _Nullable error);
+
+FOUNDATION_EXPORT BOOL DnscryptproxyFillPatternlistTrees(NSString* _Nullable filepath, NSError* _Nullable* _Nullable error);
+
+FOUNDATION_EXPORT DnscryptproxyApp* _Nullable DnscryptproxyMain(NSString* _Nullable configFile);
+
+FOUNDATION_EXPORT DnscryptproxyConfig* _Nullable DnscryptproxyNewConfig(void);
+
+FOUNDATION_EXPORT BOOL DnscryptproxyPrefetchSourceURLCloak(long timeout_int, BOOL useIPv4, BOOL useIPv6, NSString* _Nullable fallbackResolver, BOOL ignoreSystemDNS, NSString* _Nullable url, NSString* _Nullable cacheFile, NSString* _Nullable minisignKey, NSError* _Nullable* _Nullable error);
+
+FOUNDATION_EXPORT void DnscryptproxyRefreshServersInfoCloak(DnscryptproxyApp* _Nullable app);
+
+@class DnscryptproxyCloakCallback;
+
+@interface DnscryptproxyCloakCallback : NSObject {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (void)proxyReady;
+@end
+
+#endif
diff --git a/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Headers/Universe.objc.h b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Headers/Universe.objc.h
new file mode 100644
index 0000000..019e750
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Headers/Universe.objc.h
@@ -0,0 +1,29 @@
+// Objective-C API for talking to Go package.
+// gobind -lang=objc
+//
+// File is generated by gobind. Do not edit.
+
+#ifndef __Universe_H__
+#define __Universe_H__
+
+@import Foundation;
+#include "ref.h"
+
+@protocol Universeerror;
+@class Universeerror;
+
+@protocol Universeerror
+- (NSString* _Nonnull)error;
+@end
+
+@class Universeerror;
+
+@interface Universeerror : NSError {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (NSString* _Nonnull)error;
+@end
+
+#endif
diff --git a/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Headers/ref.h b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Headers/ref.h
new file mode 100644
index 0000000..b8036a4
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Headers/ref.h
@@ -0,0 +1,35 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#ifndef __GO_REF_HDR__
+#define __GO_REF_HDR__
+
+#include
+
+// GoSeqRef is an object tagged with an integer for passing back and
+// forth across the language boundary. A GoSeqRef may represent either
+// an instance of a Go object, or an Objective-C object passed to Go.
+// The explicit allocation of a GoSeqRef is used to pin a Go object
+// when it is passed to Objective-C. The Go seq package maintains a
+// reference to the Go object in a map keyed by the refnum along with
+// a reference count. When the reference count reaches zero, the Go
+// seq package will clear the corresponding entry in the map.
+@interface GoSeqRef : NSObject {
+}
+@property(readonly) int32_t refnum;
+@property(strong) id obj; // NULL when representing a Go object.
+
+// new GoSeqRef object to proxy a Go object. The refnum must be
+// provided from Go side.
+- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj;
+
+- (int32_t)incNum;
+
+@end
+
+@protocol goSeqRefInterface
+-(GoSeqRef*) _ref;
+@end
+
+#endif
diff --git a/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Modules/module.modulemap b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Modules/module.modulemap
new file mode 100644
index 0000000..8fa78da
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Modules/module.modulemap
@@ -0,0 +1,8 @@
+framework module "Dnscryptproxy" {
+ header "ref.h"
+ header "Dnscryptproxy.objc.h"
+ header "Universe.objc.h"
+ header "Dnscryptproxy.h"
+
+ export *
+}
\ No newline at end of file
diff --git a/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Resources/Info.plist b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Resources/Info.plist
new file mode 100644
index 0000000..0d1a4b8
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/A/Resources/Info.plist
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/Current b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/Current
new file mode 120000
index 0000000..8c7e5a6
--- /dev/null
+++ b/Dnscryptproxy.xcframework/ios-arm64_x86_64-simulator/Dnscryptproxy.framework/Versions/Current
@@ -0,0 +1 @@
+A
\ No newline at end of file
diff --git a/DomainNameValidator.swift b/DomainNameValidator.swift
new file mode 100644
index 0000000..7b2e334
--- /dev/null
+++ b/DomainNameValidator.swift
@@ -0,0 +1,60 @@
+//
+// DomainNameValidator.swift
+// Lockdown
+//
+// Created by Oleg Dreyman on 19.05.2020.
+// Copyright © 2020 Confirmed Inc. All rights reserved.
+//
+
+import Foundation
+
+enum DomainNameValidator {
+
+ enum Status {
+ case valid
+ case notValid(FailureReason)
+
+ // Not currently shown to the user, but can be leveraged in the future
+ enum FailureReason {
+ case emptyString
+ case noDots
+ case invalidCharacters(in: String)
+ case labelEmpty
+ }
+ }
+
+ /// All "url host allowed" characters with the exception of the wildcard symbol
+ static let allowedChars = CharacterSet.urlHostAllowed
+ .subtracting(CharacterSet(charactersIn: "*"))
+
+ static func validate(_ domainName: String) -> Status {
+
+ guard domainName.isEmpty == false else {
+ return .notValid(.emptyString)
+ }
+
+ var labels = domainName.components(separatedBy: ".")
+ guard labels.count > 1 else {
+ return .notValid(.noDots)
+ }
+
+ // Wildcard is allowed only as a first label.
+ // If first label is a wildcard, we're removing
+ // it from the elements to validate
+ if labels.first == "*" {
+ labels.removeFirst()
+ }
+
+ for label in labels {
+ guard label.isEmpty == false else {
+ return .notValid(.labelEmpty)
+ }
+
+ guard allowedChars.isSuperset(of: CharacterSet(charactersIn: label)) else {
+ return .notValid(.invalidCharacters(in: label))
+ }
+ }
+
+ return .valid
+ }
+}
diff --git a/Environment.swift b/Environment.swift
index e3a4e7f..e3ea051 100644
--- a/Environment.swift
+++ b/Environment.swift
@@ -7,14 +7,34 @@
//
import Foundation
+import CocoaLumberjackSwift
-let vpnSourceID = "-111818"
-let vpnDomain = "confirmedvpn.com"
-let vpnRemoteIdentifier = "www" + vpnSourceID + "." + vpnDomain
-let mainDomain = "confirmedvpn.com"
+//Prod Environment
+let vpnSourceID = "-111818" //getEnvironmentVariable(key: "vpnSourceID", default: "-111818")
+let vpnDomain = "confirmedvpn.com" //getEnvironmentVariable(key: "vpnDomain", default: "confirmedvpn.com")
+let vpnRemoteIdentifier = "www" + vpnSourceID + "." + vpnDomain
+let mainDomain = "confirmedvpn.com" //getEnvironmentVariable(key: "mainDomain", default: "confirmedvpn.com")
let mainURL = "https://www." + mainDomain
+// Dev Environment
+// US-East US-West
+//let vpnSourceID = "-111618"
+//let vpnDomain = "trusty-ap.science"
+//let vpnRemoteIdentifier = "www" + vpnSourceID + "." + vpnDomain
+//let mainDomain = "trusty-ap.science"
+//let mainURL = "https://www." + mainDomain
+
let testFirewallDomain = "example.com"
-let lastVersionToAskForRating = "020"
+let lastVersionToAskForRating = "024"
+
+func getEnvironmentVariable(key: String, default: String) -> String {
+ if let value = ProcessInfo.processInfo.environment[key] {
+ return value
+ }
+ else {
+ DDLogError("ERROR: Could not find environment variable key \(key)")
+ return ""
+ }
+}
diff --git a/FirewallController.swift b/FirewallController.swift
index 7717cb2..ce17249 100644
--- a/FirewallController.swift
+++ b/FirewallController.swift
@@ -9,6 +9,7 @@ import UIKit
import NetworkExtension
import CocoaLumberjackSwift
import PromiseKit
+import WidgetKit
let kFirewallTunnelLocalizedDescription = "Lockdown Configuration"
@@ -27,46 +28,96 @@ class FirewallController: NSObject {
// get the reference to the latest manager in Settings
NETunnelProviderManager.loadAllFromPreferences { (managers, error) -> Void in
if let managers = managers, managers.count > 0 {
- self.manager = nil
- self.manager = managers[0]
+ if (self.manager == managers[0]) {
+ DDLogInfo("Encountered same manager while refreshing manager, not replacing it.")
+ } else {
+ self.manager = nil
+ self.manager = managers[0]
+ }
+ completion(nil)
}
completion(error)
}
}
+ func existingManagerCount(completion: @escaping (Int?) -> Void) {
+ NETunnelProviderManager.loadAllFromPreferences { (managers, error) in
+ completion(managers?.count)
+ }
+ }
+
func status() -> NEVPNStatus {
- if manager != nil {
- return manager!.connection.status
+ if let manager {
+ return manager.connection.status
}
else {
return .invalid
}
}
+
+ func deleteConfigurationAndAddAgain() {
+ refreshManager { (error) in
+ self.manager?.removeFromPreferences(completionHandler: { (removeError) in
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
+ self.setEnabled(true, isUserExplicitToggle: true)
+ }
+ })
+ }
+ }
func restart(completion: @escaping (_ error: Error?) -> Void = {_ in }) {
+ DDLogInfo("FirewallController.restart called")
// Don't let this affect userWantsFirewallOn/Off config
FirewallController.shared.setEnabled(false, completion: {
error in
+ DDLogInfo("FirewallController.restart completed disabling")
// TODO: Handle the error (throw?)
if error != nil {
DDLogError("Error disabling on Firewall restart: \(error!)")
}
- FirewallController.shared.setEnabled(true, completion: {
- error in
- if error != nil {
- DDLogError("Error enabling on Firewall restart: \(error!)")
- }
- completion(error)
- })
+ // waiting for a little bit before re-enabling:
+ // without it, sometimes Firewall fails to enable
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {
+ DDLogInfo("FirewallController.restart wait completed")
+ FirewallController.shared.setEnabled(true, completion: {
+ error in
+ DDLogInfo("FirewallController.restart completed enabling")
+ if error != nil {
+ DDLogError("Error enabling on Firewall restart: \(error!)")
+ }
+ completion(error)
+ })
+ }
})
}
+ struct CombinedBlockListEmptyError: Error { }
+
+ private func handleUserDeniedAccessToFirewallConfiguration() {
+ setUserWantsFirewallEnabled(false)
+ if #available(iOSApplicationExtension 14.0, iOS 14.0, *) {
+ WidgetCenter.shared.reloadAllTimelines()
+ }
+ manager = nil
+ }
+
func setEnabled(_ enabled: Bool, isUserExplicitToggle: Bool = false, completion: @escaping (_ error: Error?) -> Void = {_ in }) {
DDLogInfo("FirewallController set enabled: \(enabled)")
// only change this boolean if it's user action
if (isUserExplicitToggle) {
setUserWantsFirewallEnabled(enabled)
+ if #available(iOSApplicationExtension 14.0, iOS 14.0, *) {
+ WidgetCenter.shared.reloadAllTimelines()
+ }
+ }
+
+ if enabled && getIsCombinedBlockListEmpty() {
+ DDLogError("Trying to enable Firewall when combined block list is empty; not allowing")
+ completion(FirewallController.CombinedBlockListEmptyError())
+ assertionFailure("Trying to enable Firewall when combined block list is empty; not allowing. This crash only happens in DEBUG mode")
+ return
}
+
// just to be sure, reload the managers to make sure we don't make multiple configs
NETunnelProviderManager.loadAllFromPreferences { (managers, error) -> Void in
if let managers = managers, managers.count > 0 {
@@ -76,17 +127,18 @@ class FirewallController: NSObject {
else {
self.manager = nil
self.manager = NETunnelProviderManager()
- self.manager!.protocolConfiguration = NETunnelProviderProtocol()
+ self.manager?.protocolConfiguration = NETunnelProviderProtocol()
}
- self.manager!.localizedDescription = kFirewallTunnelLocalizedDescription
- self.manager!.protocolConfiguration?.serverAddress = kFirewallTunnelLocalizedDescription
- self.manager!.isEnabled = enabled
- self.manager!.isOnDemandEnabled = enabled
+ self.manager?.localizedDescription = kFirewallTunnelLocalizedDescription
+ self.manager?.protocolConfiguration?.serverAddress = kFirewallTunnelLocalizedDescription
+ self.manager?.isEnabled = enabled
+ self.manager?.isOnDemandEnabled = enabled
+ self.manager?.protocolConfiguration?.disconnectOnSleep = false
let connectRule = NEOnDemandRuleConnect()
connectRule.interfaceTypeMatch = .any
- self.manager!.onDemandRules = [connectRule]
- self.manager!.saveToPreferences(completionHandler: { (error) -> Void in
+ self.manager?.onDemandRules = [connectRule]
+ self.manager?.saveToPreferences(completionHandler: { [weak self] (error) -> Void in
// TODO: Handle each case specifically
if let e = error as? NEVPNError {
DDLogError("VPN Error while saving state: \(enabled) \(e)")
@@ -96,7 +148,8 @@ class FirewallController: NSObject {
case .configurationInvalid:
break;
case .configurationReadWriteFailed:
- break;
+ self?.handleUserDeniedAccessToFirewallConfiguration()
+ return
case .configurationStale:
break;
case .configurationUnknown:
@@ -104,18 +157,73 @@ class FirewallController: NSObject {
case .connectionFailed:
break;
}
+ completion(e)
}
else if let e = error {
DDLogError("Error saving config for enabled state: \(enabled): \(e)")
+ completion(e)
}
else {
- DDLogInfo("Successfully saved config for enabled state: \(enabled)")
+ self?.loadFromPreferenceAndStartFirewall(enabled, completion: completion)
}
- self.refreshManager(completion: { error in
- completion(nil)
- })
})
}
}
+ private func loadFromPreferenceAndStartFirewall(_ enabled: Bool, completion: @escaping (_ error: Error?) -> Void) {
+ manager?.loadFromPreferences { [weak self] error in
+ if let error {
+ DDLogError("Read preference error before start firewall: " + error.localizedDescription)
+ }
+ DDLogInfo("Successfully saved config for enabled state: \(enabled)")
+ // manually activate the starting of the tunnel, and also do a dummy connect to a nonexistant, invalid URL to force enabling
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
+ if (enabled) {
+ self?.startFirewallTunnel(completion: completion)
+ }
+ else {
+ DDLogInfo("FirewallController.setEnabled not enabled, no need to call startVPNTunnel")
+ completion(nil)
+ }
+ }
+ }
+ }
+
+ private func startFirewallTunnel(completion: @escaping (_ error: Error?) -> Void) {
+ guard let manager else {
+ DDLogInfo("FirewallController.setEnabled ignore: empty manager")
+ completion(nil)
+ return
+ }
+ DDLogInfo("FirewallController.setEnabled enabled, calling startVPNTunnel")
+ do {
+ try manager.connection.startVPNTunnel()
+ let config = URLSessionConfiguration.default
+ config.requestCachePolicy = .reloadIgnoringLocalCacheData
+ config.urlCache = nil
+ let session = URLSession.init(configuration: config)
+ if let url = URL(string: "https://nonexistant_invalid_url") {
+ let task = session.dataTask(with: url) { (data, response, error) in
+ DDLogInfo("FirewallController.setEnabled response from calling nonexistant url")
+ return
+ }
+ DDLogInfo("FirewallController.setEnabled calling nonexistant url")
+ task.resume()
+ }
+ DDLogInfo("FirewallController.setEnabled refreshing manager")
+ refreshManager(completion: { error in
+ if let error {
+ DDLogInfo("FirewallController.setEnabled error response from refreshing manager: \(error)")
+ }
+ else {
+ DDLogInfo("FirewallController.setEnabled no error from refreshing manager")
+ }
+ completion(nil)
+ })
+ }
+ catch {
+ DDLogError("Unable to start the tunnel after saving: " + error.localizedDescription)
+ completion(error.localizedDescription)
+ }
+ }
}
diff --git a/FirewallUtilities.swift b/FirewallUtilities.swift
index 58611ea..b6734af 100644
--- a/FirewallUtilities.swift
+++ b/FirewallUtilities.swift
@@ -8,15 +8,14 @@
import Foundation
import NetworkExtension
+import WidgetKit
+import UniformTypeIdentifiers
// MARK: - Constants
-let kLockdownBlockedDomains = "lockdown_domains"
-let kUserBlockedDomains = "lockdown_domains_user"
-
// MARK: - data structures
-struct IPRange : Codable {
+struct ConfirmedIPRange : Codable {
var subnetMask : String
var enabled : Bool
var IPv6 : Bool
@@ -33,13 +32,31 @@ struct LockdownGroup : Codable {
var iconURL : String
var enabled : Bool
var domains : Dictionary
- var ipRanges : Dictionary
+ var ipRanges : Dictionary
+ var warning: String?
+ var accessLevel: String = "advanced"
}
struct LockdownDefaults : Codable {
var lockdownDefaults : Dictionary
}
+struct UserBlockListsGroup: Codable {
+ var name: String
+ var enabled: Bool = false
+ var domains: Set = []
+ var description: String?
+}
+
+struct UserBlockListsDefaults: Codable {
+ var userBlockListsDefaults: Dictionary
+}
+
+//struct Domains: Codable {
+// var name: String
+// var isBlocked: Bool = true
+//}
+
// MARK: - Block Metrics & Block Log
let currentCalendar = Calendar.current
@@ -49,23 +66,55 @@ let blockLogDateFormatter : DateFormatter = {
return formatter
}()
-let kDayMetrics = "LockdownDayMetrics"
-let kWeekMetrics = "LockdownWeekMetrics"
-let kTotalMetrics = "LockdownTotalMetrics"
-
-let kActiveDay = "LockdownActiveDay"
-let kActiveWeek = "LockdownActiveWeek"
-
-let kDayLogs = "LockdownDayLogs"
-let kDayLogsMaxSize = 5000;
-let kDayLogsMaxReduction = 4500;
+enum MetricsUpdate {
+
+ enum Mode {
+ case incrementAndLog(host: String)
+ case resetIfNeeded
+
+ var incrementBy: Int {
+ switch self {
+ case .incrementAndLog:
+ return 1
+ case .resetIfNeeded:
+ return 0
+ }
+ }
+ }
+
+ enum RescheduleNotifications {
+ case always
+ case never
+ case withEnergySaving
+
+ var allowsScheduling: Bool {
+ switch self {
+ case .always, .withEnergySaving:
+ return true
+ case .never:
+ return false
+ }
+ }
+ }
+}
-func incrementMetricsAndLog(host: String) {
+func updateMetrics(_ mode: MetricsUpdate.Mode, rescheduleNotifications: MetricsUpdate.RescheduleNotifications) {
let date = Date()
// TOTAL - increment total
- defaults.set(Int(getTotalMetrics() + 1), forKey: kTotalMetrics)
+ let totalMetrics = getTotalMetrics()
+ let updatedTotal = totalMetrics + mode.incrementBy
+
+ defaults.set(updatedTotal, forKey: kTotalMetrics)
+
+ if (100 ... 200) ~= updatedTotal, rescheduleNotifications.allowsScheduling {
+ OneTimeActions.performOnce(ifHasNotSeen: .oneHundredTrackingAttemptsBlockedNotification) {
+ PushNotifications.shared.scheduleOnboardingNotification(
+ options: rescheduleNotifications == .withEnergySaving ? [.energySaving] : []
+ )
+ }
+ }
// WEEKLY - reset metrics on new week and increment week
let currentWeek = currentCalendar.component(.weekOfYear, from: date)
@@ -73,7 +122,8 @@ func incrementMetricsAndLog(host: String) {
defaults.set(0, forKey: kWeekMetrics)
defaults.set(currentWeek, forKey: kActiveWeek)
}
- defaults.set(Int(getWeekMetrics() + 1), forKey: kWeekMetrics)
+ let weekMetrics = getWeekMetrics()
+ defaults.set(Int(weekMetrics + mode.incrementBy), forKey: kWeekMetrics)
// DAY - reset metric on new day and increment day and log
// set day metric
@@ -81,58 +131,36 @@ func incrementMetricsAndLog(host: String) {
if currentDay != defaults.integer(forKey: kActiveDay) {
defaults.set(0, forKey: kDayMetrics)
defaults.set(currentDay, forKey: kActiveDay)
- defaults.set([], forKey:kDayLogs);
- }
- defaults.set(Int(getDayMetrics() + 1), forKey: kDayMetrics)
- // set log
- let logString = blockLogDateFormatter.string(from: date) + host;
- // reduce log size if it's over the maxSize
- if var dayLog = defaults.array(forKey: kDayLogs) {
- if dayLog.count > kDayLogsMaxSize {
- dayLog = dayLog.suffix(kDayLogsMaxReduction);
- }
- dayLog.append(logString);
- defaults.set(dayLog, forKey: kDayLogs);
- }
- else {
- defaults.set([logString], forKey: kDayLogs);
+ BlockDayLog.shared.clear()
}
+ defaults.set(Int(getDayMetrics() + mode.incrementBy), forKey: kDayMetrics)
-}
-
-func getDayMetrics() -> Int {
- return defaults.integer(forKey: kDayMetrics)
-}
-
-func getDayMetricsString() -> String {
- return metricsToString(metric: getDayMetrics())
-}
-
-func getWeekMetrics() -> Int {
- return defaults.integer(forKey: kWeekMetrics)
-}
-
-func getWeekMetricsString() -> String {
- return metricsToString(metric: getWeekMetrics())
-}
-
-func getTotalMetrics() -> Int {
- return defaults.integer(forKey: kTotalMetrics)
-}
-
-func getTotalMetricsString() -> String {
- return metricsToString(metric: getTotalMetrics())
-}
-
-func metricsToString(metric : Int) -> String {
- if metric < 1000 {
- return "\(metric)"
+ if #available(iOSApplicationExtension 14.0, iOS 14.0, *) {
+ WidgetCenter.shared.reloadAllTimelines()
}
- else if metric < 1000000 {
- return "\(Int(metric / 1000))k"
+
+ switch mode {
+ case .incrementAndLog(host: let host):
+ guard BlockDayLog.shared.isDisabled == false else {
+ // block log disabled
+ break
+ }
+
+ // set log
+ BlockDayLog.shared.append(host: host, date: date)
+ case .resetIfNeeded:
+ // no-act
+ break
}
- else {
- return "\(Int(metric / 1000000))m"
+
+ switch rescheduleNotifications {
+ case .always:
+ PushNotifications.shared.rescheduleWeeklyUpdate(options: [])
+ case .withEnergySaving:
+ PushNotifications.shared.rescheduleWeeklyUpdate(options: [.energySaving])
+ case .never:
+ // no-act
+ break
}
}
@@ -141,84 +169,218 @@ func metricsToString(metric : Int) -> String {
func setupFirewallDefaultBlockLists() {
var lockdownBlockedDomains = getLockdownBlockedDomains()
- let clickbait = LockdownGroup.init(
- version: 20,
- internalID: "clickbait",
- name: "Clickbait",
- iconURL: "clickbait_icon",
+ let snapchatAnalytics = LockdownGroup.init(
+ version: 28,
+ internalID: "snapchatAnalytics",
+ name: NSLocalizedString("Snapchat Trackers", comment: "The title of a list of trackers"),
+ iconURL: "snapchat_analytics_icon",
enabled: false,
- domains: getDomainBlockList(filename: "clickbait"),
+ domains: getDomainBlockList(filename: "snapchat_analytics"),
ipRanges: [:])
- let crypto = LockdownGroup.init(
- version: 20,
- internalID: "crypto_mining",
- name: "Crypto Mining",
- iconURL: "crypto_icon",
+ let gameAds = LockdownGroup.init(
+ version: 31,
+ internalID: "gameAds",
+ name: NSLocalizedString("Game Marketing", comment: "The title of a list of trackers"),
+ iconURL: "game_ads_icon",
enabled: true,
- domains: getDomainBlockList(filename: "crypto_mining"),
- ipRanges: [:])
+ domains: getDomainBlockList(filename: "game_ads"),
+ ipRanges: [:],
+ accessLevel: "basic")
+
+ let clickbait = LockdownGroup.init(
+ version: 30,
+ internalID: "clickbait",
+ name: NSLocalizedString("Clickbait", comment: "The title of a list of trackers"),
+ iconURL: "clickbait_icon",
+ enabled: true,
+ domains: getDomainBlockList(filename: "clickbait"),
+ ipRanges: [:],
+ accessLevel: "basic")
let emailOpens = LockdownGroup.init(
- version: 20,
+ version: 31,
internalID: "email_opens",
- name: "Email Opens (Beta)",
+ name: NSLocalizedString("Email Trackers", comment: "The title of a list of trackers"),
iconURL: "email_icon",
- enabled: false,
+ enabled: true,
domains: getDomainBlockList(filename: "email_opens"),
- ipRanges: [:])
+ ipRanges: [:],
+ accessLevel: "basic")
let facebookInc = LockdownGroup.init(
- version: 20,
+ version: 34,
internalID: "facebook_inc",
- name: "Facebook Inc (Beta)",
+ name: NSLocalizedString("Facebook & WhatsApp", comment: "The title of a list of trackers"),
iconURL: "facebook_icon",
enabled: false,
domains: getDomainBlockList(filename: "facebook_inc"),
- ipRanges: [:])
+ ipRanges: [:],
+ warning: "This list is intended to completely block Facebook-owned apps. Do not enable it if you use apps owned by Facebook like WhatsApp, Facebook Messenger, and Instagram.")
let facebookSDK = LockdownGroup.init(
- version: 20,
+ version: 29,
internalID: "facebook_sdk",
- name: "Facebook SDK",
+ name: NSLocalizedString("Facebook Trackers", comment: "The title of a list of trackers"),
iconURL: "facebook_white_icon",
enabled: true,
domains: getDomainBlockList(filename: "facebook_sdk"),
- ipRanges: [:])
+ ipRanges: [:],
+ accessLevel: "basic")
let marketingScripts = LockdownGroup.init(
- version: 20,
+ version: 32,
internalID: "marketing_scripts",
- name: "Marketing Scripts",
+ name: NSLocalizedString("Marketing Trackers", comment: "The title of a list of trackers"),
iconURL: "marketing_icon",
enabled: true,
domains: getDomainBlockList(filename: "marketing"),
+ ipRanges: [:],
+ accessLevel: "basic")
+
+ let marketingScriptsII = LockdownGroup.init(
+ version: 31,
+ internalID: "marketing_beta_scripts",
+ name: NSLocalizedString("Marketing Trackers II", comment: "The title of a list of trackers"),
+ iconURL: "marketing_icon",
+ enabled: false,
+ domains: getDomainBlockList(filename: "marketing_beta"),
ipRanges: [:])
+
+ let googleShoppingAds = LockdownGroup.init(
+ version: 36,
+ internalID: "google_shopping_ads",
+ name: NSLocalizedString("Google Shopping", comment: "The title of a list of trackers"),
+ iconURL: "google_icon",
+ enabled: false,
+ domains: getDomainBlockList(filename: "google_shopping_ads"),
+ ipRanges: [:],
+ warning: "This blocks background Google tracking, but also blocks the shopping results at the top of Google search results. This is on by default for maximum privacy, but if you like the Google Shopping results, you can turn blocking off.")
- let ransomware = LockdownGroup.init(
- version: 20,
- internalID: "ransomware",
- name: "Ransomware",
- iconURL: "ransomware_icon",
+ let dataTrackers = LockdownGroup.init(
+ version: 36,
+ internalID: "data_trackers",
+ name: NSLocalizedString("Data Trackers", comment: "The title of a list of trackers"),
+ iconURL: "user_data_icon",
+ enabled: true,
+ domains: getDomainBlockList(filename: "data_trackers"),
+ ipRanges: [:],
+ accessLevel: "basic")
+
+ let generalAds = LockdownGroup.init(
+ version: 41,
+ internalID: "general_ads",
+ name: NSLocalizedString("General Marketing", comment: "The title of a list of trackers"),
+ iconURL: "ads_icon",
+ enabled: true,
+ domains: getDomainBlockList(filename: "general_ads"),
+ ipRanges: [:],
+ accessLevel: "basic")
+
+ let reporting = LockdownGroup.init(
+ version: 30,
+ internalID: "reporting",
+ name: NSLocalizedString("Reporting", comment: "The title of a list of trackers"),
+ iconURL: "reporting_icon",
+ enabled: true,
+ domains: getDomainBlockList(filename: "reporting"),
+ ipRanges: [:],
+ accessLevel: "basic")
+
+ let amazonTrackers = LockdownGroup.init(
+ version: 33,
+ internalID: "amazon_trackers",
+ name: NSLocalizedString("Amazon Trackers", comment: "The title of a list of trackers"),
+ iconURL: "amazon_icon",
+ enabled: true,
+ domains: getDomainBlockList(filename: "amazon_trackers"),
+ ipRanges: [:],
+ warning: "This blocks Amazon referral link tracking too, so enabling this list may cause errors when clicking certain links on Amazon.",
+ accessLevel: "basic")
+
+ let ifunnyTrackers = LockdownGroup.init(
+ version: 4,
+ internalID: "ifunnyTrackers",
+ name: NSLocalizedString("iFunny Trackers", comment: "The title of a list of trackers"),
+ iconURL: "icn_ifunny",
enabled: false,
- domains: getDomainBlockList(filename: "ransomware"),
+ domains: getDomainBlockList(filename: "ifunny_trackers"),
ipRanges: [:])
- let defaultLockdownSettings = [clickbait,
- crypto,
- emailOpens,
- facebookInc,
- facebookSDK,
- marketingScripts,
- ransomware];
-
- for var def in defaultLockdownSettings {
- if let current = lockdownBlockedDomains.lockdownDefaults[def.internalID], current.version >= def.version {}
- else {
- if let current = lockdownBlockedDomains.lockdownDefaults[def.internalID] {
- def.enabled = current.enabled // don't replace whether it was disabled
+ let advancedGaming = LockdownGroup.init(
+ version: 5,
+ internalID: "advancedGaming",
+ name: NSLocalizedString("Advanced Gaming", comment: "The title of a list of trackers"),
+ iconURL: "icn_advanced_gaming",
+ enabled: false,
+ domains: getDomainBlockList(filename: "advanced_gaming"),
+ ipRanges: [:])
+
+ let tiktokTrackers = LockdownGroup.init(
+ version: 4,
+ internalID: "tiktokTrackers",
+ name: NSLocalizedString("Tiktok Trackers", comment: "The title of a list of trackers"),
+ iconURL: "icn_tiktok",
+ enabled: false,
+ domains: getDomainBlockList(filename: "tiktok_trackers"),
+ ipRanges: [:])
+
+ let scams = LockdownGroup.init(
+ version: 4,
+ internalID: "scams",
+ name: NSLocalizedString("Scams", comment: "The title of a list of trackers"),
+ iconURL: "icn_scams",
+ enabled: false,
+ domains: getDomainBlockList(filename: "scams"),
+ ipRanges: [:])
+
+ let junesJourneyTrackers = LockdownGroup.init(
+ version: 5,
+ internalID: "junesJourneyTrackers",
+ name: NSLocalizedString("Junes Journey Trackers", comment: "The title of a list of trackers"),
+ iconURL: "icn-junes-journey",
+ enabled: false,
+ domains: getDomainBlockList(filename: "junes_journey_trackers"),
+ ipRanges: [:])
+
+ let advancedAnalytics = LockdownGroup.init(
+ version: 4,
+ internalID: "advancedAnalytics",
+ name: NSLocalizedString("Advanced Analytics", comment: "The title of a list of trackers"),
+ iconURL: "icn_advanced_analytics",
+ enabled: false,
+ domains: getDomainBlockList(filename: "advanced_analytics"),
+ ipRanges: [:])
+
+ let defaultLockdownSettings = [
+ advancedAnalytics,
+ junesJourneyTrackers,
+ scams,
+ tiktokTrackers,
+ advancedGaming,
+ ifunnyTrackers,
+ snapchatAnalytics,
+ gameAds,
+ clickbait,
+ emailOpens,
+ facebookInc,
+ facebookSDK,
+ marketingScripts,
+ marketingScriptsII,
+ googleShoppingAds,
+ dataTrackers,
+ generalAds,
+ reporting,
+ amazonTrackers];
+
+ for var defaultGroup in defaultLockdownSettings {
+ if let current = lockdownBlockedDomains.lockdownDefaults[defaultGroup.internalID], current.version >= defaultGroup.version {
+ // no version change, no action needed
+ } else {
+ if let current = lockdownBlockedDomains.lockdownDefaults[defaultGroup.internalID] {
+ defaultGroup.enabled = current.enabled // don't replace whether it was disabled
}
- lockdownBlockedDomains.lockdownDefaults[def.internalID] = def
+ lockdownBlockedDomains.lockdownDefaults[defaultGroup.internalID] = defaultGroup
}
}
@@ -252,6 +414,7 @@ func getDomainBlockList(filename: String) -> Dictionary {
func getAllBlockedDomains() -> Array {
let lockdownBlockedDomains = getLockdownBlockedDomains()
let userBlockedDomains = getUserBlockedDomains()
+ let userListsBlockedDomains = getBlockedLists()
var allBlockedDomains = Array()
for (_, ldValue) in lockdownBlockedDomains.lockdownDefaults {
@@ -269,9 +432,67 @@ func getAllBlockedDomains() -> Array {
}
}
+ for (_, value) in userListsBlockedDomains.userBlockListsDefaults {
+ if value.enabled {
+ for domain in value.domains {
+ allBlockedDomains.append(domain)
+ }
+ }
+ }
+
return allBlockedDomains
}
+func getTotalEnabled() -> Array {
+ let lockdownBlockedDomains = getLockdownBlockedDomains()
+
+ var total = Array()
+ for (_, ldValue) in lockdownBlockedDomains.lockdownDefaults {
+ if ldValue.enabled {
+ for (key, value) in ldValue.domains {
+ if value {
+ total.append(key)
+ }
+ }
+ }
+ }
+
+ return total
+}
+
+func getTotalDisabled() -> Array {
+ let lockdownBlockedDomains = getLockdownBlockedDomains()
+
+ var total = Array()
+ for (_, ldValue) in lockdownBlockedDomains.lockdownDefaults {
+ if !ldValue.enabled {
+ for (key, value) in ldValue.domains {
+ if value {
+ total.append(key)
+ }
+ }
+ }
+ }
+
+ return total
+}
+
+func getIsCombinedBlockListEmpty() -> Bool {
+ return (getAllBlockedDomains() + getAllWhitelistedDomains()).isEmpty
+}
+
+// MARK: - Curated Lockdown blocked domains
+
+func getLockdownBlockedDomains() -> LockdownDefaults {
+ guard let lockdownDefaultsData = defaults.object(forKey: kLockdownBlockedDomains) as? Data else {
+ return LockdownDefaults(lockdownDefaults: [:])
+ }
+ guard let lockdownDefaults = try? PropertyListDecoder().decode(LockdownDefaults.self, from: lockdownDefaultsData) else {
+ return LockdownDefaults(lockdownDefaults: [:])
+ }
+ return lockdownDefaults
+}
+
// MARK: - User blocked domains
func getUserBlockedDomains() -> Dictionary {
@@ -299,67 +520,185 @@ func deleteUserBlockedDomain(domain: String) {
defaults.set(domains, forKey: kUserBlockedDomains)
}
-// MARK: - Lockdown blocked domains
+// MARK: - User blocked lists
-func getLockdownBlockedDomains() -> LockdownDefaults {
- guard let lockdownDefaultsData = defaults.object(forKey: kLockdownBlockedDomains) as? Data else {
- return LockdownDefaults(lockdownDefaults: [:])
+func getBlockedLists() -> UserBlockListsDefaults {
+ guard let listsDefaultsData = defaults.object(forKey: kUserBlockedLists) as? Data else {
+ return UserBlockListsDefaults(userBlockListsDefaults: [:])
}
- guard let lockdownDefaults = try? PropertyListDecoder().decode(LockdownDefaults.self, from: lockdownDefaultsData) else {
- return LockdownDefaults(lockdownDefaults: [:])
+
+ guard let listDefaults = try? JSONDecoder().decode(UserBlockListsDefaults.self, from: listsDefaultsData) else {
+ return UserBlockListsDefaults(userBlockListsDefaults: [:])
+ }
+ return listDefaults
+}
+
+func addBlockedList(listName: String) {
+ var data = getBlockedLists()
+ if !data.userBlockListsDefaults.keys.contains(listName) {
+ data.userBlockListsDefaults[listName] = UserBlockListsGroup(name: listName, enabled: false)
+ let encodedData = try? JSONEncoder().encode(data)
+ defaults.set(encodedData, forKey: kUserBlockedLists)
}
- return lockdownDefaults
}
-// MARK: - Unused
+func changeBlockedListName(from listName: String, to newListName: String) {
+ var data = getBlockedLists()
+ data.userBlockListsDefaults[newListName] = data.userBlockListsDefaults[listName]
+ data.userBlockListsDefaults[newListName]?.name = newListName
+ data.userBlockListsDefaults[listName] = nil
+ let encodedData = try? JSONEncoder().encode(data)
+ defaults.set(encodedData, forKey: kUserBlockedLists)
+}
+
+func deleteBlockedList(listName: String) {
+ var data = getBlockedLists()
+ data.userBlockListsDefaults[listName] = nil
+ let encodedData = try? JSONEncoder().encode(data)
+ defaults.set(encodedData, forKey: kUserBlockedLists)
+}
+
+func addDomainToBlockedList(domain: String, for listName: String) {
+ var data = getBlockedLists()
+ data.userBlockListsDefaults[listName]?.domains.insert(domain)
+ let encodedData = try? JSONEncoder().encode(data)
+ defaults.set(encodedData, forKey: kUserBlockedLists)
+}
+
+func deleteDoman(domain: String, inBlockedListName listName: String) -> UserBlockListsGroup? {
+ var data = getBlockedLists()
+ guard let index = data.userBlockListsDefaults[listName]?.domains.firstIndex(of: domain) else {
+ return nil
+ }
+ data.userBlockListsDefaults[listName]?.domains.remove(at: index)
+ let encodedData = try? JSONEncoder().encode(data)
+ defaults.set(encodedData, forKey: kUserBlockedLists)
+ return data.userBlockListsDefaults[listName]
+}
+
+extension UserBlockListsGroup {
+ func generateCurrentTimeStamp () -> String {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "yyyy_MM_dd_hh_mm_ss"
+ return (formatter.string(from: Date()) as NSString) as String
+ }
+
+ func exportToURL() -> URL? {
+ var csvString = ""
+
+ for domain in domains {
+ csvString = csvString.appending("\(domain)\r\n")
+ }
+
+ let documents = FileManager.default.urls(
+ for: .documentDirectory,
+ in: .userDomainMask
+ ).first
+
+ let fileName = "LOCKDOWN_\(NSDate.now)"
+
+ guard let path = documents?.appendingPathComponent("/\(fileName).csv") else {
+ return nil
+ }
+
+ do {
+ try csvString.write(to: path, atomically: true, encoding: .utf8)
+ return path
+ } catch {
+ print(error.localizedDescription)
+ return nil
+ }
+
+
+
+
+
+// guard let encoded = try? JSONEncoder().encode(self) else { return nil }
//
-//func getBlockedIPv4List(filename: String) -> Dictionary {
-// var domains = [String : IPRange]()
-// guard let path = Bundle.main.path(forResource: filename, ofType: "txt") else {
-// return domains
-// }
-// do {
-// let content = try String(contentsOfFile:path, encoding: String.Encoding.utf8)
-// let lines = content.components(separatedBy: "\n")
-// for line in lines {
-// // CIDR
-// if line.contains("/") {
-// if let subnetBits = Int(line.split(separator: "/")[1]) {
-// let d = String(line.split(separator: "/")[0])
-// let mask = 0xffffffff ^ ((1 << (32 - subnetBits)) - 1)
-// let subnetMask = String.init(format: "%d.%d.%d.%d", (mask & 0x00ff000000) >> 24, (mask & 0x00ff0000) >> 16, (mask & 0x0000ff00) >> 8, (mask & 0xff))
+// let documents = FileManager.default.urls(
+// for: .documentDirectory,
+// in: .userDomainMask
+// ).first
//
-// domains[d] = IPRange.init(subnetMask: subnetMask, enabled: true, IPv6: false, subnetBits: subnetBits)
-// }
-// }
-// // not CIDR, just feed the IP itself
-// else {
-// domains[line] = IPRange.init(subnetMask: "255.255.255.255", enabled: true, IPv6: false, subnetBits: 0)
-// }
+// guard let path = documents?.appendingPathComponent("/\(fileName).csv") else {
+// return nil
// }
-// } catch _ as NSError {
-// }
-// return domains
-//}
//
-//func getBlockedIPv6List(filename: String) -> Dictionary {
-// var domains = [String : IPRange]()
-// guard let ipv6Path = Bundle.main.path(forResource: filename, ofType: "txt") else {
-// return domains
+// do {
+// try encoded.write(to: path, options: .atomicWrite)
+// print(fileName)
+// return path
+// } catch {
+// print(error.localizedDescription)
+// return nil
+// }
+ }
+
+ static func importData(from url: URL) {
+
+// if #available(iOSApplicationExtension 14.0, *) {
+// let supportedFiles: [UTType] = [UTType.data]
+//
+//// let controller = UIDocumentPickerViewController
+//
+//
+//
+// } else {
+// // Fallback on earlier versions
+// }
+
+ guard let data = try? Data(contentsOf: url)
+// let list = try? JSONDecoder().decode(UserBlockListsGroup.self, from: data)
+
+ else { return }
+ defaults.set(data, forKey: kUserBlockedLists)
+ try? FileManager.default.removeItem(at: url)
+ }
+}
+
+//extension Domains {
+//
+// func generateCurrentTimeStamp () -> String {
+// let formatter = DateFormatter()
+// formatter.dateFormat = "yyyy_MM_dd_hh_mm_ss"
+// return (formatter.string(from: Date()) as NSString) as String
// }
-// do {
-// let content = try String(contentsOfFile:ipv6Path, encoding: String.Encoding.utf8)
-// let lines = content.components(separatedBy: "\n")
-// for line in lines {
-// if line.contains("/") {
-// if let subnetBits = Int(line.split(separator: "/")[1]) {
-// let d = String(line.split(separator: "/")[0])
-// let subnetMask = "\(subnetBits)"
-// domains[d] = IPRange.init(subnetMask: subnetMask, enabled: true, IPv6: true, subnetBits: subnetBits)
-// }
-// }
+//
+// func exportToURL() -> URL? {
+//
+// let timeStamp = generateCurrentTimeStamp()
+// let fileName = "LOCKDOWN_\(NSDate.now)"
+// guard let encoded = try? JSONEncoder().encode(self) else { return nil }
+//
+// let documents = FileManager.default.urls(
+// for: .documentDirectory,
+// in: .userDomainMask
+// ).first
+//
+// guard let path = documents?.appendingPathComponent("/\(fileName).csv") else {
+// return nil
// }
-// } catch _ as NSError {
+//
+// do {
+// try encoded.write(to: path, options: .atomicWrite)
+// print(fileName)
+// return path
+// } catch {
+// print(error.localizedDescription)
+// return nil
+// }
+// }
+//
+// static func importData(from url: URL) {
+// guard
+// let data = try? Data(contentsOf: url),
+// let domain = try? JSONDecoder().decode(Domains.self, from: data)
+//
+//
+// else { return }
+// addDomainToBlockedList(domain: domain.name, for: "oop")
+//
+//
+// try? FileManager.default.removeItem(at: url)
// }
-// return domains
//}
diff --git a/Fonts/Juana-SemiBold.ttf b/Fonts/Juana-SemiBold.ttf
new file mode 100644
index 0000000..417808c
Binary files /dev/null and b/Fonts/Juana-SemiBold.ttf differ
diff --git a/Fonts/KumbhSans-Bold.ttf b/Fonts/KumbhSans-Bold.ttf
new file mode 100644
index 0000000..87891ca
Binary files /dev/null and b/Fonts/KumbhSans-Bold.ttf differ
diff --git a/Fonts/KumbhSans-Regular.ttf b/Fonts/KumbhSans-Regular.ttf
new file mode 100644
index 0000000..55fd144
Binary files /dev/null and b/Fonts/KumbhSans-Regular.ttf differ
diff --git a/Fonts/SF-Pro-Rounded-Bold.otf b/Fonts/SF-Pro-Rounded-Bold.otf
new file mode 100755
index 0000000..b5c2aed
Binary files /dev/null and b/Fonts/SF-Pro-Rounded-Bold.otf differ
diff --git a/Fonts/SF-Pro-Rounded-Medium.otf b/Fonts/SF-Pro-Rounded-Medium.otf
new file mode 100755
index 0000000..1a0a7e4
Binary files /dev/null and b/Fonts/SF-Pro-Rounded-Medium.otf differ
diff --git a/Fonts/SF-Pro-Rounded-Regular.otf b/Fonts/SF-Pro-Rounded-Regular.otf
new file mode 100755
index 0000000..4d32308
Binary files /dev/null and b/Fonts/SF-Pro-Rounded-Regular.otf differ
diff --git a/Fonts/SF-Pro-Rounded-Semibold.otf b/Fonts/SF-Pro-Rounded-Semibold.otf
new file mode 100755
index 0000000..973f92d
Binary files /dev/null and b/Fonts/SF-Pro-Rounded-Semibold.otf differ
diff --git a/Localizable.strings b/Localizable.strings
new file mode 100644
index 0000000..b935fa2
Binary files /dev/null and b/Localizable.strings differ
diff --git a/Lockdown Blocker/BlockerPrivacyInfo.xcprivacy b/Lockdown Blocker/BlockerPrivacyInfo.xcprivacy
new file mode 100644
index 0000000..cfbe279
--- /dev/null
+++ b/Lockdown Blocker/BlockerPrivacyInfo.xcprivacy
@@ -0,0 +1,8 @@
+
+
+
+
+ NSPrivacyTracking
+
+
+
diff --git a/Lockdown Blocker/ContentBlockerRequestHandler.swift b/Lockdown Blocker/ContentBlockerRequestHandler.swift
index 45143ba..6aa80dc 100644
--- a/Lockdown Blocker/ContentBlockerRequestHandler.swift
+++ b/Lockdown Blocker/ContentBlockerRequestHandler.swift
@@ -66,5 +66,4 @@ class ContentBlockerRequestHandler: NSObject, NSExtensionRequestHandling {
context.completeRequest(returningItems: [item], completionHandler: nil)
}
-
}
diff --git a/Lockdown Blocker/Info.plist b/Lockdown Blocker/Info.plist
index 92f18fa..82fabf7 100644
--- a/Lockdown Blocker/Info.plist
+++ b/Lockdown Blocker/Info.plist
@@ -17,9 +17,9 @@
CFBundlePackageType
XPC!
CFBundleShortVersionString
- 0.2.0
+ $(MARKETING_VERSION)
CFBundleVersion
- 16
+ $(CURRENT_PROJECT_VERSION)
NSExtension
NSExtensionPointIdentifier
diff --git a/Lockdown Blocker/Lockdown Blocker.entitlements b/Lockdown Blocker/Lockdown Blocker.entitlements
index 562d4b0..2c41926 100644
--- a/Lockdown Blocker/Lockdown Blocker.entitlements
+++ b/Lockdown Blocker/Lockdown Blocker.entitlements
@@ -2,8 +2,6 @@
- com.apple.developer.icloud-container-identifiers
-
com.apple.developer.ubiquity-kvstore-identifier
$(TeamIdentifierPrefix)$(CFBundleIdentifier)
com.apple.security.application-groups
diff --git a/Lockdown Firewall Today/Base.lproj/MainInterface.storyboard b/Lockdown Firewall Today/Base.lproj/MainInterface.storyboard
index 60321d3..9d8d128 100644
--- a/Lockdown Firewall Today/Base.lproj/MainInterface.storyboard
+++ b/Lockdown Firewall Today/Base.lproj/MainInterface.storyboard
@@ -1,11 +1,9 @@
-
-
-
-
+
+
-
+
@@ -18,14 +16,15 @@
-
+
@@ -86,7 +86,6 @@
-
@@ -103,6 +102,6 @@
-
+
diff --git a/Lockdown Firewall Today/FireWallPrivacyInfo.xcprivacy b/Lockdown Firewall Today/FireWallPrivacyInfo.xcprivacy
new file mode 100644
index 0000000..454777d
--- /dev/null
+++ b/Lockdown Firewall Today/FireWallPrivacyInfo.xcprivacy
@@ -0,0 +1,8 @@
+
+
+
+
+ NSPrivacyTracking
+
+
+
diff --git a/Lockdown Firewall Today/FirewallTodayViewController.swift b/Lockdown Firewall Today/FirewallTodayViewController.swift
index 01374fc..96ffcfb 100644
--- a/Lockdown Firewall Today/FirewallTodayViewController.swift
+++ b/Lockdown Firewall Today/FirewallTodayViewController.swift
@@ -47,26 +47,28 @@ class FirewallTodayViewController: UIViewController, NCWidgetProviding {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
- setupFirewallButtons()
-
- if getUserWantsFirewallEnabled() && FirewallController.shared.status() == .connected {
- DDLogInfo("Widget Firewall Test: user wants firewall enabled and connected, testing blocking with widget")
- _ = Client.getBlockedDomainTest(connectionSuccessHandler: {
- DDLogError("Widget Firewall Test: Connected to \(testFirewallDomain) even though it's supposed to be blocked, restart the Firewall")
- self.restartFirewall()
- }, connectionFailedHandler: {
- error in
- if error != nil {
- let nsError = error! as NSError
+ FirewallController.shared.refreshManager(completion: { error in
+ if let e = error {
+ DDLogError("Error refreshing Manager in background check: \(e)")
+ return
+ }
+ self.setupFirewallButtons()
+ if getUserWantsFirewallEnabled() && (FirewallController.shared.status() == .connected || FirewallController.shared.status() == .invalid) {
+ DDLogInfo("Widget Firewall Test: user wants firewall enabled and connected, testing blocking with widget")
+ Client.getBlockedDomainTest().done {
+ DDLogError("Widget Firewall Test: Connected to \(testFirewallDomain) even though it's supposed to be blocked, restart the Firewall")
+ self.restartFirewall()
+ }.catch { error in
+ let nsError = error as NSError
if nsError.domain == NSURLErrorDomain {
DDLogInfo("Widget Firewall Test: Successful blocking of \(testFirewallDomain) with NSURLErrorDomain error: \(nsError)")
}
else {
- DDLogInfo("Widget Firewall Test: Successful blocking of \(testFirewallDomain), but seeing non-NSURLErrorDomain error: \(error!)")
+ DDLogInfo("Widget Firewall Test: Successful blocking of \(testFirewallDomain), but seeing non-NSURLErrorDomain error: \(error)")
}
}
- })
- }
+ }
+ })
}
func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) {
@@ -83,7 +85,7 @@ class FirewallTodayViewController: UIViewController, NCWidgetProviding {
@objc func updateMetrics() {
DispatchQueue.main.async {
- self.blockedTodayLabel.text = getDayMetricsString() + " Blocked Today"
+ self.blockedTodayLabel.text = getDayMetricsString() + NSLocalizedString(" Blocked Today", comment: "refers to number of connections blocked today, as in '34 Blocked Today'")
}
}
@@ -130,23 +132,23 @@ class FirewallTodayViewController: UIViewController, NCWidgetProviding {
}
func setFirewallButtonConnected() {
- firewallStatusLabel.text = "Firewall Active"
+ firewallStatusLabel.text = NSLocalizedString("Firewall Active", comment: "")
toggleFirewall?.tintColor = .tunnelsBlue
toggleFirewall.layer.borderColor = UIColor.tunnelsBlue.cgColor
}
func setFirewallButtonDisconnected() {
- firewallStatusLabel.text = "Firewall Not Active"
+ firewallStatusLabel.text = NSLocalizedString("Firewall Not Active", comment: "")
toggleFirewall?.tintColor = UIColor.darkGray
toggleFirewall?.layer.borderColor = UIColor.darkGray.cgColor
}
func setFirewallButtonConnecting() {
- firewallStatusLabel.text = "Activating..."
+ firewallStatusLabel.text = NSLocalizedString("Activating", comment: "")
}
func setFirewallButtonDisconnecting() {
- firewallStatusLabel.text = "Deactivating..."
+ firewallStatusLabel.text = NSLocalizedString("Deactivating", comment: "")
}
// MARK: - Helpers
@@ -168,6 +170,7 @@ class FirewallTodayViewController: UIViewController, NCWidgetProviding {
func createRemoteRecord(recordName: String, shouldOpenAppOnFailure: Bool = false) {
let privateDatabase = CKContainer.init(identifier: kICloudContainer).privateCloudDatabase
+ // even though this is deprecated, we're still using this for now out of concerns about compatibility
let myRecord = CKRecord(recordType: recordName, zoneID: CKRecordZone.default().zoneID)
privateDatabase.save(myRecord, completionHandler: ({returnRecord, error in
@@ -178,7 +181,7 @@ class FirewallTodayViewController: UIViewController, NCWidgetProviding {
self.openApp()
}
} else {
- DDLogInfo("Successfully saved record")
+ DDLogInfo("Successfully saved record: \(returnRecord as Any)")
}
}))
}
@@ -190,5 +193,4 @@ class FirewallTodayViewController: UIViewController, NCWidgetProviding {
@IBAction func openLockdown(sender: UIButton) {
openApp()
}
-
}
diff --git a/Lockdown Firewall Today/Info.plist b/Lockdown Firewall Today/Info.plist
index d99f1d6..a9b7927 100644
--- a/Lockdown Firewall Today/Info.plist
+++ b/Lockdown Firewall Today/Info.plist
@@ -17,9 +17,9 @@
CFBundlePackageType
XPC!
CFBundleShortVersionString
- 0.2.0
+ $(MARKETING_VERSION)
CFBundleVersion
- 16
+ $(CURRENT_PROJECT_VERSION)
NSExtension
NSExtensionMainStoryboard
diff --git a/Lockdown Firewall Today/de.lproj/MainInterface.strings b/Lockdown Firewall Today/de.lproj/MainInterface.strings
new file mode 100644
index 0000000..04880b0
--- /dev/null
+++ b/Lockdown Firewall Today/de.lproj/MainInterface.strings
@@ -0,0 +1,8 @@
+/* Class = "UILabel"; text = "Firewall Active"; ObjectID = "1pC-80-fVF"; */
+"1pC-80-fVF.text" = "Firewall Active";
+
+/* Class = "UIButton"; normalTitle = "Open Lockdown"; ObjectID = "PT0-Mi-sJG"; */
+"PT0-Mi-sJG.normalTitle" = "Open Lockdown";
+
+/* Class = "UILabel"; text = "-"; ObjectID = "Vbb-eR-jl5"; */
+"Vbb-eR-jl5.text" = "-";
diff --git a/Lockdown Firewall Today/en.lproj/MainInterface.strings b/Lockdown Firewall Today/en.lproj/MainInterface.strings
new file mode 100644
index 0000000..04880b0
--- /dev/null
+++ b/Lockdown Firewall Today/en.lproj/MainInterface.strings
@@ -0,0 +1,8 @@
+/* Class = "UILabel"; text = "Firewall Active"; ObjectID = "1pC-80-fVF"; */
+"1pC-80-fVF.text" = "Firewall Active";
+
+/* Class = "UIButton"; normalTitle = "Open Lockdown"; ObjectID = "PT0-Mi-sJG"; */
+"PT0-Mi-sJG.normalTitle" = "Open Lockdown";
+
+/* Class = "UILabel"; text = "-"; ObjectID = "Vbb-eR-jl5"; */
+"Vbb-eR-jl5.text" = "-";
diff --git a/Lockdown Firewall Today/es.lproj/MainInterface.strings b/Lockdown Firewall Today/es.lproj/MainInterface.strings
index 9b0029d..173f46f 100644
--- a/Lockdown Firewall Today/es.lproj/MainInterface.strings
+++ b/Lockdown Firewall Today/es.lproj/MainInterface.strings
@@ -1,12 +1,8 @@
+/* Class = "UILabel"; text = "Firewall Active"; ObjectID = "1pC-80-fVF"; */
+"1pC-80-fVF.text" = "Firewall Activo";
-/* Class = "UIButton"; normalTitle = "Speed: Tap to test"; ObjectID = "Apy-GL-W9N"; */
-"Apy-GL-W9N.normalTitle" = "Velocidad: Pulse para probar";
+/* Class = "UIButton"; normalTitle = "Open Lockdown"; ObjectID = "PT0-Mi-sJG"; */
+"PT0-Mi-sJG.normalTitle" = "Abrir Lockdown";
-/* Class = "UIButton"; normalTitle = "Change country"; ObjectID = "Nlw-t0-1Mi"; */
-"Nlw-t0-1Mi.normalTitle" = "Cambiar Pais";
-
-/* Class = "UILabel"; text = "Connected"; ObjectID = "XJH-bI-G1N"; */
-"XJH-bI-G1N.text" = "PROTEGIDO";
-
-/* Class = "UILabel"; text = "IP: Finding..."; ObjectID = "Zgn-9l-eoa"; */
-"Zgn-9l-eoa.text" = "Direccion IP: ...";
+/* Class = "UILabel"; text = "-"; ObjectID = "Vbb-eR-jl5"; */
+"Vbb-eR-jl5.text" = "-";
diff --git a/Lockdown Firewall Today/fr.lproj/MainInterface.strings b/Lockdown Firewall Today/fr.lproj/MainInterface.strings
new file mode 100644
index 0000000..4c7f1f7
--- /dev/null
+++ b/Lockdown Firewall Today/fr.lproj/MainInterface.strings
@@ -0,0 +1,8 @@
+/* Class = "UILabel"; text = "Firewall Active"; ObjectID = "1pC-80-fVF"; */
+"1pC-80-fVF.text" = "Pare-feu Activé";
+
+/* Class = "UIButton"; normalTitle = "Open Lockdown"; ObjectID = "PT0-Mi-sJG"; */
+"PT0-Mi-sJG.normalTitle" = "Ouvrir Lockdown";
+
+/* Class = "UILabel"; text = "-"; ObjectID = "Vbb-eR-jl5"; */
+"Vbb-eR-jl5.text" = "-";
diff --git a/Lockdown Firewall Today/ja.lproj/MainInterface.strings b/Lockdown Firewall Today/ja.lproj/MainInterface.strings
index 3e6c4fc..5df1f02 100644
--- a/Lockdown Firewall Today/ja.lproj/MainInterface.strings
+++ b/Lockdown Firewall Today/ja.lproj/MainInterface.strings
@@ -1,12 +1,8 @@
+/* Class = "UILabel"; text = "Firewall Active"; ObjectID = "1pC-80-fVF"; */
+"1pC-80-fVF.text" = "ファイアウォールがアクティブ";
-/* Class = "UIButton"; normalTitle = "Speed: Tap to test"; ObjectID = "Apy-GL-W9N"; */
-"Apy-GL-W9N.normalTitle" = "速度: タップしてください(テスト)";
+/* Class = "UIButton"; normalTitle = "Open Lockdown"; ObjectID = "PT0-Mi-sJG"; */
+"PT0-Mi-sJG.normalTitle" = "Lockdownを開く";
-/* Class = "UIButton"; normalTitle = "Change country"; ObjectID = "Nlw-t0-1Mi"; */
-"Nlw-t0-1Mi.normalTitle" = "地域を変更する";
-
-/* Class = "UILabel"; text = "Connected"; ObjectID = "XJH-bI-G1N"; */
-"XJH-bI-G1N.text" = "保護されています";
-
-/* Class = "UILabel"; text = "IP: Finding..."; ObjectID = "Zgn-9l-eoa"; */
-"Zgn-9l-eoa.text" = "IP: ...";
+/* Class = "UILabel"; text = "-"; ObjectID = "Vbb-eR-jl5"; */
+"Vbb-eR-jl5.text" = "-";
diff --git a/Lockdown Tunnel/DNSCryptThread.swift b/Lockdown Tunnel/DNSCryptThread.swift
new file mode 100644
index 0000000..9fc4aae
--- /dev/null
+++ b/Lockdown Tunnel/DNSCryptThread.swift
@@ -0,0 +1,76 @@
+//
+// DNSCryptThread.swift
+// LockdowniOS
+//
+// Created by Johnny Lin on 3/31/22.
+// Copyright © 2022 Confirmed Inc. All rights reserved.
+//
+
+import Foundation
+import Dnscryptproxy
+
+let kDNSCryptProxyReady = "DNSCryptProxyReady";
+
+class DNSCryptThread: Thread, DnscryptproxyCloakCallbackProtocol {
+ let dnsApp: DnscryptproxyApp
+
+ init?(arguments: [String]?) {
+ guard let dnsApp = DnscryptproxyMain(arguments?[0]) else { return nil }
+
+ self.dnsApp = dnsApp
+ super.init()
+ name = "DNSCloak"
+ }
+
+ override func main() {
+ dnsApp.run(self)
+ }
+
+ @objc func proxyReady() {
+ NotificationCenter.default.post(name: NSNotification.Name(kDNSCryptProxyReady), object: self)
+ }
+
+ func closeIdleConnections() {
+ dnsApp.closeIdleConnections()
+ }
+
+ func refreshServersInfo() {
+ dnsApp.refreshServersInfo()
+ }
+
+ func stopApp() {
+ do {
+ try dnsApp.stop()
+ } catch {
+ print("Error stopping app")
+ }
+ }
+
+ func logDebug(_ str: String) {
+ dnsApp.logDebug(str)
+ }
+
+ func logInfo(_ str: String) {
+ dnsApp.logInfo(str)
+ }
+
+ func logNotice(_ str: String) {
+ dnsApp.logNotice(str)
+ }
+
+ func logWarn(_ str: String) {
+ dnsApp.logWarn(str)
+ }
+
+ func logError(_ str: String) {
+ dnsApp.logError(str)
+ }
+
+ func logCritical(_ str: String) {
+ dnsApp.logCritical(str)
+ }
+
+ func logFatal(_ str: String) {
+ dnsApp.logFatal(str)
+ }
+}
diff --git a/Lockdown Tunnel/Info.plist b/Lockdown Tunnel/Info.plist
index 96baef4..2d3be5e 100644
--- a/Lockdown Tunnel/Info.plist
+++ b/Lockdown Tunnel/Info.plist
@@ -17,9 +17,9 @@
CFBundlePackageType
XPC!
CFBundleShortVersionString
- 0.2.0
+ $(MARKETING_VERSION)
CFBundleVersion
- 16
+ $(CURRENT_PROJECT_VERSION)
NSExtension
NSExtensionPointIdentifier
diff --git a/Lockdown Tunnel/LockdownTunnelBridgingHeader.h b/Lockdown Tunnel/LockdownTunnelBridgingHeader.h
new file mode 100644
index 0000000..6c60c04
--- /dev/null
+++ b/Lockdown Tunnel/LockdownTunnelBridgingHeader.h
@@ -0,0 +1,14 @@
+//
+// LockdownTunnelBridgingHeader.h
+// LockdownTunnel
+//
+// Created by Johnny Lin on 4/12/22.
+// Copyright © 2022 Confirmed Inc. All rights reserved.
+//
+
+#ifndef LockdownTunnelBridgingHeader_h
+#define LockdownTunnelBridgingHeader_h
+
+#import
+
+#endif /* LockdownTunnelBridgingHeader_h */
diff --git a/Lockdown Tunnel/PacketTunnelProvider.swift b/Lockdown Tunnel/PacketTunnelProvider.swift
index 969d5cb..399d887 100644
--- a/Lockdown Tunnel/PacketTunnelProvider.swift
+++ b/Lockdown Tunnel/PacketTunnelProvider.swift
@@ -7,65 +7,139 @@
import NetworkExtension
import NEKit
-import CocoaLumberjackSwift
+import Dnscryptproxy
+import Network
+import PromiseKit
+import CocoaLumberjack
-class LDObserverFactory: ObserverFactory {
+var latestBlockedDomains = getAllBlockedDomains()
+
+class PacketTunnelProvider: NEPacketTunnelProvider {
- override func getObserverForProxySocket(_ socket: ProxySocket) -> Observer? {
- return LDProxySocketObserver();
- }
+ let dnsServerAddress = "127.0.0.1"
+ var dns: DNSCryptThread?
+
+ let proxyServerAddress = "127.0.0.1"
+ let proxyServerPort: UInt16 = 9090
+ var proxyServer: GCDHTTPProxyServer?
+
+ let monitor = NWPathMonitor()
+ let fileManager = FileManager.default
+ let groupContainer = "group.com.confirmed"
- class LDProxySocketObserver: Observer {
+ let lastReachabilityKillKey = "lastReachabilityKillTime"
+ private var token: NSObjectProtocol?
+ private let center = NotificationCenter.default
+ private var proxyError: Error?
- let whitelistedDomains = getAllWhitelistedDomains()
+ func log(_ str: String) {
+ PacketTunnelProviderLogs.log(str)
+ NSLog("ptplog - " + str)
+ }
+
+ override func cancelTunnelWithError(_ error: Error?) {
+ self.log("===== ERROR - cancelTunnelWithError \(error?.localizedDescription ?? "")")
+ }
+
+ override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
+ log("+++++ startTunnel NEW")
+
+ // reachability check
+ monitor.pathUpdateHandler = { [weak self] path in
+ self?.pathUpdateHandler(path: path)
+ }
- override func signal(_ event: ProxySocketEvent) {
- switch event {
- case .receivedRequest(let session, let socket):
- // this is for testing if the blocking is working correctly - always block this
- if (session.host == testFirewallDomain) {
- socket.forceDisconnect()
- return
- }
- // if domain is in whitelist, just return (user probably didn't whitelist something they want to block
- for whitelistedDomain in whitelistedDomains {
- if (session.host.hasSuffix("." + whitelistedDomain) || session.host == whitelistedDomain) {
- DDLogInfo("whitelisted \(session.host), not blocking")
- return
- }
+
+ log("Calling setTunnelNetworkSettings")
+ initializeDns();
+ initializeProxy();
+
+ setupObserverDNSCryptProxyReady(completionHandler: completionHandler)
+
+ startDns();
+ proxyError = startProxy()
+ }
+
+ private func setupObserverDNSCryptProxyReady(completionHandler: @escaping (Error?) -> Void) {
+ token = center.addObserver(
+ forName: Notification.Name(kDNSCryptProxyReady),
+ object: nil,
+ queue: .main
+ ) { [weak self] notification in
+ self?.dnsCryptProxyReady(completionHandler: completionHandler)
+ }
+ }
+
+ private func dnsCryptProxyReady(completionHandler: @escaping (Error?) -> Void) {
+ log("Found available resolvers, tell iOS we are ready")
+ if let token {
+ center.removeObserver(token)
+ }
+ updateTunnelSetting(completionHandler: completionHandler)
+ let queue = DispatchQueue(label: "Monitor")
+ monitor.start(queue: queue)
+ }
+
+ private func updateTunnelSetting(completionHandler: @escaping (Error?) -> Void) {
+ let networkSettings = getNetworkSettings();
+
+ self.setTunnelNetworkSettings(networkSettings) { [weak self] error in
+ guard let self else { return }
+ if let error {
+ self.log("ERROR - StartTunnel \(error.localizedDescription)")
+ completionHandler(error);
+ } else {
+ self.log("No error on setTunnelNetworkSettings, starting dns and proxy")
+
+ if let proxyError = self.proxyError {
+ self.log("ERROR - Failed to start proxy: \(proxyError)")
+ completionHandler(proxyError)
}
- // else if firewall on, then block
- if (getUserWantsFirewallEnabled()) {
- incrementMetricsAndLog(host: session.host);
- DDLogInfo("session host: \(session.host)")
- socket.forceDisconnect()
- return
+ else {
+ self.log("SUCCESS - startTunnel")
+ completionHandler(nil)
}
- default:
- break;
+ self.proxyError = nil
}
}
-
}
-}
-
-class PacketTunnelProvider: NEPacketTunnelProvider {
-
- let proxyServerPort: UInt16 = 9090;
- let proxyServerAddress = "127.0.0.1";
- var proxyServer: GCDHTTPProxyServer!
+ private func refreshServers() {
+ stopProxyServer()
+ dns?.closeIdleConnections()
+ dns?.refreshServersInfo()
+ initializeProxy()
+ _ = startProxy()
+ }
- //MARK: - OVERRIDES
+ override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
+ self.log("+++++ stopTunnel with reason: \(reason)")
+ monitor.cancel()
+ stopProxyServer()
+ stopDnsServer()
+ self.log("stopTunnel completionHandler, exit")
+ completionHandler();
+ exit(EXIT_SUCCESS);
+ }
+
+// override func wake() {
+// log("===== wake")
+// flushBlockLog(log: log)
+// log("wake setting tunnel network settings to nil")
+// self.setTunnelNetworkSettings(nil, completionHandler: { error in
+// if (error != nil) {
+// self.log("error setting tunnelnetworksettings to nil: \(error)")
+// }
+// self.log("wake calling reactivate tunnel")
+// self.reactivateTunnel()
+// })
+// }
- override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
- if proxyServer != nil {
- proxyServer.stop()
- }
- proxyServer = nil
+ func getNetworkSettings() -> NEPacketTunnelNetworkSettings {
+ log("===== getNetworkSettings")
- let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: proxyServerAddress)
- settings.mtu = NSNumber(value: 1500)
+ let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: dnsServerAddress)
+ networkSettings.mtu = 1500
let proxySettings = NEProxySettings()
proxySettings.httpEnabled = true;
@@ -74,37 +148,387 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
proxySettings.httpsServer = NEProxyServer(address: proxyServerAddress, port: Int(proxyServerPort))
proxySettings.excludeSimpleHostnames = false;
proxySettings.exceptionList = []
- let combined: Array = getAllBlockedDomains() + getAllWhitelistedDomains() + [testFirewallDomain] // probably not blocking whitelisted so this is safe, example.com is used to ensure firewall is still working
- proxySettings.matchDomains = combined
+ proxySettings.matchDomains = getAllWhitelistedDomains()
+ networkSettings.proxySettings = proxySettings;
- settings.dnsSettings = NEDNSSettings(servers: ["127.0.0.1"])
- settings.proxySettings = proxySettings;
- RawSocketFactory.TunnelProvider = self
- ObserverFactory.currentFactory = LDObserverFactory()
+ let dnsSettings = NEDNSSettings(servers: [dnsServerAddress])
+ dnsSettings.matchDomains = [""];
+ networkSettings.dnsSettings = dnsSettings;
- self.setTunnelNetworkSettings(settings, completionHandler: { error in
- self.proxyServer = GCDHTTPProxyServer(address: IPAddress(fromString: self.proxyServerAddress), port: Port(port: self.proxyServerPort))
+ return networkSettings;
+ }
+
+ func initializeAndReturnConfigPath() -> String {
+ log("===== initializeAndReturnConfigPath")
+
+ let configFile = Bundle.main.url(forResource: "dnscrypt-proxy", withExtension: "toml")
+ let sharedDir = fileManager.containerURL(forSecurityApplicationGroupIdentifier: groupContainer)
+
+ // remove blocklist if it exists
+ let newBlocklistFile = sharedDir!.appendingPathComponent("blocklist.txt")
+ if fileManager.fileExists(atPath: newBlocklistFile.path) {
+ log("blocklist.txt exists")
+ do {
+ try fileManager.removeItem(atPath: newBlocklistFile.path)
+ log("removed old blocklist.txt")
+ } catch {
+ log("ERROR - couldnt remove old blocklist.txt: \(error)")
+ }
+ }
+
+ // generate text for new blocklist
+ let blockedDomainsArray = getAllBlockedDomains()
+ var blockedDomains: String = testFirewallDomain
+ for blockedDomain in blockedDomainsArray {
+ blockedDomains = blockedDomains + "\n" + blockedDomain
+ }
+
+ // copy blocklist file into shared dir
+ do {
+ try blockedDomains.write(to: newBlocklistFile, atomically: false, encoding: .utf8)
+ log("wrote content to blocklist.txt")
+ }
+ catch {
+ log("ERROR - couldnt write content to blocklist.txt: \(error)")
+ }
+
+ // clear prefix suffix files
+ let prefixFile = sharedDir!.appendingPathComponent("blacklist.txt.prefixes")
+ let suffixFile = sharedDir!.appendingPathComponent("blacklist.txt.suffixes")
+ if fileManager.fileExists(atPath: prefixFile.path){
+ log("prefix file exists at: \(prefixFile.path)")
do {
- try self.proxyServer.start()
- completionHandler(nil)
+ try fileManager.removeItem(atPath: prefixFile.path)
+ log("prefix file removed at: \(prefixFile.path)")
+ } catch {
}
- catch {
- DDLogError("Error starting proxy server \(error)")
- completionHandler(error)
+ }
+ if fileManager.fileExists(atPath: suffixFile.path){
+ do {
+ try fileManager.removeItem(atPath: suffixFile.path)
+ log("suffix file removed at: \(suffixFile.path)")
+ } catch {
+ log("ERROR - error removing suffix file: \(error)")
}
- })
+ }
+
+ // create new prefix/suffix files
+ let errorPtr: NSErrorPointer = nil
+ log("filling in prefix/suffix files at: \(newBlocklistFile.path)")
+ DnscryptproxyFillPatternlistTrees(newBlocklistFile.path, errorPtr)
+ if let error = errorPtr?.pointee {
+ log("ERROR - filling in prefix/suffix files: \(error)")
+ }
+
+ // read config file template
+ var configFileText = ""
+ do {
+ configFileText = try String(contentsOf: configFile!, encoding: .utf8)
+ log("Read config file template")
+ }
+ catch {
+ log("ERROR - couldn't read config file template text at: \(configFile!.path)")
+ }
+
+ // replace BLOCKLIST_FILE_HERE and BLOCKLIST_LOG_HERE with urls of blocklist file/log
+ let replacedConfig = configFileText.replacingOccurrences(of: "BLOCKLIST_FILE_HERE", with: "\(newBlocklistFile.path)").replacingOccurrences(of: "BLOCKLIST_LOG_HERE", with: "\(sharedDir!.appendingPathComponent("blocklist.log").path)")
+
+ // write replaced string to new file
+ let replacedConfigURL = sharedDir!.appendingPathComponent("replaced-config.toml")
+ log("replaced config file url: \(replacedConfigURL.path)")
+ do {
+ try replacedConfig.write(to: replacedConfigURL, atomically: false, encoding: .utf8)
+ log("replaced config written")
+ }
+ catch {
+ log("ERROR - couldn't write replaced config: \(error)")
+ }
+ log("returning replacedConfigURL \(replacedConfigURL)")
+ return replacedConfigURL.path
}
- override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
- DNSServer.currentServer = nil
- RawSocketFactory.TunnelProvider = nil
- ObserverFactory.currentFactory = nil
+ func initializeDns() {
+ log("===== initialize DNS server")
+ // stopDnsServer()
+ log("initializing DNSCryptThread")
+ dns = DNSCryptThread(arguments: [initializeAndReturnConfigPath()]);
+ }
+
+ func initializeProxy() {
+ log("===== initialize proxy server")
+ // stopProxyServer()
+ log("initializing GCDHTTPProxyServer")
+ proxyServer = GCDHTTPProxyServer(address: IPAddress(fromString: self.proxyServerAddress), port: Port(port: self.proxyServerPort))
+ }
+
+ func startProxy() -> Error? {
+ log("===== startProxy")
+ do {
+ try self.proxyServer?.start()
+ log("started proxyServer")
+ return nil
+ } catch {
+ log("ERROR - couldnt start proxyServer")
+ return error
+ }
+ }
+
+ func startDns() {
+ log("===== startDns")
+ dns?.start()
+ }
+
+ func stopDnsServer() {
+ log("===== stopDnsServer")
+ guard let dns else { return }
+
+ log("dns is not nil")
+ log("dns closing idle connections")
+ dns.closeIdleConnections()
+ log("dns stopApp")
+ dns.stopApp()
+
+ log("dns set to nil")
+ self.dns = nil
+ }
+
+ func stopProxyServer() {
+ log("===== stopProxyServer")
+ guard let proxyServer else { return }
+
+ log("proxyServer is not nil")
+ log("proxyServer stop")
proxyServer.stop()
- proxyServer = nil
- DDLogError("LockdownTunnel: error on stopping: \(reason)")
+
+ log("proxyServer nil")
+ self.proxyServer = nil
+ }
+
+// func reactivateTunnel() {
+// log("===== reactivateTunnel, reasserting true")
+// reasserting = true
+//
+// let networkSettings = getNetworkSettings()
+//
+// self.setTunnelNetworkSettings(networkSettings) { [weak self] error in
+// guard let self else { return }
+// if let error {
+// self.log("ERROR - reactivateTunnel setTunnelNetworkSettings: \(error.localizedDescription)")
+// }
+// self.log("reactivateTunnel setTunnelNetworkSettings complete, reasserting false")
+// self.reasserting = false
+//
+// self._dns.closeIdleConnections()
+// self.log("closed idle connections")
+//
+// self.log("||||| reactivate AFTER - checking availability to apple.com")
+// self.checkNetworkConnection { [weak self] success in
+// guard let self else { return }
+// self.log("ReactivateTunnel checkNetworkConnection result: \(success)")
+// }
+// }
+//
+// startDns()
+// }
+
+
+ // MARK: - reachability
+
+ func pathUpdateHandler(path: Network.NWPath) {
+ log("REACHABILITY - Connected: \(path.status == .satisfied) - NWPATH: \(path.debugDescription)")
+ if path.usesInterfaceType(.wifi) {
+ log("REACHABILITY - have connection to wifi")
+ }
+ if path.usesInterfaceType(.cellular) {
+ log("REACHABILITY - have connection to cellular")
+ }
+ let servers = Resolver().getservers().map(Resolver.getnameinfo)
+ log("REACHABILITY DNS Servers: \(servers)")
- completionHandler()
- exit(EXIT_SUCCESS)
+ log("reachability testing network")
+
+// self.checkNetworkConnection { [weak self] success in
+// guard let self else { return }
+// self.log("reachability network check result: \(success)")
+// if( success == false ) {
+// self.log("ERROR - network check failed, killing PTP if not killed in the last 30 seconds")
+//
+// // only kill PTP if it hasnt been killed in the last 30 seconds - to avoid race conditions/infinite loop
+// // TODO: maybe force VPN restart too?
+// // TODO: maybe force wait a second on stopping?
+// // TODO: make this smarter e.g- if PTP has been killed in the last 30 seconds, wait 10 seconds to kill it
+// let timeIntervalOfLastReachabilityKill = defaults.double(forKey: self.lastReachabilityKillKey)
+// let dateOfLastReachabilityKill = Date(timeIntervalSince1970: timeIntervalOfLastReachabilityKill)
+// let timeSinceLastReachabilityKill = Date().timeIntervalSince(dateOfLastReachabilityKill)
+// self.log("REACHABILITY kill - time since last kill: \(timeSinceLastReachabilityKill)")
+// if (timeSinceLastReachabilityKill < 60) {
+// self.log("REACHABILITY kill - did this < 30 seconds ago, not calling it again")
+// return
+// }
+// else {
+// // do the kill
+// defaults.set(Date().timeIntervalSince1970, forKey: self.lastReachabilityKillKey)
+// }
+// }
+// }
}
+
+ func checkNetworkConnection(callback: @escaping (Bool) -> Void, attempt: Int = 1) {
+ log("===== checkNetworkConnection - attempt #\(attempt)")
+ URLCache.shared.removeAllCachedResponses()
+ firstly {
+ try makeNetworkConnection()
+ }
+ .map { [weak self] data, response -> Void in
+ guard let self else { return }
+ try self.validateNetworkResponse(response: response)
+ callback(true)
+ }
+ .catch { [weak self] error in
+ guard let self else { return }
+ self.log("ERROR - failed checkNetworkConnection attempt #\(attempt): \(error)")
+ if attempt < 3 {
+ DispatchQueue.global(qos: .default).asyncAfter(deadline: DispatchTime.now() + (attempt == 1 ? 5 : 15)) {
+ self.checkNetworkConnection(callback: callback, attempt: attempt + 1)
+ }
+ } else {
+ self.log("ERROR - failed checkNetworkConnection attempt #\(attempt): \(error)")
+ callback(false)
+ }
+ }
+ }
+
+ func makeNetworkConnection() throws -> Promise<(data: Data, response: URLResponse)> {
+ return URLSession.shared.dataTask(.promise, with: try Client.makeGetRequest(urlString: "https://apple.com"))
+ }
+
+ func validateNetworkResponse(response: URLResponse?) throws {
+ self.log("validating checkNetworkConnection response")
+ if let resp = response as? HTTPURLResponse {
+ if (resp.statusCode >= 400 || resp.statusCode <= 0) {
+ self.log("response has bad status code \(resp.statusCode)")
+ throw "response has bad status code \(resp.statusCode)"
+ }
+ else {
+ self.log("response has good status code (2xx, 3xx) and no error code")
+ }
+ }
+ else {
+ throw "Invalid URL Response received: \(String(describing: response))"
+ }
+ }
+
+}
+
+extension PacketTunnelProvider {
+
+ #if DEBUG
+ static let debugLogsKey = AppGroupStorage.Key<[String]>(rawValue: "com.confirmed.packettunnelprovider.debuglogs")
+
+ func debugLog(_ string: String) {
+ let string = "DEBUG LOG \(PacketTunnelProviderLogs.dateFormatter.string(from: Date())) \(string)"
+ if var existing = AppGroupStorage.shared.read(key: PacketTunnelProvider.debugLogsKey) {
+ existing.append(string)
+ AppGroupStorage.shared.write(content: existing, key: PacketTunnelProvider.debugLogsKey)
+ } else {
+ AppGroupStorage.shared.write(content: [string], key: PacketTunnelProvider.debugLogsKey)
+ }
+ }
+
+ func flushDebugLogsToPacketTunnelProviderLogs() {
+ if let existing = AppGroupStorage.shared.read(key: PacketTunnelProvider.debugLogsKey) {
+ for entry in existing {
+ PacketTunnelProviderLogs.log(entry)
+ }
+ AppGroupStorage.shared.delete(forKey: PacketTunnelProvider.debugLogsKey)
+ }
+ }
+ #endif
+}
+
+open class Resolver {
+
+ fileprivate var state = __res_9_state()
+
+ public init() {
+ res_9_ninit(&state)
+ }
+
+ deinit {
+ res_9_ndestroy(&state)
+ }
+
+ public final func getservers() -> [res_9_sockaddr_union] {
+
+ let maxServers = 10
+ var servers = [res_9_sockaddr_union](repeating: res_9_sockaddr_union(), count: maxServers)
+ let found = Int(res_9_getservers(&state, &servers, Int32(maxServers)))
+
+ // filter is to remove the erroneous empty entry when there's no real servers
+ return Array(servers[0 ..< found]).filter() { $0.sin.sin_len > 0 }
+ }
+}
+
+extension Resolver {
+ public static func getnameinfo(_ s: res_9_sockaddr_union) -> String {
+ var s = s
+ var hostBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
+
+ let sinlen = socklen_t(s.sin.sin_len)
+ let _ = withUnsafePointer(to: &s) {
+ $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
+ Darwin.getnameinfo($0, sinlen,
+ &hostBuffer, socklen_t(hostBuffer.count),
+ nil, 0,
+ NI_NUMERICHOST)
+ }
+ }
+
+ return String(cString: hostBuffer)
+ }
+}
+
+extension NEProviderStopReason: CustomDebugStringConvertible {
+
+ public var debugDescription: String {
+ switch self {
+ case .none:
+ return "none"
+ case .userInitiated:
+ return "userInitiated"
+ case .providerFailed:
+ return "providerFailed"
+ case .noNetworkAvailable:
+ return "noNetworkAvailable"
+ case .unrecoverableNetworkChange:
+ return "unrecoverableNetworkChange"
+ case .providerDisabled:
+ return "providerDisabled"
+ case .authenticationCanceled:
+ return "authenticationCanceled"
+ case .configurationFailed:
+ return "configurationFailed"
+ case .idleTimeout:
+ return "idleTimeout"
+ case .configurationDisabled:
+ return "configurationDisabled"
+ case .configurationRemoved:
+ return "configurationRemoved"
+ case .superceded:
+ return "superceded"
+ case .userLogout:
+ return "userLogout"
+ case .userSwitch:
+ return "userSwitch"
+ case .connectionFailed:
+ return "connectionFailed"
+ case .sleep:
+ return "sleep"
+ case .appUpdate:
+ return "appUpdate"
+ case .internalError:
+ return "internalError"
+ }
+ }
}
diff --git a/Lockdown Tunnel/TunnelPrivacyInfo.xcprivacy b/Lockdown Tunnel/TunnelPrivacyInfo.xcprivacy
new file mode 100644
index 0000000..454777d
--- /dev/null
+++ b/Lockdown Tunnel/TunnelPrivacyInfo.xcprivacy
@@ -0,0 +1,8 @@
+
+
+
+
+ NSPrivacyTracking
+
+
+
diff --git a/LockdownFirewallWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/LockdownFirewallWidget/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/LockdownFirewallWidget/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/LockdownFirewallWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/LockdownFirewallWidget/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..9221b9b
--- /dev/null
+++ b/LockdownFirewallWidget/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "83.5x83.5"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/LockdownFirewallWidget/Assets.xcassets/Contents.json b/LockdownFirewallWidget/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/LockdownFirewallWidget/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/LockdownFirewallWidget/Assets.xcassets/Main Background.colorset/Contents.json b/LockdownFirewallWidget/Assets.xcassets/Main Background.colorset/Contents.json
new file mode 100644
index 0000000..36d7d76
--- /dev/null
+++ b/LockdownFirewallWidget/Assets.xcassets/Main Background.colorset/Contents.json
@@ -0,0 +1,31 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "extended-gray",
+ "components" : {
+ "alpha" : "1.000",
+ "white" : "0.930"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "platform" : "osx",
+ "reference" : "underPageBackgroundColor"
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/LockdownFirewallWidget/Assets.xcassets/Panel Background.colorset/Contents.json b/LockdownFirewallWidget/Assets.xcassets/Panel Background.colorset/Contents.json
new file mode 100644
index 0000000..ca11ee7
--- /dev/null
+++ b/LockdownFirewallWidget/Assets.xcassets/Panel Background.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "1.000",
+ "green" : "1.000",
+ "red" : "1.000"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.118",
+ "green" : "0.110",
+ "red" : "0.110"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/LockdownFirewallWidget/Assets.xcassets/Panel Secondary Background.colorset/Contents.json b/LockdownFirewallWidget/Assets.xcassets/Panel Secondary Background.colorset/Contents.json
new file mode 100644
index 0000000..ec3e8d4
--- /dev/null
+++ b/LockdownFirewallWidget/Assets.xcassets/Panel Secondary Background.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "1.000",
+ "green" : "1.000",
+ "red" : "1.000"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "display-p3",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.196",
+ "green" : "0.183",
+ "red" : "0.182"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/LockdownFirewallWidget/Assets.xcassets/Power Button Shadow Color.colorset/Contents.json b/LockdownFirewallWidget/Assets.xcassets/Power Button Shadow Color.colorset/Contents.json
new file mode 100644
index 0000000..742124e
--- /dev/null
+++ b/LockdownFirewallWidget/Assets.xcassets/Power Button Shadow Color.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "0.250",
+ "blue" : "0.000",
+ "green" : "0.000",
+ "red" : "0.000"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.000",
+ "green" : "0.000",
+ "red" : "0.000"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/LockdownFirewallWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/LockdownFirewallWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/LockdownFirewallWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/LockdownFirewallWidget/Assets.xcassets/power.imageset/Contents.json b/LockdownFirewallWidget/Assets.xcassets/power.imageset/Contents.json
new file mode 100644
index 0000000..0a60853
--- /dev/null
+++ b/LockdownFirewallWidget/Assets.xcassets/power.imageset/Contents.json
@@ -0,0 +1,19 @@
+{
+ "images" : [
+ {
+ "filename" : "power.pdf",
+ "idiom" : "universal",
+ "language-direction" : "left-to-right"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "auto-scaling" : "auto",
+ "compression-type" : "lossless",
+ "preserves-vector-representation" : true,
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/LockdownFirewallWidget/Assets.xcassets/power.imageset/power.pdf b/LockdownFirewallWidget/Assets.xcassets/power.imageset/power.pdf
new file mode 100644
index 0000000..6f539f6
Binary files /dev/null and b/LockdownFirewallWidget/Assets.xcassets/power.imageset/power.pdf differ
diff --git a/LockdownFirewallWidget/CombinedProvider.swift b/LockdownFirewallWidget/CombinedProvider.swift
new file mode 100644
index 0000000..758d61e
--- /dev/null
+++ b/LockdownFirewallWidget/CombinedProvider.swift
@@ -0,0 +1,80 @@
+//
+// CombinedProvider.swift
+// LockdownFirewallWidgetExtension
+//
+// Created by Oleg Dreyman on 28.09.2020.
+// Copyright © 2020 Confirmed Inc. All rights reserved.
+//
+
+import Foundation
+import WidgetKit
+
+struct CombinedProvider: TimelineProvider {
+
+ let main: Main
+ let supplemental: Supplemental
+
+ struct Entry: TimelineEntry {
+ var main: Main.Entry
+ var supplemental: Supplemental.Entry
+
+ var date: Date {
+ return min(main.date, supplemental.date)
+ }
+ }
+
+ func placeholder(in context: Context) -> Entry {
+ let leftPlaceholder = main.placeholder(in: context)
+ let rightPlaceholder = supplemental.placeholder(in: context)
+
+ return Entry(main: leftPlaceholder, supplemental: rightPlaceholder)
+ }
+
+ func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) {
+ var leftSnapshot: Main.Entry?
+ var rightSnapshot: Supplemental.Entry?
+ let group = DispatchGroup()
+
+ group.enter()
+ main.getSnapshot(in: context) { (leftEntry) in
+ leftSnapshot = leftEntry
+ group.leave()
+ }
+
+ group.enter()
+ supplemental.getSnapshot(in: context) { (rightEntry) in
+ rightSnapshot = rightEntry
+ group.leave()
+ }
+
+ dispatchPrecondition(condition: .onQueue(.main))
+ group.notify(queue: .main) {
+ completion(Entry(main: leftSnapshot!, supplemental: rightSnapshot!))
+ }
+ }
+
+ func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) {
+ var leftTimeline: Timeline?
+ var rightTimeline: Timeline?
+ let group = DispatchGroup()
+
+ group.enter()
+ main.getTimeline(in: context) { (leftEntry) in
+ leftTimeline = leftEntry
+ group.leave()
+ }
+
+ group.enter()
+ supplemental.getTimeline(in: context) { (rightEntry) in
+ rightTimeline = rightEntry
+ group.leave()
+ }
+
+ dispatchPrecondition(condition: .onQueue(.main))
+ group.notify(queue: .main) {
+ let zippedEntries = zip(leftTimeline!.entries, rightTimeline!.entries)
+ let timeline = Timeline(entries: zippedEntries.map({ Entry.init(main: $0, supplemental: $1) }), policy: leftTimeline!.policy)
+ completion(timeline)
+ }
+ }
+}
diff --git a/LockdownFirewallWidget/FireWallWidgetPrivacyInfo.xcprivacy b/LockdownFirewallWidget/FireWallWidgetPrivacyInfo.xcprivacy
new file mode 100644
index 0000000..454777d
--- /dev/null
+++ b/LockdownFirewallWidget/FireWallWidgetPrivacyInfo.xcprivacy
@@ -0,0 +1,8 @@
+
+
+
+
+ NSPrivacyTracking
+
+
+
diff --git a/ConfirmedTunnel/Info.plist b/LockdownFirewallWidget/Info.plist
similarity index 68%
rename from ConfirmedTunnel/Info.plist
rename to LockdownFirewallWidget/Info.plist
index a2b4380..355748c 100644
--- a/ConfirmedTunnel/Info.plist
+++ b/LockdownFirewallWidget/Info.plist
@@ -5,7 +5,7 @@
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
- ConfirmedTunnel
+ LockdownFirewallWidget
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
@@ -15,17 +15,20 @@
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
- XPC!
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
CFBundleShortVersionString
- 1.0
+ $(MARKETING_VERSION)
CFBundleVersion
- 1
+ $(CURRENT_PROJECT_VERSION)
NSExtension
NSExtensionPointIdentifier
- com.apple.networkextension.packet-tunnel
- NSExtensionPrincipalClass
- $(PRODUCT_MODULE_NAME).PacketTunnelProvider
+ com.apple.widgetkit-extension
+ UIAppFonts
+
+ Montserrat-Medium.ttf
+ Montserrat-Bold.ttf
+
diff --git a/LockdownFirewallWidget/LoadingCircle.swift b/LockdownFirewallWidget/LoadingCircle.swift
new file mode 100644
index 0000000..3aa4c44
--- /dev/null
+++ b/LockdownFirewallWidget/LoadingCircle.swift
@@ -0,0 +1,122 @@
+//
+// LoadingCircle.swift
+// Lockdown
+//
+// Created by Johnny Lin on 1/21/20.
+// Copyright © 2020 Confirmed, Inc. All rights reserved.
+//
+
+import Foundation
+import SwiftUI
+
+struct TunnelState {
+ var color: Color = Color.gray
+ var circleColor: Color = Color.gray
+
+ init() {
+ }
+
+ init(color: Color, circleColor: Color) {
+ self.color = color
+ self.circleColor = circleColor
+ }
+}
+
+struct StatusLabel: View {
+
+ let text: String
+ let color: Color
+
+ var body: some View {
+ Text(text)
+ .font(.system(size: 10, weight: .semibold))
+ .foregroundColor(.white)
+ .padding(EdgeInsets(top: 0, leading: 7, bottom: 0, trailing: 7))
+ .background(color)
+ .overlay(
+ RoundedRectangle(cornerRadius: 3)
+ .stroke(color, lineWidth: 4)
+ )
+ }
+}
+
+struct LoadingCircle: View {
+
+ let side: CGFloat
+ let tunnelState: TunnelState
+ let link: String
+
+ init(tunnelState: TunnelState, side: CGFloat, link: String) {
+ self.tunnelState = tunnelState
+ self.side = side
+ self.link = link
+ }
+
+ var body: some View {
+ ZStack {
+ Circle()
+ .stroke(lineWidth: 2.5)
+ .frame(width: side * 0.4, height: side * 0.4)
+ .padding(4)
+ .foregroundColor(tunnelState.circleColor)
+ .zIndex(10)
+ Circle()
+ .fill()
+ .frame(width: side * 0.4, height: side * 0.4)
+ .shadow(color: .powerButtonShadowColor, radius: 8, x: 0, y: 3.5)
+ .padding(4)
+ .foregroundColor(Color.panelSecondaryBackground)
+ .background(Color.panelBackground)
+ .zIndex(1)
+ Link.init(destination: URL.init(string: self.link)!, label: {
+ Image(uiImage: UIImage(named: "power")!.withRenderingMode(.alwaysTemplate).withTintColor(UIColor(tunnelState.circleColor)).resized(toFit: CGSize(width: side * 0.30, height: side * 0.30)))
+ .foregroundColor(tunnelState.circleColor)
+ .zIndex(40)
+ })
+ }
+ }
+}
+
+struct BlankButtonStyle: ButtonStyle {
+ func makeBody(configuration: Self.Configuration) -> some View {
+ configuration.label
+ }
+}
+
+extension Color {
+ static let confirmedBlue = Color(red: 0/255.0, green: 173/255.0, blue: 231/255.0)
+ static let panelBackground = Color("Panel Background")
+ static let panelSecondaryBackground = Color("Panel Secondary Background")
+ static let powerButtonShadowColor = Color("Power Button Shadow Color")
+ static let mainBackground = Color("Main Background")
+
+ static let lightGray = Color(UIColor.lightGray)
+ static let flatRed = Color(red: 231/255, green: 76/255, blue: 60/255)
+}
+
+extension UIImage {
+ func resized(toFit size: CGSize) -> UIImage {
+ assert(size.width > 0 && size.height > 0, "You cannot safely scale an image to a zero width or height")
+
+ let imageAspectRatio = self.size.width / self.size.height
+ let canvasAspectRatio = size.width / size.height
+
+ var resizeFactor: CGFloat
+
+ if imageAspectRatio > canvasAspectRatio {
+ resizeFactor = size.width / self.size.width
+ } else {
+ resizeFactor = size.height / self.size.height
+ }
+
+ let scaledSize = CGSize(width: self.size.width * resizeFactor, height: self.size.height * resizeFactor)
+
+ UIGraphicsBeginImageContextWithOptions(scaledSize, false, 0.0)
+ draw(in: CGRect(origin: .zero, size: scaledSize))
+
+ let scaledImage = UIGraphicsGetImageFromCurrentImageContext() ?? self
+ UIGraphicsEndImageContext()
+
+ return scaledImage
+ }
+}
diff --git a/LockdownFirewallWidget/LockdownFirewallWidget.swift b/LockdownFirewallWidget/LockdownFirewallWidget.swift
new file mode 100644
index 0000000..2737a75
--- /dev/null
+++ b/LockdownFirewallWidget/LockdownFirewallWidget.swift
@@ -0,0 +1,239 @@
+//
+// LockdownFirewallWidget.swift
+// LockdownFirewallWidget
+//
+// Created by Oleg Dreyman on 25.09.2020.
+// Copyright © 2020 Confirmed Inc. All rights reserved.
+//
+
+import WidgetKit
+import SwiftUI
+
+struct FirewallProvider: TimelineProvider {
+ func placeholder(in context: Context) -> FirewallEntry {
+ FirewallEntry(date: Date(), size: context.displaySize, isFirewallEnabled: false, dayMetricsString: "--")
+ }
+
+ func getSnapshot(in context: Context, completion: @escaping (FirewallEntry) -> ()) {
+ let entry = FirewallEntry(date: Date(), size: context.displaySize, isFirewallEnabled: getUserWantsFirewallEnabled(), dayMetricsString: getDayMetricsString(commas: true))
+ completion(entry)
+ }
+
+ func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) {
+ var entries: [FirewallEntry] = []
+
+ let currentDate = Date()
+ let entry = FirewallEntry(date: Date(), size: context.displaySize, isFirewallEnabled: getUserWantsFirewallEnabled(), dayMetricsString: getDayMetricsString(commas: true))
+ entries.append(entry)
+
+ let timeline = Timeline(
+ entries: entries,
+ policy: .after(Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!)
+ )
+ completion(timeline)
+ }
+}
+
+struct FirewallEntry: TimelineEntry {
+ let date: Date
+ let size: CGSize
+ let isFirewallEnabled: Bool
+ let dayMetricsString: String
+
+ var buttonColor: Color {
+ if isFirewallEnabled {
+ return .confirmedBlue
+ } else {
+ return Color(.systemGray)
+ }
+ }
+}
+
+struct LockdownFirewallWidgetEntryView : View {
+ var entry: FirewallProvider.Entry
+
+ var body: some View {
+ VStack(spacing: 0) {
+ LoadingCircle(
+ tunnelState: TunnelState(
+ color: entry.buttonColor,
+ circleColor: entry.buttonColor
+ ),
+ side: entry.size.height,
+ link: "lockdown://"
+ )
+ .padding(EdgeInsets(top: 12, leading: 0, bottom: 2, trailing: 0))
+ if entry.isFirewallEnabled {
+ StatusLabel(text: NSLocalizedString("FIREWALL ON", comment: ""), color: .confirmedBlue)
+ } else {
+ StatusLabel(text: NSLocalizedString("FIREWALL OFF", comment: ""), color: .flatRed)
+ }
+ if entry.size.height < 160 {
+ Spacer().frame(minHeight: 4)
+ } else {
+ Spacer()
+ }
+ Link(destination: URL(string: "lockdown://showMetrics")!, label: {
+ VStack(spacing: 0) {
+ Text(entry.dayMetricsString)
+ .font(.system(size: 21, weight: .semibold))
+ Text(NSLocalizedString("Blocked Today", comment: ""))
+ .font(.system(size: 12, weight: .medium))
+ }
+ .padding(.bottom, 12)
+ })
+ }.frame(width: entry.size.height, height: entry.size.height)
+ }
+}
+
+struct VPNProvider: TimelineProvider {
+ func placeholder(in context: Context) -> VPNEntry {
+ VPNEntry(date: Date(), size: context.displaySize, isVPNEnabled: false, vpnRegion: VPNRegion())
+ }
+
+ func getSnapshot(in context: Context, completion: @escaping (VPNEntry) -> ()) {
+ let entry = VPNEntry(date: Date(), size: context.displaySize, isVPNEnabled: LatestKnowledge.isVPNEnabled, vpnRegion: getSavedVPNRegion())
+ completion(entry)
+ }
+
+ func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) {
+ var entries: [VPNEntry] = []
+
+ let currentDate = Date()
+ let entry = VPNEntry(date: Date(), size: context.displaySize, isVPNEnabled: LatestKnowledge.isVPNEnabled, vpnRegion: getSavedVPNRegion())
+ entries.append(entry)
+
+ let timeline = Timeline(
+ entries: entries,
+ policy: .after(Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!)
+ )
+ completion(timeline)
+ }
+}
+
+struct VPNEntry: TimelineEntry {
+ let date: Date
+ let size: CGSize
+ let isVPNEnabled: Bool
+ let vpnRegion: VPNRegion
+
+ var buttonColor: Color {
+ if isVPNEnabled {
+ return .confirmedBlue
+ } else {
+ return Color(.systemGray)
+ }
+ }
+}
+
+struct LockdownVPNWidgetEntryView : View {
+ var entry: VPNProvider.Entry
+
+ var body: some View {
+ VStack(spacing: 0) {
+ LoadingCircle(
+ tunnelState: TunnelState(
+ color: entry.buttonColor,
+ circleColor: entry.buttonColor
+ ),
+ side: entry.size.height,
+ link: "lockdown://"
+ )
+ .padding(EdgeInsets(top: 12, leading: 0, bottom: 2, trailing: 0))
+ if entry.isVPNEnabled {
+ StatusLabel(text: NSLocalizedString("Tunnel On", comment: "").uppercased(), color: .confirmedBlue)
+ } else {
+ StatusLabel(text: NSLocalizedString("Tunnel Off", comment: "").uppercased(), color: .flatRed)
+ }
+ if entry.size.height < 160 {
+ Spacer().frame(minHeight: 4)
+ } else {
+ Spacer()
+ }
+ Link(destination: URL(string: "lockdown://changeVPNregion")!, label: {
+ VStack(spacing: 0) {
+ Text(entry.vpnRegion.regionFlagEmoji)
+ .font(.system(size: 21, weight: .semibold))
+ Text(entry.vpnRegion.regionDisplayNameShort)
+ .font(.system(size: 12, weight: .medium))
+ }
+ .padding(.bottom, 12)
+ })
+ }.frame(width: entry.size.height, height: entry.size.height)
+ }
+}
+
+struct CombinedWidgetView: View {
+ let firewall: FirewallEntry
+ let vpn: VPNEntry
+
+ var body: some View {
+ HStack(alignment: .top, spacing: 0) {
+ LockdownFirewallWidgetEntryView(entry: firewall)
+ LockdownVPNWidgetEntryView(entry: vpn)
+ }
+ .frame(minWidth: firewall.size.width, minHeight: firewall.size.height)
+ }
+}
+
+@main
+struct LockdownWidgetBundle: WidgetBundle {
+
+ @WidgetBundleBuilder
+ var body: some Widget {
+ LockdownFirewallWidget()
+ LockdownVPNWidget()
+ LockdownCombinedWidget()
+ }
+}
+
+struct LockdownFirewallWidget: Widget {
+ var body: some WidgetConfiguration {
+ StaticConfiguration(
+ kind: "LockdownFirewallWidget",
+ provider: FirewallProvider(),
+ content: { entry in
+ ZStack {
+ Color.panelBackground
+ LockdownFirewallWidgetEntryView(entry: entry)
+ }
+ }
+ )
+ .configurationDisplayName("Firewall")
+ .supportedFamilies([.systemSmall])
+ }
+}
+
+struct LockdownVPNWidget: Widget {
+ var body: some WidgetConfiguration {
+ StaticConfiguration(
+ kind: "LockdownVPNWidget",
+ provider: VPNProvider(),
+ content: { entry in
+ ZStack {
+ Color.panelBackground
+ LockdownVPNWidgetEntryView(entry: entry)
+ }
+ }
+ )
+ .configurationDisplayName("Secure Tunnel")
+ .supportedFamilies([.systemSmall])
+ }
+}
+
+struct LockdownCombinedWidget: Widget {
+ var body: some WidgetConfiguration {
+ StaticConfiguration(
+ kind: "LockdownCombinedWidget",
+ provider: CombinedProvider(main: FirewallProvider(), supplemental: VPNProvider()),
+ content: { entry in
+ ZStack {
+ Color.panelBackground
+ CombinedWidgetView(firewall: entry.main, vpn: entry.supplemental)
+ }
+ }
+ )
+ .configurationDisplayName("Firewall + Tunnel")
+ .supportedFamilies([.systemMedium])
+ }
+}
diff --git a/ConfirmedTunnel/ConfirmedTunnel.entitlements b/LockdownFirewallWidgetExtension.entitlements
similarity index 53%
rename from ConfirmedTunnel/ConfirmedTunnel.entitlements
rename to LockdownFirewallWidgetExtension.entitlements
index d05c0ec..291240c 100644
--- a/ConfirmedTunnel/ConfirmedTunnel.entitlements
+++ b/LockdownFirewallWidgetExtension.entitlements
@@ -2,11 +2,9 @@
- com.apple.developer.networking.networkextension
+ com.apple.security.application-groups
- app-proxy-provider
- content-filter-provider
- packet-tunnel-provider
+ group.com.confirmed
diff --git a/LockdowniOS.xcodeproj/project.pbxproj b/LockdowniOS.xcodeproj/project.pbxproj
index 4f1ccf6..ed4bc58 100644
--- a/LockdowniOS.xcodeproj/project.pbxproj
+++ b/LockdowniOS.xcodeproj/project.pbxproj
@@ -3,16 +3,35 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 46;
+ objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
- 08799CF7AFE70CC200E47EDB /* Pods_Lockdown_Firewall_Widget.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A890BF9C9CF89A7E923EDDA /* Pods_Lockdown_Firewall_Widget.framework */; };
+ 065F043966BC72234D8E0073 /* Pods_LockdownTunnel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F07BBAE85FFEFFCD9706CF39 /* Pods_LockdownTunnel.framework */; };
+ 1579100974C8086B190B35BB /* Pods-Lockdown VPN Widget-settings-metadata.plist in Resources */ = {isa = PBXBuildFile; fileRef = A19DA148E491FF88E4B0B408 /* Pods-Lockdown VPN Widget-settings-metadata.plist */; };
+ 180AC5905ADA404C13B1D170 /* Pods_Lockdown.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6FEED2B9A31E4C2EF288E61 /* Pods_Lockdown.framework */; };
20816D1FD569053C0994232B /* Pods-Lockdown-metadata.plist in Resources */ = {isa = PBXBuildFile; fileRef = E4A025BF9012D4E6454AE1D6 /* Pods-Lockdown-metadata.plist */; };
+ 388CD7581B88A7E496467546 /* Pods-Lockdown Firewall Widget-settings-metadata.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2DF472CA81A935DEF14D7039 /* Pods-Lockdown Firewall Widget-settings-metadata.plist */; };
+ 3D01D97B2480DCB3003A710C /* data_trackers.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3D01D97A2480DBED003A710C /* data_trackers.txt */; };
+ 3D01D99E2481E42B003A710C /* reporting.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3D01D99D2481E252003A710C /* reporting.txt */; };
+ 3D01D99F2481E42E003A710C /* general_ads.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3D01D99C2481E241003A710C /* general_ads.txt */; };
3D0711B822FE79BE00391C6E /* WhyTrustViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D0711B722FE79BE00391C6E /* WhyTrustViewController.swift */; };
3D0711BB22FE7B5100391C6E /* TitleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D0711BA22FE7B5100391C6E /* TitleViewController.swift */; };
3D0971D822EBAD1000CCD326 /* facebook_sdk.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3D0971D722EBAD1000CCD326 /* facebook_sdk.txt */; };
3D0971DA22EBAD4C00CCD326 /* marketing.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3D0971D922EBAD4C00CCD326 /* marketing.txt */; };
+ 3D3BF4CC233D5E9100D0C482 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3D3BF4D0233D5E9100D0C482 /* Localizable.strings */; };
+ 3D3BF4CD233D5E9100D0C482 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3D3BF4D0233D5E9100D0C482 /* Localizable.strings */; };
+ 3D3BF4CE233D5E9100D0C482 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3D3BF4D0233D5E9100D0C482 /* Localizable.strings */; };
+ 3D40826327F675F6004C146B /* dnscrypt-proxy.toml in Resources */ = {isa = PBXBuildFile; fileRef = 3D40826227F675F6004C146B /* dnscrypt-proxy.toml */; };
+ 3D40826427F675F6004C146B /* dnscrypt-proxy.toml in Resources */ = {isa = PBXBuildFile; fileRef = 3D40826227F675F6004C146B /* dnscrypt-proxy.toml */; };
+ 3D40826527F675F6004C146B /* dnscrypt-proxy.toml in Resources */ = {isa = PBXBuildFile; fileRef = 3D40826227F675F6004C146B /* dnscrypt-proxy.toml */; };
+ 3D40826627F675F6004C146B /* dnscrypt-proxy.toml in Resources */ = {isa = PBXBuildFile; fileRef = 3D40826227F675F6004C146B /* dnscrypt-proxy.toml */; };
+ 3D40826727F675F6004C146B /* dnscrypt-proxy.toml in Resources */ = {isa = PBXBuildFile; fileRef = 3D40826227F675F6004C146B /* dnscrypt-proxy.toml */; };
+ 3D40826927F6A03F004C146B /* DNSCryptThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D40826827F6A03F004C146B /* DNSCryptThread.swift */; };
+ 3D40826A27F6A03F004C146B /* DNSCryptThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D40826827F6A03F004C146B /* DNSCryptThread.swift */; };
+ 3D40826B27F6A03F004C146B /* DNSCryptThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D40826827F6A03F004C146B /* DNSCryptThread.swift */; };
+ 3D40826C27F6A03F004C146B /* DNSCryptThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D40826827F6A03F004C146B /* DNSCryptThread.swift */; };
+ 3D40826D27F6A03F004C146B /* DNSCryptThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D40826827F6A03F004C146B /* DNSCryptThread.swift */; };
3D44378022DFB22600908CDC /* Montserrat-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3D44377A22DFB22600908CDC /* Montserrat-Medium.ttf */; };
3D44378122DFB22600908CDC /* Montserrat-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3D44377B22DFB22600908CDC /* Montserrat-Light.ttf */; };
3D44378222DFB22600908CDC /* Montserrat-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3D44377C22DFB22600908CDC /* Montserrat-Thin.ttf */; };
@@ -58,24 +77,26 @@
3D47CDD422F3C3F3003BD7F7 /* NVActivityIndicatorAnimationLineScalePulseOutRapid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D47CDAC22F3C3F3003BD7F7 /* NVActivityIndicatorAnimationLineScalePulseOutRapid.swift */; };
3D47CDD522F3C3F3003BD7F7 /* NVActivityIndicatorAnimationBallPulseRise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D47CDAD22F3C3F3003BD7F7 /* NVActivityIndicatorAnimationBallPulseRise.swift */; };
3D47CDD622F3C3F3003BD7F7 /* NVActivityIndicatorAnimationOrbit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D47CDAE22F3C3F3003BD7F7 /* NVActivityIndicatorAnimationOrbit.swift */; };
+ 3D4D7FEC247F2435000369FD /* google_shopping_ads.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3D4D7FEB247F22AE000369FD /* google_shopping_ads.txt */; };
3D5464D323037CCA00AE1F73 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3D5464D223037CCA00AE1F73 /* Settings.bundle */; };
3D5464D42303839200AE1F73 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3D5464D223037CCA00AE1F73 /* Settings.bundle */; };
3D5464D52303839400AE1F73 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3D5464D223037CCA00AE1F73 /* Settings.bundle */; };
3D5464D62303839500AE1F73 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3D5464D223037CCA00AE1F73 /* Settings.bundle */; };
3D5561D4230B58F30062001D /* PrivacyPolicyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D5561D3230B58F30062001D /* PrivacyPolicyViewController.swift */; };
+ 3D5F5A0823107C1E004C3860 /* game_ads.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3D5F5A0723107C1E004C3860 /* game_ads.txt */; };
+ 3D5F5A0A23107EB8004C3860 /* snapchat_analytics.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3D5F5A0923107EB8004C3860 /* snapchat_analytics.txt */; };
+ 3D5F5A0C23109ABB004C3860 /* WhatIsVpnViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D5F5A0B23109ABB004C3860 /* WhatIsVpnViewController.swift */; };
+ 3D752C342357FA3B00C163E4 /* SF-Pro-Rounded-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3D752C302357FA3B00C163E4 /* SF-Pro-Rounded-Regular.otf */; };
+ 3D752C352357FA3B00C163E4 /* SF-Pro-Rounded-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3D752C312357FA3B00C163E4 /* SF-Pro-Rounded-Medium.otf */; };
+ 3D752C362357FA3B00C163E4 /* SF-Pro-Rounded-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3D752C322357FA3B00C163E4 /* SF-Pro-Rounded-Bold.otf */; };
+ 3D752C372357FA3B00C163E4 /* SF-Pro-Rounded-Semibold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3D752C332357FA3B00C163E4 /* SF-Pro-Rounded-Semibold.otf */; };
3D94AAF022FD7BFA0012B0DE /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1FCDA6222C7616400C928BC /* NetworkExtension.framework */; };
3D94AAF122FDEAC00012B0DE /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCA4F2D22F190720017740D /* Client.swift */; };
3D94AAF222FDEAC20012B0DE /* ClientModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCA4F3022F190AE0017740D /* ClientModels.swift */; };
- 3D94AAF322FDEAC50012B0DE /* FirewallUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DABD9FE22F7AD4D00480AAC /* FirewallUtilities.swift */; };
3D94AAF422FDEAC80012B0DE /* WhitelistUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBD57A722FBD7A100DE189F /* WhitelistUtilities.swift */; };
3D94AAF522FDEACD0012B0DE /* VPNController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DBA18921B77C80008A9322 /* VPNController.swift */; };
- 3D94AAF622FDEAD60012B0DE /* FirewallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCA4F4022F252720017740D /* FirewallController.swift */; };
3D94AAF722FDEAD70012B0DE /* FirewallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCA4F4022F252720017740D /* FirewallController.swift */; };
3D94AAF822FDEADC0012B0DE /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBD57AF22FC14CC00DE189F /* Shared.swift */; };
- 3D94AAFD22FDEB460012B0DE /* VPNSubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DBA18521B77C66008A9322 /* VPNSubscription.swift */; };
- 3D94AB0D22FE05090012B0DE /* CocoaLumberjack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939E1206D982B0060D945 /* CocoaLumberjack.framework */; };
- 3D94AB0E22FE05090012B0DE /* CocoaLumberjackSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939E2206D982B0060D945 /* CocoaLumberjackSwift.framework */; };
- 3D94AB0F22FE0CF60012B0DE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A1141A1B1F46230500F54698 /* Assets.xcassets */; };
3D94AB1022FE0CFB0012B0DE /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3D94AB0222FDEDEB0012B0DE /* MainInterface.storyboard */; };
3D94AB1222FE3A460012B0DE /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D94AB1122FE3A460012B0DE /* Environment.swift */; };
3D94AB1322FE3BA10012B0DE /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D94AB1122FE3A460012B0DE /* Environment.swift */; };
@@ -83,13 +104,22 @@
3D94AB1522FE3BA40012B0DE /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D94AB1122FE3A460012B0DE /* Environment.swift */; };
3D970DAD22EC149D00F9CC93 /* BlockLogCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D970DAC22EC149D00F9CC93 /* BlockLogCell.swift */; };
3D970DAF22EC15D800F9CC93 /* BlockLogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D970DAE22EC15D800F9CC93 /* BlockLogViewController.swift */; };
+ 3D9FC67723E503DF004122D3 /* EmailSignInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9FC67623E503DF004122D3 /* EmailSignInViewController.swift */; };
+ 3D9FC67923E521DE004122D3 /* ForgotPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9FC67823E521DE004122D3 /* ForgotPasswordViewController.swift */; };
3DAA6B4F22EA76420018FC09 /* clickbait.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3DAA6B4E22EA76420018FC09 /* clickbait.txt */; };
3DAA6B5322EA988F0018FC09 /* ransomware.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3DAA6B5222EA988F0018FC09 /* ransomware.txt */; };
3DABD9FD22F7961F00480AAC /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCA4F2D22F190720017740D /* Client.swift */; };
- 3DABD9FF22F7AD4D00480AAC /* FirewallUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DABD9FE22F7AD4D00480AAC /* FirewallUtilities.swift */; };
- 3DABDA0022F7AD4D00480AAC /* FirewallUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DABD9FE22F7AD4D00480AAC /* FirewallUtilities.swift */; };
- 3DABDA0122F7AD4D00480AAC /* FirewallUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DABD9FE22F7AD4D00480AAC /* FirewallUtilities.swift */; };
3DABDA0222F7DD7700480AAC /* ClientModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCA4F3022F190AE0017740D /* ClientModels.swift */; };
+ 3DAF73522768572300D97BB0 /* FirewallUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAF734C2768572300D97BB0 /* FirewallUtilities.swift */; };
+ 3DAF73532768572300D97BB0 /* FirewallUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAF734C2768572300D97BB0 /* FirewallUtilities.swift */; };
+ 3DAF73542768572300D97BB0 /* FirewallUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAF734C2768572300D97BB0 /* FirewallUtilities.swift */; };
+ 3DAF73552768572300D97BB0 /* FirewallUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAF734C2768572300D97BB0 /* FirewallUtilities.swift */; };
+ 3DAF73562768572300D97BB0 /* FirewallUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAF734C2768572300D97BB0 /* FirewallUtilities.swift */; };
+ 3DAF73602768583700D97BB0 /* OneTimeActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AE079247FF87E0000A7D3 /* OneTimeActions.swift */; };
+ 3DAF73612768584200D97BB0 /* PushNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3E8D20247D8057004B81D6 /* PushNotifications.swift */; };
+ 3DAF73622768584500D97BB0 /* BlockDayLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C6619BB247810E2005E8BB1 /* BlockDayLog.swift */; };
+ 3DAF73632768584D00D97BB0 /* PushNotificationsAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AE072247FD82A0000A7D3 /* PushNotificationsAuthorization.swift */; };
+ 3DAF73642768586200D97BB0 /* WhitelistUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBD57A722FBD7A100DE189F /* WhitelistUtilities.swift */; };
3DAF7C5622F4568C003C8F9C /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCA4F2D22F190720017740D /* Client.swift */; };
3DAF7C5722F456F2003C8F9C /* ClientModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCA4F3022F190AE0017740D /* ClientModels.swift */; };
3DAF907922EFD70200FB29E0 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1FCDA6222C7616400C928BC /* NetworkExtension.framework */; };
@@ -114,6 +144,212 @@
3DCA4F3122F190AE0017740D /* ClientModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCA4F3022F190AE0017740D /* ClientModels.swift */; };
3DCA4F3322F22CB40017740D /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCA4F3222F22CB40017740D /* HomeViewController.swift */; };
3DCA4F4122F252720017740D /* FirewallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCA4F4022F252720017740D /* FirewallController.swift */; };
+ 3DCBC8F22542544A00446C98 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3D3BF4D0233D5E9100D0C482 /* Localizable.strings */; };
+ 3DCFE6FA24493F9000EA9B35 /* marketing_beta.txt in Sources */ = {isa = PBXBuildFile; fileRef = 3DCFE6F924493F9000EA9B35 /* marketing_beta.txt */; };
+ 3DCFE6FB244945A100EA9B35 /* marketing_beta.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3DCFE6F924493F9000EA9B35 /* marketing_beta.txt */; };
+ 3DD545D628068AC5005E140C /* libresolv.9.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DD545CD280681AA005E140C /* libresolv.9.tbd */; };
+ 3DD545DB2808C2F6005E140C /* 5000_dummy_list.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3DD545DA2808C2F6005E140C /* 5000_dummy_list.txt */; };
+ 3DF2455423A2F8A400E46613 /* EmailSignUpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF2455323A2F8A400E46613 /* EmailSignUpViewController.swift */; };
+ 3DF2455623A306DB00E46613 /* Loader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF2455523A306DB00E46613 /* Loader.swift */; };
+ 3DF5D75F2633B1E100F77D79 /* amazon_trackers.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3DF5D75E2633B1E100F77D79 /* amazon_trackers.txt */; };
+ 40098E2A29FDA6A800886474 /* BulletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40098E2929FDA6A800886474 /* BulletView.swift */; };
+ 40098E2C29FDA6CC00886474 /* PaywallDescriptionLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40098E2B29FDA6CC00886474 /* PaywallDescriptionLabel.swift */; };
+ 40098E2E29FDA6E500886474 /* PlanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40098E2D29FDA6E500886474 /* PlanView.swift */; };
+ 40098E3129FDA73300886474 /* VPNPaywallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40098E3029FDA73200886474 /* VPNPaywallViewController.swift */; };
+ 40098E3B29FF378F00886474 /* FirewallPaywallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40098E3529FF378F00886474 /* FirewallPaywallViewController.swift */; };
+ 40098E3C29FF378F00886474 /* AnnualPlanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40098E3829FF378F00886474 /* AnnualPlanView.swift */; };
+ 40098E3D29FF378F00886474 /* MonthlyPlanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40098E3929FF378F00886474 /* MonthlyPlanView.swift */; };
+ 40098E3E29FF378F00886474 /* AdvancedPlansViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40098E3A29FF378F00886474 /* AdvancedPlansViews.swift */; };
+ 4015B4F729EFD9AC004102E0 /* AccessLevelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4015B4F629EFD9AC004102E0 /* AccessLevelView.swift */; };
+ 4015B4FD29F00DD8004102E0 /* LDVpnViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4015B4FC29F00DD8004102E0 /* LDVpnViewController.swift */; };
+ 4015B4FF29F14C95004102E0 /* LDCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4015B4FE29F14C95004102E0 /* LDCardView.swift */; };
+ 4015B50329F16E1A004102E0 /* LDConfigurationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4015B50229F16E1A004102E0 /* LDConfigurationViewController.swift */; };
+ 402BAD252A0B675B009B8820 /* LockedListsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402BAD242A0B675B009B8820 /* LockedListsView.swift */; };
+ 402BAD362A0CD37C009B8820 /* ConnectivityService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402BAD352A0CD37C009B8820 /* ConnectivityService.swift */; };
+ 402BAD382A0CD3B0009B8820 /* ConnectionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402BAD372A0CD3B0009B8820 /* ConnectionState.swift */; };
+ 402D24B829D59B4400A5AB60 /* EmptyListsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D24B729D59B4400A5AB60 /* EmptyListsView.swift */; };
+ 402D24CB29D87B5A00A5AB60 /* ListsSubmenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D24CA29D87B5A00A5AB60 /* ListsSubmenuView.swift */; };
+ 402D24D429D87F4500A5AB60 /* CustomBlockedTableHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D24D329D87F4500A5AB60 /* CustomBlockedTableHeader.swift */; };
+ 402D251629E514CF00A5AB60 /* MoveToListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D251529E514CF00A5AB60 /* MoveToListViewController.swift */; };
+ 402D251929E517E100A5AB60 /* ConfiguredNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D251829E517E100A5AB60 /* ConfiguredNavigationView.swift */; };
+ 402D251B29E519B500A5AB60 /* CustomTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D251A29E519B500A5AB60 /* CustomTableView.swift */; };
+ 402D251F29E52D6A00A5AB60 /* EditDomainsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D251E29E52D6A00A5AB60 /* EditDomainsViewController.swift */; };
+ 402D252129E52D7600A5AB60 /* BottomMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D252029E52D7600A5AB60 /* BottomMenu.swift */; };
+ 402D252329E5473E00A5AB60 /* EditDomainsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D252229E5473E00A5AB60 /* EditDomainsCell.swift */; };
+ 402D252729E5843300A5AB60 /* ImportBlockListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D252629E5843300A5AB60 /* ImportBlockListViewController.swift */; };
+ 402D252929E632F300A5AB60 /* ListSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D252829E632F300A5AB60 /* ListSettingsViewController.swift */; };
+ 402D252B29E6335100A5AB60 /* SwitchBlockingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D252A29E6335100A5AB60 /* SwitchBlockingView.swift */; };
+ 402D252D29E6346900A5AB60 /* ListBlockedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D252C29E6346900A5AB60 /* ListBlockedTableViewCell.swift */; };
+ 402D252F29E6357700A5AB60 /* ListDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D252E29E6357700A5AB60 /* ListDetailViewController.swift */; };
+ 402D253129E635CB00A5AB60 /* DomainsBlockedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D253029E635CB00A5AB60 /* DomainsBlockedTableViewCell.swift */; };
+ 402D253329E6588000A5AB60 /* ListDescriptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D253229E6588000A5AB60 /* ListDescriptionViewController.swift */; };
+ 402D253B29E8F9A400A5AB60 /* JSONSerialization+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D253A29E8F9A400A5AB60 /* JSONSerialization+Extensions.swift */; };
+ 402D254829EE112E00A5AB60 /* LDFirewallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D254729EE112E00A5AB60 /* LDFirewallViewController.swift */; };
+ 402D254A29EE1C6E00A5AB60 /* DescriptionLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D254929EE1C6E00A5AB60 /* DescriptionLabel.swift */; };
+ 402D254E29EE598D00A5AB60 /* TrackersGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D254D29EE598D00A5AB60 /* TrackersGroupView.swift */; };
+ 402D255029EE78D600A5AB60 /* OverallStatiscticView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D254F29EE78D600A5AB60 /* OverallStatiscticView.swift */; };
+ 408E7A9429F88C9200B2F587 /* CustomUISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 408E7A9329F88C9200B2F587 /* CustomUISwitch.swift */; };
+ 408E7A9729FA698C00B2F587 /* UIVIew+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 408E7A9629FA698C00B2F587 /* UIVIew+Extensions.swift */; };
+ 409481AE2A431D78001F11EB /* VPNController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DBA18921B77C80008A9322 /* VPNController.swift */; };
+ 40960AE22A029A7D000F82EB /* UIApplication+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40960AE12A029A7D000F82EB /* UIApplication+Extension.swift */; };
+ 40960AE92A033514000F82EB /* AccessLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40960AE82A033514000F82EB /* AccessLevel.swift */; };
+ 40960AEB2A03396F000F82EB /* ProductPurchasable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40960AEA2A03396F000F82EB /* ProductPurchasable.swift */; };
+ 40960AED2A0339E2000F82EB /* UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40960AEC2A0339E2000F82EB /* UserService.swift */; };
+ 40960AF02A033A41000F82EB /* LockdownUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40960AEF2A033A41000F82EB /* LockdownUser.swift */; };
+ 40960AF22A033AF9000F82EB /* String+Localized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40960AF12A033AF9000F82EB /* String+Localized.swift */; };
+ 40960AFB2A033E46000F82EB /* PaywallService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40960AFA2A033E46000F82EB /* PaywallService.swift */; };
+ 40960B032A033EA9000F82EB /* CountdownDisplayService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40960B022A033EA9000F82EB /* CountdownDisplayService.swift */; };
+ 40960B072A033F0B000F82EB /* WeakObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40960B062A033F0B000F82EB /* WeakObject.swift */; };
+ 40960B0A2A03400E000F82EB /* UserDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40960B092A03400E000F82EB /* UserDefault.swift */; };
+ 40960B0D2A034054000F82EB /* LockdownStorageIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40960B0C2A034054000F82EB /* LockdownStorageIdentifier.swift */; };
+ 40960B0E2A034054000F82EB /* LockdownStorageIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40960B0C2A034054000F82EB /* LockdownStorageIdentifier.swift */; };
+ 40960B152A034400000F82EB /* Keychainable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40960B142A034400000F82EB /* Keychainable.swift */; };
+ 409B59E02A14CB7B0010242C /* SignUpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 409B59DF2A14CB7B0010242C /* SignUpViewController.swift */; };
+ 409B59E42A15CC900010242C /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 409B59E32A15CC900010242C /* WelcomeView.swift */; };
+ 409B59E62A15D00C0010242C /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 409B59E52A15D00C0010242C /* WelcomeViewController.swift */; };
+ 40CC816E2A14B25C00F9805E /* DeleteMyAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC816D2A14B25C00F9805E /* DeleteMyAccountViewController.swift */; };
+ 40CC81702A14B29100F9805E /* EmailComposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC816F2A14B29100F9805E /* EmailComposable.swift */; };
+ 40CC81792A14BA8800F9805E /* EmailAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC81782A14BA8800F9805E /* EmailAddress.swift */; };
+ 40CC817B2A14BAA600F9805E /* EmailValidatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC817A2A14BAA600F9805E /* EmailValidatable.swift */; };
+ 40CC817E2A14BB3600F9805E /* UIView+Corners.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC817D2A14BB3600F9805E /* UIView+Corners.swift */; };
+ 40CC81822A14BD2200F9805E /* DeleteMyAccountViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 40CC81812A14BD2100F9805E /* DeleteMyAccountViewController.xib */; };
+ 40CC849E2A14BEA000F9805E /* SignUpViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 40CC849D2A14BEA000F9805E /* SignUpViewController.xib */; };
+ 40CC84A12A14BECF00F9805E /* EnableNotificationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84A02A14BECF00F9805E /* EnableNotificationsViewController.swift */; };
+ 40CC84A32A14BED800F9805E /* EnableNotificationsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 40CC84A22A14BED800F9805E /* EnableNotificationsViewController.xib */; };
+ 40CC84A52A14BEFA00F9805E /* SplashScreenViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 40CC84A42A14BEFA00F9805E /* SplashScreenViewController.xib */; };
+ 40CC84A82A14C09600F9805E /* String+URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84A72A14C09600F9805E /* String+URL.swift */; };
+ 40CC84AA2A14C0A300F9805E /* String+Attributed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84A92A14C0A200F9805E /* String+Attributed.swift */; };
+ 40CC84AF2A14C0D800F9805E /* Date+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84AB2A14C0D700F9805E /* Date+Ext.swift */; };
+ 40CC84B02A14C0D800F9805E /* CALayer+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84AC2A14C0D800F9805E /* CALayer+Ext.swift */; };
+ 40CC84B12A14C0D800F9805E /* UIStackView+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84AD2A14C0D800F9805E /* UIStackView+Ext.swift */; };
+ 40CC84B22A14C0D800F9805E /* UIAppearance+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84AE2A14C0D800F9805E /* UIAppearance+Ext.swift */; };
+ 40CC84B82A14C0EA00F9805E /* UIViewController+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84B32A14C0E900F9805E /* UIViewController+Ext.swift */; };
+ 40CC84B92A14C0EA00F9805E /* Font+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84B42A14C0E900F9805E /* Font+Ext.swift */; };
+ 40CC84BA2A14C0EA00F9805E /* UICollectionView+Dequeue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84B52A14C0EA00F9805E /* UICollectionView+Dequeue.swift */; };
+ 40CC84BB2A14C0EA00F9805E /* NibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84B62A14C0EA00F9805E /* NibLoadable.swift */; };
+ 40CC84BC2A14C0EA00F9805E /* UIView+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84B72A14C0EA00F9805E /* UIView+Ext.swift */; };
+ 40CC84BE2A14C15400F9805E /* LockdownGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84BD2A14C15400F9805E /* LockdownGradient.swift */; };
+ 40CC84C52A14C2B800F9805E /* FloatingTextInputTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84C12A14C2B700F9805E /* FloatingTextInputTextField.swift */; };
+ 40CC84C62A14C2B800F9805E /* TextBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84C22A14C2B800F9805E /* TextBox.swift */; };
+ 40CC84C72A14C2B800F9805E /* TextInputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84C32A14C2B800F9805E /* TextInputState.swift */; };
+ 40CC84C82A14C2B800F9805E /* TextBoxLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CC84C42A14C2B800F9805E /* TextBoxLabel.swift */; };
+ 40E04A222A26708200000E8C /* WhatsNewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40E04A212A26708200000E8C /* WhatsNewViewController.swift */; };
+ 40E04A242A26758200000E8C /* WhatsNewDescriptionLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40E04A232A26758200000E8C /* WhatsNewDescriptionLabel.swift */; };
+ 40E04A502A28B4A000000E8C /* dnscrypt-proxy.toml in Resources */ = {isa = PBXBuildFile; fileRef = 3D40826227F675F6004C146B /* dnscrypt-proxy.toml */; };
+ 40E04A532A29D79100000E8C /* BlockListContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40E04A522A29D79100000E8C /* BlockListContainerViewController.swift */; };
+ 40E04A552A29D7AB00000E8C /* CuratedListsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40E04A542A29D7AB00000E8C /* CuratedListsViewController.swift */; };
+ 40E04A572A29D7BC00000E8C /* CustomListsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40E04A562A29D7BC00000E8C /* CustomListsViewController.swift */; };
+ 40E04A592A2A1B4C00000E8C /* CTAView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40E04A582A2A1B4C00000E8C /* CTAView.swift */; };
+ 40E7A2F22A0CE8AE00E0231A /* advanced_gaming.txt in Resources */ = {isa = PBXBuildFile; fileRef = 40E7A2EC2A0CE8AE00E0231A /* advanced_gaming.txt */; };
+ 40E7A2F82A0CE92900E0231A /* ifunny_trackers.txt in Resources */ = {isa = PBXBuildFile; fileRef = 40E7A2F32A0CE92800E0231A /* ifunny_trackers.txt */; };
+ 40E7A2F92A0CE92900E0231A /* junes_journey_trackers.txt in Resources */ = {isa = PBXBuildFile; fileRef = 40E7A2F42A0CE92800E0231A /* junes_journey_trackers.txt */; };
+ 40E7A2FA2A0CE92900E0231A /* scams.txt in Resources */ = {isa = PBXBuildFile; fileRef = 40E7A2F52A0CE92800E0231A /* scams.txt */; };
+ 40E7A2FB2A0CE92900E0231A /* tiktok_trackers.txt in Resources */ = {isa = PBXBuildFile; fileRef = 40E7A2F62A0CE92800E0231A /* tiktok_trackers.txt */; };
+ 40E7A2FC2A0CE92900E0231A /* advanced_analytics.txt in Resources */ = {isa = PBXBuildFile; fileRef = 40E7A2F72A0CE92900E0231A /* advanced_analytics.txt */; };
+ 40E7A3012A0E1C7A00E0231A /* SplashScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40E7A3002A0E1C7A00E0231A /* SplashScreenViewController.swift */; };
+ 40FC414329F74C7900BD7396 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FC414229F74C7900BD7396 /* String+Extensions.swift */; };
+ 4A86219093026DE70A097E79 /* Pods-LockdownTests-metadata.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8DA68459884385F76BF86234 /* Pods-LockdownTests-metadata.plist */; };
+ 51006F192CBD0E7400F5142C /* ImageBannerWithTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51006F182CBD0E7400F5142C /* ImageBannerWithTitleView.swift */; };
+ 510AA2F62CB8222100E53560 /* FeedbackPaywallViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510AA2F52CB8222100E53560 /* FeedbackPaywallViewModel.swift */; };
+ 5117DE882CBD4A6600C4A61B /* SectionTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117DE872CBD4A6600C4A61B /* SectionTitleView.swift */; };
+ 5145A19A2CBE37C40074C562 /* FeedbackFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5145A1992CBE37C40074C562 /* FeedbackFlow.swift */; };
+ 51DD89E52CB7DA770028B4FE /* FeedbackPaywallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51DD89E42CB7DA770028B4FE /* FeedbackPaywallViewController.swift */; };
+ 54F0B1A0273200B0002F3630 /* FirewallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCA4F4022F252720017740D /* FirewallController.swift */; };
+ 5647ACFEBBAB001FAE27CAF9 /* Pods-LockdownTunnel-settings-metadata.plist in Resources */ = {isa = PBXBuildFile; fileRef = 6F089C7008AB8F59DE3EA7BD /* Pods-LockdownTunnel-settings-metadata.plist */; };
+ 5666ABC4D0064E4669D1943F /* Pods-LockdownTunnel-metadata.plist in Resources */ = {isa = PBXBuildFile; fileRef = B2AFAE1E2F56A1CA9EC153D4 /* Pods-LockdownTunnel-metadata.plist */; };
+ 5E13011D2D5E0131003896BD /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E13011C2D5E012E003896BD /* OnboardingView.swift */; };
+ 5E9AF7302CF5D198001239A0 /* SpecialOfferPaywallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E9AF72F2CF5D18F001239A0 /* SpecialOfferPaywallView.swift */; };
+ 5E9BD5882D787D8500E8DE4F /* ReviewAlertManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E9BD5852D787D8400E8DE4F /* ReviewAlertManager.swift */; };
+ 5EA97B2F2CF5F83D0082D3FD /* KumbhSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5EA97B2E2CF5F83D0082D3FD /* KumbhSans-Regular.ttf */; };
+ 5EA97B312CF5F83D0082D3FD /* KumbhSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5EA97B2C2CF5F83D0082D3FD /* KumbhSans-Bold.ttf */; };
+ 5EA97B322CF5F83D0082D3FD /* KumbhSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5EA97B2E2CF5F83D0082D3FD /* KumbhSans-Regular.ttf */; };
+ 5EA97B342CF5F83D0082D3FD /* KumbhSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5EA97B2C2CF5F83D0082D3FD /* KumbhSans-Bold.ttf */; };
+ 5EA97B362CF5F86A0082D3FD /* Juana-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5EA97B352CF5F86A0082D3FD /* Juana-SemiBold.ttf */; };
+ 5EA97B372CF5F86A0082D3FD /* Juana-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5EA97B352CF5F86A0082D3FD /* Juana-SemiBold.ttf */; };
+ 5EA97B392CF604F20082D3FD /* SpecialOfferPaywallModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EA97B382CF604D60082D3FD /* SpecialOfferPaywallModel.swift */; };
+ 601BF3ED11EB7CBF95BF5720 /* Pods-Lockdown Firewall Widget-metadata.plist in Resources */ = {isa = PBXBuildFile; fileRef = 12884CAB7C53B842E9E3745C /* Pods-Lockdown Firewall Widget-metadata.plist */; };
+ 78010EFC9ED40D77BD40C924 /* Pods-LockdownTests-settings-metadata.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2ADD2E8AC036859E49987E8B /* Pods-LockdownTests-settings-metadata.plist */; };
+ 7C0156542521C2F200670CB5 /* Montserrat-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3D44377A22DFB22600908CDC /* Montserrat-Medium.ttf */; };
+ 7C0156552521C2F200670CB5 /* Montserrat-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3D44377D22DFB22600908CDC /* Montserrat-Bold.ttf */; };
+ 7C0156562521C2F200670CB5 /* Montserrat-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3D44377E22DFB22600908CDC /* Montserrat-SemiBold.ttf */; };
+ 7C0156572521C2F200670CB5 /* Montserrat-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3D44377F22DFB22600908CDC /* Montserrat-Regular.ttf */; };
+ 7C0156582521C2F200670CB5 /* Montserrat-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3D44377B22DFB22600908CDC /* Montserrat-Light.ttf */; };
+ 7C0156592521C2F200670CB5 /* Montserrat-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3D44377C22DFB22600908CDC /* Montserrat-Thin.ttf */; };
+ 7C0D11122473EE2E00A26E04 /* DomainNameValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0D11112473EE2E00A26E04 /* DomainNameValidator.swift */; };
+ 7C0D111D2473FC7E00A26E04 /* LockdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0D111C2473FC7E00A26E04 /* LockdownTests.swift */; };
+ 7C0D11252473FD6500A26E04 /* DomainNameValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0D11242473FD6500A26E04 /* DomainNameValidatorTests.swift */; };
+ 7C1AE073247FD82A0000A7D3 /* PushNotificationsAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AE072247FD82A0000A7D3 /* PushNotificationsAuthorization.swift */; };
+ 7C1AE075247FE1FB0000A7D3 /* PushNotificationsAuthorizationUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AE074247FE1FB0000A7D3 /* PushNotificationsAuthorizationUI.swift */; };
+ 7C1AE076247FE2000000A7D3 /* PushNotificationsAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AE072247FD82A0000A7D3 /* PushNotificationsAuthorization.swift */; };
+ 7C1AE077247FE2010000A7D3 /* PushNotificationsAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AE072247FD82A0000A7D3 /* PushNotificationsAuthorization.swift */; };
+ 7C1AE078247FE2010000A7D3 /* PushNotificationsAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AE072247FD82A0000A7D3 /* PushNotificationsAuthorization.swift */; };
+ 7C1AE07A247FF87F0000A7D3 /* OneTimeActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AE079247FF87E0000A7D3 /* OneTimeActions.swift */; };
+ 7C1AE07B247FF87F0000A7D3 /* OneTimeActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AE079247FF87E0000A7D3 /* OneTimeActions.swift */; };
+ 7C1AE07C247FF87F0000A7D3 /* OneTimeActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AE079247FF87E0000A7D3 /* OneTimeActions.swift */; };
+ 7C1AE07D247FF87F0000A7D3 /* OneTimeActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AE079247FF87E0000A7D3 /* OneTimeActions.swift */; };
+ 7C1AE080248028F40000A7D3 /* UIKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AE07F248028F40000A7D3 /* UIKit+Extensions.swift */; };
+ 7C3E8D21247D8057004B81D6 /* PushNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3E8D20247D8057004B81D6 /* PushNotifications.swift */; };
+ 7C3E8D22247D8057004B81D6 /* PushNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3E8D20247D8057004B81D6 /* PushNotifications.swift */; };
+ 7C3EFA0224867DEE00719D96 /* TrackerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3EFA0124867DEE00719D96 /* TrackerInfo.swift */; };
+ 7C3EFA042486879800719D96 /* tracker_info.json in Resources */ = {isa = PBXBuildFile; fileRef = 7C3EFA032486879800719D96 /* tracker_info.json */; };
+ 7C422E97252796EE007F9C22 /* StaticTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C422E96252796EE007F9C22 /* StaticTableView.swift */; };
+ 7C422EA525279724007F9C22 /* Align.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C422EA425279724007F9C22 /* Align.swift */; };
+ 7C422EAF252797A6007F9C22 /* AccountVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C422EAE252797A6007F9C22 /* AccountVC.swift */; };
+ 7C422EB72527A2D1007F9C22 /* MainTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C422EB62527A2D1007F9C22 /* MainTabBarViewController.swift */; };
+ 7C44081B2539BCCE003FAD1E /* ProtectedFileAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C44081A2539BCCE003FAD1E /* ProtectedFileAccess.swift */; };
+ 7C44081C2539BCCE003FAD1E /* ProtectedFileAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C44081A2539BCCE003FAD1E /* ProtectedFileAccess.swift */; };
+ 7C44081D2539BCCE003FAD1E /* ProtectedFileAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C44081A2539BCCE003FAD1E /* ProtectedFileAccess.swift */; };
+ 7C44081E2539BCCE003FAD1E /* ProtectedFileAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C44081A2539BCCE003FAD1E /* ProtectedFileAccess.swift */; };
+ 7C4D9BBB252C8748004175EA /* AccountUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C4D9BBA252C8748004175EA /* AccountUI.swift */; };
+ 7C6619BC247810E2005E8BB1 /* BlockDayLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C6619BB247810E2005E8BB1 /* BlockDayLog.swift */; };
+ 7C6619BD247810EE005E8BB1 /* BlockDayLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C6619BB247810E2005E8BB1 /* BlockDayLog.swift */; };
+ 7C6619BE247810EE005E8BB1 /* BlockDayLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C6619BB247810E2005E8BB1 /* BlockDayLog.swift */; };
+ 7C6619BF247810EF005E8BB1 /* BlockDayLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C6619BB247810E2005E8BB1 /* BlockDayLog.swift */; };
+ 7C798A1A25409F8100A99695 /* Mailto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C798A1925409F8100A99695 /* Mailto.swift */; };
+ 7C9A936C251E1EC700DA5721 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C9A936B251E1EC700DA5721 /* WidgetKit.framework */; };
+ 7C9A936E251E1EC700DA5721 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C9A936D251E1EC700DA5721 /* SwiftUI.framework */; };
+ 7C9A9371251E1EC700DA5721 /* LockdownFirewallWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C9A9370251E1EC700DA5721 /* LockdownFirewallWidget.swift */; };
+ 7C9A9373251E1EC700DA5721 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7C9A9372251E1EC700DA5721 /* Assets.xcassets */; };
+ 7C9A9377251E1EC700DA5721 /* LockdownFirewallWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7C9A936A251E1EC700DA5721 /* LockdownFirewallWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 7C9A9384251E1F9C00DA5721 /* LoadingCircle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C9A9383251E1F9C00DA5721 /* LoadingCircle.swift */; };
+ 7CAB283F254336230087AAF4 /* CustomNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CAB283E254336230087AAF4 /* CustomNavigationView.swift */; };
+ 7CC8EFED254036050005054C /* FirewallRepair.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CC8EFEC254036050005054C /* FirewallRepair.swift */; };
+ 7CD1435F248798D4009206A9 /* TrackerInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CD1435E248798D4009206A9 /* TrackerInfoTests.swift */; };
+ 7CD52D81247E850D00D0530F /* SnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CD52D80247E850D00D0530F /* SnapshotTests.swift */; };
+ 7CD52D82247EC18800D0530F /* PushNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3E8D20247D8057004B81D6 /* PushNotifications.swift */; };
+ 7CD52D83247EC18900D0530F /* PushNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C3E8D20247D8057004B81D6 /* PushNotifications.swift */; };
+ 7CE91C592521D54F009D8269 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C582521D54F009D8269 /* UserDefaults.swift */; };
+ 7CE91C602521D564009D8269 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C582521D54F009D8269 /* UserDefaults.swift */; };
+ 7CE91C672521D565009D8269 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C582521D54F009D8269 /* UserDefaults.swift */; };
+ 7CE91C682521D565009D8269 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C582521D54F009D8269 /* UserDefaults.swift */; };
+ 7CE91C692521D566009D8269 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C582521D54F009D8269 /* UserDefaults.swift */; };
+ 7CE91C712521D58C009D8269 /* Metrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C702521D58C009D8269 /* Metrics.swift */; };
+ 7CE91C7E2521D5B6009D8269 /* Metrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C702521D58C009D8269 /* Metrics.swift */; };
+ 7CE91C852521D5B7009D8269 /* Metrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C702521D58C009D8269 /* Metrics.swift */; };
+ 7CE91C862521D5B7009D8269 /* Metrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C702521D58C009D8269 /* Metrics.swift */; };
+ 7CE91C872521D5B8009D8269 /* Metrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C702521D58C009D8269 /* Metrics.swift */; };
+ 7CE91C962521ED5E009D8269 /* VPNRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C952521ED5E009D8269 /* VPNRegion.swift */; };
+ 7CE91C972521ED5E009D8269 /* VPNRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C952521ED5E009D8269 /* VPNRegion.swift */; };
+ 7CE91C982521ED5E009D8269 /* VPNRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C952521ED5E009D8269 /* VPNRegion.swift */; };
+ 7CE91C992521ED5E009D8269 /* VPNRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C952521ED5E009D8269 /* VPNRegion.swift */; };
+ 7CE91C9A2521ED5E009D8269 /* VPNRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91C952521ED5E009D8269 /* VPNRegion.swift */; };
+ 7CE91CA8252214C9009D8269 /* CombinedProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CE91CA7252214C9009D8269 /* CombinedProvider.swift */; };
+ 7F5F41C69EE479F38F028B45 /* Pods_Lockdown_Firewall_Widget.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 171F1A80C5E933902B1708CE /* Pods_Lockdown_Firewall_Widget.framework */; };
+ 90728B81560C790FD5A02A6B /* Pods-Lockdown VPN Widget-metadata.plist in Resources */ = {isa = PBXBuildFile; fileRef = 92D3DD81205F17D004056D79 /* Pods-Lockdown VPN Widget-metadata.plist */; };
+ 92635DD62BF2764E0044673D /* Availability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92635DD52BF2764E0044673D /* Availability.swift */; };
+ 92635DD72BF2764E0044673D /* Availability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92635DD52BF2764E0044673D /* Availability.swift */; };
+ 92635DD82BF2764E0044673D /* Availability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92635DD52BF2764E0044673D /* Availability.swift */; };
+ 92635DDA2BF2764E0044673D /* Availability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92635DD52BF2764E0044673D /* Availability.swift */; };
+ 92635DDC2BF2764E0044673D /* Availability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92635DD52BF2764E0044673D /* Availability.swift */; };
+ 92635DDF2BF2780A0044673D /* Availability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92635DD52BF2764E0044673D /* Availability.swift */; };
+ 92635DE12BF784FF0044673D /* BlockerPrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 92635DE02BF784FF0044673D /* BlockerPrivacyInfo.xcprivacy */; };
+ 92635DE32BF786330044673D /* FireWallPrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 92635DE22BF786330044673D /* FireWallPrivacyInfo.xcprivacy */; };
+ 92635DE52BF7869C0044673D /* TunnelPrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 92635DE42BF7869C0044673D /* TunnelPrivacyInfo.xcprivacy */; };
+ 92635DE82BF787220044673D /* WidgetExtensionPrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 92635DE72BF787220044673D /* WidgetExtensionPrivacyInfo.xcprivacy */; };
+ 92635DEA2BF788E50044673D /* FireWallWidgetPrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 92635DE92BF788E50044673D /* FireWallWidgetPrivacyInfo.xcprivacy */; };
+ 92635DEC2BF78A9F0044673D /* VPNVidgetPrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 92635DEB2BF78A9F0044673D /* VPNVidgetPrivacyInfo.xcprivacy */; };
+ 92CCC17B2BEE40C900C38E1C /* ProductButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92CCC17A2BEE40C900C38E1C /* ProductButton.swift */; };
+ 92CCC17D2BF22ABE00C38E1C /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 92CCC17C2BF22ABE00C38E1C /* PrivacyInfo.xcprivacy */; };
A101106D202B9D4300807612 /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A101106C202B9D4300807612 /* BaseViewController.swift */; };
A1141A151F46230500F54698 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1141A141F46230500F54698 /* AppDelegate.swift */; };
A1141A1A1F46230500F54698 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A1141A181F46230500F54698 /* Main.storyboard */; };
@@ -128,27 +364,14 @@
A118F64520B33FED009A64E7 /* TimerEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = A118F63E20B33FED009A64E7 /* TimerEx.swift */; };
A118F64720B33FED009A64E7 /* SpinerLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A118F63F20B33FED009A64E7 /* SpinerLayer.swift */; };
A118F64920B33FED009A64E7 /* CGRectEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = A118F64020B33FED009A64E7 /* CGRectEx.swift */; };
- A12186271FB8F691007058B3 /* SignupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12186261FB8F691007058B3 /* SignupViewController.swift */; };
A12229AB22C014CB00BFF624 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A12229AA22C014CA00BFF624 /* StoreKit.framework */; };
A12473F41FE44285008493B8 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1912FE91F58B2D00007F6D4 /* NotificationCenter.framework */; };
A12473F71FE44285008493B8 /* VPNTodayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12473F61FE44285008493B8 /* VPNTodayViewController.swift */; };
A12473FA1FE44285008493B8 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A12473F81FE44285008493B8 /* MainInterface.storyboard */; };
A12473FE1FE44285008493B8 /* Lockdown VPN Widget.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A12473F31FE44284008493B8 /* Lockdown VPN Widget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
- A1342E8C20B0B87D0045E9DF /* CocoaLumberjackSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939E2206D982B0060D945 /* CocoaLumberjackSwift.framework */; };
- A1342E8D20B0B8870045E9DF /* CocoaLumberjack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939E1206D982B0060D945 /* CocoaLumberjack.framework */; };
A1359FDA20AF6E32008C4BF7 /* LocalLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1359FD920AF6E31008C4BF7 /* LocalLogger.swift */; };
A154A07E215C78180010FFCC /* BlockListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A154A07D215C78180010FFCC /* BlockListCell.swift */; };
A154A080215C7A8C0010FFCC /* BlockListAddCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A154A07F215C7A8C0010FFCC /* BlockListAddCell.swift */; };
- A15939C0206D965D0060D945 /* tun2socks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939B9206D965C0060D945 /* tun2socks.framework */; };
- A15939C1206D965D0060D945 /* lwip.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939BA206D965D0060D945 /* lwip.framework */; };
- A15939C2206D965D0060D945 /* MMDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939BB206D965D0060D945 /* MMDB.framework */; };
- A15939C3206D965D0060D945 /* NEKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939BC206D965D0060D945 /* NEKit.framework */; };
- A15939C4206D965D0060D945 /* Resolver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939BD206D965D0060D945 /* Resolver.framework */; };
- A15939C5206D965D0060D945 /* Yaml.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939BE206D965D0060D945 /* Yaml.framework */; };
- A15939C6206D965D0060D945 /* Sodium.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939BF206D965D0060D945 /* Sodium.framework */; };
- A15939E3206D982B0060D945 /* CocoaAsyncSocket.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939E0206D982B0060D945 /* CocoaAsyncSocket.framework */; };
- A15939E4206D982B0060D945 /* CocoaLumberjack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939E1206D982B0060D945 /* CocoaLumberjack.framework */; };
- A15939E5206D982B0060D945 /* CocoaLumberjackSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939E2206D982B0060D945 /* CocoaLumberjackSwift.framework */; };
A15F3C751F79DC8F00B07F03 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A15F3C731F79D90500B07F03 /* LaunchScreen.storyboard */; };
A174CCAE22B15B1000F1B840 /* BlockListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A174CCAD22B15B1000F1B840 /* BlockListViewController.swift */; };
A18B31F92087ED7900C0FFAA /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E78D12207BE58C007FAE70 /* CloudKit.framework */; };
@@ -162,9 +385,7 @@
A1DBA18621B77C66008A9322 /* VPNSubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DBA18521B77C66008A9322 /* VPNSubscription.swift */; };
A1DBA18A21B77C80008A9322 /* VPNController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DBA18921B77C80008A9322 /* VPNController.swift */; };
A1DBA18B21B77C88008A9322 /* VPNController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DBA18921B77C80008A9322 /* VPNController.swift */; };
- A1DBA18E21B77C8E008A9322 /* VPNSubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DBA18521B77C66008A9322 /* VPNSubscription.swift */; };
A1DBA19621B82F73008A9322 /* LICENSE.md in Resources */ = {isa = PBXBuildFile; fileRef = A1DBA19521B82F72008A9322 /* LICENSE.md */; };
- A1DD82BE1FE446CA00482632 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A1141A1B1F46230500F54698 /* Assets.xcassets */; };
A1E7481A1F9108B6004B8021 /* SpeedTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1E748191F9108B6004B8021 /* SpeedTest.swift */; };
A1E78D13207BE58C007FAE70 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E78D12207BE58C007FAE70 /* CloudKit.framework */; };
A1EBEACB2097AE6E002B9087 /* M13CheckboxDisclosurePathGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1EBEAB82097AE5B002B9087 /* M13CheckboxDisclosurePathGenerator.swift */; };
@@ -188,16 +409,6 @@
A1EBEADD2097AE6E002B9087 /* M13CheckboxStrokeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1EBEACA2097AE6D002B9087 /* M13CheckboxStrokeController.swift */; };
A1FCDA4422C0651300C928BC /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1FCDA4322C0651300C928BC /* PacketTunnelProvider.swift */; };
A1FCDA4922C0651300C928BC /* LockdownTunnel.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A1FCDA4122C0651300C928BC /* LockdownTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
- A1FCDA4E22C0666A00C928BC /* CocoaAsyncSocket.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939E0206D982B0060D945 /* CocoaAsyncSocket.framework */; };
- A1FCDA4F22C066B900C928BC /* CocoaLumberjack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939E1206D982B0060D945 /* CocoaLumberjack.framework */; };
- A1FCDA5022C066B900C928BC /* CocoaLumberjackSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939E2206D982B0060D945 /* CocoaLumberjackSwift.framework */; };
- A1FCDA5122C066B900C928BC /* lwip.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939BA206D965D0060D945 /* lwip.framework */; };
- A1FCDA5222C066B900C928BC /* MMDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939BB206D965D0060D945 /* MMDB.framework */; };
- A1FCDA5322C066B900C928BC /* Resolver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939BD206D965D0060D945 /* Resolver.framework */; };
- A1FCDA5422C066B900C928BC /* Sodium.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939BF206D965D0060D945 /* Sodium.framework */; };
- A1FCDA5522C066B900C928BC /* tun2socks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939B9206D965C0060D945 /* tun2socks.framework */; };
- A1FCDA5622C066B900C928BC /* Yaml.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939BE206D965D0060D945 /* Yaml.framework */; };
- A1FCDA5722C066F300C928BC /* NEKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A15939BC206D965D0060D945 /* NEKit.framework */; };
A1FCDA5D22C1301A00C928BC /* BlockListGroupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1FCDA5C22C1301900C928BC /* BlockListGroupViewController.swift */; };
A1FCDA5F22C14EB800C928BC /* BlockListGroupCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1FCDA5E22C14EB800C928BC /* BlockListGroupCell.swift */; };
A1FCDA6322C7616400C928BC /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1FCDA6222C7616400C928BC /* NetworkExtension.framework */; };
@@ -207,9 +418,79 @@
A1FCDA8B22D3BA1900C928BC /* facebook_inc.txt in Resources */ = {isa = PBXBuildFile; fileRef = A1FCDA8922D3BA1900C928BC /* facebook_inc.txt */; };
A1FCDA8D22D3C50A00C928BC /* email_opens.txt in Resources */ = {isa = PBXBuildFile; fileRef = A1FCDA8C22D3C50A00C928BC /* email_opens.txt */; };
A1FCDA9122D3D52C00C928BC /* facebook_inc_ipv6.txt in Resources */ = {isa = PBXBuildFile; fileRef = A1FCDA9022D3D52C00C928BC /* facebook_inc_ipv6.txt */; };
- D37198BBCE0A226BD9071F4A /* Pods_LockdownTunnel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A0C2DF90344891424A626067 /* Pods_LockdownTunnel.framework */; };
- E0C51E8814F43CD752AB740D /* Pods_Lockdown.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 31E2DCBA5F0A1C82E81F2D44 /* Pods_Lockdown.framework */; };
- F6529FCC7BC553DB6372DE40 /* Pods_Lockdown_VPN_Widget.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B555BB9C945AD99E970BE3A /* Pods_Lockdown_VPN_Widget.framework */; };
+ B1062A2D2A447B2F00FA9E8B /* RadioSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1062A2C2A447B2F00FA9E8B /* RadioSwitcher.swift */; };
+ B1062A2F2A448F1700FA9E8B /* SelectableRadioSwitcherWithTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1062A2E2A448F1700FA9E8B /* SelectableRadioSwitcherWithTitle.swift */; };
+ B1062A312A449F5200FA9E8B /* TitleAndSubtitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1062A302A449F5200FA9E8B /* TitleAndSubtitleView.swift */; };
+ B1062A332A459AC800FA9E8B /* TextViewWithPlaceholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1062A322A459AC800FA9E8B /* TextViewWithPlaceholder.swift */; };
+ B1062A362A45BD7000FA9E8B /* StepsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1062A352A45BD7000FA9E8B /* StepsViewModel.swift */; };
+ B1062A382A45BEBE00FA9E8B /* WhatProblemStepViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1062A372A45BEBE00FA9E8B /* WhatProblemStepViewModel.swift */; };
+ B157DE312A56B7F7003BA0AB /* CodableUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = B157DE302A56B7F7003BA0AB /* CodableUserDefaults.swift */; };
+ B163A2F72A2F29A100FD7C5E /* CocoaAsyncSocket.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A2F62A2F29A000FD7C5E /* CocoaAsyncSocket.xcframework */; };
+ B163A2F82A2F29A100FD7C5E /* CocoaAsyncSocket.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B163A2F62A2F29A000FD7C5E /* CocoaAsyncSocket.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ B163A2F92A2F29BB00FD7C5E /* CocoaAsyncSocket.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A2F62A2F29A000FD7C5E /* CocoaAsyncSocket.xcframework */; };
+ B163A2FF2A2F2A1800FD7C5E /* CocoaLumberjack.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A2FC2A2F29FC00FD7C5E /* CocoaLumberjack.xcframework */; };
+ B163A3002A2F2A1800FD7C5E /* CocoaLumberjack.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B163A2FC2A2F29FC00FD7C5E /* CocoaLumberjack.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ B163A3012A2F2A3200FD7C5E /* CocoaLumberjack.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A2FC2A2F29FC00FD7C5E /* CocoaLumberjack.xcframework */; };
+ B163A3042A2F2A4900FD7C5E /* CocoaLumberjack.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A2FC2A2F29FC00FD7C5E /* CocoaLumberjack.xcframework */; };
+ B163A3072A2F2A6000FD7C5E /* CocoaLumberjack.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A2FC2A2F29FC00FD7C5E /* CocoaLumberjack.xcframework */; };
+ B163A30A2A2F2A7300FD7C5E /* CocoaLumberjack.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A2FC2A2F29FC00FD7C5E /* CocoaLumberjack.xcframework */; };
+ B163A30E2A2F2AB500FD7C5E /* CocoaLumberjackSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A30D2A2F2AB500FD7C5E /* CocoaLumberjackSwift.xcframework */; };
+ B163A30F2A2F2AB500FD7C5E /* CocoaLumberjackSwift.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B163A30D2A2F2AB500FD7C5E /* CocoaLumberjackSwift.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ B163A3102A2F2ACB00FD7C5E /* CocoaLumberjackSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A30D2A2F2AB500FD7C5E /* CocoaLumberjackSwift.xcframework */; };
+ B163A3132A2F2AE300FD7C5E /* CocoaLumberjackSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A30D2A2F2AB500FD7C5E /* CocoaLumberjackSwift.xcframework */; };
+ B163A3162A2F2AF600FD7C5E /* CocoaLumberjackSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A30D2A2F2AB500FD7C5E /* CocoaLumberjackSwift.xcframework */; };
+ B163A3192A2F2B0800FD7C5E /* CocoaLumberjackSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A30D2A2F2AB500FD7C5E /* CocoaLumberjackSwift.xcframework */; };
+ B163A31D2A2F2B4000FD7C5E /* lwip.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A31C2A2F2B4000FD7C5E /* lwip.xcframework */; };
+ B163A31E2A2F2B4000FD7C5E /* lwip.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B163A31C2A2F2B4000FD7C5E /* lwip.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ B163A31F2A2F2B5200FD7C5E /* lwip.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A31C2A2F2B4000FD7C5E /* lwip.xcframework */; };
+ B163A3222A2F2B6800FD7C5E /* lwip.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A31C2A2F2B4000FD7C5E /* lwip.xcframework */; };
+ B163A3252A2F2B7900FD7C5E /* lwip.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A31C2A2F2B4000FD7C5E /* lwip.xcframework */; };
+ B163A3282A2F2B9300FD7C5E /* lwip.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A31C2A2F2B4000FD7C5E /* lwip.xcframework */; };
+ B163A32E2A2F2C0C00FD7C5E /* Resolver.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A32B2A2F2BE700FD7C5E /* Resolver.xcframework */; };
+ B163A3322A2F2C7800FD7C5E /* NEKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A3312A2F2C7800FD7C5E /* NEKit.xcframework */; };
+ B163A3332A2F2C7900FD7C5E /* NEKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B163A3312A2F2C7800FD7C5E /* NEKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ B163A3342A2F2C8B00FD7C5E /* NEKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A3312A2F2C7800FD7C5E /* NEKit.xcframework */; };
+ B163A3382A2F2CBC00FD7C5E /* tun2socks.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A3372A2F2CBC00FD7C5E /* tun2socks.xcframework */; };
+ B163A3392A2F2CBC00FD7C5E /* tun2socks.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B163A3372A2F2CBC00FD7C5E /* tun2socks.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ B163A33A2A2F2CD100FD7C5E /* tun2socks.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A3372A2F2CBC00FD7C5E /* tun2socks.xcframework */; };
+ B163A3402A2F498600FD7C5E /* Dnscryptproxy.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A33F2A2F498600FD7C5E /* Dnscryptproxy.xcframework */; };
+ B163A3422A2F4AE800FD7C5E /* Dnscryptproxy.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A33F2A2F498600FD7C5E /* Dnscryptproxy.xcframework */; };
+ B163A3452A2F4B6B00FD7C5E /* Dnscryptproxy.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A33F2A2F498600FD7C5E /* Dnscryptproxy.xcframework */; };
+ B163A3482A2F4B8300FD7C5E /* Dnscryptproxy.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A33F2A2F498600FD7C5E /* Dnscryptproxy.xcframework */; };
+ B163A34B2A2F4BBA00FD7C5E /* Dnscryptproxy.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A33F2A2F498600FD7C5E /* Dnscryptproxy.xcframework */; };
+ B163A34E2A2F4C2C00FD7C5E /* Resolver.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B163A32B2A2F2BE700FD7C5E /* Resolver.xcframework */; };
+ B163A34F2A2F4C2C00FD7C5E /* Resolver.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B163A32B2A2F2BE700FD7C5E /* Resolver.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ B17492C72B87424E005D9601 /* PaywallViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17492C62B87424E005D9601 /* PaywallViewModel.swift */; };
+ B17492C92B8742B6005D9601 /* PaywallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17492C82B8742B6005D9601 /* PaywallView.swift */; };
+ B1A01CA52A4328E1004D43EE /* StepsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A01CA42A4328E1004D43EE /* StepsViewController.swift */; };
+ B1A01CA92A432926004D43EE /* StepModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A01CA82A432926004D43EE /* StepModel.swift */; };
+ B1A01CAC2A4343F1004D43EE /* StepsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A01CAB2A4343F1004D43EE /* StepsView.swift */; };
+ B1BA87012A4C4BC400D141A8 /* QuestionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BA87002A4C4BC400D141A8 /* QuestionModel.swift */; };
+ B1F11C732A45DBF900A137A3 /* DomainListSaveable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F11C722A45DBF900A137A3 /* DomainListSaveable.swift */; };
+ B1F11C7A2A498C5300A137A3 /* QuestionsStepViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F11C792A498C5300A137A3 /* QuestionsStepViewModel.swift */; };
+ B1F11C7C2A498CBF00A137A3 /* BaseStepViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F11C7B2A498CBF00A137A3 /* BaseStepViewModel.swift */; };
+ B1F11C7E2A49AE4000A137A3 /* YesNoRadioSwitcherView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F11C7D2A49AE4000A137A3 /* YesNoRadioSwitcherView.swift */; };
+ B1F11C802A49E35800A137A3 /* QuestionTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F11C7F2A49E35800A137A3 /* QuestionTitleView.swift */; };
+ B1F11C822A49E63400A137A3 /* NavigationLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F11C812A49E63400A137A3 /* NavigationLinkView.swift */; };
+ B1F11C842A4B029800A137A3 /* SelectCountryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F11C832A4B029800A137A3 /* SelectCountryViewController.swift */; };
+ B1F11C862A4B02EE00A137A3 /* SelectCountryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F11C852A4B02EE00A137A3 /* SelectCountryViewModel.swift */; };
+ B1F11C882A4B033000A137A3 /* Country.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F11C872A4B033000A137A3 /* Country.swift */; };
+ B1F11C8A2A4B050500A137A3 /* CountryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F11C892A4B050500A137A3 /* CountryView.swift */; };
+ B1F11C8C2A4C273500A137A3 /* SelectRegionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F11C8B2A4C273500A137A3 /* SelectRegionViewModel.swift */; };
+ B1F11C8E2A4C27B800A137A3 /* BaseSelectCountryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F11C8D2A4C27B800A137A3 /* BaseSelectCountryViewModel.swift */; };
+ C9E66BB880A29A48D055FBFF /* Pods-Lockdown-settings-metadata.plist in Resources */ = {isa = PBXBuildFile; fileRef = 50F9BE503587CE4933CB7983 /* Pods-Lockdown-settings-metadata.plist */; };
+ E06F1E09E754223BF6E5D372 /* Pods_LockdownTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6F35024B0B1EB324BC94470 /* Pods_LockdownTests.framework */; };
+ F01CAB7C2C61106F009C19CF /* SUI+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F01CAB7B2C61106F009C19CF /* SUI+Extensions.swift */; };
+ F01CAB7E2C61316C009C19CF /* OneTimePaywallModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F01CAB7D2C61316C009C19CF /* OneTimePaywallModel.swift */; };
+ F09235DA0DB40BE2B52F6D96 /* Pods_Lockdown_VPN_Widget.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95D421359DD51FC306D3B4C9 /* Pods_Lockdown_VPN_Widget.framework */; };
+ F0A8E0412C64E977001303C6 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0A8E0402C64E977001303C6 /* Defaults.swift */; };
+ F0A8E0432C64E9A5001303C6 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0A8E0402C64E977001303C6 /* Defaults.swift */; };
+ F0A8E0442C64E9A6001303C6 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0A8E0402C64E977001303C6 /* Defaults.swift */; };
+ F0A8E0452C64E9A6001303C6 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0A8E0402C64E977001303C6 /* Defaults.swift */; };
+ F0A8E0462C64E9A7001303C6 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0A8E0402C64E977001303C6 /* Defaults.swift */; };
+ F0A8E0472C64E9AA001303C6 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0A8E0402C64E977001303C6 /* Defaults.swift */; };
+ F0B12AF42C60CA63008EF8AA /* PaywallRoundContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0B12AF32C60CA63008EF8AA /* PaywallRoundContainer.swift */; };
+ F0B12AF82C60D602008EF8AA /* OneTimePaywallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0B12AF72C60D602008EF8AA /* OneTimePaywallView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -220,6 +501,20 @@
remoteGlobalIDString = 3DBD57BA22FD727900DE189F;
remoteInfo = "Lockdown Firewall Today";
};
+ 7C0D111F2473FC7E00A26E04 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = A1141A091F46230500F54698 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = A1141A101F46230500F54698;
+ remoteInfo = Lockdown;
+ };
+ 7C9A9375251E1EC700DA5721 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = A1141A091F46230500F54698 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 7C9A9369251E1EC700DA5721;
+ remoteInfo = LockdownFirewallWidgetExtension;
+ };
A118F63520B33F44009A64E7 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = A1141A091F46230500F54698 /* Project object */;
@@ -244,13 +539,21 @@
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
- A15939D8206D97C40060D945 /* CopyFiles */ = {
+ 3DD3D09826CC8714002238E8 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
- );
+ B163A3332A2F2C7900FD7C5E /* NEKit.xcframework in Embed Frameworks */,
+ B163A3392A2F2CBC00FD7C5E /* tun2socks.xcframework in Embed Frameworks */,
+ B163A34F2A2F4C2C00FD7C5E /* Resolver.xcframework in Embed Frameworks */,
+ B163A2F82A2F29A100FD7C5E /* CocoaAsyncSocket.xcframework in Embed Frameworks */,
+ B163A30F2A2F2AB500FD7C5E /* CocoaLumberjackSwift.xcframework in Embed Frameworks */,
+ B163A31E2A2F2B4000FD7C5E /* lwip.xcframework in Embed Frameworks */,
+ B163A3002A2F2A1800FD7C5E /* CocoaLumberjack.xcframework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
A18B79571F8C36460042A4EF /* Embed App Extensions */ = {
@@ -263,6 +566,7 @@
3DBD57C622FD727900DE189F /* Lockdown Firewall Widget.appex in Embed App Extensions */,
A12473FE1FE44285008493B8 /* Lockdown VPN Widget.appex in Embed App Extensions */,
A1FCDA4922C0651300C928BC /* LockdownTunnel.appex in Embed App Extensions */,
+ 7C9A9377251E1EC700DA5721 /* LockdownFirewallWidgetExtension.appex in Embed App Extensions */,
);
name = "Embed App Extensions";
runOnlyForDeploymentPostprocessing = 0;
@@ -272,11 +576,22 @@
/* Begin PBXFileReference section */
0CDA77C17BF2DEC43E3D56EA /* Pods-LockdownTunnel-settings-metadata.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "Pods-LockdownTunnel-settings-metadata.plist"; path = "LockdowniOS/Settings.bundle/Pods-LockdownTunnel-settings-metadata.plist"; sourceTree = ""; };
12884CAB7C53B842E9E3745C /* Pods-Lockdown Firewall Widget-metadata.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "Pods-Lockdown Firewall Widget-metadata.plist"; path = "Pods/Pods-Lockdown Firewall Widget-metadata.plist"; sourceTree = ""; };
- 31E2DCBA5F0A1C82E81F2D44 /* Pods_Lockdown.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Lockdown.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 171F1A80C5E933902B1708CE /* Pods_Lockdown_Firewall_Widget.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Lockdown_Firewall_Widget.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 173D3911239ED434E2139981 /* Pods-Lockdown.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Lockdown.release.xcconfig"; path = "Pods/Target Support Files/Pods-Lockdown/Pods-Lockdown.release.xcconfig"; sourceTree = ""; };
+ 2ADD2E8AC036859E49987E8B /* Pods-LockdownTests-settings-metadata.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "Pods-LockdownTests-settings-metadata.plist"; path = "Settings.bundle/Pods-LockdownTests-settings-metadata.plist"; sourceTree = ""; };
+ 2C5C889B3C3F01CB4B730A22 /* Pods-Lockdown VPN Widget.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Lockdown VPN Widget.release.xcconfig"; path = "Pods/Target Support Files/Pods-Lockdown VPN Widget/Pods-Lockdown VPN Widget.release.xcconfig"; sourceTree = ""; };
+ 2DF472CA81A935DEF14D7039 /* Pods-Lockdown Firewall Widget-settings-metadata.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "Pods-Lockdown Firewall Widget-settings-metadata.plist"; path = "Settings.bundle/Pods-Lockdown Firewall Widget-settings-metadata.plist"; sourceTree = ""; };
+ 383AB71E3748C73C2FE45B7D /* Pods-Lockdown Firewall Widget.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Lockdown Firewall Widget.release.xcconfig"; path = "Pods/Target Support Files/Pods-Lockdown Firewall Widget/Pods-Lockdown Firewall Widget.release.xcconfig"; sourceTree = ""; };
+ 3D01D97A2480DBED003A710C /* data_trackers.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = data_trackers.txt; sourceTree = ""; };
+ 3D01D99C2481E241003A710C /* general_ads.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = general_ads.txt; sourceTree = ""; };
+ 3D01D99D2481E252003A710C /* reporting.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = reporting.txt; sourceTree = ""; };
3D0711B722FE79BE00391C6E /* WhyTrustViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhyTrustViewController.swift; sourceTree = ""; };
3D0711BA22FE7B5100391C6E /* TitleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleViewController.swift; sourceTree = ""; };
3D0971D722EBAD1000CCD326 /* facebook_sdk.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = facebook_sdk.txt; sourceTree = ""; };
3D0971D922EBAD4C00CCD326 /* marketing.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = marketing.txt; sourceTree = ""; };
+ 3D3BF4CF233D5E9100D0C482 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; };
+ 3D40826227F675F6004C146B /* dnscrypt-proxy.toml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "dnscrypt-proxy.toml"; sourceTree = ""; };
+ 3D40826827F6A03F004C146B /* DNSCryptThread.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSCryptThread.swift; sourceTree = ""; };
3D44377A22DFB22600908CDC /* Montserrat-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Montserrat-Medium.ttf"; sourceTree = ""; };
3D44377B22DFB22600908CDC /* Montserrat-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Montserrat-Light.ttf"; sourceTree = ""; };
3D44377C22DFB22600908CDC /* Montserrat-Thin.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Montserrat-Thin.ttf"; sourceTree = ""; };
@@ -323,15 +638,30 @@
3D47CDAC22F3C3F3003BD7F7 /* NVActivityIndicatorAnimationLineScalePulseOutRapid.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationLineScalePulseOutRapid.swift; sourceTree = ""; };
3D47CDAD22F3C3F3003BD7F7 /* NVActivityIndicatorAnimationBallPulseRise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallPulseRise.swift; sourceTree = ""; };
3D47CDAE22F3C3F3003BD7F7 /* NVActivityIndicatorAnimationOrbit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationOrbit.swift; sourceTree = ""; };
+ 3D4D7FEB247F22AE000369FD /* google_shopping_ads.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = google_shopping_ads.txt; sourceTree = ""; };
3D5464D223037CCA00AE1F73 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; };
3D5561D3230B58F30062001D /* PrivacyPolicyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyPolicyViewController.swift; sourceTree = ""; };
+ 3D5F5A0723107C1E004C3860 /* game_ads.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = game_ads.txt; sourceTree = ""; };
+ 3D5F5A0923107EB8004C3860 /* snapchat_analytics.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = snapchat_analytics.txt; sourceTree = ""; };
+ 3D5F5A0B23109ABB004C3860 /* WhatIsVpnViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhatIsVpnViewController.swift; sourceTree = ""; };
+ 3D752C302357FA3B00C163E4 /* SF-Pro-Rounded-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro-Rounded-Regular.otf"; sourceTree = ""; };
+ 3D752C312357FA3B00C163E4 /* SF-Pro-Rounded-Medium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro-Rounded-Medium.otf"; sourceTree = ""; };
+ 3D752C322357FA3B00C163E4 /* SF-Pro-Rounded-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro-Rounded-Bold.otf"; sourceTree = ""; };
+ 3D752C332357FA3B00C163E4 /* SF-Pro-Rounded-Semibold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro-Rounded-Semibold.otf"; sourceTree = ""; };
+ 3D89610D253527B1006D8C12 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Main.strings; sourceTree = ""; };
+ 3D896110253527B2006D8C12 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; };
3D94AB0322FDEDEB0012B0DE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; };
3D94AB1122FE3A460012B0DE /* Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = ""; };
3D970DAC22EC149D00F9CC93 /* BlockLogCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockLogCell.swift; sourceTree = ""; };
3D970DAE22EC15D800F9CC93 /* BlockLogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockLogViewController.swift; sourceTree = ""; };
+ 3D9FC67623E503DF004122D3 /* EmailSignInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailSignInViewController.swift; sourceTree = ""; };
+ 3D9FC67823E521DE004122D3 /* ForgotPasswordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForgotPasswordViewController.swift; sourceTree = ""; };
+ 3DA14D34255DF56E00A3658E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Main.strings; sourceTree = ""; };
+ 3DA14D3C255DF5CF00A3658E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainInterface.strings; sourceTree = ""; };
+ 3DA14D3E255DF5D400A3658E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainInterface.strings; sourceTree = ""; };
3DAA6B4E22EA76420018FC09 /* clickbait.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = clickbait.txt; sourceTree = ""; };
3DAA6B5222EA988F0018FC09 /* ransomware.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ransomware.txt; sourceTree = ""; };
- 3DABD9FE22F7AD4D00480AAC /* FirewallUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirewallUtilities.swift; sourceTree = ""; };
+ 3DAF734C2768572300D97BB0 /* FirewallUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirewallUtilities.swift; sourceTree = ""; };
3DBD57A122FBB0D900DE189F /* WebViewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewViewController.swift; sourceTree = ""; };
3DBD57A522FBCD7A00DE189F /* WhitelistViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhitelistViewController.swift; sourceTree = ""; };
3DBD57A722FBD7A100DE189F /* WhitelistUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhitelistUtilities.swift; sourceTree = ""; };
@@ -348,32 +678,189 @@
3DCA4F3022F190AE0017740D /* ClientModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientModels.swift; sourceTree = ""; };
3DCA4F3222F22CB40017740D /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; };
3DCA4F4022F252720017740D /* FirewallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirewallController.swift; sourceTree = ""; };
- 428B4B342E5EA9720C08F150 /* Pods-Today.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Today.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Today/Pods-Today.debug.xcconfig"; sourceTree = ""; };
- 4C50BEAA399D6FDF2C2672C6 /* Pods-Confirmed VPN.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Confirmed VPN.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Confirmed VPN/Pods-Confirmed VPN.debug.xcconfig"; sourceTree = ""; };
- 4D422CB6539443825E5CD91B /* Pods-Confirmed Tunnels.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Confirmed Tunnels.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Confirmed Tunnels/Pods-Confirmed Tunnels.debug.xcconfig"; sourceTree = ""; };
+ 3DCBC8FF25425AB200446C98 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; };
+ 3DCBC90025425AB200446C98 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Main.strings; sourceTree = ""; };
+ 3DCBC90125425AB200446C98 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/MainInterface.strings; sourceTree = ""; };
+ 3DCBC90225425AB200446C98 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/MainInterface.strings; sourceTree = ""; };
+ 3DCBC90925425BC900446C98 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Main.strings; sourceTree = ""; };
+ 3DCBC90A25425BC900446C98 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; };
+ 3DCBC90B25425BC900446C98 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/MainInterface.strings; sourceTree = ""; };
+ 3DCBC90C25425BC900446C98 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/MainInterface.strings; sourceTree = ""; };
+ 3DCFE6F924493F9000EA9B35 /* marketing_beta.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = marketing_beta.txt; sourceTree = ""; };
+ 3DD545CD280681AA005E140C /* libresolv.9.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.9.tbd; path = usr/lib/libresolv.9.tbd; sourceTree = SDKROOT; };
+ 3DD545D428068233005E140C /* LockdownTunnelBridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LockdownTunnelBridgingHeader.h; sourceTree = ""; };
+ 3DD545DA2808C2F6005E140C /* 5000_dummy_list.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = 5000_dummy_list.txt; sourceTree = ""; };
+ 3DE443FA25353453006DF67D /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/MainInterface.strings; sourceTree = ""; };
+ 3DE443FE253534C7006DF67D /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/MainInterface.strings; sourceTree = ""; };
+ 3DF2455323A2F8A400E46613 /* EmailSignUpViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailSignUpViewController.swift; sourceTree = ""; };
+ 3DF2455523A306DB00E46613 /* Loader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Loader.swift; sourceTree = ""; };
+ 3DF5D75E2633B1E100F77D79 /* amazon_trackers.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = amazon_trackers.txt; sourceTree = ""; };
+ 40098E2929FDA6A800886474 /* BulletView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BulletView.swift; sourceTree = ""; };
+ 40098E2B29FDA6CC00886474 /* PaywallDescriptionLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaywallDescriptionLabel.swift; sourceTree = ""; };
+ 40098E2D29FDA6E500886474 /* PlanView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlanView.swift; sourceTree = ""; };
+ 40098E3029FDA73200886474 /* VPNPaywallViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNPaywallViewController.swift; sourceTree = ""; };
+ 40098E3529FF378F00886474 /* FirewallPaywallViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirewallPaywallViewController.swift; sourceTree = ""; };
+ 40098E3829FF378F00886474 /* AnnualPlanView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnnualPlanView.swift; sourceTree = ""; };
+ 40098E3929FF378F00886474 /* MonthlyPlanView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MonthlyPlanView.swift; sourceTree = ""; };
+ 40098E3A29FF378F00886474 /* AdvancedPlansViews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvancedPlansViews.swift; sourceTree = ""; };
+ 4015B4F629EFD9AC004102E0 /* AccessLevelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessLevelView.swift; sourceTree = ""; };
+ 4015B4FC29F00DD8004102E0 /* LDVpnViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LDVpnViewController.swift; sourceTree = ""; };
+ 4015B4FE29F14C95004102E0 /* LDCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LDCardView.swift; sourceTree = ""; };
+ 4015B50229F16E1A004102E0 /* LDConfigurationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LDConfigurationViewController.swift; sourceTree = ""; };
+ 402BAD242A0B675B009B8820 /* LockedListsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockedListsView.swift; sourceTree = ""; };
+ 402BAD352A0CD37C009B8820 /* ConnectivityService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectivityService.swift; sourceTree = ""; };
+ 402BAD372A0CD3B0009B8820 /* ConnectionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionState.swift; sourceTree = ""; };
+ 402D24B729D59B4400A5AB60 /* EmptyListsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyListsView.swift; sourceTree = ""; };
+ 402D24CA29D87B5A00A5AB60 /* ListsSubmenuView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListsSubmenuView.swift; sourceTree = ""; };
+ 402D24D329D87F4500A5AB60 /* CustomBlockedTableHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomBlockedTableHeader.swift; sourceTree = ""; };
+ 402D251529E514CF00A5AB60 /* MoveToListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoveToListViewController.swift; sourceTree = ""; };
+ 402D251829E517E100A5AB60 /* ConfiguredNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfiguredNavigationView.swift; sourceTree = ""; };
+ 402D251A29E519B500A5AB60 /* CustomTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomTableView.swift; sourceTree = ""; };
+ 402D251E29E52D6A00A5AB60 /* EditDomainsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditDomainsViewController.swift; sourceTree = ""; };
+ 402D252029E52D7600A5AB60 /* BottomMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BottomMenu.swift; sourceTree = ""; };
+ 402D252229E5473E00A5AB60 /* EditDomainsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditDomainsCell.swift; sourceTree = ""; };
+ 402D252629E5843300A5AB60 /* ImportBlockListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportBlockListViewController.swift; sourceTree = ""; };
+ 402D252829E632F300A5AB60 /* ListSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListSettingsViewController.swift; sourceTree = ""; };
+ 402D252A29E6335100A5AB60 /* SwitchBlockingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchBlockingView.swift; sourceTree = ""; };
+ 402D252C29E6346900A5AB60 /* ListBlockedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListBlockedTableViewCell.swift; sourceTree = ""; };
+ 402D252E29E6357700A5AB60 /* ListDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDetailViewController.swift; sourceTree = ""; };
+ 402D253029E635CB00A5AB60 /* DomainsBlockedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainsBlockedTableViewCell.swift; sourceTree = ""; };
+ 402D253229E6588000A5AB60 /* ListDescriptionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDescriptionViewController.swift; sourceTree = ""; };
+ 402D253A29E8F9A400A5AB60 /* JSONSerialization+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSONSerialization+Extensions.swift"; sourceTree = ""; };
+ 402D254729EE112E00A5AB60 /* LDFirewallViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LDFirewallViewController.swift; sourceTree = ""; };
+ 402D254929EE1C6E00A5AB60 /* DescriptionLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptionLabel.swift; sourceTree = ""; };
+ 402D254D29EE598D00A5AB60 /* TrackersGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackersGroupView.swift; sourceTree = ""; };
+ 402D254F29EE78D600A5AB60 /* OverallStatiscticView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverallStatiscticView.swift; sourceTree = ""; };
+ 408E7A9329F88C9200B2F587 /* CustomUISwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomUISwitch.swift; sourceTree = ""; };
+ 408E7A9629FA698C00B2F587 /* UIVIew+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIVIew+Extensions.swift"; sourceTree = ""; };
+ 40960AE12A029A7D000F82EB /* UIApplication+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Extension.swift"; sourceTree = ""; };
+ 40960AE82A033514000F82EB /* AccessLevel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessLevel.swift; sourceTree = ""; };
+ 40960AEA2A03396F000F82EB /* ProductPurchasable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductPurchasable.swift; sourceTree = ""; };
+ 40960AEC2A0339E2000F82EB /* UserService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserService.swift; sourceTree = ""; };
+ 40960AEF2A033A41000F82EB /* LockdownUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockdownUser.swift; sourceTree = ""; };
+ 40960AF12A033AF9000F82EB /* String+Localized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Localized.swift"; sourceTree = ""; };
+ 40960AFA2A033E46000F82EB /* PaywallService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallService.swift; sourceTree = ""; };
+ 40960B022A033EA9000F82EB /* CountdownDisplayService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountdownDisplayService.swift; sourceTree = ""; };
+ 40960B062A033F0B000F82EB /* WeakObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakObject.swift; sourceTree = ""; };
+ 40960B092A03400E000F82EB /* UserDefault.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefault.swift; sourceTree = ""; };
+ 40960B0C2A034054000F82EB /* LockdownStorageIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockdownStorageIdentifier.swift; sourceTree = ""; };
+ 40960B142A034400000F82EB /* Keychainable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keychainable.swift; sourceTree = ""; };
+ 409B59DF2A14CB7B0010242C /* SignUpViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpViewController.swift; sourceTree = ""; };
+ 409B59E32A15CC900010242C /* WelcomeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; };
+ 409B59E52A15D00C0010242C /* WelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = ""; };
+ 40CC816D2A14B25C00F9805E /* DeleteMyAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeleteMyAccountViewController.swift; sourceTree = ""; };
+ 40CC816F2A14B29100F9805E /* EmailComposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailComposable.swift; sourceTree = ""; };
+ 40CC81782A14BA8800F9805E /* EmailAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailAddress.swift; sourceTree = ""; };
+ 40CC817A2A14BAA600F9805E /* EmailValidatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailValidatable.swift; sourceTree = ""; };
+ 40CC817D2A14BB3600F9805E /* UIView+Corners.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Corners.swift"; sourceTree = ""; };
+ 40CC81812A14BD2100F9805E /* DeleteMyAccountViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DeleteMyAccountViewController.xib; sourceTree = ""; };
+ 40CC849D2A14BEA000F9805E /* SignUpViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SignUpViewController.xib; sourceTree = ""; };
+ 40CC84A02A14BECF00F9805E /* EnableNotificationsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnableNotificationsViewController.swift; sourceTree = ""; };
+ 40CC84A22A14BED800F9805E /* EnableNotificationsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EnableNotificationsViewController.xib; sourceTree = ""; };
+ 40CC84A42A14BEFA00F9805E /* SplashScreenViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SplashScreenViewController.xib; sourceTree = ""; };
+ 40CC84A72A14C09600F9805E /* String+URL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+URL.swift"; sourceTree = ""; };
+ 40CC84A92A14C0A200F9805E /* String+Attributed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Attributed.swift"; sourceTree = ""; };
+ 40CC84AB2A14C0D700F9805E /* Date+Ext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Date+Ext.swift"; sourceTree = ""; };
+ 40CC84AC2A14C0D800F9805E /* CALayer+Ext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CALayer+Ext.swift"; sourceTree = ""; };
+ 40CC84AD2A14C0D800F9805E /* UIStackView+Ext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStackView+Ext.swift"; sourceTree = ""; };
+ 40CC84AE2A14C0D800F9805E /* UIAppearance+Ext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAppearance+Ext.swift"; sourceTree = ""; };
+ 40CC84B32A14C0E900F9805E /* UIViewController+Ext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Ext.swift"; sourceTree = ""; };
+ 40CC84B42A14C0E900F9805E /* Font+Ext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Font+Ext.swift"; sourceTree = ""; };
+ 40CC84B52A14C0EA00F9805E /* UICollectionView+Dequeue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionView+Dequeue.swift"; sourceTree = ""; };
+ 40CC84B62A14C0EA00F9805E /* NibLoadable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NibLoadable.swift; sourceTree = ""; };
+ 40CC84B72A14C0EA00F9805E /* UIView+Ext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Ext.swift"; sourceTree = ""; };
+ 40CC84BD2A14C15400F9805E /* LockdownGradient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockdownGradient.swift; sourceTree = ""; };
+ 40CC84C12A14C2B700F9805E /* FloatingTextInputTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatingTextInputTextField.swift; sourceTree = ""; };
+ 40CC84C22A14C2B800F9805E /* TextBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextBox.swift; sourceTree = ""; };
+ 40CC84C32A14C2B800F9805E /* TextInputState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextInputState.swift; sourceTree = ""; };
+ 40CC84C42A14C2B800F9805E /* TextBoxLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextBoxLabel.swift; sourceTree = ""; };
+ 40E04A212A26708200000E8C /* WhatsNewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhatsNewViewController.swift; sourceTree = ""; };
+ 40E04A232A26758200000E8C /* WhatsNewDescriptionLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhatsNewDescriptionLabel.swift; sourceTree = ""; };
+ 40E04A522A29D79100000E8C /* BlockListContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockListContainerViewController.swift; sourceTree = ""; };
+ 40E04A542A29D7AB00000E8C /* CuratedListsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CuratedListsViewController.swift; sourceTree = ""; };
+ 40E04A562A29D7BC00000E8C /* CustomListsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListsViewController.swift; sourceTree = ""; };
+ 40E04A582A2A1B4C00000E8C /* CTAView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CTAView.swift; sourceTree = ""; };
+ 40E7A2EC2A0CE8AE00E0231A /* advanced_gaming.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = advanced_gaming.txt; sourceTree = ""; };
+ 40E7A2F32A0CE92800E0231A /* ifunny_trackers.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ifunny_trackers.txt; sourceTree = ""; };
+ 40E7A2F42A0CE92800E0231A /* junes_journey_trackers.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = junes_journey_trackers.txt; sourceTree = ""; };
+ 40E7A2F52A0CE92800E0231A /* scams.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = scams.txt; sourceTree = ""; };
+ 40E7A2F62A0CE92800E0231A /* tiktok_trackers.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tiktok_trackers.txt; sourceTree = ""; };
+ 40E7A2F72A0CE92900E0231A /* advanced_analytics.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = advanced_analytics.txt; sourceTree = ""; };
+ 40E7A3002A0E1C7A00E0231A /* SplashScreenViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenViewController.swift; sourceTree = ""; };
+ 40FC414229F74C7900BD7396 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = ""; };
+ 50F9BE503587CE4933CB7983 /* Pods-Lockdown-settings-metadata.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "Pods-Lockdown-settings-metadata.plist"; path = "Settings.bundle/Pods-Lockdown-settings-metadata.plist"; sourceTree = ""; };
50FB8ADA1D444FD9486F2D44 /* Pods-Lockdown Firewall Widget-settings-metadata.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "Pods-Lockdown Firewall Widget-settings-metadata.plist"; path = "LockdowniOS/Settings.bundle/Pods-Lockdown Firewall Widget-settings-metadata.plist"; sourceTree = ""; };
- 65F695578DAA62084B36A513 /* Pods-Lockdown Tunnels.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Lockdown Tunnels.release.xcconfig"; path = "Pods/Target Support Files/Pods-Lockdown Tunnels/Pods-Lockdown Tunnels.release.xcconfig"; sourceTree = ""; };
- 6A890BF9C9CF89A7E923EDDA /* Pods_Lockdown_Firewall_Widget.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Lockdown_Firewall_Widget.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- 726E8CFC747C13F896CA72B6 /* Pods-LockdownTunnel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LockdownTunnel.release.xcconfig"; path = "Pods/Target Support Files/Pods-LockdownTunnel/Pods-LockdownTunnel.release.xcconfig"; sourceTree = ""; };
- 7B555BB9C945AD99E970BE3A /* Pods_Lockdown_VPN_Widget.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Lockdown_VPN_Widget.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- 7CDB1F5AC85EB2D826BB00C2 /* Pods-Lockdown Firewall Widget.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Lockdown Firewall Widget.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Lockdown Firewall Widget/Pods-Lockdown Firewall Widget.debug.xcconfig"; sourceTree = ""; };
- 7E013E3207564A64E3A1BD49 /* Pods-Lockdown Tunnels.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Lockdown Tunnels.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Lockdown Tunnels/Pods-Lockdown Tunnels.debug.xcconfig"; sourceTree = ""; };
- 8827B5DAD4A819CDC5115562 /* Pods-Lockdown.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Lockdown.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Lockdown/Pods-Lockdown.debug.xcconfig"; sourceTree = ""; };
+ 51006F182CBD0E7400F5142C /* ImageBannerWithTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBannerWithTitleView.swift; sourceTree = ""; };
+ 510AA2F52CB8222100E53560 /* FeedbackPaywallViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackPaywallViewModel.swift; sourceTree = ""; };
+ 5117DE872CBD4A6600C4A61B /* SectionTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionTitleView.swift; sourceTree = ""; };
+ 5145A1992CBE37C40074C562 /* FeedbackFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackFlow.swift; sourceTree = ""; };
+ 51DD89E42CB7DA770028B4FE /* FeedbackPaywallViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackPaywallViewController.swift; sourceTree = ""; };
+ 5E13011C2D5E012E003896BD /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; };
+ 5E9AF72F2CF5D18F001239A0 /* SpecialOfferPaywallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpecialOfferPaywallView.swift; sourceTree = ""; };
+ 5E9BD5852D787D8400E8DE4F /* ReviewAlertManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewAlertManager.swift; sourceTree = ""; };
+ 5EA97B2C2CF5F83D0082D3FD /* KumbhSans-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "KumbhSans-Bold.ttf"; sourceTree = ""; };
+ 5EA97B2E2CF5F83D0082D3FD /* KumbhSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "KumbhSans-Regular.ttf"; sourceTree = ""; };
+ 5EA97B352CF5F86A0082D3FD /* Juana-SemiBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Juana-SemiBold.ttf"; sourceTree = ""; };
+ 5EA97B382CF604D60082D3FD /* SpecialOfferPaywallModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpecialOfferPaywallModel.swift; sourceTree = ""; };
+ 66424506768B2196F870B04C /* Pods-LockdownTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LockdownTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-LockdownTests/Pods-LockdownTests.release.xcconfig"; sourceTree = ""; };
+ 6F089C7008AB8F59DE3EA7BD /* Pods-LockdownTunnel-settings-metadata.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "Pods-LockdownTunnel-settings-metadata.plist"; path = "Settings.bundle/Pods-LockdownTunnel-settings-metadata.plist"; sourceTree = ""; };
+ 71D50056A5E2E1F6486369F9 /* Pods-Lockdown VPN Widget.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Lockdown VPN Widget.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Lockdown VPN Widget/Pods-Lockdown VPN Widget.debug.xcconfig"; sourceTree = ""; };
+ 7C0D11112473EE2E00A26E04 /* DomainNameValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainNameValidator.swift; sourceTree = ""; };
+ 7C0D111A2473FC7E00A26E04 /* LockdownTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LockdownTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7C0D111C2473FC7E00A26E04 /* LockdownTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockdownTests.swift; sourceTree = ""; };
+ 7C0D111E2473FC7E00A26E04 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 7C0D11242473FD6500A26E04 /* DomainNameValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainNameValidatorTests.swift; sourceTree = ""; };
+ 7C1AE072247FD82A0000A7D3 /* PushNotificationsAuthorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationsAuthorization.swift; sourceTree = ""; };
+ 7C1AE074247FE1FB0000A7D3 /* PushNotificationsAuthorizationUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationsAuthorizationUI.swift; sourceTree = ""; };
+ 7C1AE079247FF87E0000A7D3 /* OneTimeActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneTimeActions.swift; sourceTree = ""; };
+ 7C1AE07F248028F40000A7D3 /* UIKit+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIKit+Extensions.swift"; sourceTree = ""; };
+ 7C3E8D20247D8057004B81D6 /* PushNotifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotifications.swift; sourceTree = ""; };
+ 7C3EFA0124867DEE00719D96 /* TrackerInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerInfo.swift; sourceTree = ""; };
+ 7C3EFA032486879800719D96 /* tracker_info.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = tracker_info.json; sourceTree = ""; };
+ 7C422E96252796EE007F9C22 /* StaticTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticTableView.swift; sourceTree = ""; };
+ 7C422EA425279724007F9C22 /* Align.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Align.swift; sourceTree = "