Showing
115 changed files
with
6405 additions
and
0 deletions
.DS_Store
0 → 100644
No preview for this file type
AdBlocker/ContentBlockerRequestHandler.swift
0 → 100644
1 | +// | ||
2 | +// ContentBlockerRequestHandler.swift | ||
3 | +// AdBlocker | ||
4 | +// | ||
5 | +// Created by Artem Talko on 24.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | +import MobileCoreServices | ||
10 | + | ||
11 | +class ContentBlockerRequestHandler: NSObject, NSExtensionRequestHandling { | ||
12 | + | ||
13 | + func beginRequest(with context: NSExtensionContext) { | ||
14 | + let attachment = NSItemProvider(contentsOf: Bundle.main.url(forResource: "blockerList", withExtension: "json"))! | ||
15 | + | ||
16 | + let item = NSExtensionItem() | ||
17 | + item.attachments = [attachment] | ||
18 | + | ||
19 | + context.completeRequest(returningItems: [item], completionHandler: nil) | ||
20 | + } | ||
21 | +} |
AdBlocker/Info.plist
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
3 | +<plist version="1.0"> | ||
4 | +<dict> | ||
5 | + <key>NSExtension</key> | ||
6 | + <dict> | ||
7 | + <key>NSExtensionPointIdentifier</key> | ||
8 | + <string>com.apple.Safari.content-blocker</string> | ||
9 | + <key>NSExtensionPrincipalClass</key> | ||
10 | + <string>$(PRODUCT_MODULE_NAME).ContentBlockerRequestHandler</string> | ||
11 | + </dict> | ||
12 | +</dict> | ||
13 | +</plist> |
browser.xcodeproj/project.pbxproj
0 → 100644
1 | +// !$*UTF8*$! | ||
2 | +{ | ||
3 | + archiveVersion = 1; | ||
4 | + classes = { | ||
5 | + }; | ||
6 | + objectVersion = 56; | ||
7 | + objects = { | ||
8 | + | ||
9 | +/* Begin PBXBuildFile section */ | ||
10 | + 1904ED472AC56BDB0035DB66 /* FontsFree-Net-SFProText-Semibold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1904ED452AC56BDB0035DB66 /* FontsFree-Net-SFProText-Semibold.ttf */; }; | ||
11 | + 1904ED482AC56BDB0035DB66 /* FontsFree-Net-SFProText-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1904ED462AC56BDB0035DB66 /* FontsFree-Net-SFProText-Regular.ttf */; }; | ||
12 | + 1907D6972ADE766F00C40E9F /* HistoryDBManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1907D6962ADE766F00C40E9F /* HistoryDBManager.swift */; }; | ||
13 | + 1907D6992ADE7E9C00C40E9F /* DateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1907D6982ADE7E9C00C40E9F /* DateManager.swift */; }; | ||
14 | + 190DB9FC2AC450F6000A7BF3 /* RemoveAdvertViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190DB9FB2AC450F6000A7BF3 /* RemoveAdvertViewController.swift */; }; | ||
15 | + 190DBA002AC45A3C000A7BF3 /* CachingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190DB9FF2AC45A3C000A7BF3 /* CachingManager.swift */; }; | ||
16 | + 190DBA0D2AC47701000A7BF3 /* BinaryFloatingPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190DBA0C2AC47701000A7BF3 /* BinaryFloatingPoint.swift */; }; | ||
17 | + 191BB84F2AC5A9C900A2DEB9 /* TabsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191BB84E2AC5A9C900A2DEB9 /* TabsView.swift */; }; | ||
18 | + 191BB8522AC5AB4600A2DEB9 /* TabsToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191BB8512AC5AB4600A2DEB9 /* TabsToolbarView.swift */; }; | ||
19 | + 191BB8542AC5B3CD00A2DEB9 /* TabsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191BB8532AC5B3CD00A2DEB9 /* TabsViewController.swift */; }; | ||
20 | + 191BB8572AC5B4AC00A2DEB9 /* OpenedTabsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191BB8562AC5B4AC00A2DEB9 /* OpenedTabsCollectionViewCell.swift */; }; | ||
21 | + 191BB8622AC6A01500A2DEB9 /* SearchingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191BB8612AC6A01500A2DEB9 /* SearchingViewController.swift */; }; | ||
22 | + 191BB8642AC6A02300A2DEB9 /* SearchingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191BB8632AC6A02300A2DEB9 /* SearchingView.swift */; }; | ||
23 | + 191BB8692AC6A66900A2DEB9 /* SearchingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191BB8682AC6A66900A2DEB9 /* SearchingTableViewCell.swift */; }; | ||
24 | + 191BB8772AC6B47700A2DEB9 /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191BB8762AC6B47700A2DEB9 /* HistoryView.swift */; }; | ||
25 | + 191BB8792AC6B48400A2DEB9 /* HistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191BB8782AC6B48400A2DEB9 /* HistoryViewController.swift */; }; | ||
26 | + 191BB87C2AC6B79C00A2DEB9 /* HistoryTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191BB87B2AC6B79C00A2DEB9 /* HistoryTableViewCell.swift */; }; | ||
27 | + 191BB87F2AC6BBBD00A2DEB9 /* HistoryToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191BB87E2AC6BBBD00A2DEB9 /* HistoryToolbarView.swift */; }; | ||
28 | + 191BB8812AC6FF6600A2DEB9 /* RemoveAdvertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191BB8802AC6FF6600A2DEB9 /* RemoveAdvertView.swift */; }; | ||
29 | + 1926E82F2B03BED200FEBCFB /* HistoryToolbarMenuCases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1926E82E2B03BED200FEBCFB /* HistoryToolbarMenuCases.swift */; }; | ||
30 | + 193B3B832ACAF394002161ED /* BrowserTabDataBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193B3B822ACAF394002161ED /* BrowserTabDataBase.swift */; }; | ||
31 | + 193B3B862ACAF480002161ED /* Realm in Frameworks */ = {isa = PBXBuildFile; productRef = 193B3B852ACAF480002161ED /* Realm */; }; | ||
32 | + 193B3B882ACAF480002161ED /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 193B3B872ACAF480002161ED /* RealmSwift */; }; | ||
33 | + 193B3B8A2ACAF714002161ED /* TabManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193B3B892ACAF714002161ED /* TabManager.swift */; }; | ||
34 | + 194635D62ADD738E00993D91 /* HistoryDataBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194635D52ADD738E00993D91 /* HistoryDataBase.swift */; }; | ||
35 | + 19696AF52AE80DBD00D1F8F9 /* blockerList.json in Resources */ = {isa = PBXBuildFile; fileRef = 19696AF42AE80DBD00D1F8F9 /* blockerList.json */; }; | ||
36 | + 19696AF72AE80DBD00D1F8F9 /* ContentBlockerRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19696AF62AE80DBD00D1F8F9 /* ContentBlockerRequestHandler.swift */; }; | ||
37 | + 19696AFB2AE80DBD00D1F8F9 /* AdBlocker.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 19696AF22AE80DBC00D1F8F9 /* AdBlocker.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; | ||
38 | + 197FC3EF2AC21E1F007F429C /* PayloadViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 197FC3EE2AC21E1F007F429C /* PayloadViewController.swift */; }; | ||
39 | + 197FC3F22AC2AF9A007F429C /* AdvantagesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 197FC3F12AC2AF9A007F429C /* AdvantagesTableViewCell.swift */; }; | ||
40 | + 197FC3F42AC2BCD7007F429C /* SearchBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 197FC3F32AC2BCD7007F429C /* SearchBarView.swift */; }; | ||
41 | + 197FC3F92AC2FB69007F429C /* SettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 197FC3F82AC2FB69007F429C /* SettingViewController.swift */; }; | ||
42 | + 197FC3FC2AC2FBA0007F429C /* SettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 197FC3FB2AC2FBA0007F429C /* SettingView.swift */; }; | ||
43 | + 197FC3FF2AC30746007F429C /* SettingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 197FC3FE2AC30746007F429C /* SettingTableViewCell.swift */; }; | ||
44 | + 197FC4012AC31D5C007F429C /* NavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 197FC4002AC31D5C007F429C /* NavigationViewController.swift */; }; | ||
45 | + 197FC4032AC41EB7007F429C /* ToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 197FC4022AC41EB7007F429C /* ToolbarView.swift */; }; | ||
46 | + 1984BF402AFB90560050F816 /* PrivacyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1984BF3F2AFB90560050F816 /* PrivacyViewController.swift */; }; | ||
47 | + 1984BF422AFB907A0050F816 /* PrivacyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1984BF412AFB907A0050F816 /* PrivacyView.swift */; }; | ||
48 | + 1984BF582AFB970F0050F816 /* TermsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1984BF572AFB970F0050F816 /* TermsViewController.swift */; }; | ||
49 | + 1984BF5A2AFB973C0050F816 /* TermsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1984BF592AFB973C0050F816 /* TermsView.swift */; }; | ||
50 | + 1989A1602AE29D4C00292680 /* OpenTabsTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1989A15F2AE29D4B00292680 /* OpenTabsTransition.swift */; }; | ||
51 | + 1990C69C2AEFDF89004AF856 /* UICollectionViewCell+convertFrameToScreenCoordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1990C69B2AEFDF89004AF856 /* UICollectionViewCell+convertFrameToScreenCoordinates.swift */; }; | ||
52 | + 199DB1EE2AD3FCFE007E6A81 /* BrowserSearchService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 199DB1ED2AD3FCFE007E6A81 /* BrowserSearchService.swift */; }; | ||
53 | + 199DB1F12AD3FDEE007E6A81 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 199DB1F02AD3FDEE007E6A81 /* Alamofire */; }; | ||
54 | + 19B41DA12AD81A70002C0D31 /* SearchBarContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B41DA02AD81A70002C0D31 /* SearchBarContainer.swift */; }; | ||
55 | + 19B7396A2AE1554E0073AA59 /* URLConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B739692AE1554E0073AA59 /* URLConstants.swift */; }; | ||
56 | + 19B7396E2AE156DE0073AA59 /* StringConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B7396D2AE156DE0073AA59 /* StringConstants.swift */; }; | ||
57 | + 19B739712AE157900073AA59 /* FontConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B739702AE157900073AA59 /* FontConstants.swift */; }; | ||
58 | + 19B739732AE15B3B0073AA59 /* ColorConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B739722AE15B3B0073AA59 /* ColorConstants.swift */; }; | ||
59 | + 19B739762AE276360073AA59 /* HistoryElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B739752AE276360073AA59 /* HistoryElement.swift */; }; | ||
60 | + 19C5F0572AD9784400133BD7 /* DataExtention+toImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19C5F0562AD9784400133BD7 /* DataExtention+toImage.swift */; }; | ||
61 | + 19C7A70C2AC9EBB800B954FC /* RemoveAdvertTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19C7A70B2AC9EBB800B954FC /* RemoveAdvertTableViewCell.swift */; }; | ||
62 | + 19C7A70E2AC9F5FD00B954FC /* HistorySearchBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19C7A70D2AC9F5FD00B954FC /* HistorySearchBarView.swift */; }; | ||
63 | + 19CD89482AC719AC0035CB55 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 19CD89472AC719AC0035CB55 /* SnapKit */; }; | ||
64 | + 19D1F2E02AC1EF3200510506 /* PayloadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D1F2DF2AC1EF3200510506 /* PayloadView.swift */; }; | ||
65 | + 19D8C9D62AE9169E0062D310 /* blockerList.json in Resources */ = {isa = PBXBuildFile; fileRef = 19696AF42AE80DBD00D1F8F9 /* blockerList.json */; }; | ||
66 | + 19EECA452ACED45A00094AFB /* SearchResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19EECA442ACED45A00094AFB /* SearchResultView.swift */; }; | ||
67 | + 19EECA472ACED48000094AFB /* SearchResultViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19EECA462ACED48000094AFB /* SearchResultViewController.swift */; }; | ||
68 | + 19FCBF222AC1727800F83A7F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19FCBF212AC1727800F83A7F /* AppDelegate.swift */; }; | ||
69 | + 19FCBF262AC1727800F83A7F /* BrowserHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19FCBF252AC1727800F83A7F /* BrowserHomeViewController.swift */; }; | ||
70 | + 19FCBF2B2AC1727900F83A7F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19FCBF2A2AC1727900F83A7F /* Assets.xcassets */; }; | ||
71 | + 19FCBF2E2AC1727900F83A7F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 19FCBF2C2AC1727900F83A7F /* LaunchScreen.storyboard */; }; | ||
72 | + 19FCBF3D2AC17AB000F83A7F /* BrowserHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19FCBF3C2AC17AB000F83A7F /* BrowserHomeView.swift */; }; | ||
73 | + 19FCBF452AC1981A00F83A7F /* TabCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19FCBF442AC1981A00F83A7F /* TabCollectionViewCell.swift */; }; | ||
74 | + 19FFD6BF2AE64A7B00D0F768 /* SnapshotService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19FFD6BE2AE64A7B00D0F768 /* SnapshotService.swift */; }; | ||
75 | +/* End PBXBuildFile section */ | ||
76 | + | ||
77 | +/* Begin PBXContainerItemProxy section */ | ||
78 | + 19696AF92AE80DBD00D1F8F9 /* PBXContainerItemProxy */ = { | ||
79 | + isa = PBXContainerItemProxy; | ||
80 | + containerPortal = 19FCBF162AC1727800F83A7F /* Project object */; | ||
81 | + proxyType = 1; | ||
82 | + remoteGlobalIDString = 19696AF12AE80DBC00D1F8F9; | ||
83 | + remoteInfo = AdBlocker; | ||
84 | + }; | ||
85 | +/* End PBXContainerItemProxy section */ | ||
86 | + | ||
87 | +/* Begin PBXCopyFilesBuildPhase section */ | ||
88 | + 19696AFC2AE80DBD00D1F8F9 /* Embed Foundation Extensions */ = { | ||
89 | + isa = PBXCopyFilesBuildPhase; | ||
90 | + buildActionMask = 2147483647; | ||
91 | + dstPath = ""; | ||
92 | + dstSubfolderSpec = 13; | ||
93 | + files = ( | ||
94 | + 19696AFB2AE80DBD00D1F8F9 /* AdBlocker.appex in Embed Foundation Extensions */, | ||
95 | + ); | ||
96 | + name = "Embed Foundation Extensions"; | ||
97 | + runOnlyForDeploymentPostprocessing = 0; | ||
98 | + }; | ||
99 | +/* End PBXCopyFilesBuildPhase section */ | ||
100 | + | ||
101 | +/* Begin PBXFileReference section */ | ||
102 | + 1904ED452AC56BDB0035DB66 /* FontsFree-Net-SFProText-Semibold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "FontsFree-Net-SFProText-Semibold.ttf"; sourceTree = "<group>"; }; | ||
103 | + 1904ED462AC56BDB0035DB66 /* FontsFree-Net-SFProText-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "FontsFree-Net-SFProText-Regular.ttf"; sourceTree = "<group>"; }; | ||
104 | + 1907D6962ADE766F00C40E9F /* HistoryDBManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryDBManager.swift; sourceTree = "<group>"; }; | ||
105 | + 1907D6982ADE7E9C00C40E9F /* DateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateManager.swift; sourceTree = "<group>"; }; | ||
106 | + 190DB9FB2AC450F6000A7BF3 /* RemoveAdvertViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveAdvertViewController.swift; sourceTree = "<group>"; }; | ||
107 | + 190DB9FF2AC45A3C000A7BF3 /* CachingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachingManager.swift; sourceTree = "<group>"; }; | ||
108 | + 190DBA0C2AC47701000A7BF3 /* BinaryFloatingPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryFloatingPoint.swift; sourceTree = "<group>"; }; | ||
109 | + 191BB84E2AC5A9C900A2DEB9 /* TabsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsView.swift; sourceTree = "<group>"; }; | ||
110 | + 191BB8512AC5AB4600A2DEB9 /* TabsToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsToolbarView.swift; sourceTree = "<group>"; }; | ||
111 | + 191BB8532AC5B3CD00A2DEB9 /* TabsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsViewController.swift; sourceTree = "<group>"; }; | ||
112 | + 191BB8562AC5B4AC00A2DEB9 /* OpenedTabsCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenedTabsCollectionViewCell.swift; sourceTree = "<group>"; }; | ||
113 | + 191BB8612AC6A01500A2DEB9 /* SearchingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchingViewController.swift; sourceTree = "<group>"; }; | ||
114 | + 191BB8632AC6A02300A2DEB9 /* SearchingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchingView.swift; sourceTree = "<group>"; }; | ||
115 | + 191BB8682AC6A66900A2DEB9 /* SearchingTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchingTableViewCell.swift; sourceTree = "<group>"; }; | ||
116 | + 191BB8762AC6B47700A2DEB9 /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = "<group>"; }; | ||
117 | + 191BB8782AC6B48400A2DEB9 /* HistoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewController.swift; sourceTree = "<group>"; }; | ||
118 | + 191BB87B2AC6B79C00A2DEB9 /* HistoryTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryTableViewCell.swift; sourceTree = "<group>"; }; | ||
119 | + 191BB87E2AC6BBBD00A2DEB9 /* HistoryToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryToolbarView.swift; sourceTree = "<group>"; }; | ||
120 | + 191BB8802AC6FF6600A2DEB9 /* RemoveAdvertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveAdvertView.swift; sourceTree = "<group>"; }; | ||
121 | + 1926E82E2B03BED200FEBCFB /* HistoryToolbarMenuCases.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryToolbarMenuCases.swift; sourceTree = "<group>"; }; | ||
122 | + 193B3B822ACAF394002161ED /* BrowserTabDataBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserTabDataBase.swift; sourceTree = "<group>"; }; | ||
123 | + 193B3B892ACAF714002161ED /* TabManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabManager.swift; sourceTree = "<group>"; }; | ||
124 | + 194635D52ADD738E00993D91 /* HistoryDataBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryDataBase.swift; sourceTree = "<group>"; }; | ||
125 | + 19696AF22AE80DBC00D1F8F9 /* AdBlocker.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AdBlocker.appex; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
126 | + 19696AF42AE80DBD00D1F8F9 /* blockerList.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = blockerList.json; sourceTree = "<group>"; }; | ||
127 | + 19696AF62AE80DBD00D1F8F9 /* ContentBlockerRequestHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentBlockerRequestHandler.swift; sourceTree = "<group>"; }; | ||
128 | + 19696AF82AE80DBD00D1F8F9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | ||
129 | + 197FC3EE2AC21E1F007F429C /* PayloadViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayloadViewController.swift; sourceTree = "<group>"; }; | ||
130 | + 197FC3F12AC2AF9A007F429C /* AdvantagesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvantagesTableViewCell.swift; sourceTree = "<group>"; }; | ||
131 | + 197FC3F32AC2BCD7007F429C /* SearchBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarView.swift; sourceTree = "<group>"; }; | ||
132 | + 197FC3F82AC2FB69007F429C /* SettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingViewController.swift; sourceTree = "<group>"; }; | ||
133 | + 197FC3FB2AC2FBA0007F429C /* SettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingView.swift; sourceTree = "<group>"; }; | ||
134 | + 197FC3FE2AC30746007F429C /* SettingTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingTableViewCell.swift; sourceTree = "<group>"; }; | ||
135 | + 197FC4002AC31D5C007F429C /* NavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewController.swift; sourceTree = "<group>"; }; | ||
136 | + 197FC4022AC41EB7007F429C /* ToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarView.swift; sourceTree = "<group>"; }; | ||
137 | + 1984BF3F2AFB90560050F816 /* PrivacyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyViewController.swift; sourceTree = "<group>"; }; | ||
138 | + 1984BF412AFB907A0050F816 /* PrivacyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyView.swift; sourceTree = "<group>"; }; | ||
139 | + 1984BF572AFB970F0050F816 /* TermsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsViewController.swift; sourceTree = "<group>"; }; | ||
140 | + 1984BF592AFB973C0050F816 /* TermsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsView.swift; sourceTree = "<group>"; }; | ||
141 | + 1989A15F2AE29D4B00292680 /* OpenTabsTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenTabsTransition.swift; sourceTree = "<group>"; }; | ||
142 | + 1990C69B2AEFDF89004AF856 /* UICollectionViewCell+convertFrameToScreenCoordinates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionViewCell+convertFrameToScreenCoordinates.swift"; sourceTree = "<group>"; }; | ||
143 | + 199DB1ED2AD3FCFE007E6A81 /* BrowserSearchService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserSearchService.swift; sourceTree = "<group>"; }; | ||
144 | + 19B41DA02AD81A70002C0D31 /* SearchBarContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarContainer.swift; sourceTree = "<group>"; }; | ||
145 | + 19B739692AE1554E0073AA59 /* URLConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLConstants.swift; sourceTree = "<group>"; }; | ||
146 | + 19B7396D2AE156DE0073AA59 /* StringConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringConstants.swift; sourceTree = "<group>"; }; | ||
147 | + 19B739702AE157900073AA59 /* FontConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontConstants.swift; sourceTree = "<group>"; }; | ||
148 | + 19B739722AE15B3B0073AA59 /* ColorConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorConstants.swift; sourceTree = "<group>"; }; | ||
149 | + 19B739752AE276360073AA59 /* HistoryElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryElement.swift; sourceTree = "<group>"; }; | ||
150 | + 19C5F0562AD9784400133BD7 /* DataExtention+toImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataExtention+toImage.swift"; sourceTree = "<group>"; }; | ||
151 | + 19C7A70B2AC9EBB800B954FC /* RemoveAdvertTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveAdvertTableViewCell.swift; sourceTree = "<group>"; }; | ||
152 | + 19C7A70D2AC9F5FD00B954FC /* HistorySearchBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistorySearchBarView.swift; sourceTree = "<group>"; }; | ||
153 | + 19D1F2DF2AC1EF3200510506 /* PayloadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayloadView.swift; sourceTree = "<group>"; }; | ||
154 | + 19EECA442ACED45A00094AFB /* SearchResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultView.swift; sourceTree = "<group>"; }; | ||
155 | + 19EECA462ACED48000094AFB /* SearchResultViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultViewController.swift; sourceTree = "<group>"; }; | ||
156 | + 19FCBF1E2AC1727800F83A7F /* browser.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = browser.app; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
157 | + 19FCBF212AC1727800F83A7F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | ||
158 | + 19FCBF252AC1727800F83A7F /* BrowserHomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserHomeViewController.swift; sourceTree = "<group>"; }; | ||
159 | + 19FCBF2A2AC1727900F83A7F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; | ||
160 | + 19FCBF2D2AC1727900F83A7F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; | ||
161 | + 19FCBF2F2AC1727900F83A7F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | ||
162 | + 19FCBF3C2AC17AB000F83A7F /* BrowserHomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserHomeView.swift; sourceTree = "<group>"; }; | ||
163 | + 19FCBF442AC1981A00F83A7F /* TabCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabCollectionViewCell.swift; sourceTree = "<group>"; }; | ||
164 | + 19FFD6BE2AE64A7B00D0F768 /* SnapshotService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotService.swift; sourceTree = "<group>"; }; | ||
165 | +/* End PBXFileReference section */ | ||
166 | + | ||
167 | +/* Begin PBXFrameworksBuildPhase section */ | ||
168 | + 19696AEF2AE80DBC00D1F8F9 /* Frameworks */ = { | ||
169 | + isa = PBXFrameworksBuildPhase; | ||
170 | + buildActionMask = 2147483647; | ||
171 | + files = ( | ||
172 | + ); | ||
173 | + runOnlyForDeploymentPostprocessing = 0; | ||
174 | + }; | ||
175 | + 19FCBF1B2AC1727800F83A7F /* Frameworks */ = { | ||
176 | + isa = PBXFrameworksBuildPhase; | ||
177 | + buildActionMask = 2147483647; | ||
178 | + files = ( | ||
179 | + 193B3B882ACAF480002161ED /* RealmSwift in Frameworks */, | ||
180 | + 199DB1F12AD3FDEE007E6A81 /* Alamofire in Frameworks */, | ||
181 | + 193B3B862ACAF480002161ED /* Realm in Frameworks */, | ||
182 | + 19CD89482AC719AC0035CB55 /* SnapKit in Frameworks */, | ||
183 | + ); | ||
184 | + runOnlyForDeploymentPostprocessing = 0; | ||
185 | + }; | ||
186 | +/* End PBXFrameworksBuildPhase section */ | ||
187 | + | ||
188 | +/* Begin PBXGroup section */ | ||
189 | + 190DB9F72AC44FCA000A7BF3 /* RemoveAdvert */ = { | ||
190 | + isa = PBXGroup; | ||
191 | + children = ( | ||
192 | + 190DB9F82AC44FD4000A7BF3 /* View */, | ||
193 | + 190DB9F92AC44FD8000A7BF3 /* Controller */, | ||
194 | + ); | ||
195 | + path = RemoveAdvert; | ||
196 | + sourceTree = "<group>"; | ||
197 | + }; | ||
198 | + 190DB9F82AC44FD4000A7BF3 /* View */ = { | ||
199 | + isa = PBXGroup; | ||
200 | + children = ( | ||
201 | + 19C7A70A2AC9EB9B00B954FC /* Cell */, | ||
202 | + 191BB8802AC6FF6600A2DEB9 /* RemoveAdvertView.swift */, | ||
203 | + ); | ||
204 | + path = View; | ||
205 | + sourceTree = "<group>"; | ||
206 | + }; | ||
207 | + 190DB9F92AC44FD8000A7BF3 /* Controller */ = { | ||
208 | + isa = PBXGroup; | ||
209 | + children = ( | ||
210 | + 190DB9FB2AC450F6000A7BF3 /* RemoveAdvertViewController.swift */, | ||
211 | + ); | ||
212 | + path = Controller; | ||
213 | + sourceTree = "<group>"; | ||
214 | + }; | ||
215 | + 190DB9FD2AC459CD000A7BF3 /* Managers */ = { | ||
216 | + isa = PBXGroup; | ||
217 | + children = ( | ||
218 | + 190DB9FF2AC45A3C000A7BF3 /* CachingManager.swift */, | ||
219 | + 193B3B892ACAF714002161ED /* TabManager.swift */, | ||
220 | + 1907D6962ADE766F00C40E9F /* HistoryDBManager.swift */, | ||
221 | + 1907D6982ADE7E9C00C40E9F /* DateManager.swift */, | ||
222 | + ); | ||
223 | + path = Managers; | ||
224 | + sourceTree = "<group>"; | ||
225 | + }; | ||
226 | + 190DBA012AC45CDA000A7BF3 /* Extentions */ = { | ||
227 | + isa = PBXGroup; | ||
228 | + children = ( | ||
229 | + 190DBA0C2AC47701000A7BF3 /* BinaryFloatingPoint.swift */, | ||
230 | + 19C5F0562AD9784400133BD7 /* DataExtention+toImage.swift */, | ||
231 | + 1990C69B2AEFDF89004AF856 /* UICollectionViewCell+convertFrameToScreenCoordinates.swift */, | ||
232 | + ); | ||
233 | + path = Extentions; | ||
234 | + sourceTree = "<group>"; | ||
235 | + }; | ||
236 | + 190DBA042AC474C4000A7BF3 /* Fonts */ = { | ||
237 | + isa = PBXGroup; | ||
238 | + children = ( | ||
239 | + 1904ED462AC56BDB0035DB66 /* FontsFree-Net-SFProText-Regular.ttf */, | ||
240 | + 1904ED452AC56BDB0035DB66 /* FontsFree-Net-SFProText-Semibold.ttf */, | ||
241 | + ); | ||
242 | + path = Fonts; | ||
243 | + sourceTree = "<group>"; | ||
244 | + }; | ||
245 | + 191BB84A2AC5A8EF00A2DEB9 /* Components */ = { | ||
246 | + isa = PBXGroup; | ||
247 | + children = ( | ||
248 | + 197FC4022AC41EB7007F429C /* ToolbarView.swift */, | ||
249 | + 197FC3F32AC2BCD7007F429C /* SearchBarView.swift */, | ||
250 | + 19B41DA02AD81A70002C0D31 /* SearchBarContainer.swift */, | ||
251 | + ); | ||
252 | + path = Components; | ||
253 | + sourceTree = "<group>"; | ||
254 | + }; | ||
255 | + 191BB84B2AC5A9A600A2DEB9 /* Tabs */ = { | ||
256 | + isa = PBXGroup; | ||
257 | + children = ( | ||
258 | + 191BB84D2AC5A9B400A2DEB9 /* View */, | ||
259 | + 191BB84C2AC5A9AD00A2DEB9 /* Controller */, | ||
260 | + ); | ||
261 | + path = Tabs; | ||
262 | + sourceTree = "<group>"; | ||
263 | + }; | ||
264 | + 191BB84C2AC5A9AD00A2DEB9 /* Controller */ = { | ||
265 | + isa = PBXGroup; | ||
266 | + children = ( | ||
267 | + 191BB8532AC5B3CD00A2DEB9 /* TabsViewController.swift */, | ||
268 | + ); | ||
269 | + path = Controller; | ||
270 | + sourceTree = "<group>"; | ||
271 | + }; | ||
272 | + 191BB84D2AC5A9B400A2DEB9 /* View */ = { | ||
273 | + isa = PBXGroup; | ||
274 | + children = ( | ||
275 | + 191BB8552AC5B48F00A2DEB9 /* Cell */, | ||
276 | + 191BB8502AC5AB2700A2DEB9 /* Components */, | ||
277 | + 191BB84E2AC5A9C900A2DEB9 /* TabsView.swift */, | ||
278 | + ); | ||
279 | + path = View; | ||
280 | + sourceTree = "<group>"; | ||
281 | + }; | ||
282 | + 191BB8502AC5AB2700A2DEB9 /* Components */ = { | ||
283 | + isa = PBXGroup; | ||
284 | + children = ( | ||
285 | + 191BB8512AC5AB4600A2DEB9 /* TabsToolbarView.swift */, | ||
286 | + ); | ||
287 | + path = Components; | ||
288 | + sourceTree = "<group>"; | ||
289 | + }; | ||
290 | + 191BB8552AC5B48F00A2DEB9 /* Cell */ = { | ||
291 | + isa = PBXGroup; | ||
292 | + children = ( | ||
293 | + 191BB8562AC5B4AC00A2DEB9 /* OpenedTabsCollectionViewCell.swift */, | ||
294 | + ); | ||
295 | + path = Cell; | ||
296 | + sourceTree = "<group>"; | ||
297 | + }; | ||
298 | + 191BB85D2AC69FC800A2DEB9 /* Searching */ = { | ||
299 | + isa = PBXGroup; | ||
300 | + children = ( | ||
301 | + 191BB85F2AC69FDB00A2DEB9 /* View */, | ||
302 | + 191BB85E2AC69FCC00A2DEB9 /* Controller */, | ||
303 | + ); | ||
304 | + path = Searching; | ||
305 | + sourceTree = "<group>"; | ||
306 | + }; | ||
307 | + 191BB85E2AC69FCC00A2DEB9 /* Controller */ = { | ||
308 | + isa = PBXGroup; | ||
309 | + children = ( | ||
310 | + 191BB8612AC6A01500A2DEB9 /* SearchingViewController.swift */, | ||
311 | + ); | ||
312 | + path = Controller; | ||
313 | + sourceTree = "<group>"; | ||
314 | + }; | ||
315 | + 191BB85F2AC69FDB00A2DEB9 /* View */ = { | ||
316 | + isa = PBXGroup; | ||
317 | + children = ( | ||
318 | + 191BB8672AC6A65600A2DEB9 /* Cell */, | ||
319 | + 191BB8632AC6A02300A2DEB9 /* SearchingView.swift */, | ||
320 | + ); | ||
321 | + path = View; | ||
322 | + sourceTree = "<group>"; | ||
323 | + }; | ||
324 | + 191BB8672AC6A65600A2DEB9 /* Cell */ = { | ||
325 | + isa = PBXGroup; | ||
326 | + children = ( | ||
327 | + 191BB8682AC6A66900A2DEB9 /* SearchingTableViewCell.swift */, | ||
328 | + ); | ||
329 | + path = Cell; | ||
330 | + sourceTree = "<group>"; | ||
331 | + }; | ||
332 | + 191BB8702AC6B19F00A2DEB9 /* History */ = { | ||
333 | + isa = PBXGroup; | ||
334 | + children = ( | ||
335 | + 191BB8722AC6B26500A2DEB9 /* View */, | ||
336 | + 191BB8712AC6B25300A2DEB9 /* Controller */, | ||
337 | + ); | ||
338 | + path = History; | ||
339 | + sourceTree = "<group>"; | ||
340 | + }; | ||
341 | + 191BB8712AC6B25300A2DEB9 /* Controller */ = { | ||
342 | + isa = PBXGroup; | ||
343 | + children = ( | ||
344 | + 191BB8782AC6B48400A2DEB9 /* HistoryViewController.swift */, | ||
345 | + ); | ||
346 | + path = Controller; | ||
347 | + sourceTree = "<group>"; | ||
348 | + }; | ||
349 | + 191BB8722AC6B26500A2DEB9 /* View */ = { | ||
350 | + isa = PBXGroup; | ||
351 | + children = ( | ||
352 | + 191BB87A2AC6B78800A2DEB9 /* Cell */, | ||
353 | + 191BB87D2AC6BB6600A2DEB9 /* Components */, | ||
354 | + 191BB8762AC6B47700A2DEB9 /* HistoryView.swift */, | ||
355 | + ); | ||
356 | + path = View; | ||
357 | + sourceTree = "<group>"; | ||
358 | + }; | ||
359 | + 191BB87A2AC6B78800A2DEB9 /* Cell */ = { | ||
360 | + isa = PBXGroup; | ||
361 | + children = ( | ||
362 | + 191BB87B2AC6B79C00A2DEB9 /* HistoryTableViewCell.swift */, | ||
363 | + ); | ||
364 | + path = Cell; | ||
365 | + sourceTree = "<group>"; | ||
366 | + }; | ||
367 | + 191BB87D2AC6BB6600A2DEB9 /* Components */ = { | ||
368 | + isa = PBXGroup; | ||
369 | + children = ( | ||
370 | + 191BB87E2AC6BBBD00A2DEB9 /* HistoryToolbarView.swift */, | ||
371 | + 19C7A70D2AC9F5FD00B954FC /* HistorySearchBarView.swift */, | ||
372 | + ); | ||
373 | + path = Components; | ||
374 | + sourceTree = "<group>"; | ||
375 | + }; | ||
376 | + 193B3B812ACAF36C002161ED /* DataBases */ = { | ||
377 | + isa = PBXGroup; | ||
378 | + children = ( | ||
379 | + 193B3B822ACAF394002161ED /* BrowserTabDataBase.swift */, | ||
380 | + 194635D52ADD738E00993D91 /* HistoryDataBase.swift */, | ||
381 | + ); | ||
382 | + path = DataBases; | ||
383 | + sourceTree = "<group>"; | ||
384 | + }; | ||
385 | + 19696AF32AE80DBD00D1F8F9 /* AdBlocker */ = { | ||
386 | + isa = PBXGroup; | ||
387 | + children = ( | ||
388 | + 19696AF62AE80DBD00D1F8F9 /* ContentBlockerRequestHandler.swift */, | ||
389 | + 19696AF82AE80DBD00D1F8F9 /* Info.plist */, | ||
390 | + ); | ||
391 | + path = AdBlocker; | ||
392 | + sourceTree = "<group>"; | ||
393 | + }; | ||
394 | + 197FC3F02AC2AF8A007F429C /* Cell */ = { | ||
395 | + isa = PBXGroup; | ||
396 | + children = ( | ||
397 | + 197FC3F12AC2AF9A007F429C /* AdvantagesTableViewCell.swift */, | ||
398 | + ); | ||
399 | + path = Cell; | ||
400 | + sourceTree = "<group>"; | ||
401 | + }; | ||
402 | + 197FC3F52AC2F9FA007F429C /* Settings */ = { | ||
403 | + isa = PBXGroup; | ||
404 | + children = ( | ||
405 | + 197FC3F72AC2FA0C007F429C /* View */, | ||
406 | + 197FC3F62AC2FA06007F429C /* Controller */, | ||
407 | + ); | ||
408 | + path = Settings; | ||
409 | + sourceTree = "<group>"; | ||
410 | + }; | ||
411 | + 197FC3F62AC2FA06007F429C /* Controller */ = { | ||
412 | + isa = PBXGroup; | ||
413 | + children = ( | ||
414 | + 197FC3F82AC2FB69007F429C /* SettingViewController.swift */, | ||
415 | + ); | ||
416 | + path = Controller; | ||
417 | + sourceTree = "<group>"; | ||
418 | + }; | ||
419 | + 197FC3F72AC2FA0C007F429C /* View */ = { | ||
420 | + isa = PBXGroup; | ||
421 | + children = ( | ||
422 | + 1984BF3A2AFB8FFA0050F816 /* Components */, | ||
423 | + 197FC3FD2AC30738007F429C /* Cell */, | ||
424 | + 197FC3FB2AC2FBA0007F429C /* SettingView.swift */, | ||
425 | + ); | ||
426 | + path = View; | ||
427 | + sourceTree = "<group>"; | ||
428 | + }; | ||
429 | + 197FC3FD2AC30738007F429C /* Cell */ = { | ||
430 | + isa = PBXGroup; | ||
431 | + children = ( | ||
432 | + 197FC3FE2AC30746007F429C /* SettingTableViewCell.swift */, | ||
433 | + ); | ||
434 | + path = Cell; | ||
435 | + sourceTree = "<group>"; | ||
436 | + }; | ||
437 | + 1984BF3A2AFB8FFA0050F816 /* Components */ = { | ||
438 | + isa = PBXGroup; | ||
439 | + children = ( | ||
440 | + 1984BF432AFB955C0050F816 /* Terms */, | ||
441 | + 1984BF3B2AFB902A0050F816 /* Privacy */, | ||
442 | + ); | ||
443 | + path = Components; | ||
444 | + sourceTree = "<group>"; | ||
445 | + }; | ||
446 | + 1984BF3B2AFB902A0050F816 /* Privacy */ = { | ||
447 | + isa = PBXGroup; | ||
448 | + children = ( | ||
449 | + 1984BF3E2AFB90430050F816 /* Controller */, | ||
450 | + 1984BF3D2AFB903C0050F816 /* View */, | ||
451 | + ); | ||
452 | + path = Privacy; | ||
453 | + sourceTree = "<group>"; | ||
454 | + }; | ||
455 | + 1984BF3D2AFB903C0050F816 /* View */ = { | ||
456 | + isa = PBXGroup; | ||
457 | + children = ( | ||
458 | + 1984BF412AFB907A0050F816 /* PrivacyView.swift */, | ||
459 | + ); | ||
460 | + path = View; | ||
461 | + sourceTree = "<group>"; | ||
462 | + }; | ||
463 | + 1984BF3E2AFB90430050F816 /* Controller */ = { | ||
464 | + isa = PBXGroup; | ||
465 | + children = ( | ||
466 | + 1984BF3F2AFB90560050F816 /* PrivacyViewController.swift */, | ||
467 | + ); | ||
468 | + path = Controller; | ||
469 | + sourceTree = "<group>"; | ||
470 | + }; | ||
471 | + 1984BF432AFB955C0050F816 /* Terms */ = { | ||
472 | + isa = PBXGroup; | ||
473 | + children = ( | ||
474 | + 1984BF562AFB97030050F816 /* Controller */, | ||
475 | + 1984BF552AFB96FE0050F816 /* View */, | ||
476 | + ); | ||
477 | + path = Terms; | ||
478 | + sourceTree = "<group>"; | ||
479 | + }; | ||
480 | + 1984BF552AFB96FE0050F816 /* View */ = { | ||
481 | + isa = PBXGroup; | ||
482 | + children = ( | ||
483 | + 1984BF592AFB973C0050F816 /* TermsView.swift */, | ||
484 | + ); | ||
485 | + path = View; | ||
486 | + sourceTree = "<group>"; | ||
487 | + }; | ||
488 | + 1984BF562AFB97030050F816 /* Controller */ = { | ||
489 | + isa = PBXGroup; | ||
490 | + children = ( | ||
491 | + 1984BF572AFB970F0050F816 /* TermsViewController.swift */, | ||
492 | + ); | ||
493 | + path = Controller; | ||
494 | + sourceTree = "<group>"; | ||
495 | + }; | ||
496 | + 199DB1EC2AD3FCE1007E6A81 /* Services */ = { | ||
497 | + isa = PBXGroup; | ||
498 | + children = ( | ||
499 | + 199DB1ED2AD3FCFE007E6A81 /* BrowserSearchService.swift */, | ||
500 | + 19FFD6BE2AE64A7B00D0F768 /* SnapshotService.swift */, | ||
501 | + ); | ||
502 | + path = Services; | ||
503 | + sourceTree = "<group>"; | ||
504 | + }; | ||
505 | + 19B2D0AF2ACDEAB600D20CE8 /* SearchResult */ = { | ||
506 | + isa = PBXGroup; | ||
507 | + children = ( | ||
508 | + 19EECA432ACED43400094AFB /* View */, | ||
509 | + 19B2D0B02ACDEB3500D20CE8 /* Controller */, | ||
510 | + ); | ||
511 | + path = SearchResult; | ||
512 | + sourceTree = "<group>"; | ||
513 | + }; | ||
514 | + 19B2D0B02ACDEB3500D20CE8 /* Controller */ = { | ||
515 | + isa = PBXGroup; | ||
516 | + children = ( | ||
517 | + 19EECA462ACED48000094AFB /* SearchResultViewController.swift */, | ||
518 | + ); | ||
519 | + path = Controller; | ||
520 | + sourceTree = "<group>"; | ||
521 | + }; | ||
522 | + 19B7396B2AE155540073AA59 /* Images */ = { | ||
523 | + isa = PBXGroup; | ||
524 | + children = ( | ||
525 | + 19FCBF2A2AC1727900F83A7F /* Assets.xcassets */, | ||
526 | + ); | ||
527 | + path = Images; | ||
528 | + sourceTree = "<group>"; | ||
529 | + }; | ||
530 | + 19B7396C2AE1555F0073AA59 /* Other */ = { | ||
531 | + isa = PBXGroup; | ||
532 | + children = ( | ||
533 | + 19FCBF2C2AC1727900F83A7F /* LaunchScreen.storyboard */, | ||
534 | + ); | ||
535 | + path = Other; | ||
536 | + sourceTree = "<group>"; | ||
537 | + }; | ||
538 | + 19B7396F2AE1573B0073AA59 /* Constants */ = { | ||
539 | + isa = PBXGroup; | ||
540 | + children = ( | ||
541 | + 19B739692AE1554E0073AA59 /* URLConstants.swift */, | ||
542 | + 19B7396D2AE156DE0073AA59 /* StringConstants.swift */, | ||
543 | + 19B739702AE157900073AA59 /* FontConstants.swift */, | ||
544 | + 19B739722AE15B3B0073AA59 /* ColorConstants.swift */, | ||
545 | + ); | ||
546 | + path = Constants; | ||
547 | + sourceTree = "<group>"; | ||
548 | + }; | ||
549 | + 19B739742AE275FF0073AA59 /* Models */ = { | ||
550 | + isa = PBXGroup; | ||
551 | + children = ( | ||
552 | + 19B739752AE276360073AA59 /* HistoryElement.swift */, | ||
553 | + 1926E82E2B03BED200FEBCFB /* HistoryToolbarMenuCases.swift */, | ||
554 | + ); | ||
555 | + path = Models; | ||
556 | + sourceTree = "<group>"; | ||
557 | + }; | ||
558 | + 19C7A70A2AC9EB9B00B954FC /* Cell */ = { | ||
559 | + isa = PBXGroup; | ||
560 | + children = ( | ||
561 | + 19C7A70B2AC9EBB800B954FC /* RemoveAdvertTableViewCell.swift */, | ||
562 | + ); | ||
563 | + path = Cell; | ||
564 | + sourceTree = "<group>"; | ||
565 | + }; | ||
566 | + 19D1F2DC2AC1EE6200510506 /* Payload */ = { | ||
567 | + isa = PBXGroup; | ||
568 | + children = ( | ||
569 | + 19D1F2DE2AC1EF0400510506 /* View */, | ||
570 | + 19D1F2DD2AC1EEF800510506 /* Controller */, | ||
571 | + ); | ||
572 | + path = Payload; | ||
573 | + sourceTree = "<group>"; | ||
574 | + }; | ||
575 | + 19D1F2DD2AC1EEF800510506 /* Controller */ = { | ||
576 | + isa = PBXGroup; | ||
577 | + children = ( | ||
578 | + 197FC3EE2AC21E1F007F429C /* PayloadViewController.swift */, | ||
579 | + ); | ||
580 | + path = Controller; | ||
581 | + sourceTree = "<group>"; | ||
582 | + }; | ||
583 | + 19D1F2DE2AC1EF0400510506 /* View */ = { | ||
584 | + isa = PBXGroup; | ||
585 | + children = ( | ||
586 | + 197FC3F02AC2AF8A007F429C /* Cell */, | ||
587 | + 19D1F2DF2AC1EF3200510506 /* PayloadView.swift */, | ||
588 | + ); | ||
589 | + path = View; | ||
590 | + sourceTree = "<group>"; | ||
591 | + }; | ||
592 | + 19EECA432ACED43400094AFB /* View */ = { | ||
593 | + isa = PBXGroup; | ||
594 | + children = ( | ||
595 | + 19EECA442ACED45A00094AFB /* SearchResultView.swift */, | ||
596 | + ); | ||
597 | + path = View; | ||
598 | + sourceTree = "<group>"; | ||
599 | + }; | ||
600 | + 19F65A712ACBFCB500B50F61 /* Resources */ = { | ||
601 | + isa = PBXGroup; | ||
602 | + children = ( | ||
603 | + 19696AF42AE80DBD00D1F8F9 /* blockerList.json */, | ||
604 | + 19B7396F2AE1573B0073AA59 /* Constants */, | ||
605 | + 19B7396B2AE155540073AA59 /* Images */, | ||
606 | + 190DBA042AC474C4000A7BF3 /* Fonts */, | ||
607 | + 19B7396C2AE1555F0073AA59 /* Other */, | ||
608 | + ); | ||
609 | + path = Resources; | ||
610 | + sourceTree = "<group>"; | ||
611 | + }; | ||
612 | + 19F65A722ACBFD7300B50F61 /* Common */ = { | ||
613 | + isa = PBXGroup; | ||
614 | + children = ( | ||
615 | + 19F65A732ACBFDD100B50F61 /* Transitions */, | ||
616 | + ); | ||
617 | + path = Common; | ||
618 | + sourceTree = "<group>"; | ||
619 | + }; | ||
620 | + 19F65A732ACBFDD100B50F61 /* Transitions */ = { | ||
621 | + isa = PBXGroup; | ||
622 | + children = ( | ||
623 | + 1989A15F2AE29D4B00292680 /* OpenTabsTransition.swift */, | ||
624 | + ); | ||
625 | + path = Transitions; | ||
626 | + sourceTree = "<group>"; | ||
627 | + }; | ||
628 | + 19FCBF152AC1727800F83A7F = { | ||
629 | + isa = PBXGroup; | ||
630 | + children = ( | ||
631 | + 19FCBF202AC1727800F83A7F /* browser */, | ||
632 | + 19696AF32AE80DBD00D1F8F9 /* AdBlocker */, | ||
633 | + 19FCBF1F2AC1727800F83A7F /* Products */, | ||
634 | + ); | ||
635 | + sourceTree = "<group>"; | ||
636 | + }; | ||
637 | + 19FCBF1F2AC1727800F83A7F /* Products */ = { | ||
638 | + isa = PBXGroup; | ||
639 | + children = ( | ||
640 | + 19FCBF1E2AC1727800F83A7F /* browser.app */, | ||
641 | + 19696AF22AE80DBC00D1F8F9 /* AdBlocker.appex */, | ||
642 | + ); | ||
643 | + name = Products; | ||
644 | + sourceTree = "<group>"; | ||
645 | + }; | ||
646 | + 19FCBF202AC1727800F83A7F /* browser */ = { | ||
647 | + isa = PBXGroup; | ||
648 | + children = ( | ||
649 | + 19B739742AE275FF0073AA59 /* Models */, | ||
650 | + 19FCBF382AC17A4800F83A7F /* Modules */, | ||
651 | + 199DB1EC2AD3FCE1007E6A81 /* Services */, | ||
652 | + 19F65A722ACBFD7300B50F61 /* Common */, | ||
653 | + 190DBA012AC45CDA000A7BF3 /* Extentions */, | ||
654 | + 193B3B812ACAF36C002161ED /* DataBases */, | ||
655 | + 19F65A712ACBFCB500B50F61 /* Resources */, | ||
656 | + 190DB9FD2AC459CD000A7BF3 /* Managers */, | ||
657 | + 19FCBF352AC1779800F83A7F /* Navigation */, | ||
658 | + 19FCBF212AC1727800F83A7F /* AppDelegate.swift */, | ||
659 | + 19FCBF2F2AC1727900F83A7F /* Info.plist */, | ||
660 | + ); | ||
661 | + path = browser; | ||
662 | + sourceTree = "<group>"; | ||
663 | + }; | ||
664 | + 19FCBF352AC1779800F83A7F /* Navigation */ = { | ||
665 | + isa = PBXGroup; | ||
666 | + children = ( | ||
667 | + 197FC4002AC31D5C007F429C /* NavigationViewController.swift */, | ||
668 | + ); | ||
669 | + path = Navigation; | ||
670 | + sourceTree = "<group>"; | ||
671 | + }; | ||
672 | + 19FCBF382AC17A4800F83A7F /* Modules */ = { | ||
673 | + isa = PBXGroup; | ||
674 | + children = ( | ||
675 | + 19B2D0AF2ACDEAB600D20CE8 /* SearchResult */, | ||
676 | + 191BB8702AC6B19F00A2DEB9 /* History */, | ||
677 | + 191BB85D2AC69FC800A2DEB9 /* Searching */, | ||
678 | + 191BB84B2AC5A9A600A2DEB9 /* Tabs */, | ||
679 | + 190DB9F72AC44FCA000A7BF3 /* RemoveAdvert */, | ||
680 | + 197FC3F52AC2F9FA007F429C /* Settings */, | ||
681 | + 19D1F2DC2AC1EE6200510506 /* Payload */, | ||
682 | + 19FCBF392AC17A6600F83A7F /* Home */, | ||
683 | + ); | ||
684 | + path = Modules; | ||
685 | + sourceTree = "<group>"; | ||
686 | + }; | ||
687 | + 19FCBF392AC17A6600F83A7F /* Home */ = { | ||
688 | + isa = PBXGroup; | ||
689 | + children = ( | ||
690 | + 19FCBF3B2AC17A8400F83A7F /* View */, | ||
691 | + 19FCBF3A2AC17A7800F83A7F /* Controller */, | ||
692 | + ); | ||
693 | + path = Home; | ||
694 | + sourceTree = "<group>"; | ||
695 | + }; | ||
696 | + 19FCBF3A2AC17A7800F83A7F /* Controller */ = { | ||
697 | + isa = PBXGroup; | ||
698 | + children = ( | ||
699 | + 19FCBF252AC1727800F83A7F /* BrowserHomeViewController.swift */, | ||
700 | + ); | ||
701 | + path = Controller; | ||
702 | + sourceTree = "<group>"; | ||
703 | + }; | ||
704 | + 19FCBF3B2AC17A8400F83A7F /* View */ = { | ||
705 | + isa = PBXGroup; | ||
706 | + children = ( | ||
707 | + 19FCBF432AC197FD00F83A7F /* Cell */, | ||
708 | + 191BB84A2AC5A8EF00A2DEB9 /* Components */, | ||
709 | + 19FCBF3C2AC17AB000F83A7F /* BrowserHomeView.swift */, | ||
710 | + ); | ||
711 | + path = View; | ||
712 | + sourceTree = "<group>"; | ||
713 | + }; | ||
714 | + 19FCBF432AC197FD00F83A7F /* Cell */ = { | ||
715 | + isa = PBXGroup; | ||
716 | + children = ( | ||
717 | + 19FCBF442AC1981A00F83A7F /* TabCollectionViewCell.swift */, | ||
718 | + ); | ||
719 | + path = Cell; | ||
720 | + sourceTree = "<group>"; | ||
721 | + }; | ||
722 | +/* End PBXGroup section */ | ||
723 | + | ||
724 | +/* Begin PBXNativeTarget section */ | ||
725 | + 19696AF12AE80DBC00D1F8F9 /* AdBlocker */ = { | ||
726 | + isa = PBXNativeTarget; | ||
727 | + buildConfigurationList = 19696AFF2AE80DBD00D1F8F9 /* Build configuration list for PBXNativeTarget "AdBlocker" */; | ||
728 | + buildPhases = ( | ||
729 | + 19696AEE2AE80DBC00D1F8F9 /* Sources */, | ||
730 | + 19696AEF2AE80DBC00D1F8F9 /* Frameworks */, | ||
731 | + 19696AF02AE80DBC00D1F8F9 /* Resources */, | ||
732 | + ); | ||
733 | + buildRules = ( | ||
734 | + ); | ||
735 | + dependencies = ( | ||
736 | + ); | ||
737 | + name = AdBlocker; | ||
738 | + productName = AdBlocker; | ||
739 | + productReference = 19696AF22AE80DBC00D1F8F9 /* AdBlocker.appex */; | ||
740 | + productType = "com.apple.product-type.app-extension"; | ||
741 | + }; | ||
742 | + 19FCBF1D2AC1727800F83A7F /* browser */ = { | ||
743 | + isa = PBXNativeTarget; | ||
744 | + buildConfigurationList = 19FCBF322AC1727900F83A7F /* Build configuration list for PBXNativeTarget "browser" */; | ||
745 | + buildPhases = ( | ||
746 | + 19FCBF1A2AC1727800F83A7F /* Sources */, | ||
747 | + 19FCBF1B2AC1727800F83A7F /* Frameworks */, | ||
748 | + 19FCBF1C2AC1727800F83A7F /* Resources */, | ||
749 | + 19696AFC2AE80DBD00D1F8F9 /* Embed Foundation Extensions */, | ||
750 | + ); | ||
751 | + buildRules = ( | ||
752 | + ); | ||
753 | + dependencies = ( | ||
754 | + 19696AFA2AE80DBD00D1F8F9 /* PBXTargetDependency */, | ||
755 | + ); | ||
756 | + name = browser; | ||
757 | + packageProductDependencies = ( | ||
758 | + 19CD89472AC719AC0035CB55 /* SnapKit */, | ||
759 | + 193B3B852ACAF480002161ED /* Realm */, | ||
760 | + 193B3B872ACAF480002161ED /* RealmSwift */, | ||
761 | + 199DB1F02AD3FDEE007E6A81 /* Alamofire */, | ||
762 | + ); | ||
763 | + productName = browser; | ||
764 | + productReference = 19FCBF1E2AC1727800F83A7F /* browser.app */; | ||
765 | + productType = "com.apple.product-type.application"; | ||
766 | + }; | ||
767 | +/* End PBXNativeTarget section */ | ||
768 | + | ||
769 | +/* Begin PBXProject section */ | ||
770 | + 19FCBF162AC1727800F83A7F /* Project object */ = { | ||
771 | + isa = PBXProject; | ||
772 | + attributes = { | ||
773 | + BuildIndependentTargetsInParallel = 1; | ||
774 | + LastSwiftUpdateCheck = 1500; | ||
775 | + LastUpgradeCheck = 1500; | ||
776 | + TargetAttributes = { | ||
777 | + 19696AF12AE80DBC00D1F8F9 = { | ||
778 | + CreatedOnToolsVersion = 15.0.1; | ||
779 | + }; | ||
780 | + 19FCBF1D2AC1727800F83A7F = { | ||
781 | + CreatedOnToolsVersion = 15.0; | ||
782 | + }; | ||
783 | + }; | ||
784 | + }; | ||
785 | + buildConfigurationList = 19FCBF192AC1727800F83A7F /* Build configuration list for PBXProject "browser" */; | ||
786 | + compatibilityVersion = "Xcode 14.0"; | ||
787 | + developmentRegion = en; | ||
788 | + hasScannedForEncodings = 0; | ||
789 | + knownRegions = ( | ||
790 | + en, | ||
791 | + Base, | ||
792 | + ); | ||
793 | + mainGroup = 19FCBF152AC1727800F83A7F; | ||
794 | + packageReferences = ( | ||
795 | + 19CD89462AC719AB0035CB55 /* XCRemoteSwiftPackageReference "SnapKit" */, | ||
796 | + 193B3B842ACAF480002161ED /* XCRemoteSwiftPackageReference "realm-swift" */, | ||
797 | + 199DB1EF2AD3FDEE007E6A81 /* XCRemoteSwiftPackageReference "Alamofire" */, | ||
798 | + ); | ||
799 | + productRefGroup = 19FCBF1F2AC1727800F83A7F /* Products */; | ||
800 | + projectDirPath = ""; | ||
801 | + projectRoot = ""; | ||
802 | + targets = ( | ||
803 | + 19FCBF1D2AC1727800F83A7F /* browser */, | ||
804 | + 19696AF12AE80DBC00D1F8F9 /* AdBlocker */, | ||
805 | + ); | ||
806 | + }; | ||
807 | +/* End PBXProject section */ | ||
808 | + | ||
809 | +/* Begin PBXResourcesBuildPhase section */ | ||
810 | + 19696AF02AE80DBC00D1F8F9 /* Resources */ = { | ||
811 | + isa = PBXResourcesBuildPhase; | ||
812 | + buildActionMask = 2147483647; | ||
813 | + files = ( | ||
814 | + 19696AF52AE80DBD00D1F8F9 /* blockerList.json in Resources */, | ||
815 | + ); | ||
816 | + runOnlyForDeploymentPostprocessing = 0; | ||
817 | + }; | ||
818 | + 19FCBF1C2AC1727800F83A7F /* Resources */ = { | ||
819 | + isa = PBXResourcesBuildPhase; | ||
820 | + buildActionMask = 2147483647; | ||
821 | + files = ( | ||
822 | + 1904ED472AC56BDB0035DB66 /* FontsFree-Net-SFProText-Semibold.ttf in Resources */, | ||
823 | + 19FCBF2E2AC1727900F83A7F /* LaunchScreen.storyboard in Resources */, | ||
824 | + 1904ED482AC56BDB0035DB66 /* FontsFree-Net-SFProText-Regular.ttf in Resources */, | ||
825 | + 19FCBF2B2AC1727900F83A7F /* Assets.xcassets in Resources */, | ||
826 | + 19D8C9D62AE9169E0062D310 /* blockerList.json in Resources */, | ||
827 | + ); | ||
828 | + runOnlyForDeploymentPostprocessing = 0; | ||
829 | + }; | ||
830 | +/* End PBXResourcesBuildPhase section */ | ||
831 | + | ||
832 | +/* Begin PBXSourcesBuildPhase section */ | ||
833 | + 19696AEE2AE80DBC00D1F8F9 /* Sources */ = { | ||
834 | + isa = PBXSourcesBuildPhase; | ||
835 | + buildActionMask = 2147483647; | ||
836 | + files = ( | ||
837 | + 19696AF72AE80DBD00D1F8F9 /* ContentBlockerRequestHandler.swift in Sources */, | ||
838 | + ); | ||
839 | + runOnlyForDeploymentPostprocessing = 0; | ||
840 | + }; | ||
841 | + 19FCBF1A2AC1727800F83A7F /* Sources */ = { | ||
842 | + isa = PBXSourcesBuildPhase; | ||
843 | + buildActionMask = 2147483647; | ||
844 | + files = ( | ||
845 | + 19B739712AE157900073AA59 /* FontConstants.swift in Sources */, | ||
846 | + 191BB8812AC6FF6600A2DEB9 /* RemoveAdvertView.swift in Sources */, | ||
847 | + 191BB8792AC6B48400A2DEB9 /* HistoryViewController.swift in Sources */, | ||
848 | + 197FC3F42AC2BCD7007F429C /* SearchBarView.swift in Sources */, | ||
849 | + 19B739732AE15B3B0073AA59 /* ColorConstants.swift in Sources */, | ||
850 | + 19FCBF262AC1727800F83A7F /* BrowserHomeViewController.swift in Sources */, | ||
851 | + 197FC3FF2AC30746007F429C /* SettingTableViewCell.swift in Sources */, | ||
852 | + 19B41DA12AD81A70002C0D31 /* SearchBarContainer.swift in Sources */, | ||
853 | + 19EECA452ACED45A00094AFB /* SearchResultView.swift in Sources */, | ||
854 | + 1907D6972ADE766F00C40E9F /* HistoryDBManager.swift in Sources */, | ||
855 | + 19B739762AE276360073AA59 /* HistoryElement.swift in Sources */, | ||
856 | + 197FC4032AC41EB7007F429C /* ToolbarView.swift in Sources */, | ||
857 | + 19B7396A2AE1554E0073AA59 /* URLConstants.swift in Sources */, | ||
858 | + 19EECA472ACED48000094AFB /* SearchResultViewController.swift in Sources */, | ||
859 | + 191BB8572AC5B4AC00A2DEB9 /* OpenedTabsCollectionViewCell.swift in Sources */, | ||
860 | + 190DB9FC2AC450F6000A7BF3 /* RemoveAdvertViewController.swift in Sources */, | ||
861 | + 193B3B832ACAF394002161ED /* BrowserTabDataBase.swift in Sources */, | ||
862 | + 1984BF402AFB90560050F816 /* PrivacyViewController.swift in Sources */, | ||
863 | + 199DB1EE2AD3FCFE007E6A81 /* BrowserSearchService.swift in Sources */, | ||
864 | + 1907D6992ADE7E9C00C40E9F /* DateManager.swift in Sources */, | ||
865 | + 191BB8522AC5AB4600A2DEB9 /* TabsToolbarView.swift in Sources */, | ||
866 | + 191BB84F2AC5A9C900A2DEB9 /* TabsView.swift in Sources */, | ||
867 | + 19FCBF452AC1981A00F83A7F /* TabCollectionViewCell.swift in Sources */, | ||
868 | + 19B7396E2AE156DE0073AA59 /* StringConstants.swift in Sources */, | ||
869 | + 191BB8542AC5B3CD00A2DEB9 /* TabsViewController.swift in Sources */, | ||
870 | + 194635D62ADD738E00993D91 /* HistoryDataBase.swift in Sources */, | ||
871 | + 197FC3F22AC2AF9A007F429C /* AdvantagesTableViewCell.swift in Sources */, | ||
872 | + 191BB8642AC6A02300A2DEB9 /* SearchingView.swift in Sources */, | ||
873 | + 190DBA002AC45A3C000A7BF3 /* CachingManager.swift in Sources */, | ||
874 | + 1984BF422AFB907A0050F816 /* PrivacyView.swift in Sources */, | ||
875 | + 191BB8692AC6A66900A2DEB9 /* SearchingTableViewCell.swift in Sources */, | ||
876 | + 1990C69C2AEFDF89004AF856 /* UICollectionViewCell+convertFrameToScreenCoordinates.swift in Sources */, | ||
877 | + 19FCBF222AC1727800F83A7F /* AppDelegate.swift in Sources */, | ||
878 | + 19C7A70C2AC9EBB800B954FC /* RemoveAdvertTableViewCell.swift in Sources */, | ||
879 | + 19D1F2E02AC1EF3200510506 /* PayloadView.swift in Sources */, | ||
880 | + 19FFD6BF2AE64A7B00D0F768 /* SnapshotService.swift in Sources */, | ||
881 | + 190DBA0D2AC47701000A7BF3 /* BinaryFloatingPoint.swift in Sources */, | ||
882 | + 191BB8772AC6B47700A2DEB9 /* HistoryView.swift in Sources */, | ||
883 | + 19FCBF3D2AC17AB000F83A7F /* BrowserHomeView.swift in Sources */, | ||
884 | + 191BB87F2AC6BBBD00A2DEB9 /* HistoryToolbarView.swift in Sources */, | ||
885 | + 1984BF582AFB970F0050F816 /* TermsViewController.swift in Sources */, | ||
886 | + 193B3B8A2ACAF714002161ED /* TabManager.swift in Sources */, | ||
887 | + 191BB87C2AC6B79C00A2DEB9 /* HistoryTableViewCell.swift in Sources */, | ||
888 | + 1926E82F2B03BED200FEBCFB /* HistoryToolbarMenuCases.swift in Sources */, | ||
889 | + 197FC3F92AC2FB69007F429C /* SettingViewController.swift in Sources */, | ||
890 | + 1984BF5A2AFB973C0050F816 /* TermsView.swift in Sources */, | ||
891 | + 197FC3FC2AC2FBA0007F429C /* SettingView.swift in Sources */, | ||
892 | + 191BB8622AC6A01500A2DEB9 /* SearchingViewController.swift in Sources */, | ||
893 | + 197FC4012AC31D5C007F429C /* NavigationViewController.swift in Sources */, | ||
894 | + 19C5F0572AD9784400133BD7 /* DataExtention+toImage.swift in Sources */, | ||
895 | + 1989A1602AE29D4C00292680 /* OpenTabsTransition.swift in Sources */, | ||
896 | + 197FC3EF2AC21E1F007F429C /* PayloadViewController.swift in Sources */, | ||
897 | + 19C7A70E2AC9F5FD00B954FC /* HistorySearchBarView.swift in Sources */, | ||
898 | + ); | ||
899 | + runOnlyForDeploymentPostprocessing = 0; | ||
900 | + }; | ||
901 | +/* End PBXSourcesBuildPhase section */ | ||
902 | + | ||
903 | +/* Begin PBXTargetDependency section */ | ||
904 | + 19696AFA2AE80DBD00D1F8F9 /* PBXTargetDependency */ = { | ||
905 | + isa = PBXTargetDependency; | ||
906 | + target = 19696AF12AE80DBC00D1F8F9 /* AdBlocker */; | ||
907 | + targetProxy = 19696AF92AE80DBD00D1F8F9 /* PBXContainerItemProxy */; | ||
908 | + }; | ||
909 | +/* End PBXTargetDependency section */ | ||
910 | + | ||
911 | +/* Begin PBXVariantGroup section */ | ||
912 | + 19FCBF2C2AC1727900F83A7F /* LaunchScreen.storyboard */ = { | ||
913 | + isa = PBXVariantGroup; | ||
914 | + children = ( | ||
915 | + 19FCBF2D2AC1727900F83A7F /* Base */, | ||
916 | + ); | ||
917 | + name = LaunchScreen.storyboard; | ||
918 | + sourceTree = "<group>"; | ||
919 | + }; | ||
920 | +/* End PBXVariantGroup section */ | ||
921 | + | ||
922 | +/* Begin XCBuildConfiguration section */ | ||
923 | + 19696AFD2AE80DBD00D1F8F9 /* Debug */ = { | ||
924 | + isa = XCBuildConfiguration; | ||
925 | + buildSettings = { | ||
926 | + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; | ||
927 | + CODE_SIGN_STYLE = Automatic; | ||
928 | + CURRENT_PROJECT_VERSION = 1; | ||
929 | + DEVELOPMENT_TEAM = 6Y97YM76EY; | ||
930 | + GENERATE_INFOPLIST_FILE = YES; | ||
931 | + INFOPLIST_FILE = AdBlocker/Info.plist; | ||
932 | + INFOPLIST_KEY_CFBundleDisplayName = AdBlocker; | ||
933 | + INFOPLIST_KEY_NSHumanReadableCopyright = ""; | ||
934 | + IPHONEOS_DEPLOYMENT_TARGET = 15.0; | ||
935 | + LD_RUNPATH_SEARCH_PATHS = ( | ||
936 | + "$(inherited)", | ||
937 | + "@executable_path/Frameworks", | ||
938 | + "@executable_path/../../Frameworks", | ||
939 | + ); | ||
940 | + MARKETING_VERSION = 1.0; | ||
941 | + PRODUCT_BUNDLE_IDENTIFIER = "com.4k-soft.browser.AdBlock"; | ||
942 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
943 | + SKIP_INSTALL = YES; | ||
944 | + SWIFT_EMIT_LOC_STRINGS = YES; | ||
945 | + SWIFT_VERSION = 5.0; | ||
946 | + TARGETED_DEVICE_FAMILY = "1,2"; | ||
947 | + }; | ||
948 | + name = Debug; | ||
949 | + }; | ||
950 | + 19696AFE2AE80DBD00D1F8F9 /* Release */ = { | ||
951 | + isa = XCBuildConfiguration; | ||
952 | + buildSettings = { | ||
953 | + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; | ||
954 | + CODE_SIGN_STYLE = Automatic; | ||
955 | + CURRENT_PROJECT_VERSION = 1; | ||
956 | + DEVELOPMENT_TEAM = 6Y97YM76EY; | ||
957 | + GENERATE_INFOPLIST_FILE = YES; | ||
958 | + INFOPLIST_FILE = AdBlocker/Info.plist; | ||
959 | + INFOPLIST_KEY_CFBundleDisplayName = AdBlocker; | ||
960 | + INFOPLIST_KEY_NSHumanReadableCopyright = ""; | ||
961 | + IPHONEOS_DEPLOYMENT_TARGET = 15.0; | ||
962 | + LD_RUNPATH_SEARCH_PATHS = ( | ||
963 | + "$(inherited)", | ||
964 | + "@executable_path/Frameworks", | ||
965 | + "@executable_path/../../Frameworks", | ||
966 | + ); | ||
967 | + MARKETING_VERSION = 1.0; | ||
968 | + PRODUCT_BUNDLE_IDENTIFIER = "com.4k-soft.browser.AdBlock"; | ||
969 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
970 | + SKIP_INSTALL = YES; | ||
971 | + SWIFT_EMIT_LOC_STRINGS = YES; | ||
972 | + SWIFT_VERSION = 5.0; | ||
973 | + TARGETED_DEVICE_FAMILY = "1,2"; | ||
974 | + }; | ||
975 | + name = Release; | ||
976 | + }; | ||
977 | + 19FCBF302AC1727900F83A7F /* Debug */ = { | ||
978 | + isa = XCBuildConfiguration; | ||
979 | + buildSettings = { | ||
980 | + ALWAYS_SEARCH_USER_PATHS = NO; | ||
981 | + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; | ||
982 | + CLANG_ANALYZER_NONNULL = YES; | ||
983 | + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; | ||
984 | + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; | ||
985 | + CLANG_ENABLE_MODULES = YES; | ||
986 | + CLANG_ENABLE_OBJC_ARC = YES; | ||
987 | + CLANG_ENABLE_OBJC_WEAK = YES; | ||
988 | + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||
989 | + CLANG_WARN_BOOL_CONVERSION = YES; | ||
990 | + CLANG_WARN_COMMA = YES; | ||
991 | + CLANG_WARN_CONSTANT_CONVERSION = YES; | ||
992 | + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | ||
993 | + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||
994 | + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | ||
995 | + CLANG_WARN_EMPTY_BODY = YES; | ||
996 | + CLANG_WARN_ENUM_CONVERSION = YES; | ||
997 | + CLANG_WARN_INFINITE_RECURSION = YES; | ||
998 | + CLANG_WARN_INT_CONVERSION = YES; | ||
999 | + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | ||
1000 | + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | ||
1001 | + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | ||
1002 | + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||
1003 | + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; | ||
1004 | + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | ||
1005 | + CLANG_WARN_STRICT_PROTOTYPES = YES; | ||
1006 | + CLANG_WARN_SUSPICIOUS_MOVE = YES; | ||
1007 | + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; | ||
1008 | + CLANG_WARN_UNREACHABLE_CODE = YES; | ||
1009 | + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||
1010 | + COPY_PHASE_STRIP = NO; | ||
1011 | + DEBUG_INFORMATION_FORMAT = dwarf; | ||
1012 | + ENABLE_STRICT_OBJC_MSGSEND = YES; | ||
1013 | + ENABLE_TESTABILITY = YES; | ||
1014 | + ENABLE_USER_SCRIPT_SANDBOXING = YES; | ||
1015 | + GCC_C_LANGUAGE_STANDARD = gnu17; | ||
1016 | + GCC_DYNAMIC_NO_PIC = NO; | ||
1017 | + GCC_NO_COMMON_BLOCKS = YES; | ||
1018 | + GCC_OPTIMIZATION_LEVEL = 0; | ||
1019 | + GCC_PREPROCESSOR_DEFINITIONS = ( | ||
1020 | + "DEBUG=1", | ||
1021 | + "$(inherited)", | ||
1022 | + ); | ||
1023 | + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||
1024 | + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||
1025 | + GCC_WARN_UNDECLARED_SELECTOR = YES; | ||
1026 | + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||
1027 | + GCC_WARN_UNUSED_FUNCTION = YES; | ||
1028 | + GCC_WARN_UNUSED_VARIABLE = YES; | ||
1029 | + IPHONEOS_DEPLOYMENT_TARGET = 15.0; | ||
1030 | + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; | ||
1031 | + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; | ||
1032 | + MTL_FAST_MATH = YES; | ||
1033 | + ONLY_ACTIVE_ARCH = YES; | ||
1034 | + SDKROOT = iphoneos; | ||
1035 | + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; | ||
1036 | + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | ||
1037 | + }; | ||
1038 | + name = Debug; | ||
1039 | + }; | ||
1040 | + 19FCBF312AC1727900F83A7F /* Release */ = { | ||
1041 | + isa = XCBuildConfiguration; | ||
1042 | + buildSettings = { | ||
1043 | + ALWAYS_SEARCH_USER_PATHS = NO; | ||
1044 | + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; | ||
1045 | + CLANG_ANALYZER_NONNULL = YES; | ||
1046 | + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; | ||
1047 | + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; | ||
1048 | + CLANG_ENABLE_MODULES = YES; | ||
1049 | + CLANG_ENABLE_OBJC_ARC = YES; | ||
1050 | + CLANG_ENABLE_OBJC_WEAK = YES; | ||
1051 | + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||
1052 | + CLANG_WARN_BOOL_CONVERSION = YES; | ||
1053 | + CLANG_WARN_COMMA = YES; | ||
1054 | + CLANG_WARN_CONSTANT_CONVERSION = YES; | ||
1055 | + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | ||
1056 | + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||
1057 | + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | ||
1058 | + CLANG_WARN_EMPTY_BODY = YES; | ||
1059 | + CLANG_WARN_ENUM_CONVERSION = YES; | ||
1060 | + CLANG_WARN_INFINITE_RECURSION = YES; | ||
1061 | + CLANG_WARN_INT_CONVERSION = YES; | ||
1062 | + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | ||
1063 | + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | ||
1064 | + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | ||
1065 | + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||
1066 | + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; | ||
1067 | + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | ||
1068 | + CLANG_WARN_STRICT_PROTOTYPES = YES; | ||
1069 | + CLANG_WARN_SUSPICIOUS_MOVE = YES; | ||
1070 | + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; | ||
1071 | + CLANG_WARN_UNREACHABLE_CODE = YES; | ||
1072 | + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||
1073 | + COPY_PHASE_STRIP = NO; | ||
1074 | + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | ||
1075 | + ENABLE_NS_ASSERTIONS = NO; | ||
1076 | + ENABLE_STRICT_OBJC_MSGSEND = YES; | ||
1077 | + ENABLE_USER_SCRIPT_SANDBOXING = YES; | ||
1078 | + GCC_C_LANGUAGE_STANDARD = gnu17; | ||
1079 | + GCC_NO_COMMON_BLOCKS = YES; | ||
1080 | + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||
1081 | + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||
1082 | + GCC_WARN_UNDECLARED_SELECTOR = YES; | ||
1083 | + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||
1084 | + GCC_WARN_UNUSED_FUNCTION = YES; | ||
1085 | + GCC_WARN_UNUSED_VARIABLE = YES; | ||
1086 | + IPHONEOS_DEPLOYMENT_TARGET = 15.0; | ||
1087 | + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; | ||
1088 | + MTL_ENABLE_DEBUG_INFO = NO; | ||
1089 | + MTL_FAST_MATH = YES; | ||
1090 | + SDKROOT = iphoneos; | ||
1091 | + SWIFT_COMPILATION_MODE = wholemodule; | ||
1092 | + VALIDATE_PRODUCT = YES; | ||
1093 | + }; | ||
1094 | + name = Release; | ||
1095 | + }; | ||
1096 | + 19FCBF332AC1727900F83A7F /* Debug */ = { | ||
1097 | + isa = XCBuildConfiguration; | ||
1098 | + buildSettings = { | ||
1099 | + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; | ||
1100 | + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||
1101 | + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; | ||
1102 | + CODE_SIGN_STYLE = Automatic; | ||
1103 | + CURRENT_PROJECT_VERSION = 1; | ||
1104 | + DEVELOPMENT_TEAM = 6Y97YM76EY; | ||
1105 | + GENERATE_INFOPLIST_FILE = YES; | ||
1106 | + INFOPLIST_FILE = browser/Info.plist; | ||
1107 | + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; | ||
1108 | + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; | ||
1109 | + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; | ||
1110 | + INFOPLIST_KEY_UIUserInterfaceStyle = Light; | ||
1111 | + IPHONEOS_DEPLOYMENT_TARGET = 15.0; | ||
1112 | + LD_RUNPATH_SEARCH_PATHS = ( | ||
1113 | + "$(inherited)", | ||
1114 | + "@executable_path/Frameworks", | ||
1115 | + ); | ||
1116 | + MARKETING_VERSION = 1.0; | ||
1117 | + PRODUCT_BUNDLE_IDENTIFIER = "com.4k-soft.browser"; | ||
1118 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
1119 | + SWIFT_EMIT_LOC_STRINGS = YES; | ||
1120 | + SWIFT_VERSION = 5.0; | ||
1121 | + TARGETED_DEVICE_FAMILY = "1,2"; | ||
1122 | + }; | ||
1123 | + name = Debug; | ||
1124 | + }; | ||
1125 | + 19FCBF342AC1727900F83A7F /* Release */ = { | ||
1126 | + isa = XCBuildConfiguration; | ||
1127 | + buildSettings = { | ||
1128 | + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; | ||
1129 | + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||
1130 | + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; | ||
1131 | + CODE_SIGN_STYLE = Automatic; | ||
1132 | + CURRENT_PROJECT_VERSION = 1; | ||
1133 | + DEVELOPMENT_TEAM = 6Y97YM76EY; | ||
1134 | + GENERATE_INFOPLIST_FILE = YES; | ||
1135 | + INFOPLIST_FILE = browser/Info.plist; | ||
1136 | + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; | ||
1137 | + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; | ||
1138 | + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; | ||
1139 | + INFOPLIST_KEY_UIUserInterfaceStyle = Light; | ||
1140 | + IPHONEOS_DEPLOYMENT_TARGET = 15.0; | ||
1141 | + LD_RUNPATH_SEARCH_PATHS = ( | ||
1142 | + "$(inherited)", | ||
1143 | + "@executable_path/Frameworks", | ||
1144 | + ); | ||
1145 | + MARKETING_VERSION = 1.0; | ||
1146 | + PRODUCT_BUNDLE_IDENTIFIER = "com.4k-soft.browser"; | ||
1147 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
1148 | + SWIFT_EMIT_LOC_STRINGS = YES; | ||
1149 | + SWIFT_VERSION = 5.0; | ||
1150 | + TARGETED_DEVICE_FAMILY = "1,2"; | ||
1151 | + }; | ||
1152 | + name = Release; | ||
1153 | + }; | ||
1154 | +/* End XCBuildConfiguration section */ | ||
1155 | + | ||
1156 | +/* Begin XCConfigurationList section */ | ||
1157 | + 19696AFF2AE80DBD00D1F8F9 /* Build configuration list for PBXNativeTarget "AdBlocker" */ = { | ||
1158 | + isa = XCConfigurationList; | ||
1159 | + buildConfigurations = ( | ||
1160 | + 19696AFD2AE80DBD00D1F8F9 /* Debug */, | ||
1161 | + 19696AFE2AE80DBD00D1F8F9 /* Release */, | ||
1162 | + ); | ||
1163 | + defaultConfigurationIsVisible = 0; | ||
1164 | + defaultConfigurationName = Release; | ||
1165 | + }; | ||
1166 | + 19FCBF192AC1727800F83A7F /* Build configuration list for PBXProject "browser" */ = { | ||
1167 | + isa = XCConfigurationList; | ||
1168 | + buildConfigurations = ( | ||
1169 | + 19FCBF302AC1727900F83A7F /* Debug */, | ||
1170 | + 19FCBF312AC1727900F83A7F /* Release */, | ||
1171 | + ); | ||
1172 | + defaultConfigurationIsVisible = 0; | ||
1173 | + defaultConfigurationName = Release; | ||
1174 | + }; | ||
1175 | + 19FCBF322AC1727900F83A7F /* Build configuration list for PBXNativeTarget "browser" */ = { | ||
1176 | + isa = XCConfigurationList; | ||
1177 | + buildConfigurations = ( | ||
1178 | + 19FCBF332AC1727900F83A7F /* Debug */, | ||
1179 | + 19FCBF342AC1727900F83A7F /* Release */, | ||
1180 | + ); | ||
1181 | + defaultConfigurationIsVisible = 0; | ||
1182 | + defaultConfigurationName = Release; | ||
1183 | + }; | ||
1184 | +/* End XCConfigurationList section */ | ||
1185 | + | ||
1186 | +/* Begin XCRemoteSwiftPackageReference section */ | ||
1187 | + 193B3B842ACAF480002161ED /* XCRemoteSwiftPackageReference "realm-swift" */ = { | ||
1188 | + isa = XCRemoteSwiftPackageReference; | ||
1189 | + repositoryURL = "https://github.com/realm/realm-swift.git"; | ||
1190 | + requirement = { | ||
1191 | + branch = master; | ||
1192 | + kind = branch; | ||
1193 | + }; | ||
1194 | + }; | ||
1195 | + 199DB1EF2AD3FDEE007E6A81 /* XCRemoteSwiftPackageReference "Alamofire" */ = { | ||
1196 | + isa = XCRemoteSwiftPackageReference; | ||
1197 | + repositoryURL = "https://github.com/Alamofire/Alamofire.git"; | ||
1198 | + requirement = { | ||
1199 | + kind = upToNextMajorVersion; | ||
1200 | + minimumVersion = 5.8.0; | ||
1201 | + }; | ||
1202 | + }; | ||
1203 | + 19CD89462AC719AB0035CB55 /* XCRemoteSwiftPackageReference "SnapKit" */ = { | ||
1204 | + isa = XCRemoteSwiftPackageReference; | ||
1205 | + repositoryURL = "https://github.com/SnapKit/SnapKit.git"; | ||
1206 | + requirement = { | ||
1207 | + kind = upToNextMajorVersion; | ||
1208 | + minimumVersion = 5.6.0; | ||
1209 | + }; | ||
1210 | + }; | ||
1211 | +/* End XCRemoteSwiftPackageReference section */ | ||
1212 | + | ||
1213 | +/* Begin XCSwiftPackageProductDependency section */ | ||
1214 | + 193B3B852ACAF480002161ED /* Realm */ = { | ||
1215 | + isa = XCSwiftPackageProductDependency; | ||
1216 | + package = 193B3B842ACAF480002161ED /* XCRemoteSwiftPackageReference "realm-swift" */; | ||
1217 | + productName = Realm; | ||
1218 | + }; | ||
1219 | + 193B3B872ACAF480002161ED /* RealmSwift */ = { | ||
1220 | + isa = XCSwiftPackageProductDependency; | ||
1221 | + package = 193B3B842ACAF480002161ED /* XCRemoteSwiftPackageReference "realm-swift" */; | ||
1222 | + productName = RealmSwift; | ||
1223 | + }; | ||
1224 | + 199DB1F02AD3FDEE007E6A81 /* Alamofire */ = { | ||
1225 | + isa = XCSwiftPackageProductDependency; | ||
1226 | + package = 199DB1EF2AD3FDEE007E6A81 /* XCRemoteSwiftPackageReference "Alamofire" */; | ||
1227 | + productName = Alamofire; | ||
1228 | + }; | ||
1229 | + 19CD89472AC719AC0035CB55 /* SnapKit */ = { | ||
1230 | + isa = XCSwiftPackageProductDependency; | ||
1231 | + package = 19CD89462AC719AB0035CB55 /* XCRemoteSwiftPackageReference "SnapKit" */; | ||
1232 | + productName = SnapKit; | ||
1233 | + }; | ||
1234 | +/* End XCSwiftPackageProductDependency section */ | ||
1235 | + }; | ||
1236 | + rootObject = 19FCBF162AC1727800F83A7F /* Project object */; | ||
1237 | +} |
1 | +{ | ||
2 | + "pins" : [ | ||
3 | + { | ||
4 | + "identity" : "alamofire", | ||
5 | + "kind" : "remoteSourceControl", | ||
6 | + "location" : "https://github.com/Alamofire/Alamofire.git", | ||
7 | + "state" : { | ||
8 | + "revision" : "3dc6a42c7727c49bf26508e29b0a0b35f9c7e1ad", | ||
9 | + "version" : "5.8.1" | ||
10 | + } | ||
11 | + }, | ||
12 | + { | ||
13 | + "identity" : "realm-core", | ||
14 | + "kind" : "remoteSourceControl", | ||
15 | + "location" : "https://github.com/realm/realm-core.git", | ||
16 | + "state" : { | ||
17 | + "revision" : "c569bec4d04da84030d94f376437bc4efda3686b", | ||
18 | + "version" : "13.23.1" | ||
19 | + } | ||
20 | + }, | ||
21 | + { | ||
22 | + "identity" : "realm-swift", | ||
23 | + "kind" : "remoteSourceControl", | ||
24 | + "location" : "https://github.com/realm/realm-swift.git", | ||
25 | + "state" : { | ||
26 | + "branch" : "master", | ||
27 | + "revision" : "6b992e508b1fab07d47775f2fd7485fd5f110893" | ||
28 | + } | ||
29 | + }, | ||
30 | + { | ||
31 | + "identity" : "snapkit", | ||
32 | + "kind" : "remoteSourceControl", | ||
33 | + "location" : "https://github.com/SnapKit/SnapKit.git", | ||
34 | + "state" : { | ||
35 | + "revision" : "f222cbdf325885926566172f6f5f06af95473158", | ||
36 | + "version" : "5.6.0" | ||
37 | + } | ||
38 | + } | ||
39 | + ], | ||
40 | + "version" : 2 | ||
41 | +} |
browser.xcodeproj/project.xcworkspace/xcuserdata/artemtalko.xcuserdatad/Bookmarks/bookmarks.plist
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
3 | +<plist version="1.0"> | ||
4 | +<dict> | ||
5 | + <key>top-level-items</key> | ||
6 | + <array> | ||
7 | + <dict> | ||
8 | + <key>destination</key> | ||
9 | + <dict> | ||
10 | + <key>rebasable-url</key> | ||
11 | + <dict> | ||
12 | + <key>base</key> | ||
13 | + <string>workspace</string> | ||
14 | + <key>payload</key> | ||
15 | + <dict> | ||
16 | + <key>relative-path</key> | ||
17 | + <string>browser/Modules/History/Controller/HistoryViewController.swift</string> | ||
18 | + </dict> | ||
19 | + </dict> | ||
20 | + <key>type</key> | ||
21 | + <string>DVTDocumentLocation</string> | ||
22 | + </dict> | ||
23 | + <key>type</key> | ||
24 | + <string>bookmark</string> | ||
25 | + </dict> | ||
26 | + </array> | ||
27 | +</dict> | ||
28 | +</plist> |
No preview for this file type
browser.xcodeproj/project.xcworkspace/xcuserdata/falliot.xcuserdatad/UserInterfaceState.xcuserstate
0 → 100644
No preview for this file type
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<Bucket | ||
3 | + uuid = "7D2BF3CE-0152-4175-98D9-E41B0FA9C333" | ||
4 | + type = "1" | ||
5 | + version = "2.0"> | ||
6 | + <Breakpoints> | ||
7 | + <BreakpointProxy | ||
8 | + BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
9 | + <BreakpointContent | ||
10 | + uuid = "F889FE98-5ADA-45D9-9094-5319F398E5D0" | ||
11 | + shouldBeEnabled = "Yes" | ||
12 | + ignoreCount = "0" | ||
13 | + continueAfterRunningActions = "No" | ||
14 | + filePath = "browser/Models/HistoryToolbarMenuCases.swift" | ||
15 | + startingColumnNumber = "9223372036854775807" | ||
16 | + endingColumnNumber = "9223372036854775807" | ||
17 | + startingLineNumber = "21" | ||
18 | + endingLineNumber = "21" | ||
19 | + landmarkName = "action" | ||
20 | + landmarkType = "24"> | ||
21 | + </BreakpointContent> | ||
22 | + </BreakpointProxy> | ||
23 | + <BreakpointProxy | ||
24 | + BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
25 | + <BreakpointContent | ||
26 | + uuid = "2E2EC010-6906-4B8A-9667-CF365A133EA9" | ||
27 | + shouldBeEnabled = "No" | ||
28 | + ignoreCount = "0" | ||
29 | + continueAfterRunningActions = "No" | ||
30 | + filePath = "browser/Modules/SearchResult/View/SearchResultView.swift" | ||
31 | + startingColumnNumber = "9223372036854775807" | ||
32 | + endingColumnNumber = "9223372036854775807" | ||
33 | + startingLineNumber = "20" | ||
34 | + endingLineNumber = "20" | ||
35 | + landmarkName = "searchResultView" | ||
36 | + landmarkType = "24"> | ||
37 | + </BreakpointContent> | ||
38 | + </BreakpointProxy> | ||
39 | + <BreakpointProxy | ||
40 | + BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
41 | + <BreakpointContent | ||
42 | + uuid = "AF29D2F2-0DE8-426B-B8C0-6E69F97CA3CA" | ||
43 | + shouldBeEnabled = "No" | ||
44 | + ignoreCount = "0" | ||
45 | + continueAfterRunningActions = "No" | ||
46 | + filePath = "browser/Modules/RemoveAdvert/Controller/RemoveAdvertViewController.swift" | ||
47 | + startingColumnNumber = "9223372036854775807" | ||
48 | + endingColumnNumber = "9223372036854775807" | ||
49 | + startingLineNumber = "82" | ||
50 | + endingLineNumber = "82" | ||
51 | + landmarkName = "shieldButtonTapped(_:)" | ||
52 | + landmarkType = "7"> | ||
53 | + </BreakpointContent> | ||
54 | + </BreakpointProxy> | ||
55 | + <BreakpointProxy | ||
56 | + BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
57 | + <BreakpointContent | ||
58 | + uuid = "2F610807-91FB-4B53-BAE6-77E3D9664161" | ||
59 | + shouldBeEnabled = "No" | ||
60 | + ignoreCount = "0" | ||
61 | + continueAfterRunningActions = "No" | ||
62 | + filePath = "browser/Modules/RemoveAdvert/Controller/RemoveAdvertViewController.swift" | ||
63 | + startingColumnNumber = "9223372036854775807" | ||
64 | + endingColumnNumber = "9223372036854775807" | ||
65 | + startingLineNumber = "86" | ||
66 | + endingLineNumber = "86" | ||
67 | + landmarkName = "shieldButtonTapped(_:)" | ||
68 | + landmarkType = "7"> | ||
69 | + </BreakpointContent> | ||
70 | + </BreakpointProxy> | ||
71 | + <BreakpointProxy | ||
72 | + BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
73 | + <BreakpointContent | ||
74 | + uuid = "2DAFEA73-1A5C-412B-9CAB-F119AA59AC98" | ||
75 | + shouldBeEnabled = "No" | ||
76 | + ignoreCount = "0" | ||
77 | + continueAfterRunningActions = "No" | ||
78 | + filePath = "browser/Modules/SearchResult/Controller/SearchResultViewController.swift" | ||
79 | + startingColumnNumber = "9223372036854775807" | ||
80 | + endingColumnNumber = "9223372036854775807" | ||
81 | + startingLineNumber = "55" | ||
82 | + endingLineNumber = "55" | ||
83 | + landmarkName = "setupAdBlocker()" | ||
84 | + landmarkType = "7"> | ||
85 | + </BreakpointContent> | ||
86 | + </BreakpointProxy> | ||
87 | + <BreakpointProxy | ||
88 | + BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
89 | + <BreakpointContent | ||
90 | + uuid = "3D9602FE-229B-4486-9E84-86F3385737CB" | ||
91 | + shouldBeEnabled = "No" | ||
92 | + ignoreCount = "0" | ||
93 | + continueAfterRunningActions = "No" | ||
94 | + filePath = "browser/Modules/Home/Controller/BrowserHomeViewController.swift" | ||
95 | + startingColumnNumber = "9223372036854775807" | ||
96 | + endingColumnNumber = "9223372036854775807" | ||
97 | + startingLineNumber = "236" | ||
98 | + endingLineNumber = "236" | ||
99 | + landmarkName = "tabsButtonHandler()" | ||
100 | + landmarkType = "7"> | ||
101 | + </BreakpointContent> | ||
102 | + </BreakpointProxy> | ||
103 | + <BreakpointProxy | ||
104 | + BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
105 | + <BreakpointContent | ||
106 | + uuid = "890732EB-28CC-4180-BED8-D29D0A5F9C6A" | ||
107 | + shouldBeEnabled = "No" | ||
108 | + ignoreCount = "0" | ||
109 | + continueAfterRunningActions = "No" | ||
110 | + filePath = "browser/Modules/Home/Controller/BrowserHomeViewController.swift" | ||
111 | + startingColumnNumber = "9223372036854775807" | ||
112 | + endingColumnNumber = "9223372036854775807" | ||
113 | + startingLineNumber = "235" | ||
114 | + endingLineNumber = "235" | ||
115 | + landmarkName = "tabsButtonHandler()" | ||
116 | + landmarkType = "7"> | ||
117 | + </BreakpointContent> | ||
118 | + </BreakpointProxy> | ||
119 | + </Breakpoints> | ||
120 | +</Bucket> |
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
3 | +<plist version="1.0"> | ||
4 | +<dict> | ||
5 | + <key>SchemeUserState</key> | ||
6 | + <dict> | ||
7 | + <key>AdBlocker.xcscheme_^#shared#^_</key> | ||
8 | + <dict> | ||
9 | + <key>orderHint</key> | ||
10 | + <integer>0</integer> | ||
11 | + </dict> | ||
12 | + <key>GettingStarted (Playground) 1.xcscheme</key> | ||
13 | + <dict> | ||
14 | + <key>isShown</key> | ||
15 | + <false/> | ||
16 | + <key>orderHint</key> | ||
17 | + <integer>5</integer> | ||
18 | + </dict> | ||
19 | + <key>GettingStarted (Playground) 2.xcscheme</key> | ||
20 | + <dict> | ||
21 | + <key>isShown</key> | ||
22 | + <false/> | ||
23 | + <key>orderHint</key> | ||
24 | + <integer>6</integer> | ||
25 | + </dict> | ||
26 | + <key>GettingStarted (Playground).xcscheme</key> | ||
27 | + <dict> | ||
28 | + <key>isShown</key> | ||
29 | + <false/> | ||
30 | + <key>orderHint</key> | ||
31 | + <integer>2</integer> | ||
32 | + </dict> | ||
33 | + <key>SnapKitPlayground (Playground) 1.xcscheme</key> | ||
34 | + <dict> | ||
35 | + <key>isShown</key> | ||
36 | + <false/> | ||
37 | + <key>orderHint</key> | ||
38 | + <integer>3</integer> | ||
39 | + </dict> | ||
40 | + <key>SnapKitPlayground (Playground) 2.xcscheme</key> | ||
41 | + <dict> | ||
42 | + <key>isShown</key> | ||
43 | + <false/> | ||
44 | + <key>orderHint</key> | ||
45 | + <integer>4</integer> | ||
46 | + </dict> | ||
47 | + <key>SnapKitPlayground (Playground) 3.xcscheme</key> | ||
48 | + <dict> | ||
49 | + <key>isShown</key> | ||
50 | + <false/> | ||
51 | + <key>orderHint</key> | ||
52 | + <integer>0</integer> | ||
53 | + </dict> | ||
54 | + <key>SnapKitPlayground (Playground) 4.xcscheme</key> | ||
55 | + <dict> | ||
56 | + <key>isShown</key> | ||
57 | + <false/> | ||
58 | + <key>orderHint</key> | ||
59 | + <integer>5</integer> | ||
60 | + </dict> | ||
61 | + <key>SnapKitPlayground (Playground) 5.xcscheme</key> | ||
62 | + <dict> | ||
63 | + <key>isShown</key> | ||
64 | + <false/> | ||
65 | + <key>orderHint</key> | ||
66 | + <integer>6</integer> | ||
67 | + </dict> | ||
68 | + <key>SnapKitPlayground (Playground).xcscheme</key> | ||
69 | + <dict> | ||
70 | + <key>isShown</key> | ||
71 | + <false/> | ||
72 | + <key>orderHint</key> | ||
73 | + <integer>1</integer> | ||
74 | + </dict> | ||
75 | + <key>browser.xcscheme_^#shared#^_</key> | ||
76 | + <dict> | ||
77 | + <key>orderHint</key> | ||
78 | + <integer>1</integer> | ||
79 | + </dict> | ||
80 | + </dict> | ||
81 | +</dict> | ||
82 | +</plist> |
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
3 | +<plist version="1.0"> | ||
4 | +<dict> | ||
5 | + <key>SchemeUserState</key> | ||
6 | + <dict> | ||
7 | + <key>AdBlocker.xcscheme_^#shared#^_</key> | ||
8 | + <dict> | ||
9 | + <key>orderHint</key> | ||
10 | + <integer>1</integer> | ||
11 | + </dict> | ||
12 | + <key>GettingStarted (Playground) 1.xcscheme</key> | ||
13 | + <dict> | ||
14 | + <key>isShown</key> | ||
15 | + <false/> | ||
16 | + <key>orderHint</key> | ||
17 | + <integer>6</integer> | ||
18 | + </dict> | ||
19 | + <key>GettingStarted (Playground) 2.xcscheme</key> | ||
20 | + <dict> | ||
21 | + <key>isShown</key> | ||
22 | + <false/> | ||
23 | + <key>orderHint</key> | ||
24 | + <integer>7</integer> | ||
25 | + </dict> | ||
26 | + <key>GettingStarted (Playground).xcscheme</key> | ||
27 | + <dict> | ||
28 | + <key>isShown</key> | ||
29 | + <false/> | ||
30 | + <key>orderHint</key> | ||
31 | + <integer>5</integer> | ||
32 | + </dict> | ||
33 | + <key>SnapKitPlayground (Playground) 1.xcscheme</key> | ||
34 | + <dict> | ||
35 | + <key>isShown</key> | ||
36 | + <false/> | ||
37 | + <key>orderHint</key> | ||
38 | + <integer>3</integer> | ||
39 | + </dict> | ||
40 | + <key>SnapKitPlayground (Playground) 2.xcscheme</key> | ||
41 | + <dict> | ||
42 | + <key>isShown</key> | ||
43 | + <false/> | ||
44 | + <key>orderHint</key> | ||
45 | + <integer>4</integer> | ||
46 | + </dict> | ||
47 | + <key>SnapKitPlayground (Playground).xcscheme</key> | ||
48 | + <dict> | ||
49 | + <key>isShown</key> | ||
50 | + <false/> | ||
51 | + <key>orderHint</key> | ||
52 | + <integer>2</integer> | ||
53 | + </dict> | ||
54 | + <key>browser.xcscheme_^#shared#^_</key> | ||
55 | + <dict> | ||
56 | + <key>orderHint</key> | ||
57 | + <integer>0</integer> | ||
58 | + </dict> | ||
59 | + </dict> | ||
60 | +</dict> | ||
61 | +</plist> |
browser/.DS_Store
0 → 100644
No preview for this file type
browser/AppDelegate.swift
0 → 100644
1 | +import UIKit | ||
2 | + | ||
3 | +@main | ||
4 | + class AppDelegate: UIResponder, UIApplicationDelegate { | ||
5 | + var window: UIWindow? | ||
6 | + | ||
7 | + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { | ||
8 | + | ||
9 | + let screenRect = UIScreen.main.bounds | ||
10 | + window = UIWindow(frame: screenRect) | ||
11 | + | ||
12 | + if let window = window { | ||
13 | + let navigationViewController = NavigationViewController() | ||
14 | + window.rootViewController = navigationViewController | ||
15 | + window.makeKeyAndVisible() | ||
16 | + } | ||
17 | + return true | ||
18 | + } | ||
19 | +} |
1 | +// Transition.swift | ||
2 | +// browser | ||
3 | +// | ||
4 | +// Created by Artem Talko on 23.11.2023. | ||
5 | +// | ||
6 | + | ||
7 | +import UIKit | ||
8 | + | ||
9 | + | ||
10 | +final class OpenTabsTransition: NSObject, UIViewControllerAnimatedTransitioning { | ||
11 | + private var operation: UINavigationController.Operation | ||
12 | + | ||
13 | + init( operation: UINavigationController.Operation ) { | ||
14 | + self.operation = operation | ||
15 | + super.init() | ||
16 | + } | ||
17 | + | ||
18 | + func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { | ||
19 | + return 0.4 | ||
20 | + } | ||
21 | + | ||
22 | + func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { | ||
23 | + guard let toController = transitionContext.viewController(forKey: .to), | ||
24 | + let fromController = transitionContext.viewController(forKey: .from) else { | ||
25 | + transitionContext.completeTransition(true) | ||
26 | + return | ||
27 | + } | ||
28 | + switch operation { | ||
29 | + case .push : | ||
30 | + pushController(fromController: fromController, toController: toController, transitionContext: transitionContext) | ||
31 | + case .pop: | ||
32 | + popController(fromController: fromController, toController: toController, transitionContext: transitionContext) | ||
33 | + case .none: | ||
34 | + transitionContext.completeTransition(true) | ||
35 | + return | ||
36 | + @unknown default: | ||
37 | + break | ||
38 | + } | ||
39 | + } | ||
40 | +} | ||
41 | + | ||
42 | + | ||
43 | +//MARK: animation handlers | ||
44 | +extension OpenTabsTransition { | ||
45 | + private func pushController(fromController: UIViewController, toController: UIViewController, transitionContext: UIViewControllerContextTransitioning) { | ||
46 | + let container = transitionContext.containerView | ||
47 | + guard let tabsController = fromController as? TabsViewController else { | ||
48 | + transitionContext.completeTransition(true) | ||
49 | + return | ||
50 | + } | ||
51 | + guard let browserHomeController = toController as? BrowserHomeViewController else { | ||
52 | + transitionContext.completeTransition(true) | ||
53 | + return | ||
54 | + } | ||
55 | + let cellIndexPathForTransition = tabsController.getCellIndex() | ||
56 | + | ||
57 | + container.insertSubview(browserHomeController.mainView, at: 0) | ||
58 | + DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) { | ||
59 | + let tabsCollectionView = tabsController.mainView.openedTabsCollectionView | ||
60 | + tabsCollectionView.scrollToItem(at: cellIndexPathForTransition, at: .bottom, animated: false) | ||
61 | + DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) { | ||
62 | + let cell = tabsController.mainView.openedTabsCollectionView.cellForItem(at: cellIndexPathForTransition) | ||
63 | + let browserHomeImage = UIImageView() | ||
64 | + browserHomeImage.image = SnapshotService.shared.makeSnapshot(browserHomeController.mainView) | ||
65 | + | ||
66 | + guard let lastTab = cell as? OpenedTabsCollectionViewCell else { | ||
67 | + return | ||
68 | + } | ||
69 | + lastTab.isHidden = true | ||
70 | + | ||
71 | + container.addSubview(browserHomeImage) | ||
72 | + browserHomeImage.frame = lastTab.frame | ||
73 | + UIView.animate(withDuration: 0.2) { | ||
74 | + browserHomeImage.frame = UIScreen.main.bounds | ||
75 | + } completion: { _ in | ||
76 | + browserHomeImage.removeFromSuperview() | ||
77 | + lastTab.isHidden = false | ||
78 | + transitionContext.completeTransition(true) | ||
79 | + } | ||
80 | + } | ||
81 | + } | ||
82 | + } | ||
83 | + | ||
84 | + private func popController(fromController: UIViewController, toController: UIViewController, transitionContext: UIViewControllerContextTransitioning) { | ||
85 | + let container = transitionContext.containerView | ||
86 | + guard let tabsController = toController as? TabsViewController else { | ||
87 | + transitionContext.completeTransition(true) | ||
88 | + return | ||
89 | + } | ||
90 | + guard let browserHomeController = fromController as? BrowserHomeViewController else { | ||
91 | + transitionContext.completeTransition(true) | ||
92 | + return | ||
93 | + } | ||
94 | + let cellIndexPathForTransition = tabsController.getCellIndex() | ||
95 | + | ||
96 | + container.insertSubview(tabsController.mainView, at: 0) | ||
97 | + DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) { | ||
98 | + let tabsCollectionView = tabsController.mainView.openedTabsCollectionView | ||
99 | + tabsCollectionView.scrollToItem(at: cellIndexPathForTransition, at: .bottom, animated: false) | ||
100 | + let lastItemIndex = tabsController.getCellIndex() | ||
101 | + | ||
102 | + DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) { | ||
103 | + let cell = tabsController.mainView.openedTabsCollectionView.cellForItem(at: lastItemIndex) | ||
104 | + let browserHomeImage = UIImageView() | ||
105 | + browserHomeImage.image = SnapshotService.shared.makeSnapshot(browserHomeController.mainView) | ||
106 | + | ||
107 | + guard let lastTab = cell as? OpenedTabsCollectionViewCell else { | ||
108 | + return | ||
109 | + } | ||
110 | + lastTab.isHidden = true | ||
111 | + | ||
112 | + container.addSubview(browserHomeImage) | ||
113 | + browserHomeImage.frame = browserHomeController.mainView.frame | ||
114 | + UIView.animate(withDuration: 0.2) { | ||
115 | + fromController.view.isHidden = true | ||
116 | + browserHomeImage.frame = lastTab.convertFrameToScreenCoordinates() ?? CGRect() | ||
117 | + } completion: { _ in | ||
118 | + browserHomeImage.removeFromSuperview() | ||
119 | + lastTab.isHidden = false | ||
120 | + transitionContext.completeTransition(true) | ||
121 | + } | ||
122 | + } | ||
123 | + } | ||
124 | + } | ||
125 | +} | ||
126 | + |
browser/DataBases/BrowserTabDataBase.swift
0 → 100644
1 | +// | ||
2 | +// BrowserTab.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 02.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | + | ||
9 | +import UIKit | ||
10 | +import Realm | ||
11 | +import RealmSwift | ||
12 | + | ||
13 | +class BrowserTabDataBase: Object { | ||
14 | + @Persisted(primaryKey: true) var tabId: String = UUID().uuidString | ||
15 | + @Persisted var tabTitle: String | ||
16 | + @Persisted var TabUrl: String | ||
17 | + @Persisted var snapshotImageData: Data? | ||
18 | + | ||
19 | + convenience init(tabTitle: String, snapshotImage: UIImage?, tabUrl: String) { | ||
20 | + self.init() | ||
21 | + self.tabTitle = tabTitle | ||
22 | + self.TabUrl = tabUrl | ||
23 | + if let image = snapshotImage { | ||
24 | + self.snapshotImageData = image.pngData() | ||
25 | + } | ||
26 | + } | ||
27 | +} |
browser/DataBases/HistoryDataBase.swift
0 → 100644
1 | +// | ||
2 | +// HistoryDataBase.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 16.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | + | ||
9 | +import UIKit | ||
10 | +import Realm | ||
11 | +import RealmSwift | ||
12 | + | ||
13 | +class HistoryDataBase: Object { | ||
14 | + @Persisted(primaryKey: true) var historyCellId: String = UUID().uuidString | ||
15 | + @Persisted var siteTitle: String | ||
16 | + @Persisted var siteUrl: String | ||
17 | + @Persisted var lastVisit: Date | ||
18 | + | ||
19 | + convenience init(siteTitle: String, siteUrl: String, lastVisit: Date) { | ||
20 | + self.init() | ||
21 | + self.siteTitle = siteTitle | ||
22 | + self.siteUrl = siteUrl | ||
23 | + self.lastVisit = lastVisit | ||
24 | + } | ||
25 | +} | ||
26 | + |
browser/Extentions/BinaryFloatingPoint.swift
0 → 100644
1 | +// | ||
2 | +// File.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 27.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | + | ||
11 | +extension BinaryFloatingPoint { | ||
12 | + private var size: CGSize { | ||
13 | + return CGSize(width: 375, height: 812) | ||
14 | + } | ||
15 | + | ||
16 | + var sizeW: CGFloat { | ||
17 | + (CGFloat(self) * UIScreen.main.bounds.width / size.width) | ||
18 | + } | ||
19 | + | ||
20 | + var sizeH: CGFloat { | ||
21 | + (CGFloat(self) * UIScreen.main.bounds.height / size.height) | ||
22 | + } | ||
23 | + | ||
24 | + var lessThanOrEqualToSizeW: CGFloat { | ||
25 | + UIScreen.main.bounds.width < size.width ? self.sizeW : CGFloat(self) | ||
26 | + } | ||
27 | + | ||
28 | + var lessThanOrEqualToSizeH: CGFloat { | ||
29 | + UIScreen.main.bounds.height < size.height ? self.sizeH : CGFloat(self) | ||
30 | + } | ||
31 | +} | ||
32 | + | ||
33 | +extension BinaryInteger { | ||
34 | + var sizeW: CGFloat { | ||
35 | + CGFloat(self).sizeW | ||
36 | + } | ||
37 | + | ||
38 | + var sizeH: CGFloat { | ||
39 | + CGFloat(self).sizeH | ||
40 | + } | ||
41 | + | ||
42 | + var lessThanOrEqualToSizeW: CGFloat { | ||
43 | + CGFloat(self).lessThanOrEqualToSizeW | ||
44 | + } | ||
45 | + | ||
46 | + var lessThanOrEqualToSizeH: CGFloat { | ||
47 | + CGFloat(self).lessThanOrEqualToSizeH | ||
48 | + } | ||
49 | +} |
1 | +// | ||
2 | +// UIView+convertFrameToScreenCoordinates.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 30.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +extension UICollectionViewCell { | ||
11 | + func convertFrameToScreenCoordinates() -> CGRect? { | ||
12 | + guard let rootView = UIApplication.shared.keyWindow?.rootViewController?.view else { | ||
13 | + return nil | ||
14 | + } | ||
15 | + | ||
16 | + let convertedOrigin = convert(bounds.origin, to: rootView) | ||
17 | + return CGRect(origin: convertedOrigin, size: bounds.size) | ||
18 | + } | ||
19 | + | ||
20 | + func convertRectToFullScreen(_ rect: CGRect) -> CGRect? { | ||
21 | + guard let window = UIApplication.shared.windows.first else { | ||
22 | + return nil | ||
23 | + } | ||
24 | + | ||
25 | + let rectInWindow = convert(rect, to: window) | ||
26 | + let fullScreenRect = window.convert(rectInWindow, to: UIScreen.main.coordinateSpace) | ||
27 | + | ||
28 | + return fullScreenRect | ||
29 | + } | ||
30 | +} |
browser/Info.plist
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
3 | +<plist version="1.0"> | ||
4 | +<dict> | ||
5 | + <key>NSAppTransportSecurity</key> | ||
6 | + <dict> | ||
7 | + <key>NSAllowsArbitraryLoads</key> | ||
8 | + <true/> | ||
9 | + <key>NSAllowsArbitraryLoadsForMedia</key> | ||
10 | + <true/> | ||
11 | + </dict> | ||
12 | + <key>UIAppFonts</key> | ||
13 | + <array> | ||
14 | + <string>FontsFree-Net-SFProText-Semibold.ttf</string> | ||
15 | + <string>FontsFree-Net-SFProText-Regular.ttf</string> | ||
16 | + </array> | ||
17 | +</dict> | ||
18 | +</plist> |
browser/Managers/CachingManager.swift
0 → 100644
1 | +// | ||
2 | +// FirstTimeOpen.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 27.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import Foundation | ||
9 | + | ||
10 | +final class CachingManager { | ||
11 | + private enum Keys { | ||
12 | + static let isFirstAppLoad = "IsFirstAppLoad" | ||
13 | + static let isAdBlocking = "IsAdBlocking" | ||
14 | + } | ||
15 | + | ||
16 | + static let shared = CachingManager() | ||
17 | + private let userDefaults = UserDefaults.standard | ||
18 | + | ||
19 | + var isFirstAppLoad: Bool { | ||
20 | + get { return userDefaults.bool(forKey: Keys.isFirstAppLoad) } | ||
21 | + set { userDefaults.set(newValue, forKey: Keys.isFirstAppLoad) } | ||
22 | + } | ||
23 | + var isAdBlocking: Bool { | ||
24 | + get { return userDefaults.bool(forKey: Keys.isAdBlocking) } | ||
25 | + set { userDefaults.set(newValue, forKey: Keys.isAdBlocking) } | ||
26 | + } | ||
27 | +} |
browser/Managers/DateManager.swift
0 → 100644
1 | +// | ||
2 | +// DateManager.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 17.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import Foundation | ||
9 | + | ||
10 | + | ||
11 | +final class DateManager { | ||
12 | + static let shared = DateManager() | ||
13 | + | ||
14 | + func getTimeFromDB(_ date: Date?) -> String { | ||
15 | + let timeFormatter = DateFormatter() | ||
16 | + timeFormatter.dateFormat = "h:mm a" | ||
17 | + return timeFormatter.string(from: date ?? Date()) | ||
18 | + } | ||
19 | + | ||
20 | + var dateFormatter: DateFormatter{ | ||
21 | + let obj = DateFormatter() | ||
22 | + obj.dateFormat = "EEEE, d MMM" | ||
23 | + return obj | ||
24 | + } | ||
25 | + | ||
26 | + var currentDate: Date { | ||
27 | + let obj = Date() | ||
28 | + return obj | ||
29 | + } | ||
30 | +} | ||
31 | + | ||
32 | + | ||
33 | + |
browser/Managers/HistoryDBManager.swift
0 → 100644
1 | +// | ||
2 | +// HistoryDBManager.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 17.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | +import Realm | ||
10 | +import RealmSwift | ||
11 | + | ||
12 | +class HistoryDBManager { | ||
13 | + static let shared = HistoryDBManager() | ||
14 | + private let realm: Realm | ||
15 | + | ||
16 | + | ||
17 | + | ||
18 | + private init() { | ||
19 | + do { | ||
20 | + self.realm = try Realm() | ||
21 | + } catch { | ||
22 | + fatalError("Error initializing Realm: \(error.localizedDescription)") | ||
23 | + } | ||
24 | + } | ||
25 | + | ||
26 | + //MARK: ADDING | ||
27 | + func saveToHistory(siteTitle: String, siteUrl: String, lastVisit: Date) { | ||
28 | + let historyCell = HistoryDataBase(siteTitle: siteTitle, siteUrl: siteUrl, lastVisit: lastVisit) | ||
29 | + do { | ||
30 | + try realm.write { | ||
31 | + realm.add(historyCell) | ||
32 | + } | ||
33 | + | ||
34 | + } catch { | ||
35 | + print("Error adding new tab: \(error.localizedDescription)") | ||
36 | + } | ||
37 | + } | ||
38 | + | ||
39 | + //MARK: GETTING | ||
40 | + func getHistory(sorted: Bool = false) -> [HistoryDataBase] { | ||
41 | + if sorted { | ||
42 | + let history = realm.objects(HistoryDataBase.self).sorted(byKeyPath: "lastVisit", ascending: false) | ||
43 | + return Array(history) | ||
44 | + } else { | ||
45 | + let history = realm.objects(HistoryDataBase.self) | ||
46 | + return Array(history) | ||
47 | + } | ||
48 | + } | ||
49 | + | ||
50 | + func getHistoryDataForTableView() -> ([HistoryElement]) { | ||
51 | + let stories = getHistory(sorted: true) | ||
52 | + return createHistoryData(elements: Array(stories)) | ||
53 | + } | ||
54 | + | ||
55 | + func createHistoryData(elements: [HistoryDataBase]) -> ([HistoryElement]) { | ||
56 | + var groupedData: [String: [HistoryDataBase]] = [:] | ||
57 | + let dateFormatter = DateManager.shared.dateFormatter | ||
58 | + | ||
59 | + for data in elements { | ||
60 | + let formattedDate = dateFormatter.string(from: data.lastVisit) | ||
61 | + if groupedData[formattedDate] == nil { | ||
62 | + groupedData[formattedDate] = [] | ||
63 | + } | ||
64 | + groupedData[formattedDate]?.append(data) | ||
65 | + } | ||
66 | + | ||
67 | + let result = groupedData.map({ HistoryElement( name: $0.key, stories: $0.value )}) | ||
68 | + | ||
69 | + return result | ||
70 | + } | ||
71 | + | ||
72 | + func getFilteredHistory(filter: String) -> ([HistoryElement]) { | ||
73 | + let lowercasedFilter = filter.lowercased() | ||
74 | + let history = getHistory(sorted: true) | ||
75 | + let filteredHistory = history.filter({ $0.siteTitle.lowercased().contains(lowercasedFilter) || $0.siteUrl.lowercased().contains(lowercasedFilter)}) | ||
76 | + return createHistoryData(elements: filteredHistory) | ||
77 | + } | ||
78 | + | ||
79 | + //MARK: DELETING | ||
80 | + func cleanHistory() { | ||
81 | + do { | ||
82 | + let history = realm.objects(HistoryDataBase.self) | ||
83 | + try realm.write { | ||
84 | + realm.delete(history) | ||
85 | + } | ||
86 | + | ||
87 | + } catch { | ||
88 | + print("Error cleaning history: \(error.localizedDescription)") | ||
89 | + } | ||
90 | + } | ||
91 | + | ||
92 | + func deleteHistoryInRange(startDate: Date) { | ||
93 | + do { | ||
94 | + let history = realm.objects(HistoryDataBase.self).filter("lastVisit >= %@", startDate) | ||
95 | + try realm.write { | ||
96 | + realm.delete(history) | ||
97 | + } | ||
98 | + | ||
99 | + } catch { | ||
100 | + print("Error deleting history within the specified time range: \(error.localizedDescription)") | ||
101 | + } | ||
102 | + } | ||
103 | + | ||
104 | + func deleteHistoryCell(tabId: String) { | ||
105 | + if let historyCellToDelete = realm.object(ofType: HistoryDataBase.self, forPrimaryKey: tabId) { | ||
106 | + do { | ||
107 | + try realm.write { | ||
108 | + realm.delete(historyCellToDelete) | ||
109 | + } | ||
110 | + | ||
111 | + } catch { | ||
112 | + print("Error deleting history cell: \(error.localizedDescription)") | ||
113 | + } | ||
114 | + } | ||
115 | + } | ||
116 | + | ||
117 | + | ||
118 | +} | ||
119 | + |
browser/Managers/TabManager.swift
0 → 100644
1 | +// | ||
2 | +// DataBaseManager.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 02.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import Realm | ||
9 | +import RealmSwift | ||
10 | +import Alamofire | ||
11 | +import UIKit | ||
12 | + | ||
13 | +final class TabManager { | ||
14 | + static let shared = TabManager() | ||
15 | + private let realm: Realm | ||
16 | + | ||
17 | + | ||
18 | + private init() { | ||
19 | + do { | ||
20 | + self.realm = try Realm() | ||
21 | + } catch { | ||
22 | + fatalError("Error initializing Realm: \(error.localizedDescription)") | ||
23 | + } | ||
24 | + } | ||
25 | + | ||
26 | + | ||
27 | + func saveTabWithComplition(tabTitle: String, snapshotImage: UIImage?, tabUrl: String, completion: (Result<Void,Error>) -> Void) { | ||
28 | + let newTab = BrowserTabDataBase(tabTitle: tabTitle, snapshotImage: snapshotImage, tabUrl: tabUrl) | ||
29 | + | ||
30 | + do { | ||
31 | + try realm.write { | ||
32 | + realm.add(newTab) | ||
33 | + completion(.success(())) | ||
34 | + } | ||
35 | + } catch { | ||
36 | + print("Error adding new tab: \(error.localizedDescription)") | ||
37 | + completion(.failure(error)) | ||
38 | + } | ||
39 | + } | ||
40 | + | ||
41 | + func saveTab(tabTitle: String, snapshotImage: UIImage?, tabUrl: String) { | ||
42 | + let newTab = BrowserTabDataBase(tabTitle: tabTitle, snapshotImage: snapshotImage, tabUrl: tabUrl) | ||
43 | + | ||
44 | + do { | ||
45 | + try realm.write { | ||
46 | + realm.add(newTab) | ||
47 | + } | ||
48 | + } catch { | ||
49 | + print("Error adding new tab: \(error.localizedDescription)") | ||
50 | + } | ||
51 | + } | ||
52 | + | ||
53 | + func updateTab(tabId: String, newTabTitle: String, newSnapshotImage: UIImage?, newTabUrl: String, completion: (Result<Void, Error>) -> Void) { | ||
54 | + if let tabToUpdate = realm.object(ofType: BrowserTabDataBase.self, forPrimaryKey: tabId) { | ||
55 | + do { | ||
56 | + try realm.write { | ||
57 | + tabToUpdate.tabTitle = newTabTitle | ||
58 | + tabToUpdate.TabUrl = newTabUrl | ||
59 | + if let image = newSnapshotImage { | ||
60 | + tabToUpdate.snapshotImageData = image.pngData() | ||
61 | + } | ||
62 | + completion(.success(())) | ||
63 | + } | ||
64 | + } catch { | ||
65 | + print("Error updating tab: \(error.localizedDescription)") | ||
66 | + completion(.failure(error)) | ||
67 | + } | ||
68 | + } else { | ||
69 | + let error = NSError(domain: "Error", code: 404, userInfo: [NSLocalizedDescriptionKey: "Tab not found"]) | ||
70 | + completion(.failure(error)) | ||
71 | + } | ||
72 | + } | ||
73 | + | ||
74 | + func getAllTabs() -> [BrowserTabDataBase] { | ||
75 | + let savedTabs = realm.objects(BrowserTabDataBase.self) | ||
76 | + return Array(savedTabs) | ||
77 | + } | ||
78 | + | ||
79 | + func deleteTab(tabId: String) { | ||
80 | + if let tabToDelete = realm.object(ofType: BrowserTabDataBase.self, forPrimaryKey: tabId) { | ||
81 | + do { | ||
82 | + try realm.write { | ||
83 | + realm.delete(tabToDelete) | ||
84 | + } | ||
85 | + } catch { | ||
86 | + print("Error deleting tab: \(error.localizedDescription)") | ||
87 | + } | ||
88 | + } | ||
89 | + } | ||
90 | +} | ||
91 | + |
browser/Models/HistoryElement.swift
0 → 100644
browser/Models/HistoryToolbarMenuCases.swift
0 → 100644
1 | +// | ||
2 | +// HistoryToolbar.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 14.11.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +enum menuCases: CaseIterable { | ||
11 | + case allTime | ||
12 | + case lastMonth | ||
13 | + case todayAndYesterday | ||
14 | + case today | ||
15 | + case lastHour | ||
16 | + //MARK: todo | ||
17 | + var action: UIAction { | ||
18 | + switch self { | ||
19 | + case .allTime: | ||
20 | + return UIAction(title: "All time") { _ in | ||
21 | + HistoryDBManager.shared.cleanHistory() | ||
22 | + | ||
23 | + } | ||
24 | + case .lastMonth: | ||
25 | + return UIAction(title: "The last 30 days") { _ in | ||
26 | + let lastMonth = Calendar.current.date(byAdding: .day, value: -30, to: Date()) ?? Date() | ||
27 | + HistoryDBManager.shared.deleteHistoryInRange(startDate: lastMonth) | ||
28 | + } | ||
29 | + case .todayAndYesterday: | ||
30 | + return UIAction(title: "Today and yesterday") { _ in | ||
31 | + let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: Date()) ?? Date() | ||
32 | + HistoryDBManager.shared.deleteHistoryInRange(startDate: yesterday) | ||
33 | + } | ||
34 | + case .today: | ||
35 | + return UIAction(title: "Today") { _ in | ||
36 | + let startOfToday = Calendar.current.startOfDay(for: Date()) | ||
37 | + HistoryDBManager.shared.deleteHistoryInRange(startDate: startOfToday) | ||
38 | + } | ||
39 | + case .lastHour: | ||
40 | + return UIAction(title: "The last hour") { _ in | ||
41 | + let lastHour = Calendar.current.date(byAdding: .hour, value: -1, to: Date()) ?? Date() | ||
42 | + HistoryDBManager.shared.deleteHistoryInRange(startDate: lastHour) | ||
43 | + } | ||
44 | + } | ||
45 | + } | ||
46 | +} |
1 | +// | ||
2 | +// HistoryViewController.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 29.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | +import RealmSwift | ||
10 | + | ||
11 | +final class HistoryViewController: UIViewController { | ||
12 | + private let mainView = HistoryView() | ||
13 | + | ||
14 | + private var objectNotificationToken: NotificationToken? | ||
15 | + | ||
16 | + private var historyData: [HistoryElement] = [] { | ||
17 | + didSet { | ||
18 | + filteredData = historyData | ||
19 | + } | ||
20 | + } | ||
21 | + private var filteredData: [HistoryElement] = [] { | ||
22 | + didSet { | ||
23 | + mainView.historyTabsTableView.reloadData() | ||
24 | + } | ||
25 | + } | ||
26 | + | ||
27 | + override func viewWillAppear(_ animated: Bool) { | ||
28 | + super.viewWillAppear(animated) | ||
29 | + navigationController?.isNavigationBarHidden = false | ||
30 | + navigationItem.title = "History" | ||
31 | + } | ||
32 | + | ||
33 | + override func viewDidLoad() { | ||
34 | + super.viewDidLoad() | ||
35 | + initViewController() | ||
36 | + } | ||
37 | + | ||
38 | + private func initViewController() { | ||
39 | + setupTableView() | ||
40 | + addTargets() | ||
41 | + setupSearchBar() | ||
42 | + historyData = HistoryDBManager.shared.getHistoryDataForTableView() | ||
43 | + historyChangesObserve() | ||
44 | + } | ||
45 | + | ||
46 | + override func loadView() { | ||
47 | + view = mainView | ||
48 | + view.backgroundColor = .white | ||
49 | + navigationController?.isNavigationBarHidden = false | ||
50 | + navigationItem.title = "History" | ||
51 | + // historyChangesObserve() | ||
52 | + } | ||
53 | + | ||
54 | + init() { | ||
55 | + super.init(nibName: nil, bundle: nil) | ||
56 | + } | ||
57 | + | ||
58 | + required init?(coder: NSCoder) { | ||
59 | + fatalError("init(coder:) has not been implemented") | ||
60 | + } | ||
61 | +} | ||
62 | + | ||
63 | +//MARK: - TableView Extention | ||
64 | +extension HistoryViewController: UITableViewDelegate, UITableViewDataSource { | ||
65 | + | ||
66 | + func numberOfSections(in tableView: UITableView) -> Int { | ||
67 | + return filteredData.count | ||
68 | + } | ||
69 | + | ||
70 | + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { | ||
71 | + return filteredData[section].name | ||
72 | + } | ||
73 | + | ||
74 | + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | ||
75 | + return filteredData[section].stories.count | ||
76 | + } | ||
77 | + | ||
78 | + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | ||
79 | + guard let cell = tableView.dequeueReusableCell(withIdentifier: "HistoryTableViewCell", for: indexPath) as? HistoryTableViewCell else { | ||
80 | + return HistoryTableViewCell() | ||
81 | + } | ||
82 | + let row = indexPath.row | ||
83 | + let section = indexPath.section | ||
84 | + | ||
85 | + cell.model = filteredData[section].stories[row] | ||
86 | + | ||
87 | + return cell | ||
88 | + } | ||
89 | + | ||
90 | + private func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { | ||
91 | + return true | ||
92 | + } | ||
93 | + | ||
94 | + func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { | ||
95 | + if (editingStyle == .delete) { | ||
96 | + let row = indexPath.row | ||
97 | + let section = indexPath.section | ||
98 | + let historyCellData = filteredData[section].stories[row] | ||
99 | + let tabId = historyCellData.historyCellId | ||
100 | + HistoryDBManager.shared.deleteHistoryCell(tabId: tabId) | ||
101 | + filteredData[section].stories.remove(at: row) | ||
102 | + mainView.historyTabsTableView.reloadData() | ||
103 | + } | ||
104 | + } | ||
105 | + | ||
106 | + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { | ||
107 | + let section = indexPath.section | ||
108 | + let index = indexPath.row | ||
109 | + let urlForFunc = historyData[section].stories[index].siteUrl | ||
110 | + if let navigationController = presentingViewController as? NavigationViewController { | ||
111 | + for viewController in navigationController.viewControllers { | ||
112 | + if let browserHomeViewController = viewController as? BrowserHomeViewController { | ||
113 | + browserHomeViewController.urlManagment(urlForFunc) | ||
114 | + } | ||
115 | + } | ||
116 | + dismiss(animated: true) | ||
117 | + } | ||
118 | + } | ||
119 | + | ||
120 | + func setupTableView() { | ||
121 | + mainView.historyTabsTableView.dataSource = self | ||
122 | + mainView.historyTabsTableView.delegate = self | ||
123 | + mainView.historyTabsTableView.register(HistoryTableViewCell.self, forCellReuseIdentifier: StringConstants.historyTableViewCell) | ||
124 | + } | ||
125 | +} | ||
126 | + | ||
127 | + | ||
128 | + | ||
129 | +//MARK: Helper | ||
130 | +extension HistoryViewController { | ||
131 | + private func addTargets() { | ||
132 | + mainView.historySearchBar.searchTextFieldView.addTarget(self, action: #selector(searchBarTextChanged), for: .editingChanged) | ||
133 | + addToolbarTargets() | ||
134 | + } | ||
135 | + | ||
136 | + private func addToolbarTargets() { | ||
137 | + mainView.historyToolbarView.action = didTabButtonTapped | ||
138 | + } | ||
139 | +} | ||
140 | + | ||
141 | + | ||
142 | +//MARK: Targets | ||
143 | +extension HistoryViewController { | ||
144 | + func didTabButtonTapped(type: historyToolbarElementType) { | ||
145 | + if type == .clean { | ||
146 | + HistoryDBManager.shared.cleanHistory() | ||
147 | + historyData.removeAll() | ||
148 | + mainView.historyTabsTableView.reloadData() | ||
149 | + } | ||
150 | + } | ||
151 | +} | ||
152 | + | ||
153 | + | ||
154 | +//MARK: Action | ||
155 | +extension HistoryViewController { | ||
156 | + @objc | ||
157 | + private func searchResultAction() { | ||
158 | + mainView.historySearchBar.searchTextFieldView.text = nil | ||
159 | + filteredData = historyData | ||
160 | + } | ||
161 | + | ||
162 | + @objc private func searchBarTextChanged() { | ||
163 | + if let text = mainView.historySearchBar.searchTextFieldView.text { | ||
164 | + if text.isEmpty { | ||
165 | + filteredData = historyData | ||
166 | + } else { | ||
167 | + filteredData = HistoryDBManager.shared.getFilteredHistory(filter: text) | ||
168 | + } | ||
169 | + } | ||
170 | + } | ||
171 | +} | ||
172 | + | ||
173 | + | ||
174 | +//MARK: SearchBar | ||
175 | +extension HistoryViewController: UITextFieldDelegate { | ||
176 | + func textFieldDidBeginEditing(_ textField: UITextField) { | ||
177 | + mainView.historySearchBar.searchTextFieldView.text = nil | ||
178 | + mainView.historySearchBar.searchTextFieldView.textColor = .black | ||
179 | + } | ||
180 | + | ||
181 | + func setupSearchBar() { | ||
182 | + mainView.historySearchBar.searchTextFieldView.delegate = self | ||
183 | + } | ||
184 | +} | ||
185 | + | ||
186 | +//MARK: History DB observe | ||
187 | +extension HistoryViewController { | ||
188 | + func historyChangesObserve() { | ||
189 | + let historyObjects = HistoryDBManager.shared.getHistory() | ||
190 | + for historyObject in historyObjects { | ||
191 | + objectNotificationToken = historyObject.observe { [self] changes in | ||
192 | + switch changes { | ||
193 | + case .error(let error): | ||
194 | + print("Error observing changes in Realm: \(error)") | ||
195 | + case .change(_, _): | ||
196 | + break | ||
197 | + case .deleted: | ||
198 | + print("Object deleted in Realm") | ||
199 | + self.historyData = HistoryDBManager.shared.getHistoryDataForTableView() | ||
200 | + self.mainView.historyTabsTableView.reloadData() | ||
201 | + } | ||
202 | + } | ||
203 | + } | ||
204 | + } | ||
205 | +} |
1 | +// | ||
2 | +// HistoryTableViewCell.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 29.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | +import SnapKit | ||
10 | + | ||
11 | +class HistoryTableViewCell: UITableViewCell { | ||
12 | + var model: HistoryDataBase? { | ||
13 | + didSet { | ||
14 | + handleUI() | ||
15 | + } | ||
16 | + } | ||
17 | + | ||
18 | + let searchImageLogo: UIImageView = { | ||
19 | + let obj = UIImageView() | ||
20 | + obj.image = UIImage(systemName: "magnifyingglass") | ||
21 | + obj.tintColor = .gray | ||
22 | + obj.contentMode = .scaleAspectFit | ||
23 | + return obj | ||
24 | + }() | ||
25 | + | ||
26 | + let lastVisitSiteLabel: UILabel = { | ||
27 | + let obj = UILabel() | ||
28 | + obj.font = FontConstants.regularFont_14 | ||
29 | + return obj | ||
30 | + }() | ||
31 | + | ||
32 | + let lastVisitSiteUrlLabel: UILabel = { | ||
33 | + let obj = UILabel() | ||
34 | + obj.textColor = .gray | ||
35 | + obj.font = FontConstants.regularFont_12 | ||
36 | + return obj | ||
37 | + }() | ||
38 | + | ||
39 | + let lastVisitDateLabel: UILabel = { | ||
40 | + let obj = UILabel() | ||
41 | + obj.font = FontConstants.regularFont_12 | ||
42 | + obj.textColor = .gray | ||
43 | + return obj | ||
44 | + }() | ||
45 | + | ||
46 | + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { | ||
47 | + super.init(style: style, reuseIdentifier: reuseIdentifier) | ||
48 | + setup() | ||
49 | + } | ||
50 | + | ||
51 | + required init?(coder: NSCoder) { | ||
52 | + fatalError("init(coder:) has not been implemented") | ||
53 | + } | ||
54 | + | ||
55 | + private func setup() { | ||
56 | + contentView.addSubview(searchImageLogo) | ||
57 | + contentView.addSubview(lastVisitSiteLabel) | ||
58 | + contentView.addSubview(lastVisitSiteUrlLabel) | ||
59 | + contentView.addSubview(lastVisitDateLabel) | ||
60 | + setupConstraints() | ||
61 | + } | ||
62 | + | ||
63 | + private func setupConstraints() { | ||
64 | + searchImageLogo.snp.makeConstraints { make in | ||
65 | + make.leading.equalToSuperview().inset(8.sizeW) | ||
66 | + make.top.equalToSuperview().inset(8.sizeH) | ||
67 | + make.height.equalTo(18.sizeW) | ||
68 | + make.width.equalTo(18.sizeW) | ||
69 | + } | ||
70 | + lastVisitSiteLabel.snp.makeConstraints { make in | ||
71 | + make.leading.equalTo(searchImageLogo.snp.trailing).offset(8.sizeW) | ||
72 | + make.trailing.equalTo(lastVisitDateLabel.snp.leading).offset(-8.sizeW) | ||
73 | + make.top.equalToSuperview().inset(8.sizeH) | ||
74 | + } | ||
75 | + lastVisitDateLabel.snp.makeConstraints { make in | ||
76 | + make.trailing.equalToSuperview().inset(8.sizeW) | ||
77 | + make.top.equalToSuperview().inset(8.sizeH) | ||
78 | + } | ||
79 | + lastVisitSiteUrlLabel.snp.makeConstraints { make in | ||
80 | + make.leading.equalToSuperview().offset(34.sizeW) | ||
81 | + make.top.equalTo(lastVisitSiteLabel.snp.bottom).offset(4.sizeH) | ||
82 | + make.bottom.equalToSuperview().inset(8.sizeH) | ||
83 | + make.trailing.equalToSuperview().inset(61.sizeW) | ||
84 | + } | ||
85 | + } | ||
86 | +} | ||
87 | + | ||
88 | +extension HistoryTableViewCell { | ||
89 | + private func handleUI() { | ||
90 | + let dateForLabel = DateManager.shared.getTimeFromDB(model?.lastVisit) | ||
91 | + lastVisitDateLabel.text = dateForLabel | ||
92 | + lastVisitSiteLabel.text = model?.siteTitle | ||
93 | + lastVisitSiteUrlLabel.text = model?.siteUrl | ||
94 | + } | ||
95 | +} | ||
96 | + |
1 | +// | ||
2 | +// HistorySearchBarView.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 01.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class HistorySearchBarView: UIView { | ||
11 | + let searchImageView: UIImageView = { | ||
12 | + let obj = UIImageView() | ||
13 | + let magnifyImage = UIImage(systemName: "magnifyingglass")?.withTintColor(.gray, renderingMode: .alwaysOriginal) | ||
14 | + obj.image = magnifyImage | ||
15 | + obj.contentMode = .scaleAspectFit | ||
16 | + return obj | ||
17 | + }() | ||
18 | + | ||
19 | + let searchTextFieldView: UITextField = { | ||
20 | + let obj = UITextField() | ||
21 | + obj.text = StringConstants.search | ||
22 | + obj.textColor = ColorConstants.gray | ||
23 | + return obj | ||
24 | + }() | ||
25 | + | ||
26 | + override init(frame: CGRect) { | ||
27 | + super.init(frame: frame) | ||
28 | + setup() | ||
29 | + setupConstraints() | ||
30 | + } | ||
31 | + | ||
32 | + required init?(coder: NSCoder) { | ||
33 | + fatalError("init(coder:) has not been implemented") | ||
34 | + } | ||
35 | + | ||
36 | + private func setup() { | ||
37 | + addSubview(searchImageView) | ||
38 | + addSubview(searchTextFieldView) | ||
39 | + } | ||
40 | + | ||
41 | + private func setupConstraints() { | ||
42 | + searchImageView.snp.makeConstraints { make in | ||
43 | + make.leading.equalToSuperview().offset(16.sizeW) | ||
44 | + make.top.bottom.equalToSuperview().inset(8.sizeH) | ||
45 | + make.height.equalTo(16.sizeH) | ||
46 | + } | ||
47 | + searchTextFieldView.snp.makeConstraints { make in | ||
48 | + make.leading.equalTo(searchImageView).offset(32.sizeW) | ||
49 | + make.trailing.equalToSuperview().offset(-16.sizeW) | ||
50 | + make.top.bottom.equalToSuperview().inset(8.sizeH) | ||
51 | + } | ||
52 | + } | ||
53 | +} | ||
54 | + |
1 | +// | ||
2 | +// ToolbarView.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 29.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +enum historyToolbarElementType { | ||
11 | + case clean | ||
12 | +} | ||
13 | + | ||
14 | +final class HistoryToolbarView: UIView { | ||
15 | + var action: ((historyToolbarElementType) -> Void)? | ||
16 | + | ||
17 | + let cleanHistoryButton: UIButton = { | ||
18 | + let obj = UIButton() | ||
19 | + obj.setTitle("Clean", for: .normal) | ||
20 | + obj.setTitleColor(.systemBlue, for: .normal) | ||
21 | + let menuItems: [UIMenuElement] = menuCases.allCases.map { $0.action } | ||
22 | + obj.menu = UIMenu(title: "Clearing will remove history, cookies and other browsing data. History will be cleared from devices signed into your iCloud Account. Clear from:", children: menuItems) | ||
23 | + obj.showsMenuAsPrimaryAction = true | ||
24 | + return obj | ||
25 | + }() | ||
26 | + | ||
27 | + override init(frame: CGRect) { | ||
28 | + super.init(frame: frame) | ||
29 | + setup() | ||
30 | + setupConstraints() | ||
31 | + } | ||
32 | + | ||
33 | + required init?(coder: NSCoder) { | ||
34 | + fatalError("init(coder:) has not been implemented") | ||
35 | + } | ||
36 | + | ||
37 | + private func setup() { | ||
38 | + backgroundColor = .white | ||
39 | + addSubview(cleanHistoryButton) | ||
40 | + } | ||
41 | + | ||
42 | + private func setupConstraints() { | ||
43 | + cleanHistoryButton.snp.makeConstraints { make in | ||
44 | + make.trailing.equalToSuperview().inset(20.sizeW) | ||
45 | + make.height.equalTo(20.sizeH) | ||
46 | + make.top.bottom.equalToSuperview() | ||
47 | + } | ||
48 | + } | ||
49 | +} | ||
50 | + |
1 | +// | ||
2 | +// HistoryView.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 29.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class HistoryView: UIView { | ||
11 | + let historySearchBar: HistorySearchBarView = { | ||
12 | + let obj = HistorySearchBarView() | ||
13 | + obj.layer.cornerRadius = 10 | ||
14 | + obj.backgroundColor = UIColor(red: 0.913, green: 0.913, blue: 0.947, alpha: 1) | ||
15 | + return obj | ||
16 | + }() | ||
17 | + | ||
18 | + let historyTabsTableView: UITableView = { | ||
19 | + let obj = UITableView() | ||
20 | + obj.separatorStyle = .none | ||
21 | + return obj | ||
22 | + }() | ||
23 | + | ||
24 | + let historyToolbarView: HistoryToolbarView = { | ||
25 | + let obj = HistoryToolbarView() | ||
26 | + return obj | ||
27 | + }() | ||
28 | + | ||
29 | + override init(frame: CGRect) { | ||
30 | + super.init(frame: frame) | ||
31 | + setup() | ||
32 | + } | ||
33 | + | ||
34 | + required init?(coder: NSCoder) { | ||
35 | + fatalError("init(coder:) has not been implemented") | ||
36 | + } | ||
37 | + | ||
38 | + private func setup() { | ||
39 | + backgroundColor = .gray.withAlphaComponent(0.1) | ||
40 | + addSubview(historySearchBar) | ||
41 | + addSubview(historyTabsTableView) | ||
42 | + addSubview(historyToolbarView) | ||
43 | + setupConstraints() | ||
44 | + } | ||
45 | + | ||
46 | + private func setupConstraints() { | ||
47 | + historySearchBar.snp.makeConstraints { make in | ||
48 | + make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(16.sizeH) | ||
49 | + make.leading.trailing.equalToSuperview().inset(16.sizeW) | ||
50 | + } | ||
51 | + | ||
52 | + historyTabsTableView.snp.makeConstraints { make in | ||
53 | + make.top.equalTo(historySearchBar.snp.bottom).offset(32.sizeH) | ||
54 | + make.bottom.equalTo(historyToolbarView.snp.top).inset(7.sizeH) | ||
55 | + make.leading.trailing.equalToSuperview().inset(16.sizeW) | ||
56 | + make.height.equalTo(600.sizeH) | ||
57 | + } | ||
58 | + historyToolbarView.snp.makeConstraints { make in | ||
59 | + make.bottom.equalTo(safeAreaLayoutGuide.snp.bottom) | ||
60 | + make.leading.trailing.equalToSuperview() | ||
61 | + } | ||
62 | + } | ||
63 | +} | ||
64 | + | ||
65 | +//MARK: - Helpers | ||
66 | +extension HistoryView: UITextFieldDelegate { | ||
67 | + func textFieldDidBeginEditing(_ textField: UITextField) { | ||
68 | + historySearchBar.searchTextFieldView.text = "" | ||
69 | + } | ||
70 | +} | ||
71 | + | ||
72 | + |
1 | +// | ||
2 | +// ViewController.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 25.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | +import SnapKit | ||
10 | +import WebKit | ||
11 | +import Combine | ||
12 | +import Realm | ||
13 | + | ||
14 | +final class BrowserHomeViewController: UIViewController { | ||
15 | + private let images = StringConstants.browserHomeVCImages | ||
16 | + private let searchingViewController = SearchingViewController(dataForReq: []) | ||
17 | + private let searchResultViewController = SearchResultViewController(searchLink: String()) | ||
18 | + private var subscriptions: Set<AnyCancellable> = [] | ||
19 | + private var finalSearchRequest = URLConstants.googleURL | ||
20 | + private let tabsViewController = TabsViewController() | ||
21 | + private var currentTabId: Int? | ||
22 | + | ||
23 | + let mainView = BrowserHomeView() | ||
24 | + | ||
25 | + private var url: String? { | ||
26 | + didSet { | ||
27 | + finalSearchRequest = url ?? String() | ||
28 | + openSearchResult() | ||
29 | + } | ||
30 | + } | ||
31 | + | ||
32 | + override func viewDidLoad() { | ||
33 | + super.viewDidLoad() | ||
34 | + view.backgroundColor = ColorConstants.lightGray | ||
35 | + initViewController() | ||
36 | + } | ||
37 | + | ||
38 | + override func viewWillAppear(_ animated: Bool) { | ||
39 | + keyboardObserver() | ||
40 | + } | ||
41 | + | ||
42 | + private func initViewController() { | ||
43 | + setupCollectionView() | ||
44 | + addTargets() | ||
45 | + setupSearchBarComponentsAction() | ||
46 | + setupSearchingViewController() | ||
47 | + progressObserver() | ||
48 | + } | ||
49 | + | ||
50 | + override func viewIsAppearing(_ animated: Bool) { | ||
51 | + navigationController?.isNavigationBarHidden = true | ||
52 | + } | ||
53 | + | ||
54 | + override func loadView() { | ||
55 | + view = mainView | ||
56 | + } | ||
57 | + | ||
58 | + init(url: String?, currentTabId: Int?) { | ||
59 | + super.init(nibName: nil, bundle: nil) | ||
60 | + self.url = url | ||
61 | + self.currentTabId = currentTabId | ||
62 | + } | ||
63 | + | ||
64 | + required init?(coder: NSCoder) { | ||
65 | + fatalError("init(coder:) has not been implemented") | ||
66 | + } | ||
67 | + | ||
68 | + override func viewWillDisappear(_ animated: Bool) { | ||
69 | + super.viewWillDisappear(animated) | ||
70 | + NotificationCenter.default.removeObserver(self) | ||
71 | + } | ||
72 | +} | ||
73 | + | ||
74 | + | ||
75 | +//MARK: CollectionView Setup | ||
76 | +extension BrowserHomeViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { | ||
77 | + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { | ||
78 | + images.count | ||
79 | + } | ||
80 | + | ||
81 | + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { | ||
82 | + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TabCollectionViewCell", for: indexPath) as? TabCollectionViewCell else { | ||
83 | + return TabCollectionViewCell() | ||
84 | + } | ||
85 | + let finalCell = setupCellForCollectionView(cell, indexPath) | ||
86 | + return finalCell | ||
87 | + } | ||
88 | + | ||
89 | + private func setupCellForCollectionView(_ cell: TabCollectionViewCell, _ indexPath: IndexPath) -> TabCollectionViewCell { | ||
90 | + let imageName = images[indexPath.item] | ||
91 | + cell.tabCellImage.image = UIImage(named: imageName) | ||
92 | + cell.tabCellLabel.text = imageName | ||
93 | + cell.backgroundColor = .white | ||
94 | + return cell | ||
95 | + } | ||
96 | + | ||
97 | + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { | ||
98 | + return CGSize(width: 72.sizeW, height: 94.sizeH) | ||
99 | + } | ||
100 | + | ||
101 | + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { | ||
102 | + finalSearchRequest = StringConstants.browserHomeVCurl[indexPath.row] | ||
103 | + openSearchResult() | ||
104 | + } | ||
105 | + | ||
106 | + private func setupCollectionView() { | ||
107 | + mainView.frequentlyVisitedCollectionView.dataSource = self | ||
108 | + mainView.frequentlyVisitedCollectionView.delegate = self | ||
109 | + mainView.frequentlyVisitedCollectionView.register(TabCollectionViewCell.self, forCellWithReuseIdentifier: "TabCollectionViewCell") | ||
110 | + } | ||
111 | +} | ||
112 | + | ||
113 | + | ||
114 | +//MARK: - Keyboard extention | ||
115 | +extension BrowserHomeViewController { | ||
116 | + func keyboardObserver() { | ||
117 | + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear), name: UIResponder.keyboardWillHideNotification, object: nil) | ||
118 | + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) | ||
119 | + } | ||
120 | +} | ||
121 | + | ||
122 | + | ||
123 | +//MARK: - Action | ||
124 | +extension BrowserHomeViewController { | ||
125 | + @objc private func searchResultAction() { | ||
126 | + removeChildViewController() | ||
127 | + mainView.searchBarContainer.searchBarView.searchTextFieldView.text = nil | ||
128 | + mainView.searchBarContainer.searchBarView.cleanTextFieldButton.isHidden = true | ||
129 | + } | ||
130 | + | ||
131 | + func searchAndReloadTableView() async { | ||
132 | + let sitesFromSearch: [String] = await BrowserSearchService().searchSuggest(finalSearchRequest) | ||
133 | + if !sitesFromSearch.isEmpty { | ||
134 | + searchingViewController.dataForReq = sitesFromSearch | ||
135 | + } | ||
136 | + addingSearchingViewController(searchingViewController) | ||
137 | + } | ||
138 | + | ||
139 | + @objc | ||
140 | + func keyboardWillShow(_ notification: Notification) { | ||
141 | + guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } | ||
142 | + let keyboardHeight = keyboardFrame.height | ||
143 | + self.mainView.animateSearchBar(keyboardHeight) | ||
144 | + } | ||
145 | + | ||
146 | + @objc | ||
147 | + func keyboardWillDisappear() { | ||
148 | + mainView.animateSearchBarDismiss() | ||
149 | + } | ||
150 | + | ||
151 | + @objc | ||
152 | + private func settingButtonTapped(_ sender: UIButton) { | ||
153 | + let settingViewController = SettingViewController() | ||
154 | + openAdditionViewController(settingViewController) | ||
155 | + } | ||
156 | + | ||
157 | + @objc | ||
158 | + private func removeAdButtonTapped(_ sender: UIButton) { | ||
159 | + let removeAdViewController = RemoveAdvertViewController() | ||
160 | + openAdditionViewController(removeAdViewController) | ||
161 | + } | ||
162 | + | ||
163 | + @objc | ||
164 | + private func removeSearchingViewController(_ sender: UIButton) { | ||
165 | + searchResultAction() | ||
166 | + } | ||
167 | + | ||
168 | + func didTabButtonTapped(type: ToolbarElementType) { | ||
169 | + switch type { | ||
170 | + case .history: | ||
171 | + let historyViewController = HistoryViewController() | ||
172 | + present(historyViewController, animated: true, completion: nil) | ||
173 | + break | ||
174 | + case .tabs: | ||
175 | + tabsButtonHandler() | ||
176 | + break | ||
177 | + case .back: | ||
178 | + searchResultViewController.mainView.searchResultView.goBack() | ||
179 | + break | ||
180 | + case .forward: | ||
181 | + searchResultViewController.mainView.searchResultView.goForward() | ||
182 | + break | ||
183 | + case .share: | ||
184 | + let items = [URL(string: finalSearchRequest)] | ||
185 | + let shareView = UIActivityViewController(activityItems: items as [Any], applicationActivities: nil) | ||
186 | + present(shareView, animated: true) | ||
187 | + break | ||
188 | + } | ||
189 | + } | ||
190 | +} | ||
191 | + | ||
192 | + | ||
193 | +//MARK: - Helpers | ||
194 | +extension BrowserHomeViewController { | ||
195 | + private func addTargets() { | ||
196 | + mainView.settingButton.addTarget(self, action: #selector(settingButtonTapped(_ :)), for: .touchUpInside) | ||
197 | + mainView.removeAddButton.addTarget(self, action: #selector(removeAdButtonTapped(_ :)), for: .touchUpInside) | ||
198 | + mainView.searchBarContainer.searchBarView.searchTextFieldView.addTarget(self, action: #selector(searchBarTextChanged), for: .editingChanged) | ||
199 | + searchingViewController.mainView.searchingButton.addTarget(self, action: #selector(removeSearchingViewController(_ :)), for: .touchUpInside) | ||
200 | + addToolbarTargets() | ||
201 | + } | ||
202 | + | ||
203 | + private func addToolbarTargets() { | ||
204 | + mainView.toolbarView.action = didTabButtonTapped | ||
205 | + } | ||
206 | + | ||
207 | + private func openAdditionViewController(_ viewController: UIViewController) { | ||
208 | + viewController.modalPresentationStyle = .fullScreen | ||
209 | + present(viewController, animated: true, completion: nil) | ||
210 | + } | ||
211 | + | ||
212 | + private func setupSearchingViewController() { | ||
213 | + searchingViewController.searchCell = { [self] cell in | ||
214 | + guard let cell = cell as? SearchingTableViewCell else { | ||
215 | + return | ||
216 | + } | ||
217 | + finalSearchRequest = "https://www.google.com/search?q=" + (cell.searchLabel.text ?? "") | ||
218 | + openSearchResult() | ||
219 | + mainView.searchBarContainer.searchBarView.searchTextFieldView.endEditing(true) | ||
220 | + } | ||
221 | + } | ||
222 | + | ||
223 | + private func openSearchResult() { | ||
224 | + let finalSearchRequest = finalSearchRequest.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) | ||
225 | + searchResultViewController.searchLink = finalSearchRequest ?? "" | ||
226 | + if let localUrl = URL(string: finalSearchRequest ?? "") { | ||
227 | + let request = URLRequest(url: localUrl) | ||
228 | + print(request) | ||
229 | + searchResultViewController.mainView.searchResultView.load(request) | ||
230 | + } | ||
231 | + addingSearchingViewController(searchResultViewController) | ||
232 | + } | ||
233 | + | ||
234 | + private func tabsButtonHandler() { | ||
235 | + if currentTabId == nil { | ||
236 | + addNewTab() | ||
237 | + navigationController?.popViewController(animated: true) | ||
238 | + } else { | ||
239 | + updateCurrentTab() | ||
240 | + } | ||
241 | + } | ||
242 | + | ||
243 | + private func addNewTab() { | ||
244 | + if let childViewController = self.children.first as? SearchResultViewController { | ||
245 | + guard let siteTitle = childViewController.mainView.searchResultView.title else { | ||
246 | + return | ||
247 | + } | ||
248 | + guard let siteUrl = childViewController.mainView.searchResultView.url else { | ||
249 | + return | ||
250 | + } | ||
251 | + let finalUrl = siteUrl.absoluteString | ||
252 | + let lastVisit = DateManager.shared.currentDate | ||
253 | + let wkWVForSnapshot = childViewController.mainView.searchResultView | ||
254 | + | ||
255 | + SnapshotService.shared.makeWKWebViewSnapshot(wkWVForSnapshot) { snapshotForDB in | ||
256 | + TabManager.shared.saveTab(tabTitle: siteTitle, snapshotImage: snapshotForDB, tabUrl: finalUrl) | ||
257 | + } | ||
258 | + HistoryDBManager.shared.saveToHistory(siteTitle: siteTitle, siteUrl: finalUrl, lastVisit: lastVisit) | ||
259 | + } else { | ||
260 | + let homeViewSnapshot = SnapshotService.shared.makeSnapshot(mainView) | ||
261 | + TabManager.shared.saveTab(tabTitle: "Start Page", snapshotImage: homeViewSnapshot, tabUrl: "homeUrl") | ||
262 | + currentTabId = tabsViewController.getCellIndex().row | ||
263 | + } | ||
264 | + } | ||
265 | + | ||
266 | + private func updateCurrentTab() { | ||
267 | + let allTabs = TabManager.shared.getAllTabs() | ||
268 | + let chosenTab = allTabs[self.currentTabId ?? Int()] | ||
269 | + let gottedCellId = chosenTab.tabId | ||
270 | + let imageForUpdatedTab = SnapshotService.shared.makeSnapshot(self.mainView) | ||
271 | + if let childViewController = self.children.first as? SearchResultViewController { | ||
272 | + guard let siteTitle = childViewController.mainView.searchResultView.title else { | ||
273 | + return | ||
274 | + } | ||
275 | + guard let siteUrl = childViewController.mainView.searchResultView.url?.absoluteString else { | ||
276 | + return | ||
277 | + } | ||
278 | + let lastDate = DateManager.shared.currentDate | ||
279 | + | ||
280 | + TabManager.shared.updateTab(tabId: gottedCellId, newTabTitle: siteTitle, newSnapshotImage: imageForUpdatedTab, newTabUrl: siteUrl) { result in | ||
281 | + self.navigationController?.popViewController(animated: true) | ||
282 | + } | ||
283 | + HistoryDBManager.shared.saveToHistory(siteTitle: siteTitle, siteUrl: siteUrl, lastVisit: lastDate) | ||
284 | + } else { | ||
285 | + TabManager.shared.updateTab(tabId: gottedCellId, newTabTitle: "Start Page", newSnapshotImage: imageForUpdatedTab, newTabUrl: "homeUrl") { result in | ||
286 | + self.navigationController?.popViewController(animated: true) | ||
287 | + } | ||
288 | + } | ||
289 | + } | ||
290 | +} | ||
291 | + | ||
292 | + | ||
293 | +//MARK: - ChildVC actions | ||
294 | +extension BrowserHomeViewController { | ||
295 | + func addingSearchingViewController(_ childViewController: UIViewController) { | ||
296 | + if self.children.isEmpty { | ||
297 | + addNewChildViewController(childViewController) | ||
298 | + } else { | ||
299 | + removeChildViewController() | ||
300 | + addNewChildViewController(childViewController) | ||
301 | + } | ||
302 | + } | ||
303 | + | ||
304 | + func addNewChildViewController(_ child: UIViewController) { | ||
305 | + addChild(child) | ||
306 | + mainView.addSearchView(child.view) | ||
307 | + child.didMove(toParent: self) | ||
308 | + } | ||
309 | + | ||
310 | + func removeChildViewController() { | ||
311 | + if let childViewController = self.children.first as? SearchingViewController { | ||
312 | + childViewController.view.removeFromSuperview() | ||
313 | + childViewController.willMove(toParent: nil) | ||
314 | + childViewController.removeFromParent() | ||
315 | + } | ||
316 | + if let childViewController = self.children.first as? SearchResultViewController { | ||
317 | + childViewController.view.removeFromSuperview() | ||
318 | + childViewController.willMove(toParent: nil) | ||
319 | + childViewController.removeFromParent() | ||
320 | + } | ||
321 | + } | ||
322 | +} | ||
323 | + | ||
324 | + | ||
325 | +//MARK: - SearchBar extention | ||
326 | +extension BrowserHomeViewController: UITextFieldDelegate { | ||
327 | + @objc | ||
328 | + func searchBarTextChanged() { | ||
329 | + if let text = mainView.searchBarContainer.searchBarView.searchTextFieldView.text { | ||
330 | + finalSearchRequest = text | ||
331 | + mainView.searchBarContainer.searchBarView.cleanTextFieldButton.isHidden = false | ||
332 | + if finalSearchRequest.isEmpty { | ||
333 | + searchResultAction() | ||
334 | + } else { | ||
335 | + Task { | ||
336 | + await searchAndReloadTableView() | ||
337 | + } | ||
338 | + } | ||
339 | + } | ||
340 | + } | ||
341 | + | ||
342 | + func textFieldDidBeginEditing(_ textField: UITextField) { | ||
343 | + mainView.searchBarContainer.searchBarView.searchTextFieldView.text = nil | ||
344 | + mainView.searchBarContainer.searchBarView.searchTextFieldView.textColor = .black | ||
345 | + } | ||
346 | + | ||
347 | + func textFieldShouldReturn(_ textField: UITextField) -> Bool { | ||
348 | + finalSearchRequest = "https://www.google.com/search?q=" + (textField.text ?? "") | ||
349 | + openSearchResult() | ||
350 | + textField.resignFirstResponder() | ||
351 | + return true | ||
352 | + } | ||
353 | + | ||
354 | + func setupSearchBarComponentsAction() { | ||
355 | + mainView.searchBarContainer.searchBarView.searchTextFieldView.autocorrectionType = .no | ||
356 | + isButtonClickable() | ||
357 | + mainView.searchBarContainer.searchBarView.searchTextFieldView.delegate = self | ||
358 | + mainView.searchBarContainer.searchBarView.cleanTextFieldButton.addTarget(self, action: #selector(searchResultAction), for: .touchUpInside) | ||
359 | + } | ||
360 | +} | ||
361 | + | ||
362 | + | ||
363 | +//MARK: - WebView extention | ||
364 | +extension BrowserHomeViewController { | ||
365 | + func isButtonClickable() { | ||
366 | + searchResultViewController.mainView.searchResultView | ||
367 | + .publisher(for: \.canGoBack) | ||
368 | + .sink { [weak self] canGoBack in | ||
369 | + self?.mainView.toolbarView.backBarButtonItem.isEnabled = canGoBack | ||
370 | + } | ||
371 | + .store(in: &subscriptions) | ||
372 | + | ||
373 | + searchResultViewController.mainView.searchResultView | ||
374 | + .publisher(for: \.canGoForward) | ||
375 | + .sink { [weak self] canGoForward in | ||
376 | + self?.mainView.toolbarView.forwardBarButtonItem.isEnabled = canGoForward | ||
377 | + } | ||
378 | + .store(in: &subscriptions) | ||
379 | + | ||
380 | + searchResultViewController.mainView.searchResultView | ||
381 | + .publisher(for: \.isLoading) | ||
382 | + .sink { [weak self] isLoading in | ||
383 | + guard let self = self else { return } | ||
384 | + if let url = self.searchResultViewController.mainView.searchResultView.url { | ||
385 | + self.finalSearchRequest = url.absoluteString | ||
386 | + } | ||
387 | + } | ||
388 | + .store(in: &subscriptions) | ||
389 | + } | ||
390 | + | ||
391 | + private func progressObserver() { | ||
392 | + searchResultViewController.mainView.searchResultView | ||
393 | + .publisher(for: \.estimatedProgress) | ||
394 | + .sink { [weak self] progress in | ||
395 | + guard let self = self else { return } | ||
396 | + self.mainView.searchBarContainer.searchBarView.progressBar.progress = Float(progress) | ||
397 | + if progress == 1 { | ||
398 | + self.mainView.searchBarContainer.searchBarView.progressBar.isHidden = true | ||
399 | + self.mainView.toolbarView.shareBarButtonItem.isEnabled = true | ||
400 | + } else if progress == 0 { | ||
401 | + self.mainView.searchBarContainer.searchBarView.progressBar.isHidden = true | ||
402 | + } else { | ||
403 | + self.mainView.searchBarContainer.searchBarView.progressBar.isHidden = false | ||
404 | + self.mainView.toolbarView.shareBarButtonItem.isEnabled = false | ||
405 | + } | ||
406 | + } | ||
407 | + .store(in: &subscriptions) | ||
408 | + } | ||
409 | + } | ||
410 | + | ||
411 | +extension BrowserHomeViewController { | ||
412 | + func urlManagment(_ url: String) { | ||
413 | + finalSearchRequest = url | ||
414 | + openSearchResult() | ||
415 | + } | ||
416 | +} |
1 | +// | ||
2 | +// MainView.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 25.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | +import SnapKit | ||
10 | + | ||
11 | +final class BrowserHomeView: UIView { | ||
12 | + let removeAddButton: UIButton = { | ||
13 | + let obj = UIButton() | ||
14 | + obj.backgroundColor = ColorConstants.buttonsBackgroundColor | ||
15 | + obj.setTitle("Remove advert", for: .normal) | ||
16 | + obj.titleLabel?.font = FontConstants.semiboldFont_15 | ||
17 | + obj.translatesAutoresizingMaskIntoConstraints = false | ||
18 | + obj.setTitleColor(.white, for: .normal) | ||
19 | + obj.setImage(UIImage(systemName: "shield.lefthalf.filled")?.withTintColor(.white, renderingMode: .alwaysOriginal), for: .normal) | ||
20 | + obj.layer.cornerRadius = 10 | ||
21 | + obj.contentEdgeInsets = UIEdgeInsets(top: 10.sizeW, left: 10.sizeW, bottom: 10.sizeW, right: 10.sizeW) | ||
22 | + obj.clipsToBounds = true | ||
23 | + obj.layer.masksToBounds = false | ||
24 | + return obj | ||
25 | + }() | ||
26 | + | ||
27 | + let settingButton: UIButton = { | ||
28 | + let obj = UIButton() | ||
29 | + obj.setImage(UIImage(named: "Settings")?.withTintColor(.systemBlue), for: .normal) | ||
30 | + return obj | ||
31 | + }() | ||
32 | + | ||
33 | + let titleLabel: UILabel = { | ||
34 | + let obj = UILabel() | ||
35 | + obj.text = StringConstants.frequentlyVisited | ||
36 | + obj.font = FontConstants.semiboldFont_18 | ||
37 | + return obj | ||
38 | + }() | ||
39 | + | ||
40 | + let frequentlyVisitedCollectionView: UICollectionView = { | ||
41 | + let obj = UICollectionViewFlowLayout() | ||
42 | + obj.minimumInteritemSpacing = 12.sizeW | ||
43 | + obj.scrollDirection = .horizontal | ||
44 | + obj.sectionInset = UIEdgeInsets(top: 4.sizeH, left: 4.sizeW, bottom: 4.sizeH, right: 4.sizeW) | ||
45 | + | ||
46 | + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: obj) | ||
47 | + collectionView.backgroundColor = .clear | ||
48 | + collectionView.showsHorizontalScrollIndicator = false | ||
49 | + return collectionView | ||
50 | + }() | ||
51 | + | ||
52 | + let toolbarView: ToolbarView = { | ||
53 | + let obj = ToolbarView() | ||
54 | + return obj | ||
55 | + }() | ||
56 | + | ||
57 | + let searchBarContainer: SearchBarContainer = { | ||
58 | + let obj = SearchBarContainer() | ||
59 | + obj.backgroundColor = .clear | ||
60 | + return obj | ||
61 | + }() | ||
62 | + | ||
63 | + override init(frame: CGRect) { | ||
64 | + super.init(frame: frame) | ||
65 | + setup() | ||
66 | + setupConstraints() | ||
67 | + } | ||
68 | + | ||
69 | + required init?(coder: NSCoder) { | ||
70 | + fatalError("init(coder:) has not been implemented") | ||
71 | + } | ||
72 | + | ||
73 | + private func setup() { | ||
74 | + addSubview(titleLabel) | ||
75 | + addSubview(frequentlyVisitedCollectionView) | ||
76 | + addSubview(removeAddButton) | ||
77 | + addSubview(settingButton) | ||
78 | + addSubview(searchBarContainer) | ||
79 | + addSubview(toolbarView) | ||
80 | + } | ||
81 | + | ||
82 | + private func setupConstraints() { | ||
83 | + removeAddButton.snp.makeConstraints { make in | ||
84 | + make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(16.sizeH) | ||
85 | + make.leading.equalTo(safeAreaLayoutGuide.snp.leading).offset(20.sizeW) | ||
86 | + make.trailing.equalToSuperview().offset(-158.sizeW) | ||
87 | + } | ||
88 | + settingButton.snp.makeConstraints { make in | ||
89 | + make.centerY.equalTo(removeAddButton) | ||
90 | + make.height.equalTo(24.sizeH) | ||
91 | + make.width.equalTo(24.sizeH) | ||
92 | + make.trailing.equalToSuperview().offset(-16.sizeW) | ||
93 | + } | ||
94 | + titleLabel.snp.makeConstraints { make in | ||
95 | + make.top.equalTo(removeAddButton.snp.bottom).offset(64.sizeH) | ||
96 | + make.leading.equalTo(safeAreaLayoutGuide.snp.leading).offset(16.sizeW) | ||
97 | + } | ||
98 | + frequentlyVisitedCollectionView.snp.makeConstraints { make in | ||
99 | + make.leading.trailing.equalToSuperview().inset(24.sizeW) | ||
100 | + make.height.equalTo(120.sizeH) | ||
101 | + make.top.equalTo(titleLabel.snp.bottom).offset(8.sizeH) | ||
102 | + } | ||
103 | + toolbarView.snp.makeConstraints { make in | ||
104 | + make.leading.equalToSuperview() | ||
105 | + make.trailing.equalToSuperview() | ||
106 | + make.bottom.equalTo(safeAreaLayoutGuide.snp.bottom) | ||
107 | + } | ||
108 | + searchBarContainer.snp.makeConstraints { make in | ||
109 | + make.leading.equalToSuperview() | ||
110 | + make.trailing.equalToSuperview() | ||
111 | + make.bottom.equalToSuperview().offset(-90.sizeH) | ||
112 | + } | ||
113 | + } | ||
114 | +} | ||
115 | + | ||
116 | +//MARK: - childView extension | ||
117 | +extension BrowserHomeView { | ||
118 | + func addSearchView(_ childView: UIView){ | ||
119 | + addSubview(childView) | ||
120 | + childView.snp.makeConstraints { make in | ||
121 | + make.top.equalToSuperview() | ||
122 | + make.leading.trailing.equalToSuperview() | ||
123 | + make.bottom.equalTo(searchBarContainer.snp.top).offset(-2.sizeH) | ||
124 | + } | ||
125 | + } | ||
126 | +} | ||
127 | + | ||
128 | +//MARK: - Search bar extention | ||
129 | +extension BrowserHomeView { | ||
130 | + func animateSearchBar(_ keyboardHeight: CGFloat) { | ||
131 | + UIView.animate(withDuration: 0.3) { | ||
132 | + self.searchBarContainer.backgroundColor = UIColor(red: 0.808, green: 0.823, blue: 0.85, alpha: 0.9) | ||
133 | + self.searchBarContainer.snp.updateConstraints { make in | ||
134 | + make.leading.equalToSuperview() | ||
135 | + make.trailing.equalToSuperview() | ||
136 | + make.bottom.equalToSuperview().offset(-keyboardHeight) | ||
137 | + } | ||
138 | + self.layoutIfNeeded() | ||
139 | + } | ||
140 | + } | ||
141 | + | ||
142 | + func animateSearchBarDismiss() { | ||
143 | + BrowserHomeView.animate(withDuration: 0.3) { | ||
144 | + self.searchBarContainer.backgroundColor = .clear | ||
145 | + self.searchBarContainer.snp.updateConstraints { make in | ||
146 | + make.leading.equalToSuperview() | ||
147 | + make.trailing.equalToSuperview() | ||
148 | + make.bottom.equalToSuperview().offset(-90.sizeH) | ||
149 | + } | ||
150 | + } | ||
151 | + self.layoutIfNeeded() | ||
152 | + } | ||
153 | +} |
1 | +// | ||
2 | +// tabCell.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 25.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class TabCollectionViewCell: UICollectionViewCell { | ||
11 | + let tabCellImage: UIImageView = { | ||
12 | + let obj = UIImageView() | ||
13 | + obj.contentMode = .scaleToFill | ||
14 | + return obj | ||
15 | + }() | ||
16 | + | ||
17 | + let tabCellLabel: UILabel = { | ||
18 | + let obj = UILabel() | ||
19 | + obj.textColor = .black | ||
20 | + obj.font = FontConstants.regularFont_14 | ||
21 | + return obj | ||
22 | + }() | ||
23 | + | ||
24 | + override init(frame: CGRect) { | ||
25 | + super.init(frame: frame) | ||
26 | + setup() | ||
27 | + } | ||
28 | + | ||
29 | + required init?(coder: NSCoder) { | ||
30 | + fatalError("init(coder:) has not been implemented") | ||
31 | + } | ||
32 | + | ||
33 | + func setup() { | ||
34 | + | ||
35 | + layer.cornerRadius = 10 | ||
36 | + contentView.backgroundColor = UIColor(red: 0.867, green: 0.867, blue: 0.863, alpha: 0.4) | ||
37 | + contentView.layer.cornerRadius = 10 | ||
38 | + contentView.addSubview(tabCellImage) | ||
39 | + contentView.addSubview(tabCellLabel) | ||
40 | + setupConstraints() | ||
41 | + } | ||
42 | + | ||
43 | + private func setupConstraints() { | ||
44 | + tabCellImage.snp.makeConstraints { make in | ||
45 | + make.top.equalToSuperview().offset(14.sizeH) | ||
46 | + make.leading.trailing.equalToSuperview().inset(9.sizeW) | ||
47 | + make.height.equalTo(50.sizeW) | ||
48 | + } | ||
49 | + | ||
50 | + tabCellLabel.snp.makeConstraints { make in | ||
51 | + make.centerX.equalToSuperview() | ||
52 | + make.top.equalTo(tabCellImage.snp.bottom).offset(8.sizeH) | ||
53 | + } | ||
54 | + } | ||
55 | + | ||
56 | + override func layoutSubviews() { | ||
57 | + super.layoutSubviews() | ||
58 | + shadowSetup() | ||
59 | + } | ||
60 | +} | ||
61 | + | ||
62 | +//MARK: - Shadows | ||
63 | +extension TabCollectionViewCell { | ||
64 | + private func shadowSetup() { | ||
65 | + layer.shadowColor = UIColor.gray.cgColor | ||
66 | + layer.shadowOpacity = 0.2 | ||
67 | + layer.shadowRadius = 3 | ||
68 | + layer.shadowOffset = CGSize(width: 2, height: 2) | ||
69 | + } | ||
70 | +} |
1 | +// | ||
2 | +// SearchBarSpace.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 12.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class SearchBarContainer: UIView { | ||
11 | + let searchBarView: SearchBarView = { | ||
12 | + let obj = SearchBarView() | ||
13 | + obj.backgroundColor = .white | ||
14 | + obj.layer.cornerRadius = 10 | ||
15 | + obj.layer.shadowColor = UIColor.black.withAlphaComponent(0.1).cgColor | ||
16 | + obj.layer.shadowOpacity = 1 | ||
17 | + obj.layer.shadowRadius = 10 | ||
18 | + obj.layer.shadowOffset = CGSize(width: 0, height: 4.sizeW) | ||
19 | + return obj | ||
20 | + }() | ||
21 | + | ||
22 | + override init(frame: CGRect) { | ||
23 | + super.init(frame: frame) | ||
24 | + setup() | ||
25 | + setupConstraints() | ||
26 | + } | ||
27 | + | ||
28 | + required init?(coder: NSCoder) { | ||
29 | + fatalError("init(coder:) has not been implemented") | ||
30 | + } | ||
31 | + | ||
32 | + private func setup() { | ||
33 | + addSubview(searchBarView) | ||
34 | + } | ||
35 | + | ||
36 | + private func setupConstraints() { | ||
37 | + searchBarView.snp.makeConstraints { make in | ||
38 | + make.top.equalToSuperview().offset(4.sizeH) | ||
39 | + make.leading.equalToSuperview().offset(16.sizeW) | ||
40 | + make.trailing.equalToSuperview().offset(-16.sizeW) | ||
41 | + make.bottom.equalToSuperview().offset(-6.sizeH) | ||
42 | + } | ||
43 | + } | ||
44 | +} |
1 | +// | ||
2 | +// SearchBarView.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 26.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class SearchBarView: UIView { | ||
11 | + let searchImageView: UIImageView = { | ||
12 | + let obj = UIImageView() | ||
13 | + let magnifyImage = UIImage(systemName: "magnifyingglass")?.withTintColor(.gray, renderingMode: .alwaysOriginal) | ||
14 | + obj.image = magnifyImage | ||
15 | + return obj | ||
16 | + }() | ||
17 | + | ||
18 | + let searchTextFieldView: UITextField = { | ||
19 | + let obj = UITextField() | ||
20 | + obj.text = "Search" | ||
21 | + obj.textColor = ColorConstants.gray | ||
22 | + return obj | ||
23 | + }() | ||
24 | + | ||
25 | + let cleanTextFieldButton: UIButton = { | ||
26 | + let obj = UIButton() | ||
27 | + obj.setImage(UIImage(systemName: "xmark.circle.fill"), for: .normal) | ||
28 | + obj.tintColor = .black | ||
29 | + obj.isHidden = true | ||
30 | + return obj | ||
31 | + }() | ||
32 | + | ||
33 | + let progressBar: UIProgressView = { | ||
34 | + let obj = UIProgressView(progressViewStyle: .default) | ||
35 | + obj.progressTintColor = .systemIndigo | ||
36 | + obj.progress = 0 | ||
37 | + obj.translatesAutoresizingMaskIntoConstraints = false | ||
38 | + return obj | ||
39 | + }() | ||
40 | + | ||
41 | + override init(frame: CGRect) { | ||
42 | + super.init(frame: frame) | ||
43 | + setup() | ||
44 | + setupConstraints() | ||
45 | + } | ||
46 | + | ||
47 | + required init?(coder: NSCoder) { | ||
48 | + fatalError("init(coder:) has not been implemented") | ||
49 | + } | ||
50 | + | ||
51 | + private func setup() { | ||
52 | + addSubview(progressBar) | ||
53 | + addSubview(searchImageView) | ||
54 | + addSubview(searchTextFieldView) | ||
55 | + addSubview(cleanTextFieldButton) | ||
56 | + } | ||
57 | + | ||
58 | + private func setupConstraints() { | ||
59 | + searchImageView.snp.makeConstraints { make in | ||
60 | + make.leading.equalToSuperview().offset(16.sizeW) | ||
61 | + make.top.bottom.equalToSuperview().inset(8.sizeH) | ||
62 | + } | ||
63 | + searchTextFieldView.snp.makeConstraints { make in | ||
64 | + make.leading.equalTo(searchImageView).offset(32.sizeW) | ||
65 | + make.trailing.equalToSuperview().offset(-16.sizeW) | ||
66 | + make.top.bottom.equalToSuperview().inset(8.sizeH) | ||
67 | + } | ||
68 | + cleanTextFieldButton.snp.makeConstraints { make in | ||
69 | + make.trailing.equalToSuperview().offset(-16.sizeW) | ||
70 | + make.top.bottom.equalToSuperview().inset(8.sizeH) | ||
71 | + } | ||
72 | + progressBar.snp.makeConstraints { make in | ||
73 | + make.bottom.equalToSuperview().inset(2.sizeH) | ||
74 | + make.leading.trailing.equalToSuperview() | ||
75 | + make.height.equalTo(2) | ||
76 | + } | ||
77 | + } | ||
78 | +} |
1 | +// | ||
2 | +// UINavigationToolbar.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 27.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +enum ToolbarElementType { | ||
11 | + case back | ||
12 | + case forward | ||
13 | + case share | ||
14 | + case history | ||
15 | + case tabs | ||
16 | +} | ||
17 | + | ||
18 | +final class ToolbarView: UIView { | ||
19 | + var action: ((ToolbarElementType) -> Void)? | ||
20 | + | ||
21 | + let toolbar: UIToolbar = { | ||
22 | + let obj = UIToolbar() | ||
23 | + obj.sizeToFit() | ||
24 | + obj.barTintColor = ColorConstants.lightGray | ||
25 | + obj.clipsToBounds = true | ||
26 | + return obj | ||
27 | + }() | ||
28 | + | ||
29 | + let backBarButtonItem: UIBarButtonItem = { | ||
30 | + let obj = UIBarButtonItem() | ||
31 | + obj.isEnabled = false | ||
32 | + obj.image = UIImage(systemName: "chevron.backward") | ||
33 | + return obj | ||
34 | + }() | ||
35 | + | ||
36 | + let forwardBarButtonItem: UIBarButtonItem = { | ||
37 | + let obj = UIBarButtonItem() | ||
38 | + obj.isEnabled = false | ||
39 | + obj.image = UIImage(systemName: "chevron.forward") | ||
40 | + return obj | ||
41 | + }() | ||
42 | + | ||
43 | + let shareBarButtonItem: UIBarButtonItem = { | ||
44 | + let obj = UIBarButtonItem() | ||
45 | + obj.isEnabled = false | ||
46 | + obj.image = UIImage(systemName: "square.and.arrow.up") | ||
47 | + return obj | ||
48 | + }() | ||
49 | + | ||
50 | + let historyBarButtonItem: UIBarButtonItem = { | ||
51 | + let obj = UIBarButtonItem() | ||
52 | + obj.image = UIImage(systemName: "book") | ||
53 | + return obj | ||
54 | + }() | ||
55 | + | ||
56 | + let tabsBarButtonItem: UIBarButtonItem = { | ||
57 | + let obj = UIBarButtonItem() | ||
58 | + obj.image = UIImage(systemName: "square.on.square") | ||
59 | + return obj | ||
60 | + }() | ||
61 | + | ||
62 | + override init(frame: CGRect) { | ||
63 | + super.init(frame: frame) | ||
64 | + setup() | ||
65 | + } | ||
66 | + | ||
67 | + required init?(coder: NSCoder) { | ||
68 | + fatalError("init(coder:) has not been implemented") | ||
69 | + } | ||
70 | + | ||
71 | + private func setup() { | ||
72 | + addSubview(toolbar) | ||
73 | + let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) | ||
74 | + toolbar.items = [ | ||
75 | + backBarButtonItem, | ||
76 | + flexibleSpace, | ||
77 | + forwardBarButtonItem, | ||
78 | + flexibleSpace, | ||
79 | + shareBarButtonItem, | ||
80 | + flexibleSpace, | ||
81 | + historyBarButtonItem, | ||
82 | + flexibleSpace, | ||
83 | + tabsBarButtonItem, | ||
84 | + ] | ||
85 | + initActions() | ||
86 | + setupConstraints() | ||
87 | + } | ||
88 | + | ||
89 | + private func setupConstraints() { | ||
90 | + toolbar.snp.makeConstraints { make in | ||
91 | + make.top.equalToSuperview() | ||
92 | + make.leading.trailing.equalToSuperview() | ||
93 | + make.bottom.equalTo(safeAreaLayoutGuide.snp.bottom) | ||
94 | + } | ||
95 | + } | ||
96 | +} | ||
97 | + | ||
98 | +// MARK: - Helpers | ||
99 | +extension ToolbarView { | ||
100 | + private func initActions() { | ||
101 | + backBarButtonItem.action = #selector(didItemTapped(_:)) | ||
102 | + backBarButtonItem.target = self | ||
103 | + forwardBarButtonItem.action = #selector(didItemTapped(_:)) | ||
104 | + forwardBarButtonItem.target = self | ||
105 | + shareBarButtonItem.action = #selector(didItemTapped(_:)) | ||
106 | + shareBarButtonItem.target = self | ||
107 | + historyBarButtonItem.action = #selector(didItemTapped(_:)) | ||
108 | + historyBarButtonItem.target = self | ||
109 | + tabsBarButtonItem.action = #selector(didItemTapped(_:)) | ||
110 | + tabsBarButtonItem.target = self | ||
111 | + } | ||
112 | +} | ||
113 | + | ||
114 | +// MARK: - Actions | ||
115 | +extension ToolbarView { | ||
116 | + @objc | ||
117 | + private func didItemTapped(_ sender: UIBarButtonItem) { | ||
118 | + guard let action else { | ||
119 | + return | ||
120 | + } | ||
121 | + switch sender { | ||
122 | + case backBarButtonItem: | ||
123 | + action(.back) | ||
124 | + case forwardBarButtonItem: | ||
125 | + action(.forward) | ||
126 | + case shareBarButtonItem: | ||
127 | + action(.share) | ||
128 | + case historyBarButtonItem: | ||
129 | + action(.history) | ||
130 | + case tabsBarButtonItem: | ||
131 | + action(.tabs) | ||
132 | + default: | ||
133 | + break | ||
134 | + } | ||
135 | + } | ||
136 | +} |
1 | +// | ||
2 | +// PayloadViewController.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 25.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class PayloadViewController: UIViewController { | ||
11 | + private let mainView = PayloadView() | ||
12 | + private let data = StringConstants.payloadViewControllerData | ||
13 | + | ||
14 | + override func viewDidLoad() { | ||
15 | + super.viewDidLoad() | ||
16 | + view.backgroundColor = ColorConstants.lightGray | ||
17 | + initViewController() | ||
18 | + } | ||
19 | + | ||
20 | + private func initViewController() { | ||
21 | + setupTableView() | ||
22 | + addTargets() | ||
23 | + navigationController?.isNavigationBarHidden = true | ||
24 | + } | ||
25 | + | ||
26 | + override func loadView() { | ||
27 | + view = mainView | ||
28 | + } | ||
29 | + | ||
30 | + init(){ | ||
31 | + super.init(nibName: nil, bundle: nil) | ||
32 | + } | ||
33 | + required init?(coder: NSCoder) { | ||
34 | + fatalError("init(coder:) has not been implemented") | ||
35 | + } | ||
36 | + | ||
37 | + override func viewWillDisappear(_ animated: Bool) { | ||
38 | + super.viewWillDisappear(animated) | ||
39 | + NotificationCenter.default.removeObserver(self) | ||
40 | + } | ||
41 | +} | ||
42 | + | ||
43 | +extension PayloadViewController: UITableViewDelegate, UITableViewDataSource { | ||
44 | + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | ||
45 | + return data.count | ||
46 | + } | ||
47 | + | ||
48 | + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | ||
49 | + guard let cell = tableView.dequeueReusableCell(withIdentifier: "AdvantagesTableViewCell") as? | ||
50 | + AdvantagesTableViewCell else { | ||
51 | + return UITableViewCell() } | ||
52 | + cell.advantagesCellLabel.text = data[indexPath.row] | ||
53 | + cell.selectionStyle = .none | ||
54 | + return cell | ||
55 | + } | ||
56 | + | ||
57 | + private func setupTableView() { | ||
58 | + mainView.advantagesTableView.dataSource = self | ||
59 | + mainView.advantagesTableView.delegate = self | ||
60 | + mainView.advantagesTableView.register(AdvantagesTableViewCell.self, forCellReuseIdentifier: StringConstants.advantagesTableViewCell) | ||
61 | + } | ||
62 | +} | ||
63 | + | ||
64 | + | ||
65 | +//MARK: - Action | ||
66 | +extension PayloadViewController { | ||
67 | + @objc | ||
68 | + private func getStartedButtonTapped(_ sender: UIButton) { | ||
69 | + navigationController?.popViewController(animated: false) | ||
70 | + } | ||
71 | + | ||
72 | + @objc | ||
73 | + private func textPressed(_ sender: UITapGestureRecognizer) { | ||
74 | + // navigationController?.popViewController(animated: false) | ||
75 | + let termsViewController = TermsViewController() | ||
76 | + termsViewController.modalPresentationStyle = .fullScreen | ||
77 | + present(termsViewController, animated: true) | ||
78 | + } | ||
79 | + | ||
80 | + private func addTargets() { | ||
81 | + mainView.getStartedButton.addTarget(self, action: #selector(getStartedButtonTapped(_ :)), for: .touchUpInside) | ||
82 | + mainView.privacyLabelView.isUserInteractionEnabled = true | ||
83 | + mainView.privacyLabelView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(textPressed(_:)))) | ||
84 | + | ||
85 | + GestureSetup() | ||
86 | + } | ||
87 | + | ||
88 | + private func GestureSetup() { | ||
89 | + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textPressed(_ :))) | ||
90 | + mainView.infoLabel.addGestureRecognizer(tapGesture) | ||
91 | + } | ||
92 | +} | ||
93 | + | ||
94 | + |
1 | +// | ||
2 | +// AdvantagesTableViewCell.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 26.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class AdvantagesTableViewCell: UITableViewCell { | ||
11 | + let advantagesCellLabel: UILabel = { | ||
12 | + let obj = UILabel() | ||
13 | + obj.font = FontConstants.semiboldFont_14 | ||
14 | + return obj | ||
15 | + }() | ||
16 | + | ||
17 | + let advantagesCellImage: UIImageView = { | ||
18 | + let obj = UIImageView() | ||
19 | + let checkmarkImg = UIImage(systemName: "checkmark")?.withTintColor(.blue, renderingMode: .alwaysOriginal) | ||
20 | + obj.image = checkmarkImg | ||
21 | + obj.contentMode = .scaleToFill | ||
22 | + return obj | ||
23 | + }() | ||
24 | + | ||
25 | + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { | ||
26 | + super.init(style: style, reuseIdentifier: reuseIdentifier) | ||
27 | + setup() | ||
28 | + | ||
29 | + } | ||
30 | + | ||
31 | + required init?(coder: NSCoder) { | ||
32 | + fatalError("init(coder:) has not been implemented") | ||
33 | + } | ||
34 | + | ||
35 | + | ||
36 | + | ||
37 | + func setup() { | ||
38 | + contentView.addSubview(advantagesCellLabel) | ||
39 | + contentView.addSubview(advantagesCellImage) | ||
40 | + setupConstraints() | ||
41 | + backgroundColor = .clear | ||
42 | + } | ||
43 | + | ||
44 | + private func setupConstraints() { | ||
45 | + advantagesCellImage.snp.makeConstraints { make in | ||
46 | + make.top.equalToSuperview().inset(5.sizeH) | ||
47 | + make.bottom.equalToSuperview().inset(5.sizeH) | ||
48 | + make.leading.equalToSuperview() | ||
49 | + make.trailing.equalTo(advantagesCellLabel.snp.leading).offset(-8.sizeW) | ||
50 | + } | ||
51 | + advantagesCellLabel.snp.makeConstraints { make in | ||
52 | + make.top.equalToSuperview().inset(5.sizeH) | ||
53 | + make.bottom.equalToSuperview().inset(5.sizeH) | ||
54 | + make.leading.equalTo(advantagesCellImage.snp.trailing) | ||
55 | + make.trailing.equalToSuperview() | ||
56 | + } | ||
57 | + } | ||
58 | +} |
1 | +// | ||
2 | +// PayloadView.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 25.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class PayloadView: UIView { | ||
11 | + | ||
12 | + let logoImageView: UIImageView = { | ||
13 | + let obj = UIImageView() | ||
14 | + obj.image = UIImage(named: "Gotoweb") | ||
15 | + obj.contentMode = .scaleAspectFit | ||
16 | + return obj | ||
17 | + }() | ||
18 | + | ||
19 | + let phoneImageView: UIImageView = { | ||
20 | + let obj = UIImageView() | ||
21 | + obj.image = UIImage(named: "Phone") | ||
22 | + obj.contentMode = .scaleAspectFit | ||
23 | + return obj | ||
24 | + }() | ||
25 | + | ||
26 | + let zoomedTabImageView: UIImageView = { | ||
27 | + let obj = UIImageView() | ||
28 | + obj.image = UIImage(named: "ZoomedTab") | ||
29 | + obj.contentMode = .scaleAspectFit | ||
30 | + return obj | ||
31 | + }() | ||
32 | + | ||
33 | + let infoLabel: UILabel = { | ||
34 | + let obj = UILabel() | ||
35 | + obj.text = StringConstants.ourBrowser | ||
36 | + obj.font = FontConstants.regularFont_12 | ||
37 | + obj.numberOfLines = 0 | ||
38 | + return obj | ||
39 | + }() | ||
40 | + | ||
41 | + let advantagesTableView: UITableView = { | ||
42 | + let obj = UITableView() | ||
43 | + obj.backgroundColor = .clear | ||
44 | + obj.isScrollEnabled = false | ||
45 | + obj.separatorStyle = .none | ||
46 | + return obj | ||
47 | + }() | ||
48 | + | ||
49 | + let gradientLayer = CAGradientLayer() | ||
50 | + | ||
51 | + let getStartedButton: UIButton = { | ||
52 | + let obj = UIButton() | ||
53 | + obj.setTitle("Get started", for: .normal) | ||
54 | + obj.titleLabel?.font = FontConstants.semiboldFont_17 | ||
55 | + obj.setTitleColor(.white, for: .normal) | ||
56 | + obj.layer.cornerRadius = 10 | ||
57 | + obj.layer.masksToBounds = true | ||
58 | + return obj | ||
59 | + }() | ||
60 | + | ||
61 | + let privacyLabelView: UILabel = { | ||
62 | + let obj = UILabel() | ||
63 | + obj.textAlignment = .center | ||
64 | + | ||
65 | + let fullText = "By starting, you agree to our Terms and Condition and Privacy Policy." | ||
66 | + let attributedText = NSMutableAttributedString(string: fullText) | ||
67 | + attributedText.addAttribute(.foregroundColor, value: UIColor(red: 0.349, green: 0.565, blue: 1, alpha: 1).cgColor, range: NSRange(location: fullText.range(of: "Terms and Condition")?.lowerBound.utf16Offset(in: fullText) ?? 0, length: "Terms and Condition".count)) | ||
68 | + attributedText.addAttribute(.foregroundColor, value: UIColor(red: 0.349, green: 0.565, blue: 1, alpha: 1).cgColor, range: NSRange(location: fullText.range(of: "Privacy Policy")?.lowerBound.utf16Offset(in: fullText) ?? 0, length: "Privacy Policy".count)) | ||
69 | + obj.attributedText = attributedText | ||
70 | + obj.font = FontConstants.regularFont_12 | ||
71 | + obj.numberOfLines = 0 | ||
72 | + obj.isUserInteractionEnabled = true | ||
73 | + return obj | ||
74 | + }() | ||
75 | + | ||
76 | + override init (frame: CGRect) { | ||
77 | + super.init(frame: frame) | ||
78 | + setup() | ||
79 | + } | ||
80 | + | ||
81 | + required init?(coder: NSCoder) { | ||
82 | + fatalError("init(coder:) has not been implemented") | ||
83 | + } | ||
84 | + | ||
85 | + override func layoutSubviews() { | ||
86 | + super.layoutSubviews() | ||
87 | + gradientLayer.frame = getStartedButton.bounds | ||
88 | + } | ||
89 | + | ||
90 | + private func setup() { | ||
91 | + addSubview(logoImageView) | ||
92 | + addSubview(phoneImageView) | ||
93 | + addSubview(zoomedTabImageView) | ||
94 | + addSubview(infoLabel) | ||
95 | + addSubview(advantagesTableView) | ||
96 | + addSubview(getStartedButton) | ||
97 | + addSubview(privacyLabelView) | ||
98 | + setupConstraints() | ||
99 | + gradientSetup() | ||
100 | + } | ||
101 | + | ||
102 | + func setupConstraints() { | ||
103 | + logoImageView.snp.makeConstraints { make in | ||
104 | + make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(16.sizeH) | ||
105 | + make.leading.trailing.equalToSuperview().inset(143.sizeW) | ||
106 | + make.height.equalTo(26.sizeH) | ||
107 | + } | ||
108 | + | ||
109 | + phoneImageView.snp.makeConstraints { make in | ||
110 | + make.top.equalTo(logoImageView.snp.bottom).offset(16.sizeH) | ||
111 | + make.leading.trailing.equalToSuperview().inset(90.sizeW) | ||
112 | + make.height.equalTo(355.sizeH) | ||
113 | + } | ||
114 | + | ||
115 | + zoomedTabImageView.snp.makeConstraints { make in | ||
116 | + make.top.equalTo(phoneImageView.snp.top).offset(119.sizeH) | ||
117 | + make.leading.equalTo(phoneImageView.snp.leading).offset(91.sizeW) | ||
118 | + make.trailing.equalToSuperview().offset(-71) | ||
119 | + make.bottom.equalTo(phoneImageView.snp.bottom).offset(-104.sizeH) | ||
120 | + } | ||
121 | + | ||
122 | + infoLabel.snp.makeConstraints { make in | ||
123 | + make.top.equalTo(phoneImageView.snp.bottom).offset(16.sizeH) | ||
124 | + make.leading.trailing.equalToSuperview().inset(55.sizeW) | ||
125 | + } | ||
126 | + | ||
127 | + advantagesTableView.snp.makeConstraints { make in | ||
128 | + make.top.equalTo(infoLabel.snp.bottom).offset(16.sizeH) | ||
129 | + make.leading.equalToSuperview().offset(55.sizeW) | ||
130 | + make.trailing.equalToSuperview().offset(-92.sizeW) | ||
131 | + make.bottom.equalTo(getStartedButton.snp.top).offset(-38.sizeH) | ||
132 | + } | ||
133 | + | ||
134 | + getStartedButton.snp.makeConstraints { make in | ||
135 | + make.top.equalTo(advantagesTableView.snp.bottom).offset(24.sizeH) | ||
136 | + make.leading.trailing.equalToSuperview().inset(32.sizeW) | ||
137 | + make.height.equalTo(40.sizeH) | ||
138 | + } | ||
139 | + | ||
140 | + privacyLabelView.snp.makeConstraints { make in | ||
141 | + make.top.equalTo(getStartedButton.snp.bottom).offset(16.sizeH) | ||
142 | + make.leading.trailing.equalToSuperview().inset(32.sizeW) | ||
143 | + make.bottom.equalTo(safeAreaLayoutGuide).offset(-16.sizeH) | ||
144 | + } | ||
145 | + | ||
146 | + } | ||
147 | +} | ||
148 | + | ||
149 | +//MARK: Gradient | ||
150 | +extension PayloadView { | ||
151 | + private func gradientSetup() { | ||
152 | + getStartedButton.layer.insertSublayer(gradientLayer, at: 0) | ||
153 | + gradientLayer.colors = [UIColor(red: 0.349, green: 0.565, blue: 1, alpha: 1).cgColor, UIColor(red: 0.349, green: 0.565, blue: 1, alpha: 0.7).cgColor] | ||
154 | + gradientLayer.locations = [0.0, 1.0] | ||
155 | + } | ||
156 | +} |
1 | +// | ||
2 | +// RemoveAdvertViewController.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 27.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class RemoveAdvertViewController: UIViewController { | ||
11 | + | ||
12 | + private let mainView = RemoveAdvertView() | ||
13 | + let data = StringConstants.removeAdvertTableViewData | ||
14 | + | ||
15 | + override func viewDidLoad() { | ||
16 | + super.viewDidLoad() | ||
17 | + view.backgroundColor = ColorConstants.lightGray | ||
18 | + initViewController() | ||
19 | + } | ||
20 | + | ||
21 | + private func initViewController() { | ||
22 | + setupTableView() | ||
23 | + addTargets() | ||
24 | + } | ||
25 | + | ||
26 | + override func loadView() { | ||
27 | + view = mainView | ||
28 | + } | ||
29 | + | ||
30 | + init(){ | ||
31 | + super.init(nibName: nil, bundle: nil) | ||
32 | + } | ||
33 | + | ||
34 | + required init?(coder: NSCoder) { | ||
35 | + fatalError("init(coder:) has not been implemented") | ||
36 | + } | ||
37 | +} | ||
38 | + | ||
39 | + | ||
40 | +//MARK: - Table View | ||
41 | +extension RemoveAdvertViewController: UITableViewDelegate, UITableViewDataSource { | ||
42 | + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | ||
43 | + return data.count | ||
44 | + } | ||
45 | + | ||
46 | + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | ||
47 | + guard let cell = tableView.dequeueReusableCell(withIdentifier: "RemoveAdvertTableViewCell") as? | ||
48 | + RemoveAdvertTableViewCell else { | ||
49 | + return UITableViewCell() } | ||
50 | + | ||
51 | + cell.advantagesCellLabel.text = data[indexPath.row] | ||
52 | + cell.selectionStyle = .none | ||
53 | + | ||
54 | + return cell | ||
55 | + } | ||
56 | + | ||
57 | + private func setupTableView() { | ||
58 | + mainView.advantagesTableView.dataSource = self | ||
59 | + mainView.advantagesTableView.delegate = self | ||
60 | + mainView.advantagesTableView.register(RemoveAdvertTableViewCell.self, forCellReuseIdentifier: StringConstants.removeAdvertTableViewCell) | ||
61 | + } | ||
62 | +} | ||
63 | + | ||
64 | + | ||
65 | +//MARK: - Action | ||
66 | +extension RemoveAdvertViewController { | ||
67 | + private func addTargets() { | ||
68 | + mainView.shieldView.addTarget(self, action: #selector(shieldButtonTapped(_ :)), for: .touchUpInside) | ||
69 | + mainView.closeButton.addTarget(self, action: #selector(closeViewController(_ :)), for: .touchUpInside) | ||
70 | + } | ||
71 | + | ||
72 | + @objc | ||
73 | + private func shieldButtonTapped(_ sender: UIButton) { | ||
74 | + UIView.animate(withDuration: 0.2) { | ||
75 | + sender.transform = CGAffineTransform(scaleX: 1.1, y: 1.1) | ||
76 | + sender.alpha = 0.7 | ||
77 | + } | ||
78 | + let userDefaultsAdBlockerValue = CachingManager.shared.isAdBlocking | ||
79 | + if userDefaultsAdBlockerValue { | ||
80 | + mainView.shieldView.setImage(.shieldInactive, for: .normal) | ||
81 | + mainView.tapActionLabel.text = StringConstants.turnOff | ||
82 | + CachingManager.shared.isAdBlocking = false | ||
83 | + } else { | ||
84 | + mainView.shieldView.setImage(.shieldActive, for: .normal) | ||
85 | + mainView.tapActionLabel.text = StringConstants.turnOn | ||
86 | + CachingManager.shared.isAdBlocking = true | ||
87 | + } | ||
88 | + UIView.animate(withDuration: 0.2, delay: 0.2) { | ||
89 | + sender.transform = .identity | ||
90 | + sender.alpha = 1.0 | ||
91 | + } | ||
92 | + } | ||
93 | + | ||
94 | + @objc | ||
95 | + private func closeViewController(_ sender: UIButton) { | ||
96 | + dismiss(animated: true, completion: nil) | ||
97 | + } | ||
98 | +} |
1 | +// | ||
2 | +// RemoveAdvertTableViewCell.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 01.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | + | ||
11 | +final class RemoveAdvertTableViewCell: UITableViewCell { | ||
12 | + let advantagesCellLabel: UILabel = { | ||
13 | + let obj = UILabel() | ||
14 | + obj.font = FontConstants.regularFont_14 | ||
15 | + return obj | ||
16 | + }() | ||
17 | + | ||
18 | + let advantagesCellImage: UIImageView = { | ||
19 | + let obj = UIImageView() | ||
20 | + let checkmarkImg = UIImage(systemName: "checkmark")?.withTintColor(.blue, renderingMode: .alwaysOriginal) | ||
21 | + obj.image = checkmarkImg | ||
22 | + obj.contentMode = .scaleToFill | ||
23 | + return obj | ||
24 | + }() | ||
25 | + | ||
26 | + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { | ||
27 | + super.init(style: style, reuseIdentifier: reuseIdentifier) | ||
28 | + setup() | ||
29 | + | ||
30 | + } | ||
31 | + | ||
32 | + required init?(coder: NSCoder) { | ||
33 | + fatalError("init(coder:) has not been implemented") | ||
34 | + } | ||
35 | + | ||
36 | + func setup() { | ||
37 | + contentView.addSubview(advantagesCellLabel) | ||
38 | + contentView.addSubview(advantagesCellImage) | ||
39 | + backgroundColor = .clear | ||
40 | + setupConstraints() | ||
41 | + } | ||
42 | + | ||
43 | + private func setupConstraints() { | ||
44 | + advantagesCellImage.snp.makeConstraints { make in | ||
45 | + make.top.equalToSuperview().inset(5.sizeH) | ||
46 | + make.bottom.equalToSuperview().inset(5.sizeH) | ||
47 | + make.leading.equalToSuperview() | ||
48 | + make.trailing.equalTo(advantagesCellLabel.snp.leading).offset(-8.sizeW) | ||
49 | + } | ||
50 | + advantagesCellLabel.snp.makeConstraints { make in | ||
51 | + make.top.equalToSuperview().inset(5.sizeH) | ||
52 | + make.bottom.equalToSuperview().inset(5.sizeH) | ||
53 | + make.leading.equalTo(advantagesCellImage.snp.trailing) | ||
54 | + make.trailing.equalToSuperview() | ||
55 | + } | ||
56 | + } | ||
57 | +} |
1 | +// | ||
2 | +// RemoveAdvertView.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 29.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | +import SnapKit | ||
10 | + | ||
11 | +final class RemoveAdvertView: UIView { | ||
12 | + | ||
13 | + let closeButton: UIButton = { | ||
14 | + let obj = UIButton() | ||
15 | + obj.setImage(UIImage(systemName: "xmark"), for: .normal) | ||
16 | + obj.contentMode = .scaleToFill | ||
17 | + obj.tintColor = .gray | ||
18 | + return obj | ||
19 | + }() | ||
20 | + | ||
21 | + let shieldView: UIButton = { | ||
22 | + let obj = UIButton() | ||
23 | + let userDefaultsValue = CachingManager.shared.isAdBlocking | ||
24 | + | ||
25 | + if userDefaultsValue { | ||
26 | + obj.setImage(UIImage(named: "ShieldActive"), for: .normal) | ||
27 | + print(userDefaultsValue) | ||
28 | + } else { | ||
29 | + obj.setImage(UIImage(named: "ShieldInactive"), for: .normal) | ||
30 | + print(userDefaultsValue) | ||
31 | + } | ||
32 | + | ||
33 | + obj.contentMode = .scaleAspectFit | ||
34 | + return obj | ||
35 | + }() | ||
36 | + | ||
37 | + let gradientLayer = CAGradientLayer() | ||
38 | + | ||
39 | + let abvertBlockerModeLabel: UILabel = { | ||
40 | + let obj = UILabel() | ||
41 | + obj.text = StringConstants.advertMode | ||
42 | + obj.font = FontConstants.semiboldFont_18 | ||
43 | + return obj | ||
44 | + }() | ||
45 | + | ||
46 | + let tapActionLabel: UILabel = { | ||
47 | + let obj = UILabel() | ||
48 | + obj.text = StringConstants.turnOff | ||
49 | + obj.font = FontConstants.semiboldFont_14 | ||
50 | + return obj | ||
51 | + }() | ||
52 | + | ||
53 | + let advantagesTableView: UITableView = { | ||
54 | + let obj = UITableView() | ||
55 | + obj.backgroundColor = .clear | ||
56 | + obj.isScrollEnabled = false | ||
57 | + obj.separatorStyle = .none | ||
58 | + return obj | ||
59 | + }() | ||
60 | + | ||
61 | + let claimOfferLabel: UILabel = { | ||
62 | + let obj = UILabel() | ||
63 | + obj.text = StringConstants.claimOffer | ||
64 | + obj.font = FontConstants.regularFont_12 | ||
65 | + return obj | ||
66 | + }() | ||
67 | + | ||
68 | + let priceLabel: UILabel = { | ||
69 | + let obj = UILabel() | ||
70 | + obj.text = StringConstants.price | ||
71 | + obj.font = FontConstants.regularFont_18 | ||
72 | + return obj | ||
73 | + }() | ||
74 | + | ||
75 | + let subscribeButton: UIButton = { | ||
76 | + let obj = UIButton() | ||
77 | + obj.backgroundColor = .systemIndigo | ||
78 | + obj.setTitle(StringConstants.subscribe, for: .normal) | ||
79 | + obj.titleLabel?.font = FontConstants.semiboldFont_17 | ||
80 | + obj.setTitleColor(.white, for: .normal) | ||
81 | + obj.layer.cornerRadius = 10 | ||
82 | + obj.layer.masksToBounds = true | ||
83 | + return obj | ||
84 | + }() | ||
85 | + | ||
86 | + let freeTrialLabel: UILabel = { | ||
87 | + let obj = UILabel() | ||
88 | + obj.textAlignment = .center | ||
89 | + obj.font = FontConstants.semiboldFont_12 | ||
90 | + obj.text = StringConstants.freeTrial | ||
91 | + return obj | ||
92 | + }() | ||
93 | + | ||
94 | + let discountLabel: UILabel = { | ||
95 | + let obj = UILabel() | ||
96 | + obj.textAlignment = .center | ||
97 | + obj.text = StringConstants.yourDiscount | ||
98 | + obj.font = FontConstants.regularFont_12 | ||
99 | + return obj | ||
100 | + }() | ||
101 | + | ||
102 | + override init (frame: CGRect) { | ||
103 | + super.init(frame: frame) | ||
104 | + setup() | ||
105 | + } | ||
106 | + | ||
107 | + override func layoutSubviews() { | ||
108 | + super.layoutSubviews() | ||
109 | + gradientLayer.frame = subscribeButton.bounds | ||
110 | + } | ||
111 | + | ||
112 | + required init?(coder: NSCoder) { | ||
113 | + fatalError("init(coder:) has not been implemented") | ||
114 | + } | ||
115 | + | ||
116 | + private func setup() { | ||
117 | + addSubview(closeButton) | ||
118 | + addSubview(shieldView) | ||
119 | + addSubview(abvertBlockerModeLabel) | ||
120 | + addSubview(tapActionLabel) | ||
121 | + addSubview(advantagesTableView) | ||
122 | + addSubview(claimOfferLabel) | ||
123 | + addSubview(priceLabel) | ||
124 | + addSubview(subscribeButton) | ||
125 | + addSubview(freeTrialLabel) | ||
126 | + addSubview(discountLabel) | ||
127 | + setupConstraints() | ||
128 | + gradientSetup() | ||
129 | + } | ||
130 | + | ||
131 | + func setupConstraints() { | ||
132 | + closeButton.snp.makeConstraints { make in | ||
133 | + make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(16.sizeH) | ||
134 | + make.height.equalTo(24.sizeH) | ||
135 | + make.trailing.equalToSuperview().inset(16.sizeW) | ||
136 | + } | ||
137 | + | ||
138 | + shieldView.snp.makeConstraints { make in | ||
139 | + make.top.equalTo(closeButton.snp.top).offset(8.sizeH) | ||
140 | + make.height.equalTo(280.sizeH) | ||
141 | + make.leading.trailing.equalToSuperview().inset(47.sizeW) | ||
142 | + } | ||
143 | + abvertBlockerModeLabel.snp.makeConstraints { make in | ||
144 | + make.top.equalTo(shieldView.snp.bottom).offset(16.sizeH) | ||
145 | + make.centerX.equalToSuperview() | ||
146 | + } | ||
147 | + tapActionLabel.snp.makeConstraints { make in | ||
148 | + make.top.equalTo(abvertBlockerModeLabel.snp.bottom).offset(8.sizeH) | ||
149 | + make.centerX.equalToSuperview() | ||
150 | + } | ||
151 | + advantagesTableView.snp.makeConstraints { make in | ||
152 | + make.top.equalTo(tapActionLabel.snp.bottom).offset(26.sizeH) | ||
153 | + make.bottom.equalTo(claimOfferLabel.snp.top).offset(-24.sizeH) | ||
154 | + make.leading.equalToSuperview().offset(80.sizeW) | ||
155 | + make.trailing.equalToSuperview().offset(-86.sizeW) | ||
156 | + } | ||
157 | + claimOfferLabel.snp.makeConstraints { make in | ||
158 | + make.bottom.equalTo(priceLabel.snp.top).offset(-4.sizeH) | ||
159 | + make.centerX.equalToSuperview() | ||
160 | + } | ||
161 | + priceLabel.snp.makeConstraints { make in | ||
162 | + make.bottom.equalTo(subscribeButton.snp.top).offset(-16.sizeH) | ||
163 | + make.centerX.equalToSuperview() | ||
164 | + } | ||
165 | + subscribeButton.snp.makeConstraints { make in | ||
166 | + make.bottom.equalTo(freeTrialLabel.snp.top).offset(-16.sizeH) | ||
167 | + make.leading.trailing.equalToSuperview().inset(32.sizeW) | ||
168 | + make.height.equalTo(40.sizeH) | ||
169 | + } | ||
170 | + freeTrialLabel.snp.makeConstraints { make in | ||
171 | + make.bottom.equalTo(discountLabel.snp.top).offset(-8.sizeH) | ||
172 | + make.centerX.equalToSuperview() | ||
173 | + } | ||
174 | + discountLabel.snp.makeConstraints { make in | ||
175 | + make.centerX.equalToSuperview() | ||
176 | + make.bottom.equalTo(safeAreaLayoutGuide.snp.bottom).offset(-24.sizeH) | ||
177 | + } | ||
178 | + } | ||
179 | +} | ||
180 | + | ||
181 | +//MARK: gradient | ||
182 | +extension RemoveAdvertView { | ||
183 | + private func gradientSetup() { | ||
184 | + subscribeButton.layer.insertSublayer(gradientLayer, at: 0) | ||
185 | + gradientLayer.colors = [UIColor(red: 0.349, green: 0.565, blue: 1, alpha: 1).cgColor, UIColor(red: 0.349, green: 0.565, blue: 1, alpha: 0.7).cgColor] | ||
186 | + gradientLayer.locations = [0.0, 1.0] | ||
187 | + } | ||
188 | +} |
1 | +// | ||
2 | +// SearchResultViewColroller.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 05.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | +import WebKit | ||
10 | + | ||
11 | +final class SearchResultViewController: UIViewController { | ||
12 | + let mainView = SearchResultView() | ||
13 | + var searchLink: String | ||
14 | + | ||
15 | + init(searchLink: String) { | ||
16 | + self.searchLink = searchLink | ||
17 | + super.init(nibName: nil, bundle: nil) | ||
18 | + } | ||
19 | + | ||
20 | + required init?(coder: NSCoder) { | ||
21 | + fatalError("init(coder:) has not been implemented") | ||
22 | + } | ||
23 | + | ||
24 | + override func loadView() { | ||
25 | + view = mainView | ||
26 | + } | ||
27 | + | ||
28 | + override func viewDidLoad() { | ||
29 | + super.viewDidLoad() | ||
30 | + initViewController() | ||
31 | + } | ||
32 | + | ||
33 | + private func initViewController() { | ||
34 | + view.backgroundColor = ColorConstants.lightGray | ||
35 | + urlChecker(searchLink) | ||
36 | + setupAdBlocker() | ||
37 | + } | ||
38 | + | ||
39 | + override func viewIsAppearing(_ animated: Bool) { | ||
40 | + navigationController?.isNavigationBarHidden = true | ||
41 | + } | ||
42 | +} | ||
43 | + | ||
44 | +extension SearchResultViewController { | ||
45 | + func urlChecker(_ url: String) { | ||
46 | + let request = URLRequest(url: URL(string: url) ?? URL(fileURLWithPath: "")) | ||
47 | + mainView.searchResultView.load(request) | ||
48 | + } | ||
49 | +} | ||
50 | + | ||
51 | + | ||
52 | +//MARK: AdBlocker | ||
53 | +extension SearchResultViewController { | ||
54 | + func setupAdBlocker() { | ||
55 | + let userDefaultsValue = CachingManager.shared.isAdBlocking | ||
56 | + if userDefaultsValue { | ||
57 | + WKContentRuleListStore.default().getAvailableContentRuleListIdentifiers { res in | ||
58 | +// if res?.isEmpty ?? true { | ||
59 | + if let url = Bundle.main.url(forResource: "blockerList", withExtension: "json"), | ||
60 | + let str = try? String(contentsOf: url) { | ||
61 | + WKContentRuleListStore.default().compileContentRuleList(forIdentifier: "blockerList", encodedContentRuleList: str) { list, error in | ||
62 | + if let list { | ||
63 | + self.mainView.searchResultView.configuration.userContentController.add(list) | ||
64 | + } | ||
65 | + } | ||
66 | + } | ||
67 | +// } | ||
68 | + } | ||
69 | + } else { | ||
70 | + WKContentRuleListStore.default().getAvailableContentRuleListIdentifiers { res in | ||
71 | + if res != nil { | ||
72 | + WKContentRuleListStore.default().removeContentRuleList(forIdentifier: "blockerList") { error in | ||
73 | + if let error { | ||
74 | + print(error) | ||
75 | + } | ||
76 | + } | ||
77 | + } | ||
78 | + } | ||
79 | + } | ||
80 | + } | ||
81 | +} |
1 | +// | ||
2 | +// SearchResultView.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 05.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | +import WebKit | ||
10 | + | ||
11 | +final class SearchResultView: UIView { | ||
12 | + let searchResultView: WKWebView = { | ||
13 | +// let userDefaultsValue = CachingManager.shared.isAdBlocking | ||
14 | +// if userDefaultsValue { | ||
15 | +// let configuration = WKWebViewConfiguration() | ||
16 | +// let contentController = WKUserContentController() | ||
17 | +// let url = Bundle.main.url(forResource: "blockerList", withExtension: "json")?.absoluteString ?? "" | ||
18 | +// contentController.addUserScript(WKUserScript(source: url, injectionTime: .atDocumentStart, forMainFrameOnly: false)) | ||
19 | +// configuration.userContentController = contentController | ||
20 | +// let obj = WKWebView(frame: .zero, configuration: configuration) | ||
21 | +// obj.allowsBackForwardNavigationGestures = true | ||
22 | +// obj.allowsLinkPreview = true | ||
23 | +// return obj | ||
24 | +// } else { | ||
25 | + let obj = WKWebView() | ||
26 | + obj.allowsBackForwardNavigationGestures = true | ||
27 | + obj.allowsLinkPreview = true | ||
28 | + return obj | ||
29 | + // } | ||
30 | + }() | ||
31 | + | ||
32 | + override init(frame: CGRect) { | ||
33 | + super.init(frame: frame) | ||
34 | + setup() | ||
35 | + } | ||
36 | + | ||
37 | + | ||
38 | + required init?(coder: NSCoder) { | ||
39 | + fatalError("init(coder:) has not been implemented") | ||
40 | + } | ||
41 | + | ||
42 | + private func setup() { | ||
43 | + addSubview(searchResultView) | ||
44 | + setupConstaraints() | ||
45 | + backgroundColor = .white | ||
46 | + } | ||
47 | + | ||
48 | + private func setupConstaraints() { | ||
49 | + searchResultView.snp.makeConstraints { make in | ||
50 | + make.top.equalTo(safeAreaLayoutGuide.snp.top) | ||
51 | + make.leading.trailing.equalToSuperview() | ||
52 | + make.bottom.equalToSuperview() | ||
53 | + } | ||
54 | + } | ||
55 | +} | ||
56 | + |
1 | +// | ||
2 | +// SearchingViewController.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 29.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class SearchingViewController: UIViewController { | ||
11 | + let mainView = SearchingView() | ||
12 | + var dataForReq: [String] { | ||
13 | + didSet { | ||
14 | + mainView.searchTableView.reloadData() | ||
15 | + } | ||
16 | + } | ||
17 | + var searchCell: ((UITableViewCell) -> Void)? | ||
18 | + | ||
19 | + init(dataForReq: [String]) { | ||
20 | + self.dataForReq = dataForReq | ||
21 | + super.init(nibName: nil, bundle: nil) | ||
22 | + } | ||
23 | + | ||
24 | + required init?(coder: NSCoder) { | ||
25 | + fatalError("init(coder:) has not been implemented") | ||
26 | + } | ||
27 | + | ||
28 | + override func loadView() { | ||
29 | + view = mainView | ||
30 | + } | ||
31 | + | ||
32 | + override func viewDidLoad() { | ||
33 | + super.viewDidLoad() | ||
34 | + view.backgroundColor = ColorConstants.lightGray | ||
35 | + initViewController() | ||
36 | + } | ||
37 | + | ||
38 | + private func initViewController() { | ||
39 | + setupTableView() | ||
40 | + } | ||
41 | +} | ||
42 | + | ||
43 | +extension SearchingViewController: UITableViewDelegate, UITableViewDataSource { | ||
44 | + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | ||
45 | + return dataForReq.count | ||
46 | + } | ||
47 | + | ||
48 | + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | ||
49 | + guard let cell = tableView.dequeueReusableCell(withIdentifier: "SearchingTableViewCell", for: indexPath) as? SearchingTableViewCell else { | ||
50 | + return SearchingTableViewCell() | ||
51 | + } | ||
52 | + let searchCellData = dataForReq[indexPath.row] | ||
53 | + cell.model = searchCellData | ||
54 | + return cell | ||
55 | + } | ||
56 | + | ||
57 | + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { | ||
58 | + guard let tappedCell = tableView.cellForRow(at: indexPath) else { return } | ||
59 | + searchCell?(tappedCell) | ||
60 | + } | ||
61 | + | ||
62 | + func setupTableView() { | ||
63 | + mainView.searchTableView.dataSource = self | ||
64 | + mainView.searchTableView.delegate = self | ||
65 | + mainView.searchTableView.register(SearchingTableViewCell.self, forCellReuseIdentifier: StringConstants.searchTableViewCell) | ||
66 | + } | ||
67 | +} | ||
68 | + |
1 | +// | ||
2 | +// SearchingTableViewCell.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 29.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +class SearchingTableViewCell: UITableViewCell { | ||
11 | + var model: String? { | ||
12 | + didSet { | ||
13 | + handleUI() | ||
14 | + } | ||
15 | + } | ||
16 | + | ||
17 | + var searchImage: UIImageView = { | ||
18 | + let obj = UIImageView() | ||
19 | + obj.image = UIImage(systemName: "magnifyingglass") | ||
20 | + obj.tintColor = .gray | ||
21 | + return obj | ||
22 | + }() | ||
23 | + | ||
24 | + var searchLabel: UILabel = { | ||
25 | + let obj = UILabel() | ||
26 | + obj.font = FontConstants.regularFont_14 | ||
27 | + return obj | ||
28 | + }() | ||
29 | + | ||
30 | + var siteLogoImage: UIImageView = { | ||
31 | + let obj = UIImageView() | ||
32 | + return obj | ||
33 | + }() | ||
34 | + | ||
35 | + var linkImage: UIImageView = { | ||
36 | + let obj = UIImageView() | ||
37 | + obj.image = UIImage(systemName: "arrow.up.left") | ||
38 | + obj.tintColor = .gray | ||
39 | + return obj | ||
40 | + }() | ||
41 | + | ||
42 | + | ||
43 | + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { | ||
44 | + super.init(style: style, reuseIdentifier: reuseIdentifier) | ||
45 | + setup() | ||
46 | + } | ||
47 | + | ||
48 | + required init?(coder: NSCoder) { | ||
49 | + fatalError("init(coder:) has not been implemented") | ||
50 | + } | ||
51 | + | ||
52 | + func setup() { | ||
53 | + contentView.addSubview(searchImage) | ||
54 | + contentView.addSubview(searchLabel) | ||
55 | + contentView.addSubview(siteLogoImage) | ||
56 | + contentView.addSubview(linkImage) | ||
57 | + setupConstraints() | ||
58 | + } | ||
59 | + | ||
60 | + private func setupConstraints() { | ||
61 | + searchImage.snp.makeConstraints { make in | ||
62 | + make.top.equalToSuperview().offset(8.sizeH) | ||
63 | + make.leading.equalToSuperview().offset(8.sizeW) | ||
64 | + make.bottom.equalToSuperview().offset(-8.sizeH) | ||
65 | + } | ||
66 | + searchLabel.snp.makeConstraints { make in | ||
67 | + make.top.equalToSuperview().inset(8.sizeH) | ||
68 | + make.leading.equalTo(searchImage.snp.trailing).offset(8.sizeW) | ||
69 | + } | ||
70 | + siteLogoImage.snp.makeConstraints { make in | ||
71 | + make.top.equalToSuperview().inset(8.sizeH) | ||
72 | + make.height.width.equalTo(18.sizeH) | ||
73 | + } | ||
74 | + linkImage.snp.makeConstraints { make in | ||
75 | + make.top.equalToSuperview().inset(8.sizeH) | ||
76 | + make.leading.equalTo(siteLogoImage.snp.trailing).offset(8.sizeW) | ||
77 | + make.trailing.equalToSuperview().inset(8.sizeW) | ||
78 | + } | ||
79 | + } | ||
80 | +} | ||
81 | + | ||
82 | +extension SearchingTableViewCell { | ||
83 | + private func handleUI() { | ||
84 | + searchLabel.text = model | ||
85 | +// if model?.siteLogo != UIImage() { | ||
86 | +// siteLogoImage.image = model?.siteLogo | ||
87 | +// } else { | ||
88 | +// siteLogoImage.image = UIImage() | ||
89 | +// } | ||
90 | + } | ||
91 | +} |
1 | +// | ||
2 | +// SearchingView.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 29.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class SearchingView: UIView, UITextFieldDelegate { | ||
11 | + let searchingButton: UIButton = { | ||
12 | + let obj = UIButton() | ||
13 | + obj.setImage(UIImage(systemName: "arrow.left"), for: .normal) | ||
14 | + return obj | ||
15 | + }() | ||
16 | + | ||
17 | + let searchingLabel: UILabel = { | ||
18 | + let obj = UILabel() | ||
19 | + obj.text = "Searching" | ||
20 | + obj.font = FontConstants.semiboldFont_18 | ||
21 | + return obj | ||
22 | + }() | ||
23 | + | ||
24 | + let searchTableView: UITableView = { | ||
25 | + let obj = UITableView() | ||
26 | + obj.backgroundColor = .clear | ||
27 | + obj.separatorStyle = .none | ||
28 | + return obj | ||
29 | + }() | ||
30 | + | ||
31 | + override init(frame: CGRect) { | ||
32 | + super.init(frame: frame) | ||
33 | + setup() | ||
34 | + } | ||
35 | + | ||
36 | + required init?(coder: NSCoder) { | ||
37 | + fatalError("init(coder:) has not been implemented") | ||
38 | + } | ||
39 | + | ||
40 | + private func setup() { | ||
41 | + addSubview(searchingButton) | ||
42 | + addSubview(searchingLabel) | ||
43 | + addSubview(searchTableView) | ||
44 | + setupConstaraints() | ||
45 | + backgroundColor = .white | ||
46 | + } | ||
47 | + | ||
48 | + private func setupConstaraints() { | ||
49 | + searchingButton.snp.makeConstraints { make in | ||
50 | + make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(16.sizeH) | ||
51 | + make.leading.equalToSuperview().offset(16.sizeW) | ||
52 | + make.height.equalTo(24.sizeH) | ||
53 | + } | ||
54 | + | ||
55 | + searchingLabel.snp.makeConstraints { make in | ||
56 | + make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(16.sizeH) | ||
57 | + make.leading.equalTo(searchingButton.snp.trailing).offset(109.sizeW) | ||
58 | + make.trailing.equalToSuperview().offset(-138.sizeW) | ||
59 | + } | ||
60 | + | ||
61 | + searchTableView.snp.makeConstraints { make in | ||
62 | + make.top.equalTo(searchingLabel.snp.bottom).offset(16.sizeH) | ||
63 | + make.leading.trailing.equalToSuperview().inset(16.sizeW) | ||
64 | + make.bottom.equalToSuperview()/*.offset(-16.sizeH)*/ | ||
65 | + } | ||
66 | + } | ||
67 | +} | ||
68 | + |
1 | +// | ||
2 | +// SettingController.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 26.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | +import MessageUI | ||
10 | + | ||
11 | +final class SettingViewController: UIViewController { | ||
12 | + private let mainView = SettingView() | ||
13 | + private let param = StringConstants.settingViewControllerTableViewData | ||
14 | + init() { | ||
15 | + super.init(nibName: nil, bundle: nil) | ||
16 | + } | ||
17 | + | ||
18 | + required init?(coder: NSCoder) { | ||
19 | + fatalError("init(coder:) has not been implemented") | ||
20 | + } | ||
21 | + | ||
22 | + override func loadView() { | ||
23 | + view = mainView | ||
24 | + } | ||
25 | + | ||
26 | + override func viewDidLoad() { | ||
27 | + super.viewDidLoad() | ||
28 | + view.backgroundColor = .white | ||
29 | + initViewController() | ||
30 | + } | ||
31 | + | ||
32 | + private func initViewController() { | ||
33 | + navigationController?.isNavigationBarHidden = false | ||
34 | + setupSettingTableView() | ||
35 | + addTargets() | ||
36 | + } | ||
37 | +} | ||
38 | + | ||
39 | + | ||
40 | +//MARK: SettingTableView | ||
41 | +extension SettingViewController: UITableViewDelegate, UITableViewDataSource { | ||
42 | + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | ||
43 | + return param.count | ||
44 | + } | ||
45 | + | ||
46 | + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | ||
47 | + guard let cell = tableView.dequeueReusableCell(withIdentifier: "SettingTableViewCell", for: indexPath) as? SettingTableViewCell | ||
48 | + else { return UITableViewCell() } | ||
49 | + let info = param[indexPath.item] | ||
50 | + cell.settingTableViewLabel.text = info | ||
51 | + cell.selectionStyle = .none | ||
52 | + return cell | ||
53 | + } | ||
54 | + | ||
55 | + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { | ||
56 | + return 60.0 | ||
57 | + } | ||
58 | + | ||
59 | + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { | ||
60 | + let index = indexPath.row | ||
61 | + cellPresshandler(index) | ||
62 | + } | ||
63 | +} | ||
64 | + | ||
65 | + | ||
66 | +//MARK: - Helpers | ||
67 | +extension SettingViewController { | ||
68 | + private func setupSettingTableView() { | ||
69 | + mainView.settingTableView.delegate = self | ||
70 | + mainView.settingTableView.dataSource = self | ||
71 | + mainView.settingTableView.register(SettingTableViewCell.self, forCellReuseIdentifier: StringConstants.settingTableViewCell) | ||
72 | + } | ||
73 | + | ||
74 | + private func setupMessage() { | ||
75 | + guard MFMailComposeViewController.canSendMail() else { | ||
76 | + return | ||
77 | + } | ||
78 | + let composer = MFMailComposeViewController() | ||
79 | + composer.mailComposeDelegate = self | ||
80 | + composer.setToRecipients(["test@gmail.com"]) | ||
81 | + composer.setSubject("Browser") | ||
82 | + present(composer, animated: true, completion: nil) | ||
83 | + } | ||
84 | + | ||
85 | + private func cellPresshandler(_ index: Int) { | ||
86 | + switch index { | ||
87 | + case 0: | ||
88 | + let privacyViewController = PrivacyViewController() | ||
89 | + presentViewController(privacyViewController) | ||
90 | + case 1: | ||
91 | + let termsViewController = TermsViewController() | ||
92 | + presentViewController(termsViewController) | ||
93 | + case 2: | ||
94 | + shareButtonPressed() | ||
95 | + case 3: | ||
96 | + break | ||
97 | + case 4: | ||
98 | + setupMessage() | ||
99 | + default: | ||
100 | + break | ||
101 | + } | ||
102 | + } | ||
103 | +} | ||
104 | + | ||
105 | +//MARK: - Action | ||
106 | +extension SettingViewController { | ||
107 | + func addTargets() { | ||
108 | + mainView.backButton.addTarget(self, action: #selector(closeViewController(_ :)), for: .touchUpInside) | ||
109 | + } | ||
110 | + | ||
111 | + @objc | ||
112 | + private func closeViewController(_ sender: UIButton) { | ||
113 | + dismiss(animated: true, completion: nil) | ||
114 | + } | ||
115 | + | ||
116 | + private func shareButtonPressed() { | ||
117 | + let items = [URL(string: URLConstants.shareAppLink)] | ||
118 | + let shareView = UIActivityViewController(activityItems: items as [Any], applicationActivities: nil) | ||
119 | + present(shareView, animated: true) | ||
120 | + } | ||
121 | + | ||
122 | + private func presentViewController(_ viewController: UIViewController) { | ||
123 | + viewController.modalPresentationStyle = .fullScreen | ||
124 | + present(viewController, animated: true) | ||
125 | + } | ||
126 | + | ||
127 | +} | ||
128 | + | ||
129 | +//MARK: Mail helper | ||
130 | +extension SettingViewController: MFMailComposeViewControllerDelegate { | ||
131 | + func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { | ||
132 | + if let _ = error { | ||
133 | + controller.dismiss(animated: true) | ||
134 | + } | ||
135 | + switch result { | ||
136 | + case .cancelled: | ||
137 | + print("cancelled") | ||
138 | + case .saved: | ||
139 | + print("saved") | ||
140 | + case .sent: | ||
141 | + print("send") | ||
142 | + case .failed: | ||
143 | + print("fail") | ||
144 | + @unknown default: | ||
145 | + print("error!") | ||
146 | + } | ||
147 | + | ||
148 | + controller.dismiss(animated: true) | ||
149 | + } | ||
150 | +} |
1 | +// | ||
2 | +// settingTableViewCell.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 26.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class SettingTableViewCell: UITableViewCell { | ||
11 | + let settingTableViewLabel: UILabel = { | ||
12 | + let obj = UILabel() | ||
13 | + obj.font = FontConstants.semiboldFont_17 | ||
14 | + obj.textColor = ColorConstants.lightGray | ||
15 | + obj.textAlignment = .center | ||
16 | + obj.backgroundColor = ColorConstants.buttonsBackgroundColor | ||
17 | + obj.layer.cornerRadius = 10 | ||
18 | + obj.layer.masksToBounds = true | ||
19 | + return obj | ||
20 | + }() | ||
21 | + | ||
22 | + let gradientLayer = CAGradientLayer() | ||
23 | + | ||
24 | + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { | ||
25 | + super.init(style: style, reuseIdentifier: reuseIdentifier) | ||
26 | + setup() | ||
27 | + } | ||
28 | + | ||
29 | + required init?(coder: NSCoder) { | ||
30 | + fatalError("init(coder:) has not been implemented") | ||
31 | + } | ||
32 | + | ||
33 | + private func setup() { | ||
34 | + contentView.addSubview(settingTableViewLabel) | ||
35 | + setupConstraints() | ||
36 | + } | ||
37 | + | ||
38 | + override func layoutSubviews() { | ||
39 | + super.layoutSubviews() | ||
40 | + gradientLayer.frame = self.bounds | ||
41 | + } | ||
42 | + | ||
43 | + private func setupConstraints() { | ||
44 | + settingTableViewLabel.snp.makeConstraints { make in | ||
45 | + make.top.equalToSuperview().inset(10) | ||
46 | + make.bottom.equalToSuperview().inset(10) | ||
47 | + make.leading.trailing.equalToSuperview().inset(10) | ||
48 | + } | ||
49 | + } | ||
50 | +} | ||
51 | + | ||
52 | +//MARK: Gradient | ||
53 | +extension SettingTableViewCell { | ||
54 | + private func gradientSetup() { | ||
55 | + self.layer.insertSublayer(gradientLayer, at: 0) | ||
56 | + gradientLayer.colors = [UIColor(red: 0.349, green: 0.565, blue: 1, alpha: 1).cgColor, UIColor(red: 0.349, green: 0.565, blue: 1, alpha: 0.7).cgColor] | ||
57 | + gradientLayer.locations = [0.0, 1.0] | ||
58 | + } | ||
59 | +} |
1 | +// | ||
2 | +// PrivacyViewController.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 08.11.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class PrivacyViewController: UIViewController { | ||
11 | + private let mainView = PrivacyView() | ||
12 | + | ||
13 | + override func viewDidLoad() { | ||
14 | + super.viewDidLoad() | ||
15 | + initViewController() | ||
16 | + } | ||
17 | + | ||
18 | + override func loadView() { | ||
19 | + view = mainView | ||
20 | + } | ||
21 | + | ||
22 | + init() { | ||
23 | + super.init(nibName: nil, bundle: nil) | ||
24 | + } | ||
25 | + | ||
26 | + required init?(coder: NSCoder) { | ||
27 | + fatalError("init(coder:) has not been implemented") | ||
28 | + } | ||
29 | + | ||
30 | + private func initViewController() { | ||
31 | + addTargets() | ||
32 | + mainView.backgroundColor = .white | ||
33 | + | ||
34 | + } | ||
35 | +} | ||
36 | + | ||
37 | + | ||
38 | +//MARK: Helper | ||
39 | +extension PrivacyViewController { | ||
40 | + func addTargets() { | ||
41 | + mainView.privacyButton.addTarget(self, action: #selector(privacyButtonPressed), for: .touchUpInside) | ||
42 | + } | ||
43 | +} | ||
44 | + | ||
45 | +//MARK: Targets | ||
46 | +extension PrivacyViewController { | ||
47 | + @objc | ||
48 | + func privacyButtonPressed() { | ||
49 | + self.dismiss(animated: true) | ||
50 | + } | ||
51 | +} |
1 | +// | ||
2 | +// PrivacyView.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 08.11.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | + | ||
11 | +final class PrivacyView: UIView { | ||
12 | + private let privacyHeaderLabel: UILabel = { | ||
13 | + let obj = UILabel() | ||
14 | + obj.text = StringConstants.privacyHeaderText | ||
15 | + obj.numberOfLines = 0 | ||
16 | + obj.textAlignment = .center | ||
17 | + obj.font = FontConstants.semiboldFont_18 | ||
18 | + return obj | ||
19 | + }() | ||
20 | + | ||
21 | + private let privacyMainInfoLabel: UILabel = { | ||
22 | + let obj = UILabel() | ||
23 | + obj.text = StringConstants.settingTerms | ||
24 | + obj.numberOfLines = 0 | ||
25 | + obj.font = FontConstants.regularFont_18 | ||
26 | + return obj | ||
27 | + }() | ||
28 | + | ||
29 | + let privacyButton: UIButton = { | ||
30 | + let obj = UIButton() | ||
31 | + obj.backgroundColor = .systemIndigo.withAlphaComponent(0.4) | ||
32 | + obj.layer.cornerRadius = 10 | ||
33 | + obj.setTitle(StringConstants.settingAgreeButtonText, for: .normal) | ||
34 | + obj.setTitleColor(.black, for: .normal) | ||
35 | + return obj | ||
36 | + }() | ||
37 | + | ||
38 | + private let privacyStackView: UIStackView = { | ||
39 | + let obj = UIStackView() | ||
40 | + obj.axis = .vertical | ||
41 | + return obj | ||
42 | + }() | ||
43 | + | ||
44 | + private let privacyScrollView: UIScrollView = { | ||
45 | + let obj = UIScrollView() | ||
46 | + obj.showsVerticalScrollIndicator = false | ||
47 | + return obj | ||
48 | + }() | ||
49 | + | ||
50 | + override init(frame: CGRect) { | ||
51 | + super.init(frame: frame) | ||
52 | + setup() | ||
53 | + } | ||
54 | + | ||
55 | + required init?(coder: NSCoder) { | ||
56 | + fatalError("init(coder:) has not been implemented") | ||
57 | + } | ||
58 | + | ||
59 | + private func setup() { | ||
60 | + addSubview(privacyHeaderLabel) | ||
61 | + addSubview(privacyButton) | ||
62 | + privacyStackView.addSubview(privacyMainInfoLabel) | ||
63 | + privacyScrollView.addSubview(privacyStackView) | ||
64 | + addSubview(privacyScrollView) | ||
65 | + setupConstraints() | ||
66 | + } | ||
67 | + | ||
68 | + private func setupConstraints() { | ||
69 | + privacyHeaderLabel.snp.makeConstraints { make in | ||
70 | + make.top.equalTo(safeAreaLayoutGuide.snp.top) | ||
71 | + make.leading.trailing.equalToSuperview().inset(20.sizeW) | ||
72 | + make.centerX.equalToSuperview() | ||
73 | + } | ||
74 | + privacyScrollView.snp.makeConstraints { make in | ||
75 | + make.top.equalTo(privacyHeaderLabel.snp.bottom).offset(30.sizeH) | ||
76 | + make.leading.trailing.equalToSuperview().inset(20) | ||
77 | + make.height.equalTo(600.sizeH) | ||
78 | + } | ||
79 | + privacyStackView.snp.makeConstraints { make in | ||
80 | + make.edges.equalToSuperview() | ||
81 | + make.centerX.equalToSuperview() | ||
82 | + } | ||
83 | + privacyMainInfoLabel.snp.makeConstraints { make in | ||
84 | + make.edges.equalToSuperview() | ||
85 | + make.width.equalTo(300.sizeW) | ||
86 | + } | ||
87 | + privacyButton.snp.makeConstraints { make in | ||
88 | + make.top.equalTo(privacyScrollView.snp.bottom) | ||
89 | + make.leading.trailing.equalToSuperview().inset(20.sizeW) | ||
90 | + make.height.equalTo(50.sizeH) | ||
91 | + } | ||
92 | + } | ||
93 | +} |
1 | +// | ||
2 | +// TermsViewController.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 08.11.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class TermsViewController: UIViewController { | ||
11 | + private let mainView = TermsView() | ||
12 | + | ||
13 | + override func viewDidLoad() { | ||
14 | + super.viewDidLoad() | ||
15 | + initViewController() | ||
16 | + } | ||
17 | + | ||
18 | + override func loadView() { | ||
19 | + view = mainView | ||
20 | + } | ||
21 | + | ||
22 | + init() { | ||
23 | + super.init(nibName: nil, bundle: nil) | ||
24 | + } | ||
25 | + | ||
26 | + required init?(coder: NSCoder) { | ||
27 | + fatalError("init(coder:) has not been implemented") | ||
28 | + } | ||
29 | + | ||
30 | + private func initViewController() { | ||
31 | + addTargets() | ||
32 | + mainView.backgroundColor = .white | ||
33 | + | ||
34 | + } | ||
35 | +} | ||
36 | + | ||
37 | + | ||
38 | +//MARK: Helper | ||
39 | +extension TermsViewController { | ||
40 | + func addTargets() { | ||
41 | + mainView.termsButton.addTarget(self, action: #selector(privacyButtonPressed), for: .touchUpInside) | ||
42 | + } | ||
43 | +} | ||
44 | + | ||
45 | +//MARK: Targets | ||
46 | +extension TermsViewController { | ||
47 | + @objc | ||
48 | + func privacyButtonPressed() { | ||
49 | + self.dismiss(animated: true) | ||
50 | + } | ||
51 | +} |
1 | +// | ||
2 | +// TermsView.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 08.11.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class TermsView: UIView { | ||
11 | + private let termsHeaderLabel: UILabel = { | ||
12 | + let obj = UILabel() | ||
13 | + obj.text = StringConstants.settingHeader | ||
14 | + obj.numberOfLines = 0 | ||
15 | + obj.textAlignment = .center | ||
16 | + obj.font = FontConstants.semiboldFont_18 | ||
17 | + return obj | ||
18 | + }() | ||
19 | + | ||
20 | + private let termsMainInfoLabel: UILabel = { | ||
21 | + let obj = UILabel() | ||
22 | + obj.text = StringConstants.settingTerms | ||
23 | + obj.numberOfLines = 0 | ||
24 | + obj.font = FontConstants.regularFont_18 | ||
25 | + return obj | ||
26 | + }() | ||
27 | + | ||
28 | + let termsButton: UIButton = { | ||
29 | + let obj = UIButton() | ||
30 | + obj.backgroundColor = .systemIndigo.withAlphaComponent(0.4) | ||
31 | + obj.layer.cornerRadius = 10 | ||
32 | + obj.setTitle(StringConstants.settingAgreeButtonText, for: .normal) | ||
33 | + obj.setTitleColor(.black, for: .normal) | ||
34 | + return obj | ||
35 | + }() | ||
36 | + | ||
37 | + private let termsStackView: UIStackView = { | ||
38 | + let obj = UIStackView() | ||
39 | + obj.axis = .vertical | ||
40 | + return obj | ||
41 | + }() | ||
42 | + | ||
43 | + private let termsScrollView: UIScrollView = { | ||
44 | + let obj = UIScrollView() | ||
45 | + obj.showsVerticalScrollIndicator = false | ||
46 | + return obj | ||
47 | + }() | ||
48 | + | ||
49 | + override init(frame: CGRect) { | ||
50 | + super.init(frame: frame) | ||
51 | + setup() | ||
52 | + } | ||
53 | + | ||
54 | + required init?(coder: NSCoder) { | ||
55 | + fatalError("init(coder:) has not been implemented") | ||
56 | + } | ||
57 | + | ||
58 | + private func setup() { | ||
59 | + addSubview(termsHeaderLabel) | ||
60 | + addSubview(termsButton) | ||
61 | + termsStackView.addSubview(termsMainInfoLabel) | ||
62 | + termsScrollView.addSubview(termsStackView) | ||
63 | + addSubview(termsScrollView) | ||
64 | + setupConstraints() | ||
65 | + } | ||
66 | + | ||
67 | + private func setupConstraints() { | ||
68 | + termsHeaderLabel.snp.makeConstraints { make in | ||
69 | + make.top.equalTo(safeAreaLayoutGuide.snp.top) | ||
70 | + make.leading.trailing.equalToSuperview().inset(20.sizeW) | ||
71 | + make.centerX.equalToSuperview() | ||
72 | + } | ||
73 | + termsScrollView.snp.makeConstraints { make in | ||
74 | + make.top.equalTo(termsHeaderLabel.snp.bottom).offset(30.sizeH) | ||
75 | + make.leading.trailing.equalToSuperview().inset(20) | ||
76 | + make.height.equalTo(600.sizeH) | ||
77 | + } | ||
78 | + termsStackView.snp.makeConstraints { make in | ||
79 | + make.edges.equalToSuperview() | ||
80 | + make.centerX.equalToSuperview() | ||
81 | + } | ||
82 | + termsMainInfoLabel.snp.makeConstraints { make in | ||
83 | + make.edges.equalToSuperview() | ||
84 | + make.width.equalTo(300.sizeW) | ||
85 | + } | ||
86 | + termsButton.snp.makeConstraints { make in | ||
87 | + make.top.equalTo(termsScrollView.snp.bottom) | ||
88 | + make.leading.trailing.equalToSuperview().inset(20.sizeW) | ||
89 | + make.height.equalTo(50.sizeH) | ||
90 | + } | ||
91 | + } | ||
92 | +} |
1 | +// | ||
2 | +// SettingView.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 26.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class SettingView: UIView { | ||
11 | + let backButton: UIButton = { | ||
12 | + let obj = UIButton() | ||
13 | + obj.setImage(UIImage(systemName: "arrow.left"), for: .normal) | ||
14 | + obj.contentMode = .scaleToFill | ||
15 | + return obj | ||
16 | + }() | ||
17 | + | ||
18 | + let settingTableView: UITableView = { | ||
19 | + let obj = UITableView() | ||
20 | + obj.isScrollEnabled = false | ||
21 | + obj.separatorStyle = .none | ||
22 | + return obj | ||
23 | + }() | ||
24 | + | ||
25 | + override init(frame: CGRect) { | ||
26 | + super.init(frame: frame) | ||
27 | + setup() | ||
28 | + } | ||
29 | + | ||
30 | + required init?(coder: NSCoder) { | ||
31 | + fatalError("init(coder:) has not been implemented") | ||
32 | + } | ||
33 | + | ||
34 | + private func setup() { | ||
35 | + addSubview(settingTableView) | ||
36 | + addSubview(backButton) | ||
37 | + setupConstraints() | ||
38 | + } | ||
39 | + | ||
40 | + private func setupConstraints() { | ||
41 | + settingTableView.snp.makeConstraints { make in | ||
42 | + make.top.equalToSuperview().offset(168.sizeH) | ||
43 | + make.bottom.equalToSuperview().offset(-100.sizeH) | ||
44 | + make.leading.trailing.equalToSuperview().inset(32.sizeW) | ||
45 | + } | ||
46 | + backButton.snp.makeConstraints { make in | ||
47 | + make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(16.sizeH) | ||
48 | + make.height.equalTo(24.sizeH) | ||
49 | + make.leading.equalToSuperview().inset(16.sizeW) | ||
50 | + } | ||
51 | + } | ||
52 | +} |
1 | +// | ||
2 | +// TabsViewController.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 28.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | +import RealmSwift | ||
10 | +import Realm | ||
11 | + | ||
12 | + | ||
13 | + | ||
14 | +final class TabsViewController: UIViewController { | ||
15 | + private var tabsData: [BrowserTabDataBase] = [] | ||
16 | + var cellIndexPathForTransition: IndexPath? | ||
17 | + let mainView = TabsView() | ||
18 | + | ||
19 | + override func viewDidLoad() { | ||
20 | + super.viewDidLoad() | ||
21 | + initViewController() | ||
22 | + } | ||
23 | + | ||
24 | + private func initViewController() { | ||
25 | + setupCollectionView() | ||
26 | + addTargets() | ||
27 | + view.backgroundColor = UIColor(red: 0.984, green: 0.984, blue: 0.984, alpha: 1) | ||
28 | + } | ||
29 | + | ||
30 | + override func viewWillAppear(_ animated: Bool) { | ||
31 | + super.viewWillAppear(animated) | ||
32 | + } | ||
33 | + | ||
34 | + override func viewIsAppearing(_ animated: Bool) { | ||
35 | + refreshData() | ||
36 | + setCountOfTabs() | ||
37 | + navigationController?.isNavigationBarHidden = true | ||
38 | + } | ||
39 | + | ||
40 | + override func loadView() { | ||
41 | + view = mainView | ||
42 | + } | ||
43 | + | ||
44 | + init() { | ||
45 | + super.init(nibName: nil, bundle: nil) | ||
46 | + } | ||
47 | + | ||
48 | + required init?(coder: NSCoder) { | ||
49 | + fatalError("init(coder:) has not been implemented") | ||
50 | + } | ||
51 | +} | ||
52 | + | ||
53 | + | ||
54 | +//MARK: CollectionView | ||
55 | +extension TabsViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { | ||
56 | + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { | ||
57 | + return tabsData.count | ||
58 | + } | ||
59 | + | ||
60 | + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { | ||
61 | + let tab = tabsData[indexPath.row] | ||
62 | + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OpenedTabsCollectionViewCell", for: indexPath) as? OpenedTabsCollectionViewCell else { | ||
63 | + return OpenedTabsCollectionViewCell() | ||
64 | + } | ||
65 | + cell.model = tab | ||
66 | + cell.delegate = self | ||
67 | + cell.closeTabButton.addTarget(self, action: #selector(closeTab(_:)), for: .touchUpInside) | ||
68 | + return cell | ||
69 | + } | ||
70 | + | ||
71 | + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { | ||
72 | + return CGSize(width: 163.sizeW, height: 184.sizeH) | ||
73 | + } | ||
74 | + | ||
75 | + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { | ||
76 | + cellIndexPathForTransition = indexPath | ||
77 | + pushSelectedTab(indexPath) | ||
78 | + } | ||
79 | + | ||
80 | + private func setupCollectionView() { | ||
81 | + mainView.openedTabsCollectionView.dataSource = self | ||
82 | + mainView.openedTabsCollectionView.delegate = self | ||
83 | + mainView.openedTabsCollectionView.register(OpenedTabsCollectionViewCell.self, forCellWithReuseIdentifier: "OpenedTabsCollectionViewCell") | ||
84 | + } | ||
85 | +} | ||
86 | + | ||
87 | + | ||
88 | +//MARK: - Action | ||
89 | +extension TabsViewController { | ||
90 | + func didTabButtonTapped(type: HistoryToolbarElementType) { | ||
91 | + switch type { | ||
92 | + case .add: | ||
93 | + addNewDefaultTab() | ||
94 | + refreshData() | ||
95 | + let lastIndex = tabsData.endIndex - 1 | ||
96 | + let lastIndexPath = IndexPath(row: lastIndex, section: 0) | ||
97 | + cellIndexPathForTransition = lastIndexPath | ||
98 | + pushSelectedTab(lastIndexPath) | ||
99 | + break | ||
100 | + case .done: | ||
101 | + refreshData() | ||
102 | + let lastIndex = tabsData.endIndex - 1 | ||
103 | + let lastIndexPath = IndexPath(row: lastIndex, section: 0) | ||
104 | + cellIndexPathForTransition = lastIndexPath | ||
105 | + pushSelectedTab(lastIndexPath) | ||
106 | + break | ||
107 | + | ||
108 | + default: | ||
109 | + break | ||
110 | + } | ||
111 | + } | ||
112 | + | ||
113 | + @objc | ||
114 | + private func closeTab(_ sender: TabCollectionViewCell) {} | ||
115 | +} | ||
116 | + | ||
117 | + | ||
118 | +//MARK: - Helpers | ||
119 | +extension TabsViewController { | ||
120 | + private func addTargets() { | ||
121 | + addToolbarTargets() | ||
122 | + } | ||
123 | + | ||
124 | + private func addToolbarTargets() { | ||
125 | + mainView.tabsToolbarView.action = didTabButtonTapped | ||
126 | + } | ||
127 | + | ||
128 | + func getCellIndex() -> IndexPath { | ||
129 | + return cellIndexPathForTransition ?? IndexPath(row: tabsData.count - 1, section: 0) | ||
130 | + } | ||
131 | + | ||
132 | + func addNewDefaultTab() { | ||
133 | + let defaultImage = UIImage(named: "defaultImage") | ||
134 | + TabManager.shared.saveTabWithComplition(tabTitle: "Start Page", snapshotImage: defaultImage, tabUrl: "homeUrl") { [weak self] result in | ||
135 | + switch result { | ||
136 | + default: | ||
137 | + self?.refreshData() | ||
138 | + } | ||
139 | + } | ||
140 | + } | ||
141 | + | ||
142 | + func pushSelectedTab(_ indexPath: IndexPath) { | ||
143 | + let index = indexPath.row | ||
144 | + if tabsData[index].TabUrl != "homeUrl" { | ||
145 | + let browserHomeViewController = BrowserHomeViewController(url: tabsData[index].TabUrl, currentTabId: index) | ||
146 | + browserHomeViewController.urlManagment(tabsData[index].TabUrl) | ||
147 | + navigationController?.pushViewController(browserHomeViewController, animated: true) | ||
148 | + } else { | ||
149 | + let browserHomeViewController = BrowserHomeViewController(url: tabsData[index].TabUrl, currentTabId: index) | ||
150 | + navigationController?.pushViewController(browserHomeViewController, animated: true) | ||
151 | + } | ||
152 | + } | ||
153 | + | ||
154 | + func refreshData() { | ||
155 | + tabsData = TabManager.shared.getAllTabs() | ||
156 | + mainView.openedTabsCollectionView.reloadData() | ||
157 | + } | ||
158 | +} | ||
159 | + | ||
160 | +//MARK: - Delete tab | ||
161 | +extension TabsViewController: OpenedTabsCellDelegate { | ||
162 | + func deleteTabDelegateFunc(cell: OpenedTabsCollectionViewCell) { | ||
163 | + guard let indexPath = mainView.openedTabsCollectionView.indexPath(for: cell) else { | ||
164 | + return | ||
165 | + } | ||
166 | + tabsData.remove(at: indexPath.row) | ||
167 | + mainView.openedTabsCollectionView.performBatchUpdates({ | ||
168 | + mainView.openedTabsCollectionView.deleteItems(at: [indexPath]) | ||
169 | + }, completion: { _ in | ||
170 | + if self.tabsData.isEmpty { | ||
171 | + self.addNewDefaultTab() | ||
172 | + let browserHomeViewController = BrowserHomeViewController(url: "homeUrl", currentTabId: self.tabsData.count - 1) | ||
173 | + self.navigationController?.pushViewController(browserHomeViewController, animated: true) | ||
174 | + self.cellIndexPathForTransition = IndexPath(row: self.tabsData.count - 1, section: 0) | ||
175 | + } | ||
176 | + self.setCountOfTabs() | ||
177 | + }) | ||
178 | + } | ||
179 | + func setCountOfTabs() { | ||
180 | + mainView.tabsToolbarView.showTabToolbarButtonItem.title = "\(tabsData.count) Tabs" | ||
181 | + } | ||
182 | +} |
1 | +import UIKit | ||
2 | +import SnapKit | ||
3 | + | ||
4 | +protocol OpenedTabsCellDelegate: AnyObject { | ||
5 | + func deleteTabDelegateFunc(cell: OpenedTabsCollectionViewCell) | ||
6 | +} | ||
7 | + | ||
8 | +final class OpenedTabsCollectionViewCell: UICollectionViewCell { | ||
9 | + var model: BrowserTabDataBase? { | ||
10 | + didSet { | ||
11 | + handleUI() | ||
12 | + } | ||
13 | + } | ||
14 | + | ||
15 | + weak var delegate: OpenedTabsCellDelegate? | ||
16 | + | ||
17 | + let siteNameLabel: UILabel = { | ||
18 | + let obj = UILabel() | ||
19 | + obj.font = FontConstants.regularFont_14 | ||
20 | + return obj | ||
21 | + }() | ||
22 | + | ||
23 | + let sitePreviewImageContainer: UIView = { | ||
24 | + let obj = UIView() | ||
25 | + obj.clipsToBounds = true | ||
26 | + obj.layer.cornerRadius = 10 | ||
27 | + return obj | ||
28 | + }() | ||
29 | + | ||
30 | + let sitePreviewImage: UIImageView = { | ||
31 | + let obj = UIImageView() | ||
32 | + obj.contentMode = .scaleAspectFill | ||
33 | + return obj | ||
34 | + }() | ||
35 | + | ||
36 | + let closeTabButton: UIButton = { | ||
37 | + let obj = UIButton() | ||
38 | + obj.setImage(UIImage(systemName: "xmark"), for: .normal) | ||
39 | + obj.contentMode = .scaleAspectFill | ||
40 | + obj.tintColor = .gray | ||
41 | + return obj | ||
42 | + }() | ||
43 | + | ||
44 | + var panGestureRecognizer: UIPanGestureRecognizer! | ||
45 | + | ||
46 | + override init(frame: CGRect) { | ||
47 | + super.init(frame: frame) | ||
48 | + setup() | ||
49 | + } | ||
50 | + | ||
51 | + required init?(coder: NSCoder) { | ||
52 | + fatalError("init(coder:) has not been implemented") | ||
53 | + } | ||
54 | + | ||
55 | + func setup() { | ||
56 | + self.backgroundColor = .white | ||
57 | + layer.cornerRadius = 10 | ||
58 | + shadowSetup() | ||
59 | + contentView.addSubview(siteNameLabel) | ||
60 | + contentView.addSubview(closeTabButton) | ||
61 | + contentView.addSubview(sitePreviewImageContainer) | ||
62 | + sitePreviewImageContainer.addSubview(sitePreviewImage) | ||
63 | + setupConstraints() | ||
64 | + addingTargets() | ||
65 | + setupPanGesture() | ||
66 | + } | ||
67 | + | ||
68 | + private func setupConstraints() { | ||
69 | + closeTabButton.snp.makeConstraints { make in | ||
70 | + make.top.equalToSuperview().offset(8) | ||
71 | + make.trailing.equalToSuperview().offset(-8) | ||
72 | + make.height.equalTo(14) | ||
73 | + make.width.equalTo(14) | ||
74 | + } | ||
75 | + | ||
76 | + siteNameLabel.snp.makeConstraints { make in | ||
77 | + make.top.equalToSuperview().offset(8) | ||
78 | + make.leading.equalToSuperview().offset(16) | ||
79 | + make.trailing.equalTo(closeTabButton.snp.leading).offset(-2) | ||
80 | + } | ||
81 | + | ||
82 | + sitePreviewImageContainer.snp.makeConstraints { make in | ||
83 | + make.top.equalTo(closeTabButton.snp.bottom).offset(8) | ||
84 | + make.bottom.equalToSuperview().offset(-8) | ||
85 | + make.leading.trailing.equalToSuperview().inset(8) | ||
86 | + } | ||
87 | + | ||
88 | + sitePreviewImage.snp.makeConstraints { make in | ||
89 | + make.edges.equalToSuperview() | ||
90 | + } | ||
91 | + } | ||
92 | + | ||
93 | + override func layoutSubviews() { | ||
94 | + super.layoutSubviews() | ||
95 | + shadowSetup() | ||
96 | + } | ||
97 | +} | ||
98 | + | ||
99 | + | ||
100 | + | ||
101 | +//MARK: Helpers | ||
102 | +extension OpenedTabsCollectionViewCell { | ||
103 | + func setupPanGesture() { | ||
104 | + panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:))) | ||
105 | + addGestureRecognizer(panGestureRecognizer) | ||
106 | + } | ||
107 | + | ||
108 | + func gestureRecognizer() { | ||
109 | + let swipeRightGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe)) | ||
110 | + swipeRightGestureRecognizer.direction = .right | ||
111 | + addGestureRecognizer(swipeRightGestureRecognizer) | ||
112 | + } | ||
113 | + | ||
114 | + private func shadowSetup() { | ||
115 | + layer.shadowColor = UIColor.black.withAlphaComponent(0.1).cgColor | ||
116 | + layer.shadowOpacity = 1 | ||
117 | + layer.shadowRadius = 30 | ||
118 | + layer.shadowOffset = CGSize(width: 0, height: 0) | ||
119 | + } | ||
120 | + | ||
121 | + func addingTargets() { | ||
122 | + closeTabButton.addTarget(self, action: #selector(deleteTab), for: .touchUpInside) | ||
123 | + } | ||
124 | + | ||
125 | +} | ||
126 | + | ||
127 | + | ||
128 | +//MARK: Handlers | ||
129 | +extension OpenedTabsCollectionViewCell { | ||
130 | + private func handleUI() { | ||
131 | + siteNameLabel.text = model?.tabTitle | ||
132 | + sitePreviewImage.image = model?.snapshotImageData?.toImage() | ||
133 | + } | ||
134 | +} | ||
135 | + | ||
136 | + | ||
137 | +//MARK: Actions | ||
138 | +extension OpenedTabsCollectionViewCell { | ||
139 | + @objc | ||
140 | + private func deleteTab() { | ||
141 | + delegate?.deleteTabDelegateFunc(cell: self) | ||
142 | + if let tabId = model?.tabId { | ||
143 | + TabManager.shared.deleteTab(tabId: tabId) | ||
144 | + } | ||
145 | + } | ||
146 | + | ||
147 | + @objc | ||
148 | + private func handleSwipe() { | ||
149 | + deleteTab() | ||
150 | + } | ||
151 | + | ||
152 | + @objc | ||
153 | + private func handlePan(_ gesture: UIPanGestureRecognizer) { | ||
154 | + let translation = gesture.translation(in: self) | ||
155 | + let width = self.frame.width | ||
156 | + let threshold = width / 2 | ||
157 | + | ||
158 | + if gesture.state == .changed { | ||
159 | + transform = CGAffineTransform(translationX: translation.x, y: 0) | ||
160 | + } else if gesture.state == .ended { | ||
161 | + if abs(translation.x) >= threshold { | ||
162 | + deleteTab() | ||
163 | + } else { | ||
164 | + UIView.animate(withDuration: 0.3) { | ||
165 | + self.transform = .identity | ||
166 | + } | ||
167 | + } | ||
168 | + } | ||
169 | + } | ||
170 | +} |
1 | +// | ||
2 | +// Tabs.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 28.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +enum HistoryToolbarElementType { | ||
11 | + case add | ||
12 | + case tabs | ||
13 | + case done | ||
14 | +} | ||
15 | + | ||
16 | +final class TabsToolbarView: UIView { | ||
17 | + var action: ((HistoryToolbarElementType) -> Void)? | ||
18 | + | ||
19 | + let toolbar: UIToolbar = { | ||
20 | + let obj = UIToolbar() | ||
21 | + obj.barTintColor = .white | ||
22 | + return obj | ||
23 | + }() | ||
24 | + | ||
25 | + let newTabToolbarButtonItem: UIBarButtonItem = { | ||
26 | + let obj = UIBarButtonItem() | ||
27 | + obj.image = UIImage(systemName: "plus") | ||
28 | + obj.tintColor = .gray | ||
29 | + return obj | ||
30 | + }() | ||
31 | + | ||
32 | + | ||
33 | + let showTabToolbarButtonItem: UIBarButtonItem = { | ||
34 | + let obj = UIBarButtonItem() | ||
35 | + obj.tintColor = .gray | ||
36 | + return obj | ||
37 | + }() | ||
38 | + | ||
39 | + let doneTabToolbarButtonItem: UIBarButtonItem = { | ||
40 | + let obj = UIBarButtonItem() | ||
41 | + obj.title = "Done" | ||
42 | + return obj | ||
43 | + }() | ||
44 | + | ||
45 | + override init(frame: CGRect) { | ||
46 | + super.init(frame: frame) | ||
47 | + setup() | ||
48 | + } | ||
49 | + | ||
50 | + required init?(coder: NSCoder) { | ||
51 | + fatalError("init(coder:) has not been implemented") | ||
52 | + } | ||
53 | + | ||
54 | + private func setup() { | ||
55 | + addSubview(toolbar) | ||
56 | + let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) | ||
57 | + toolbar.items = [ newTabToolbarButtonItem, | ||
58 | + flexibleSpace, showTabToolbarButtonItem, | ||
59 | + flexibleSpace, doneTabToolbarButtonItem ] | ||
60 | + | ||
61 | + toolbar.backgroundColor = .white | ||
62 | + initActions() | ||
63 | + setupConstraints() | ||
64 | + } | ||
65 | + | ||
66 | + private func setupConstraints() { | ||
67 | + toolbar.snp.makeConstraints { make in | ||
68 | + make.top.equalToSuperview() | ||
69 | + make.leading.trailing.equalToSuperview() | ||
70 | + make.bottom.equalTo(safeAreaLayoutGuide.snp.bottom) | ||
71 | + } | ||
72 | + } | ||
73 | +} | ||
74 | + | ||
75 | + | ||
76 | +// MARK: - Helpers | ||
77 | +extension TabsToolbarView { | ||
78 | + private func initActions() { | ||
79 | + newTabToolbarButtonItem.target = self | ||
80 | + newTabToolbarButtonItem.action = #selector(didItemTapped(_:)) | ||
81 | + showTabToolbarButtonItem.target = self | ||
82 | + showTabToolbarButtonItem.action = #selector(didItemTapped(_:)) | ||
83 | + doneTabToolbarButtonItem.target = self | ||
84 | + doneTabToolbarButtonItem.action = #selector(didItemTapped(_:)) | ||
85 | + } | ||
86 | +} | ||
87 | + | ||
88 | +// MARK: - Actions | ||
89 | +extension TabsToolbarView { | ||
90 | + @objc | ||
91 | + private func didItemTapped(_ sender: UIBarButtonItem) { | ||
92 | + guard let action else { | ||
93 | + return | ||
94 | + } | ||
95 | + switch sender { | ||
96 | + case newTabToolbarButtonItem: | ||
97 | + action(.add) | ||
98 | + case showTabToolbarButtonItem: | ||
99 | + action(.tabs) | ||
100 | + case doneTabToolbarButtonItem: | ||
101 | + action(.done) | ||
102 | + default: | ||
103 | + break | ||
104 | + } | ||
105 | + } | ||
106 | +} |
browser/Modules/Tabs/View/TabsView.swift
0 → 100644
1 | +// | ||
2 | +// TabsView.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 28.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | +import SnapKit | ||
10 | + | ||
11 | +final class TabsView: UIView { | ||
12 | + let openedTabsCollectionView: UICollectionView = { | ||
13 | + let obj = UICollectionViewFlowLayout() | ||
14 | + obj.minimumInteritemSpacing = 4.sizeW | ||
15 | + obj.sectionInset = UIEdgeInsets(top: 16.sizeH, left: 16.sizeW, bottom: 16.sizeH, right: 16.sizeW) | ||
16 | + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: obj) | ||
17 | + collectionView.showsVerticalScrollIndicator = false | ||
18 | + collectionView.backgroundColor = .clear | ||
19 | + return collectionView | ||
20 | + }() | ||
21 | + | ||
22 | + let tabsToolbarView: TabsToolbarView = { | ||
23 | + let obj = TabsToolbarView() | ||
24 | + obj.backgroundColor = ColorConstants.lightGray | ||
25 | + return obj | ||
26 | + }() | ||
27 | + | ||
28 | + override init(frame: CGRect) { | ||
29 | + super.init(frame: frame) | ||
30 | + setup() | ||
31 | + setupConstraints() | ||
32 | + } | ||
33 | + | ||
34 | + required init?(coder: NSCoder) { | ||
35 | + fatalError("init(coder:) has not been implemented") | ||
36 | + } | ||
37 | + | ||
38 | + private func setup() { | ||
39 | + addSubview(openedTabsCollectionView) | ||
40 | + addSubview(tabsToolbarView) | ||
41 | + } | ||
42 | + | ||
43 | + private func setupConstraints() { | ||
44 | + openedTabsCollectionView.snp.makeConstraints { make in | ||
45 | + make.top.equalTo(safeAreaLayoutGuide.snp.top) | ||
46 | + make.leading.trailing.equalToSuperview() | ||
47 | + make.bottom.equalTo(tabsToolbarView.snp.top) | ||
48 | + } | ||
49 | + tabsToolbarView.snp.makeConstraints { make in | ||
50 | + make.leading.equalToSuperview() | ||
51 | + make.trailing.equalToSuperview() | ||
52 | + make.bottom.equalTo(safeAreaLayoutGuide.snp.bottom) | ||
53 | + } | ||
54 | + } | ||
55 | +} |
1 | +// | ||
2 | +// NavigationControllerViewController.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 26.09.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +final class NavigationViewController: UINavigationController { | ||
11 | + override func viewDidLoad() { | ||
12 | + super.viewDidLoad() | ||
13 | + initViewController() | ||
14 | + } | ||
15 | + | ||
16 | + private func initViewController() { | ||
17 | + setStartControllers() | ||
18 | + view.backgroundColor = .blue | ||
19 | + delegate = self | ||
20 | + } | ||
21 | +} | ||
22 | + | ||
23 | +// MARK: - Helpers | ||
24 | +extension NavigationViewController { | ||
25 | + private func setStartControllers() { | ||
26 | + let tabsViewController = TabsViewController() | ||
27 | + let browserHomeViewController = BrowserHomeViewController(url: nil, currentTabId: nil) | ||
28 | + let payloadViewController = PayloadViewController() | ||
29 | + let cachingManager = CachingManager.shared | ||
30 | + if cachingManager.isFirstAppLoad == false { | ||
31 | + setViewControllers([tabsViewController, browserHomeViewController, payloadViewController], animated: true) | ||
32 | + cachingManager.isFirstAppLoad.toggle() | ||
33 | + } else { | ||
34 | + setViewControllers([tabsViewController, browserHomeViewController], animated: true) | ||
35 | + } | ||
36 | + | ||
37 | + } | ||
38 | +} | ||
39 | + | ||
40 | +//MARK: Navigation Controller delegate | ||
41 | +extension NavigationViewController: UINavigationControllerDelegate { | ||
42 | + func navigationController(_ navigationController: UINavigationController, | ||
43 | + animationControllerFor operation: UINavigationController.Operation, | ||
44 | + from fromVC: UIViewController, | ||
45 | + to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { | ||
46 | + if (fromVC is BrowserHomeViewController && toVC is TabsViewController) { | ||
47 | + return OpenTabsTransition(operation: operation) | ||
48 | + } | ||
49 | + | ||
50 | + if (fromVC is TabsViewController && toVC is BrowserHomeViewController) { | ||
51 | + return OpenTabsTransition(operation: operation) | ||
52 | + } | ||
53 | + return nil | ||
54 | + } | ||
55 | +} |
1 | +// | ||
2 | +// ColorConstants.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 19.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +struct ColorConstants { | ||
11 | + static let lightGray = UIColor(red: 0.984, green: 0.984, blue: 0.984, alpha: 1) | ||
12 | + static let gray = UIColor(red: 0.62, green: 0.62, blue: 0.62, alpha: 1) | ||
13 | + static let payloadTextColor = UIColor(red: 0.121, green: 0.121, blue: 0.121, alpha: 1) | ||
14 | + static let buttonsBackgroundColor = UIColor(red: 0.349, green: 0.565, blue: 1, alpha: 1) | ||
15 | +} |
1 | +// | ||
2 | +// FontConstants.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 19.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | + | ||
10 | +struct FontConstants { | ||
11 | + | ||
12 | + //SFProText-Regular | ||
13 | + static let regularFont_18 = UIFont(name: "SFProText-Regular", size: 18.sizeW) | ||
14 | + static let regularFont_14 = UIFont(name: "SFProText-Regular", size: 14.sizeW) | ||
15 | + static let regularFont_12 = UIFont(name: "SFProText-Regular", size: 12.sizeW) | ||
16 | + | ||
17 | + //SFProText-Semibold | ||
18 | + static let semiboldFont_18 = UIFont(name: "SFProText-Semibold", size: 18.sizeW) | ||
19 | + static let semiboldFont_17 = UIFont(name: "SFProText-Semibold", size: 17.sizeW) | ||
20 | + static let semiboldFont_15 = UIFont(name: "SFProText-Semibold", size: 15.sizeW) | ||
21 | + static let semiboldFont_14 = UIFont(name: "SFProText-Semibold", size: 14.sizeW) | ||
22 | + static let semiboldFont_12 = UIFont(name: "SFProText-Semibold", size: 12.sizeW) | ||
23 | +} |
1 | +// | ||
2 | +// Strings.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 19.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import Foundation | ||
9 | + | ||
10 | +struct StringConstants { | ||
11 | + //static let shared = StringConstants() | ||
12 | + | ||
13 | + ///PayloadView | ||
14 | + static let ourBrowser = "Our browser lets you quickly and safely access \nthe internet. You can use it to look at web \npages, find information, and chat with friends." | ||
15 | + static let privacyLabelText = "By starting, you agree to our Terms and Condition and Privacy Policy." | ||
16 | + static let payloadViewControllerData = ["Unlimited searching prompts", "Advert blocker activation", "Free to use"] | ||
17 | + | ||
18 | + | ||
19 | + ///BrowserHomeView | ||
20 | + static let frequentlyVisited = "Frequently visited" | ||
21 | + | ||
22 | + ///BrowserHomeViewController | ||
23 | + static let browserHomeVCImages = ["Linkedin", "Opera", "Gmail", "GitHub"] | ||
24 | + static let browserHomeVCurl = [ | ||
25 | + "https://www.linkedin.com/", | ||
26 | + "https://www.opera.com/", | ||
27 | + "https://mail.google.com/", | ||
28 | + "https://github.com/" ] | ||
29 | + | ||
30 | + ///SettingView | ||
31 | + static let settingViewControllerTableViewData = ["Privacy policy", "Terms and condition", "Share app", "Rate app", "Support"] | ||
32 | + static let removeAdvertTableViewData = ["Unlimited blocks & uploads", "Enhanced User Experience", "Faster Page Load Times", "Privacy and Security"] | ||
33 | + static let search = "Search" | ||
34 | + | ||
35 | + | ||
36 | + ///RemoveAdvertView | ||
37 | + static let yourDiscount = "You save 91% - normally US $9,99" | ||
38 | + static let freeTrial = "3-day free trial" | ||
39 | + static let claimOffer = "Claim your one time sing up offer" | ||
40 | + static let price = "0.99$" | ||
41 | + static let subscribe = "Subscribe now" | ||
42 | + static let advertMode = "Advertblocker Mode" | ||
43 | + static let turnOff = "Tap to turn off" | ||
44 | + static let turnOn = "Tap to turn on" | ||
45 | + | ||
46 | + | ||
47 | + ///Cell reuse identifiers | ||
48 | + static let historyTableViewCell = "HistoryTableViewCell" | ||
49 | + static let searchTableViewCell = "SearchingTableViewCell" | ||
50 | + static let removeAdvertTableViewCell = "RemoveAdvertTableViewCell" | ||
51 | + static let settingTableViewCell = "SettingTableViewCell" | ||
52 | + static let advantagesTableViewCell = "AdvantagesTableViewCell" | ||
53 | + | ||
54 | + ///setting cell detail info | ||
55 | + static let settingTerms = "Thank you for choosing VPN Proxy Master.\n\n We will collect only the minimum data required to offer you a safe and stable VPN experience. For your convenience, hereby enclosed are the bullet points of data collected by us:\n\n · Device’s System Language which we do not retain. This is for settings the in-app language.\n\n · IP address and System Country which we do not retain. Your IP information is only used to provide you with the fastest default server list.\n\n We will strictly follow our no-logs policy and relevant data privacy laws, and will try our best to protect your privacy and security.\n\n For more information, please read our Terms and Service and Privacy Policy." | ||
56 | + static let settingHeader = "Terms\nand conditions" | ||
57 | + static let settingAgreeButtonText = "Agree & Continue" | ||
58 | + static let privacyHeaderText = "Your Privacy\n is our top priority" | ||
59 | +} | ||
60 | + |
1 | +// | ||
2 | +// URLs.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 19.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import Foundation | ||
9 | + | ||
10 | + | ||
11 | +struct URLConstants { | ||
12 | + static let baseURL = "https://suggestqueries.google.com/complete/search?output=toolbar&hl=%@&q=%@" | ||
13 | + static let googleURL = "https://www.google.com/search?q=" | ||
14 | + static let linkedInURL = "https://www.linkedin.com/" | ||
15 | + static let opera = "https://www.opera.com/" | ||
16 | + static let gmail = "https://mail.google.com/" | ||
17 | + static let github = "https://github.com/" | ||
18 | + static let shareAppLink = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley" | ||
19 | +} |
No preview for this file type
No preview for this file type
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "Settings (1)..png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
18.7 KB
browser/Resources/Images/Assets.xcassets/frequentlyVisitedTab/GitHub.imageset/Contents.json
0 → 100644
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "github.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
5.73 KB
browser/Resources/Images/Assets.xcassets/frequentlyVisitedTab/Gmail.imageset/Contents.json
0 → 100644
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "gmail.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
5.73 KB
browser/Resources/Images/Assets.xcassets/frequentlyVisitedTab/Linkedin.imageset/Contents.json
0 → 100644
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "Image.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
browser/Resources/Images/Assets.xcassets/frequentlyVisitedTab/Linkedin.imageset/Image.png
0 → 100644
4.51 KB
browser/Resources/Images/Assets.xcassets/frequentlyVisitedTab/Opera.imageset/Contents.json
0 → 100644
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "opera.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
9.44 KB
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "Image.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
4.76 KB
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "Gotoweb 1.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + }, | ||
16 | + { | ||
17 | + "filename" : "Gotoweb.png", | ||
18 | + "idiom" : "iphone", | ||
19 | + "scale" : "1x" | ||
20 | + }, | ||
21 | + { | ||
22 | + "idiom" : "iphone", | ||
23 | + "scale" : "2x" | ||
24 | + }, | ||
25 | + { | ||
26 | + "idiom" : "iphone", | ||
27 | + "scale" : "3x" | ||
28 | + } | ||
29 | + ], | ||
30 | + "info" : { | ||
31 | + "author" : "xcode", | ||
32 | + "version" : 1 | ||
33 | + } | ||
34 | +} |
7.86 KB
7.86 KB
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "Image.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "Image.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
42 KB
browser/Resources/Images/Assets.xcassets/remove advert screen/ShieldActive.imageset/Contents.json
0 → 100644
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "Image.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
browser/Resources/Images/Assets.xcassets/remove advert screen/ShieldActive.imageset/Image.png
0 → 100644
191 KB
browser/Resources/Images/Assets.xcassets/remove advert screen/ShieldInactive.imageset/Contents.json
0 → 100644
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "Image.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
browser/Resources/Images/Assets.xcassets/remove advert screen/ShieldInactive.imageset/Image.png
0 → 100644
148 KB
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "Image.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "Image.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
41.2 KB
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "Image.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "Image.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
1 | +{ | ||
2 | + "images" : [ | ||
3 | + { | ||
4 | + "filename" : "defaultTabImage.png", | ||
5 | + "idiom" : "universal", | ||
6 | + "scale" : "1x" | ||
7 | + }, | ||
8 | + { | ||
9 | + "idiom" : "universal", | ||
10 | + "scale" : "2x" | ||
11 | + }, | ||
12 | + { | ||
13 | + "idiom" : "universal", | ||
14 | + "scale" : "3x" | ||
15 | + } | ||
16 | + ], | ||
17 | + "info" : { | ||
18 | + "author" : "xcode", | ||
19 | + "version" : 1 | ||
20 | + } | ||
21 | +} |
browser/Resources/Images/Assets.xcassets/tabImages/defaultImage.imageset/defaultTabImage.png
0 → 100644
97.1 KB
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> | ||
3 | + <device id="retina6_12" orientation="portrait" appearance="light"/> | ||
4 | + <dependencies> | ||
5 | + <deployment identifier="iOS"/> | ||
6 | + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/> | ||
7 | + <capability name="Safe area layout guides" minToolsVersion="9.0"/> | ||
8 | + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | ||
9 | + </dependencies> | ||
10 | + <scenes> | ||
11 | + <!--View Controller--> | ||
12 | + <scene sceneID="EHf-IW-A2E"> | ||
13 | + <objects> | ||
14 | + <viewController id="01J-lp-oVM" sceneMemberID="viewController"> | ||
15 | + <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> | ||
16 | + <rect key="frame" x="0.0" y="0.0" width="393" height="852"/> | ||
17 | + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||
18 | + <subviews> | ||
19 | + <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="Gotoweb" translatesAutoresizingMaskIntoConstraints="NO" id="M9l-6x-NJv"> | ||
20 | + <rect key="frame" x="128" y="395" width="136" height="62"/> | ||
21 | + <autoresizingMask key="autoresizingMask"/> | ||
22 | + <edgeInsets key="layoutMargins" top="8" left="8" bottom="8" right="8"/> | ||
23 | + </imageView> | ||
24 | + </subviews> | ||
25 | + <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> | ||
26 | + <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | ||
27 | + </view> | ||
28 | + </viewController> | ||
29 | + <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> | ||
30 | + </objects> | ||
31 | + <point key="canvasLocation" x="52.671755725190835" y="374.64788732394368"/> | ||
32 | + </scene> | ||
33 | + </scenes> | ||
34 | + <resources> | ||
35 | + <image name="Gotoweb" width="178" height="52"/> | ||
36 | + </resources> | ||
37 | +</document> |
browser/Resources/blockerList.json
0 → 100644
This diff could not be displayed because it is too large.
browser/Services/BrowserSearchService.swift
0 → 100644
1 | +// | ||
2 | +// BrowserSearchService.swift | ||
3 | +// Pro Seecurity VPN | ||
4 | +// | ||
5 | +// Created by Leyter on 30.05.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import Foundation | ||
9 | +import Alamofire | ||
10 | + | ||
11 | +class BrowserSearchService { | ||
12 | + private let baseUrl = "https://suggestqueries.google.com/complete/search?output=toolbar&hl=%@&q=%@" | ||
13 | + | ||
14 | + func searchSuggest(_ query: String) async -> [String] { | ||
15 | + let url = String(format: baseUrl, Locale.current.languageCode ?? "", query.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "") | ||
16 | + | ||
17 | + guard let xmlResponse = await AF.request(url, method: .get).serializingString().response.value?.data(using: .utf8) else { | ||
18 | + return [] | ||
19 | + } | ||
20 | + let parser = XMLParser(data: xmlResponse) | ||
21 | + let delegate = XMLParserSearchResponseDelegate() | ||
22 | + parser.delegate = delegate | ||
23 | + parser.parse() | ||
24 | + | ||
25 | + return delegate.dataNames | ||
26 | + } | ||
27 | +} | ||
28 | + | ||
29 | +// MARK: parser | ||
30 | +extension BrowserSearchService { | ||
31 | + class XMLParserSearchResponseDelegate: NSObject, XMLParserDelegate { | ||
32 | + var dataNames: [String] = [] | ||
33 | + | ||
34 | + func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) { | ||
35 | + if elementName == "suggestion", let data = attributeDict["data"] { | ||
36 | + dataNames.append(data) | ||
37 | + } | ||
38 | + } | ||
39 | + } | ||
40 | +} |
browser/Services/SnapshotService.swift
0 → 100644
1 | +// | ||
2 | +// SnapshotService.swift | ||
3 | +// browser | ||
4 | +// | ||
5 | +// Created by Artem Talko on 23.10.2023. | ||
6 | +// | ||
7 | + | ||
8 | +import UIKit | ||
9 | +import WebKit | ||
10 | + | ||
11 | + | ||
12 | +final class SnapshotService { | ||
13 | + static let shared = SnapshotService() | ||
14 | + let snapshotConfiguration = WKSnapshotConfiguration() | ||
15 | + | ||
16 | + func makeSnapshot(_ viewForSnap: UIView) -> UIImage { | ||
17 | + let renderer = UIGraphicsImageRenderer(size: viewForSnap.bounds.size) | ||
18 | + let siteSnapshotImage = renderer.image { (context) in | ||
19 | + viewForSnap.layer.render(in: context.cgContext) | ||
20 | + } | ||
21 | + return siteSnapshotImage | ||
22 | + } | ||
23 | + | ||
24 | + func makeWKWebViewSnapshot(_ wkWV: WKWebView?, completion: @escaping (UIImage?) -> Void) { | ||
25 | + wkWV?.takeSnapshot(with: snapshotConfiguration) { (image, error) in | ||
26 | + if let snaphotIMG = image { | ||
27 | + completion(snaphotIMG) | ||
28 | + } else if let error = error { | ||
29 | + print(error) | ||
30 | + completion(nil) | ||
31 | + } | ||
32 | + } | ||
33 | + } | ||
34 | +} |
Please
register
or
login
to post a comment