git.iko.soy/parseable-macos
on master help sign in
Sync saved filters with Parseable server (#34)
7eebda4iko
ParseableViewer.xcodeproj/project.pbxprojnew file+625
@@ -0,0 +1,625 @@
1+// !$*UTF8*$!
2+{
3+ archiveVersion = 1;
4+ classes = {
5+ };
6+ objectVersion = 77;
7+ objects = {
8+
9+/* Begin PBXBuildFile section */
10+ 073AFD1207B61E46CA993E98 /* LogStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194F81AC44933EFE8C5F9824 /* LogStream.swift */; };
11+ 0E0B747CF1B650BF70A7DC56 /* LiveTailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA23566B4B78428A8A5D17D0 /* LiveTailView.swift */; };
12+ 11A5740A00A2E29D4EC77B66 /* SQLAutoComplete.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBF33043C9CB623F028C5E5C /* SQLAutoComplete.swift */; };
13+ 15A0B57711D4B39118F09D93 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93D1C596856E6FB5C39249DD /* AppState.swift */; };
14+ 1DF7164680B3EAF1F5E4C47E /* TimeRangePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B96F0F5013CC46086EFEDA5A /* TimeRangePicker.swift */; };
15+ 1E07C534FCC62B7B5CC15DDB /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3EB52E9BEDCAF82C6A155FA /* ContentView.swift */; };
16+ 2C35EBC496AF8DAB215800F0 /* LogDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AA610FFA85948D794C5A4CC /* LogDetailView.swift */; };
17+ 2E3E0E23D3388B90955AD9A8 /* KeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D36767781BD4F0650C1CD27 /* KeychainService.swift */; };
18+ 308E024A853EDA08411FE6B0 /* UsersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 187537810EBBCC784604017D /* UsersView.swift */; };
19+ 3CDE9527F8C25B7C32571CB4 /* ConnectionStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 996D7D107A1959CF67E82DF5 /* ConnectionStoreTests.swift */; };
20+ 3F7EB642D3EBA91FCC962F15 /* ServerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B7C589A8DC2293DDDE4D60F /* ServerInfo.swift */; };
21+ 3FDB32277A679A14D1455A7D /* SQLAutoCompleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B692AC5D403FB0CBB28935B /* SQLAutoCompleteTests.swift */; };
22+ 45286251E8F4CF0718E0595C /* LiveTailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F29CF341F7B9EB2A9B0326 /* LiveTailViewModel.swift */; };
23+ 47E1670726D4104D80F3F952 /* StreamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45C3FC0434D42379E8103913 /* StreamDetailView.swift */; };
24+ 4A269FCA596D52F688BB055A /* AlertsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 878573090C3AB36A21A32DFA /* AlertsView.swift */; };
25+ 4EBF4D4702F460A6AD962232 /* SyntaxHighlighting.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6596EAA474BC10AE4741FBF /* SyntaxHighlighting.swift */; };
26+ 5033B2FE28A2EEB990CA2114 /* ConnectionStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C7F0C26B0065609D29197D7 /* ConnectionStore.swift */; };
27+ 507DF05FC7A223B2F7E701EF /* SyntaxHighlightingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2535C31705B3ADC1C8628380 /* SyntaxHighlightingTests.swift */; };
28+ 55A93DAF2D88AAB1F42B5E04 /* ConnectionSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DF1D667CE043D657046ABC1 /* ConnectionSheet.swift */; };
29+ 5681C7459B58D71A83496890 /* QueryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB957A716A1554537EA69333 /* QueryViewModel.swift */; };
30+ 5BC9FA9419A918C16937E4D3 /* LiveTailViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4200B45B223178AE1426FE7 /* LiveTailViewModelTests.swift */; };
31+ 62014FBBAD5AA68F2C768DEF /* LogTableHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFBAD6576A7594337CCC7952 /* LogTableHelperTests.swift */; };
32+ 69485FFE5D25180B422D9230 /* QueryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E18F652C0ED65976591C0E9A /* QueryView.swift */; };
33+ 70CC22F812F9151CBA042375 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F65288FEAE1281DB05D97147 /* SettingsView.swift */; };
34+ 7128E20D6592E907210BBF1A /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0519F396B1D5C6335B0F1B7 /* SidebarView.swift */; };
35+ 822B3C27B35EA429E522AFB4 /* LogTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC934220A4BBA1B37F944573 /* LogTableView.swift */; };
36+ 835943BF8C2C9AB9CA203B2F /* ParseableClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1771C7B952AEB93E35ADF /* ParseableClient.swift */; };
37+ 836F637D54951D3BC6A2AF11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4EB9B6688633BC4E8D968065 /* Assets.xcassets */; };
38+ 9C860DEEFE4446A152C18B62 /* ParseableViewerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C07A9EF327212C2C44D281 /* ParseableViewerApp.swift */; };
39+ B1BBABAB489838F84E670DD6 /* AlertModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D3CDFCD82AF6D988009AC5A /* AlertModels.swift */; };
40+ BA06B242DE817368D1BAD194 /* SQLEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF186DF367E2C7EEE4780D14 /* SQLEditorView.swift */; };
41+ BAA005C4A067CB6EBA4294DE /* ServerInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A83116AF430E859E6A7FDF0 /* ServerInfoView.swift */; };
42+ BCEB51336ECF5827F888614E /* ServerConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F0DA80D3E3AD784E8B06E7 /* ServerConnectionTests.swift */; };
43+ CCA017BAFD0977B66B5CCBCD /* JSONValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0856310107CC1545CE5B627C /* JSONValue.swift */; };
44+ D9C618815EEBE79EE7529EC2 /* QueryViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FBAA52C12C4E02A735DA6EA /* QueryViewModelTests.swift */; };
45+ DE4DD2EB56F92820EE01CBE1 /* ServerConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18D05E439E6063E901A3E7EC /* ServerConnection.swift */; };
46+ EF5ECFAAF8B2485D452021EB /* LogStreamTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDBC51E5343A4BE35D8FF997 /* LogStreamTests.swift */; };
47+ F48FF7028BEB8C84234C018B /* JSONValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9559128AC1709175C3A392 /* JSONValueTests.swift */; };
48+ F691C8702AF273C0B9D79963 /* ParseableFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0ED2520AE908C73BF5C00AA /* ParseableFilter.swift */; };
49+ F8F9730A83587099FF50E77B /* ParseableClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86F88A71716091F7703BC3A /* ParseableClientTests.swift */; };
50+/* End PBXBuildFile section */
51+
52+/* Begin PBXContainerItemProxy section */
53+ 31032B334B864955687341D2 /* PBXContainerItemProxy */ = {
54+ isa = PBXContainerItemProxy;
55+ containerPortal = AFB3E6B7F230B722DBCD4143 /* Project object */;
56+ proxyType = 1;
57+ remoteGlobalIDString = 2BFA29C07896DADE27FE8DF9;
58+ remoteInfo = ParseableViewer;
59+ };
60+/* End PBXContainerItemProxy section */
61+
62+/* Begin PBXFileReference section */
63+ 03F29CF341F7B9EB2A9B0326 /* LiveTailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTailViewModel.swift; sourceTree = "<group>"; };
64+ 0856310107CC1545CE5B627C /* JSONValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONValue.swift; sourceTree = "<group>"; };
65+ 0E9559128AC1709175C3A392 /* JSONValueTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONValueTests.swift; sourceTree = "<group>"; };
66+ 187537810EBBCC784604017D /* UsersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsersView.swift; sourceTree = "<group>"; };
67+ 18D05E439E6063E901A3E7EC /* ServerConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConnection.swift; sourceTree = "<group>"; };
68+ 194F81AC44933EFE8C5F9824 /* LogStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogStream.swift; sourceTree = "<group>"; };
69+ 2535C31705B3ADC1C8628380 /* SyntaxHighlightingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyntaxHighlightingTests.swift; sourceTree = "<group>"; };
70+ 2B692AC5D403FB0CBB28935B /* SQLAutoCompleteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SQLAutoCompleteTests.swift; sourceTree = "<group>"; };
71+ 30A1771C7B952AEB93E35ADF /* ParseableClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseableClient.swift; sourceTree = "<group>"; };
72+ 45C3FC0434D42379E8103913 /* StreamDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamDetailView.swift; sourceTree = "<group>"; };
73+ 4D3CDFCD82AF6D988009AC5A /* AlertModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertModels.swift; sourceTree = "<group>"; };
74+ 4EB9B6688633BC4E8D968065 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
75+ 5AA610FFA85948D794C5A4CC /* LogDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogDetailView.swift; sourceTree = "<group>"; };
76+ 62C07A9EF327212C2C44D281 /* ParseableViewerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseableViewerApp.swift; sourceTree = "<group>"; };
77+ 6A83116AF430E859E6A7FDF0 /* ServerInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerInfoView.swift; sourceTree = "<group>"; };
78+ 6B7C589A8DC2293DDDE4D60F /* ServerInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerInfo.swift; sourceTree = "<group>"; };
79+ 7C7F0C26B0065609D29197D7 /* ConnectionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionStore.swift; sourceTree = "<group>"; };
80+ 81003B1CAEBB75C72076AE85 /* ParseableViewerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ParseableViewerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
81+ 878573090C3AB36A21A32DFA /* AlertsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertsView.swift; sourceTree = "<group>"; };
82+ 8D36767781BD4F0650C1CD27 /* KeychainService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainService.swift; sourceTree = "<group>"; };
83+ 8FBAA52C12C4E02A735DA6EA /* QueryViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryViewModelTests.swift; sourceTree = "<group>"; };
84+ 93D1C596856E6FB5C39249DD /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
85+ 996D7D107A1959CF67E82DF5 /* ConnectionStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionStoreTests.swift; sourceTree = "<group>"; };
86+ 9DF1D667CE043D657046ABC1 /* ConnectionSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionSheet.swift; sourceTree = "<group>"; };
87+ A6596EAA474BC10AE4741FBF /* SyntaxHighlighting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyntaxHighlighting.swift; sourceTree = "<group>"; };
88+ AF186DF367E2C7EEE4780D14 /* SQLEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SQLEditorView.swift; sourceTree = "<group>"; };
89+ B0ED2520AE908C73BF5C00AA /* ParseableFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseableFilter.swift; sourceTree = "<group>"; };
90+ B2F7F4E5D5A9542B5E7E6C62 /* ParseableViewer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ParseableViewer.app; sourceTree = BUILT_PRODUCTS_DIR; };
91+ B38EDA7C380889A9DF25566E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
92+ B96F0F5013CC46086EFEDA5A /* TimeRangePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeRangePicker.swift; sourceTree = "<group>"; };
93+ BB957A716A1554537EA69333 /* QueryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryViewModel.swift; sourceTree = "<group>"; };
94+ C0519F396B1D5C6335B0F1B7 /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = "<group>"; };
95+ C3EB52E9BEDCAF82C6A155FA /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
96+ C86F88A71716091F7703BC3A /* ParseableClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseableClientTests.swift; sourceTree = "<group>"; };
97+ CFBAD6576A7594337CCC7952 /* LogTableHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogTableHelperTests.swift; sourceTree = "<group>"; };
98+ D4F0DA80D3E3AD784E8B06E7 /* ServerConnectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConnectionTests.swift; sourceTree = "<group>"; };
99+ DA23566B4B78428A8A5D17D0 /* LiveTailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTailView.swift; sourceTree = "<group>"; };
100+ DBF33043C9CB623F028C5E5C /* SQLAutoComplete.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SQLAutoComplete.swift; sourceTree = "<group>"; };
101+ E18F652C0ED65976591C0E9A /* QueryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryView.swift; sourceTree = "<group>"; };
102+ F0C9C3804CDFF785226EA452 /* ParseableViewer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ParseableViewer.entitlements; sourceTree = "<group>"; };
103+ F4200B45B223178AE1426FE7 /* LiveTailViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTailViewModelTests.swift; sourceTree = "<group>"; };
104+ F65288FEAE1281DB05D97147 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
105+ FC934220A4BBA1B37F944573 /* LogTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogTableView.swift; sourceTree = "<group>"; };
106+ FDBC51E5343A4BE35D8FF997 /* LogStreamTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogStreamTests.swift; sourceTree = "<group>"; };
107+/* End PBXFileReference section */
108+
109+/* Begin PBXGroup section */
110+ 078D5FA1E5D2884218FF9433 /* Services */ = {
111+ isa = PBXGroup;
112+ children = (
113+ 7C7F0C26B0065609D29197D7 /* ConnectionStore.swift */,
114+ 8D36767781BD4F0650C1CD27 /* KeychainService.swift */,
115+ 30A1771C7B952AEB93E35ADF /* ParseableClient.swift */,
116+ );
117+ path = Services;
118+ sourceTree = "<group>";
119+ };
120+ 171EC835252990F6B7BBB45D /* Views */ = {
121+ isa = PBXGroup;
122+ children = (
123+ 878573090C3AB36A21A32DFA /* AlertsView.swift */,
124+ 9DF1D667CE043D657046ABC1 /* ConnectionSheet.swift */,
125+ C3EB52E9BEDCAF82C6A155FA /* ContentView.swift */,
126+ DA23566B4B78428A8A5D17D0 /* LiveTailView.swift */,
127+ 5AA610FFA85948D794C5A4CC /* LogDetailView.swift */,
128+ FC934220A4BBA1B37F944573 /* LogTableView.swift */,
129+ E18F652C0ED65976591C0E9A /* QueryView.swift */,
130+ 6A83116AF430E859E6A7FDF0 /* ServerInfoView.swift */,
131+ F65288FEAE1281DB05D97147 /* SettingsView.swift */,
132+ C0519F396B1D5C6335B0F1B7 /* SidebarView.swift */,
133+ DBF33043C9CB623F028C5E5C /* SQLAutoComplete.swift */,
134+ AF186DF367E2C7EEE4780D14 /* SQLEditorView.swift */,
135+ 45C3FC0434D42379E8103913 /* StreamDetailView.swift */,
136+ A6596EAA474BC10AE4741FBF /* SyntaxHighlighting.swift */,
137+ B96F0F5013CC46086EFEDA5A /* TimeRangePicker.swift */,
138+ 187537810EBBCC784604017D /* UsersView.swift */,
139+ );
140+ path = Views;
141+ sourceTree = "<group>";
142+ };
143+ 40B5CA6A795614C8B53AAD76 /* ParseableViewerTests */ = {
144+ isa = PBXGroup;
145+ children = (
146+ 996D7D107A1959CF67E82DF5 /* ConnectionStoreTests.swift */,
147+ 0E9559128AC1709175C3A392 /* JSONValueTests.swift */,
148+ F4200B45B223178AE1426FE7 /* LiveTailViewModelTests.swift */,
149+ FDBC51E5343A4BE35D8FF997 /* LogStreamTests.swift */,
150+ CFBAD6576A7594337CCC7952 /* LogTableHelperTests.swift */,
151+ C86F88A71716091F7703BC3A /* ParseableClientTests.swift */,
152+ 8FBAA52C12C4E02A735DA6EA /* QueryViewModelTests.swift */,
153+ D4F0DA80D3E3AD784E8B06E7 /* ServerConnectionTests.swift */,
154+ 2B692AC5D403FB0CBB28935B /* SQLAutoCompleteTests.swift */,
155+ 2535C31705B3ADC1C8628380 /* SyntaxHighlightingTests.swift */,
156+ );
157+ path = ParseableViewerTests;
158+ sourceTree = "<group>";
159+ };
160+ 6F721D552C363ADAC39442BB /* Resources */ = {
161+ isa = PBXGroup;
162+ children = (
163+ 4EB9B6688633BC4E8D968065 /* Assets.xcassets */,
164+ );
165+ path = Resources;
166+ sourceTree = "<group>";
167+ };
168+ 8A5E1C4DD012FF60BE222F22 /* Models */ = {
169+ isa = PBXGroup;
170+ children = (
171+ 4D3CDFCD82AF6D988009AC5A /* AlertModels.swift */,
172+ 0856310107CC1545CE5B627C /* JSONValue.swift */,
173+ 194F81AC44933EFE8C5F9824 /* LogStream.swift */,
174+ B0ED2520AE908C73BF5C00AA /* ParseableFilter.swift */,
175+ 18D05E439E6063E901A3E7EC /* ServerConnection.swift */,
176+ 6B7C589A8DC2293DDDE4D60F /* ServerInfo.swift */,
177+ );
178+ path = Models;
179+ sourceTree = "<group>";
180+ };
181+ 8E8BE77500B325C361C4F4DD /* App */ = {
182+ isa = PBXGroup;
183+ children = (
184+ 93D1C596856E6FB5C39249DD /* AppState.swift */,
185+ 62C07A9EF327212C2C44D281 /* ParseableViewerApp.swift */,
186+ );
187+ path = App;
188+ sourceTree = "<group>";
189+ };
190+ D06ED124AC4DFCB31F6EF0C1 = {
191+ isa = PBXGroup;
192+ children = (
193+ F71281985A6BB118039F256B /* ParseableViewer */,
194+ 40B5CA6A795614C8B53AAD76 /* ParseableViewerTests */,
195+ D37830AB19DD594BCD2B5499 /* Products */,
196+ );
197+ sourceTree = "<group>";
198+ };
199+ D37830AB19DD594BCD2B5499 /* Products */ = {
200+ isa = PBXGroup;
201+ children = (
202+ B2F7F4E5D5A9542B5E7E6C62 /* ParseableViewer.app */,
203+ 81003B1CAEBB75C72076AE85 /* ParseableViewerTests.xctest */,
204+ );
205+ name = Products;
206+ sourceTree = "<group>";
207+ };
208+ D5139E26A9B5E21217ED3842 /* ViewModels */ = {
209+ isa = PBXGroup;
210+ children = (
211+ 03F29CF341F7B9EB2A9B0326 /* LiveTailViewModel.swift */,
212+ BB957A716A1554537EA69333 /* QueryViewModel.swift */,
213+ );
214+ path = ViewModels;
215+ sourceTree = "<group>";
216+ };
217+ F71281985A6BB118039F256B /* ParseableViewer */ = {
218+ isa = PBXGroup;
219+ children = (
220+ B38EDA7C380889A9DF25566E /* Info.plist */,
221+ F0C9C3804CDFF785226EA452 /* ParseableViewer.entitlements */,
222+ 8E8BE77500B325C361C4F4DD /* App */,
223+ 8A5E1C4DD012FF60BE222F22 /* Models */,
224+ 6F721D552C363ADAC39442BB /* Resources */,
225+ 078D5FA1E5D2884218FF9433 /* Services */,
226+ D5139E26A9B5E21217ED3842 /* ViewModels */,
227+ 171EC835252990F6B7BBB45D /* Views */,
228+ );
229+ path = ParseableViewer;
230+ sourceTree = "<group>";
231+ };
232+/* End PBXGroup section */
233+
234+/* Begin PBXNativeTarget section */
235+ 2BFA29C07896DADE27FE8DF9 /* ParseableViewer */ = {
236+ isa = PBXNativeTarget;
237+ buildConfigurationList = 9AA32267150BB93C4ECA4508 /* Build configuration list for PBXNativeTarget "ParseableViewer" */;
238+ buildPhases = (
239+ E7518B0D053D4667ECBB99E3 /* Sources */,
240+ 27EFF17FAE6D34920BFD0BB2 /* Resources */,
241+ );
242+ buildRules = (
243+ );
244+ dependencies = (
245+ );
246+ name = ParseableViewer;
247+ packageProductDependencies = (
248+ );
249+ productName = ParseableViewer;
250+ productReference = B2F7F4E5D5A9542B5E7E6C62 /* ParseableViewer.app */;
251+ productType = "com.apple.product-type.application";
252+ };
253+ E39BE21B6A9A7720B55CA1FB /* ParseableViewerTests */ = {
254+ isa = PBXNativeTarget;
255+ buildConfigurationList = E74244CF9426D49C14DD00DF /* Build configuration list for PBXNativeTarget "ParseableViewerTests" */;
256+ buildPhases = (
257+ F7A337D06360FBCA62555BF6 /* Sources */,
258+ );
259+ buildRules = (
260+ );
261+ dependencies = (
262+ C4A66B65B7BBF9C28D40C249 /* PBXTargetDependency */,
263+ );
264+ name = ParseableViewerTests;
265+ packageProductDependencies = (
266+ );
267+ productName = ParseableViewerTests;
268+ productReference = 81003B1CAEBB75C72076AE85 /* ParseableViewerTests.xctest */;
269+ productType = "com.apple.product-type.bundle.unit-test";
270+ };
271+/* End PBXNativeTarget section */
272+
273+/* Begin PBXProject section */
274+ AFB3E6B7F230B722DBCD4143 /* Project object */ = {
275+ isa = PBXProject;
276+ attributes = {
277+ BuildIndependentTargetsInParallel = YES;
278+ LastUpgradeCheck = 1620;
279+ TargetAttributes = {
280+ 2BFA29C07896DADE27FE8DF9 = {
281+ ProvisioningStyle = Manual;
282+ };
283+ };
284+ };
285+ buildConfigurationList = 91629F84D63DA522312E65D6 /* Build configuration list for PBXProject "ParseableViewer" */;
286+ compatibilityVersion = "Xcode 14.0";
287+ developmentRegion = en;
288+ hasScannedForEncodings = 0;
289+ knownRegions = (
290+ Base,
291+ en,
292+ );
293+ mainGroup = D06ED124AC4DFCB31F6EF0C1;
294+ minimizedProjectReferenceProxies = 1;
295+ preferredProjectObjectVersion = 77;
296+ projectDirPath = "";
297+ projectRoot = "";
298+ targets = (
299+ 2BFA29C07896DADE27FE8DF9 /* ParseableViewer */,
300+ E39BE21B6A9A7720B55CA1FB /* ParseableViewerTests */,
301+ );
302+ };
303+/* End PBXProject section */
304+
305+/* Begin PBXResourcesBuildPhase section */
306+ 27EFF17FAE6D34920BFD0BB2 /* Resources */ = {
307+ isa = PBXResourcesBuildPhase;
308+ buildActionMask = 2147483647;
309+ files = (
310+ 836F637D54951D3BC6A2AF11 /* Assets.xcassets in Resources */,
311+ );
312+ runOnlyForDeploymentPostprocessing = 0;
313+ };
314+/* End PBXResourcesBuildPhase section */
315+
316+/* Begin PBXSourcesBuildPhase section */
317+ E7518B0D053D4667ECBB99E3 /* Sources */ = {
318+ isa = PBXSourcesBuildPhase;
319+ buildActionMask = 2147483647;
320+ files = (
321+ B1BBABAB489838F84E670DD6 /* AlertModels.swift in Sources */,
322+ 4A269FCA596D52F688BB055A /* AlertsView.swift in Sources */,
323+ 15A0B57711D4B39118F09D93 /* AppState.swift in Sources */,
324+ 55A93DAF2D88AAB1F42B5E04 /* ConnectionSheet.swift in Sources */,
325+ 5033B2FE28A2EEB990CA2114 /* ConnectionStore.swift in Sources */,
326+ 1E07C534FCC62B7B5CC15DDB /* ContentView.swift in Sources */,
327+ CCA017BAFD0977B66B5CCBCD /* JSONValue.swift in Sources */,
328+ 2E3E0E23D3388B90955AD9A8 /* KeychainService.swift in Sources */,
329+ 0E0B747CF1B650BF70A7DC56 /* LiveTailView.swift in Sources */,
330+ 45286251E8F4CF0718E0595C /* LiveTailViewModel.swift in Sources */,
331+ 2C35EBC496AF8DAB215800F0 /* LogDetailView.swift in Sources */,
332+ 073AFD1207B61E46CA993E98 /* LogStream.swift in Sources */,
333+ 822B3C27B35EA429E522AFB4 /* LogTableView.swift in Sources */,
334+ 835943BF8C2C9AB9CA203B2F /* ParseableClient.swift in Sources */,
335+ F691C8702AF273C0B9D79963 /* ParseableFilter.swift in Sources */,
336+ 9C860DEEFE4446A152C18B62 /* ParseableViewerApp.swift in Sources */,
337+ 69485FFE5D25180B422D9230 /* QueryView.swift in Sources */,
338+ 5681C7459B58D71A83496890 /* QueryViewModel.swift in Sources */,
339+ 11A5740A00A2E29D4EC77B66 /* SQLAutoComplete.swift in Sources */,
340+ BA06B242DE817368D1BAD194 /* SQLEditorView.swift in Sources */,
341+ DE4DD2EB56F92820EE01CBE1 /* ServerConnection.swift in Sources */,
342+ 3F7EB642D3EBA91FCC962F15 /* ServerInfo.swift in Sources */,
343+ BAA005C4A067CB6EBA4294DE /* ServerInfoView.swift in Sources */,
344+ 70CC22F812F9151CBA042375 /* SettingsView.swift in Sources */,
345+ 7128E20D6592E907210BBF1A /* SidebarView.swift in Sources */,
346+ 47E1670726D4104D80F3F952 /* StreamDetailView.swift in Sources */,
347+ 4EBF4D4702F460A6AD962232 /* SyntaxHighlighting.swift in Sources */,
348+ 1DF7164680B3EAF1F5E4C47E /* TimeRangePicker.swift in Sources */,
349+ 308E024A853EDA08411FE6B0 /* UsersView.swift in Sources */,
350+ );
351+ runOnlyForDeploymentPostprocessing = 0;
352+ };
353+ F7A337D06360FBCA62555BF6 /* Sources */ = {
354+ isa = PBXSourcesBuildPhase;
355+ buildActionMask = 2147483647;
356+ files = (
357+ 3CDE9527F8C25B7C32571CB4 /* ConnectionStoreTests.swift in Sources */,
358+ F48FF7028BEB8C84234C018B /* JSONValueTests.swift in Sources */,
359+ 5BC9FA9419A918C16937E4D3 /* LiveTailViewModelTests.swift in Sources */,
360+ EF5ECFAAF8B2485D452021EB /* LogStreamTests.swift in Sources */,
361+ 62014FBBAD5AA68F2C768DEF /* LogTableHelperTests.swift in Sources */,
362+ F8F9730A83587099FF50E77B /* ParseableClientTests.swift in Sources */,
363+ D9C618815EEBE79EE7529EC2 /* QueryViewModelTests.swift in Sources */,
364+ 3FDB32277A679A14D1455A7D /* SQLAutoCompleteTests.swift in Sources */,
365+ BCEB51336ECF5827F888614E /* ServerConnectionTests.swift in Sources */,
366+ 507DF05FC7A223B2F7E701EF /* SyntaxHighlightingTests.swift in Sources */,
367+ );
368+ runOnlyForDeploymentPostprocessing = 0;
369+ };
370+/* End PBXSourcesBuildPhase section */
371+
372+/* Begin PBXTargetDependency section */
373+ C4A66B65B7BBF9C28D40C249 /* PBXTargetDependency */ = {
374+ isa = PBXTargetDependency;
375+ target = 2BFA29C07896DADE27FE8DF9 /* ParseableViewer */;
376+ targetProxy = 31032B334B864955687341D2 /* PBXContainerItemProxy */;
377+ };
378+/* End PBXTargetDependency section */
379+
380+/* Begin XCBuildConfiguration section */
381+ 07794B90ACFADCCF82C3DC80 /* Debug */ = {
382+ isa = XCBuildConfiguration;
383+ buildSettings = {
384+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
385+ CODE_SIGN_ENTITLEMENTS = ParseableViewer/ParseableViewer.entitlements;
386+ CODE_SIGN_IDENTITY = "-";
387+ CODE_SIGN_STYLE = Manual;
388+ COMBINE_HIDPI_IMAGES = YES;
389+ CURRENT_PROJECT_VERSION = 1;
390+ ENABLE_HARDENED_RUNTIME = YES;
391+ INFOPLIST_FILE = ParseableViewer/Info.plist;
392+ LD_RUNPATH_SEARCH_PATHS = (
393+ "$(inherited)",
394+ "@executable_path/../Frameworks",
395+ );
396+ MACOSX_DEPLOYMENT_TARGET = 14.0;
397+ MARKETING_VERSION = 1.0.0;
398+ PRODUCT_BUNDLE_IDENTIFIER = com.parseableviewer.app;
399+ PRODUCT_MODULE_NAME = ParseableViewer;
400+ PRODUCT_NAME = "Parseable Viewer";
401+ SDKROOT = macosx;
402+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
403+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
404+ SWIFT_VERSION = 5.9;
405+ };
406+ name = Debug;
407+ };
408+ 1E24A379628C0BC01BF229A8 /* Debug */ = {
409+ isa = XCBuildConfiguration;
410+ buildSettings = {
411+ ALWAYS_SEARCH_USER_PATHS = NO;
412+ CLANG_ANALYZER_NONNULL = YES;
413+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
414+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
415+ CLANG_CXX_LIBRARY = "libc++";
416+ CLANG_ENABLE_MODULES = YES;
417+ CLANG_ENABLE_OBJC_ARC = YES;
418+ CLANG_ENABLE_OBJC_WEAK = YES;
419+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
420+ CLANG_WARN_BOOL_CONVERSION = YES;
421+ CLANG_WARN_COMMA = YES;
422+ CLANG_WARN_CONSTANT_CONVERSION = YES;
423+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
424+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
425+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
426+ CLANG_WARN_EMPTY_BODY = YES;
427+ CLANG_WARN_ENUM_CONVERSION = YES;
428+ CLANG_WARN_INFINITE_RECURSION = YES;
429+ CLANG_WARN_INT_CONVERSION = YES;
430+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
431+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
432+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
433+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
434+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
435+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
436+ CLANG_WARN_STRICT_PROTOTYPES = YES;
437+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
438+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
439+ CLANG_WARN_UNREACHABLE_CODE = YES;
440+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
441+ COPY_PHASE_STRIP = NO;
442+ DEBUG_INFORMATION_FORMAT = dwarf;
443+ ENABLE_STRICT_OBJC_MSGSEND = YES;
444+ ENABLE_TESTABILITY = YES;
445+ GCC_C_LANGUAGE_STANDARD = gnu11;
446+ GCC_DYNAMIC_NO_PIC = NO;
447+ GCC_NO_COMMON_BLOCKS = YES;
448+ GCC_OPTIMIZATION_LEVEL = 0;
449+ GCC_PREPROCESSOR_DEFINITIONS = (
450+ "$(inherited)",
451+ "DEBUG=1",
452+ );
453+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
454+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
455+ GCC_WARN_UNDECLARED_SELECTOR = YES;
456+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
457+ GCC_WARN_UNUSED_FUNCTION = YES;
458+ GCC_WARN_UNUSED_VARIABLE = YES;
459+ MACOSX_DEPLOYMENT_TARGET = 14.0;
460+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
461+ MTL_FAST_MATH = YES;
462+ ONLY_ACTIVE_ARCH = YES;
463+ PRODUCT_NAME = "$(TARGET_NAME)";
464+ SDKROOT = macosx;
465+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
466+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
467+ SWIFT_VERSION = 5.0;
468+ };
469+ name = Debug;
470+ };
471+ 292D3E9E352934CD0271F677 /* Release */ = {
472+ isa = XCBuildConfiguration;
473+ buildSettings = {
474+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
475+ CODE_SIGN_ENTITLEMENTS = ParseableViewer/ParseableViewer.entitlements;
476+ CODE_SIGN_IDENTITY = "-";
477+ CODE_SIGN_STYLE = Manual;
478+ COMBINE_HIDPI_IMAGES = YES;
479+ CURRENT_PROJECT_VERSION = 1;
480+ ENABLE_HARDENED_RUNTIME = YES;
481+ INFOPLIST_FILE = ParseableViewer/Info.plist;
482+ LD_RUNPATH_SEARCH_PATHS = (
483+ "$(inherited)",
484+ "@executable_path/../Frameworks",
485+ );
486+ MACOSX_DEPLOYMENT_TARGET = 14.0;
487+ MARKETING_VERSION = 1.0.0;
488+ PRODUCT_BUNDLE_IDENTIFIER = com.parseableviewer.app;
489+ PRODUCT_MODULE_NAME = ParseableViewer;
490+ PRODUCT_NAME = "Parseable Viewer";
491+ SDKROOT = macosx;
492+ SWIFT_COMPILATION_MODE = wholemodule;
493+ SWIFT_OPTIMIZATION_LEVEL = "-O";
494+ SWIFT_VERSION = 5.9;
495+ };
496+ name = Release;
497+ };
498+ 5C0248843F0B397AF07C25EF /* Debug */ = {
499+ isa = XCBuildConfiguration;
500+ buildSettings = {
501+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Parseable Viewer.app/Contents/MacOS/Parseable Viewer";
502+ COMBINE_HIDPI_IMAGES = YES;
503+ GENERATE_INFOPLIST_FILE = YES;
504+ LD_RUNPATH_SEARCH_PATHS = (
505+ "$(inherited)",
506+ "@executable_path/../Frameworks",
507+ "@loader_path/../Frameworks",
508+ );
509+ MACOSX_DEPLOYMENT_TARGET = 14.0;
510+ PRODUCT_BUNDLE_IDENTIFIER = com.parseableviewer.ParseableViewerTests;
511+ SDKROOT = macosx;
512+ SWIFT_VERSION = 5.9;
513+ TEST_HOST = "$(BUNDLE_LOADER)";
514+ };
515+ name = Debug;
516+ };
517+ 8344458AE89EAC72B8A11105 /* Release */ = {
518+ isa = XCBuildConfiguration;
519+ buildSettings = {
520+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Parseable Viewer.app/Contents/MacOS/Parseable Viewer";
521+ COMBINE_HIDPI_IMAGES = YES;
522+ GENERATE_INFOPLIST_FILE = YES;
523+ LD_RUNPATH_SEARCH_PATHS = (
524+ "$(inherited)",
525+ "@executable_path/../Frameworks",
526+ "@loader_path/../Frameworks",
527+ );
528+ MACOSX_DEPLOYMENT_TARGET = 14.0;
529+ PRODUCT_BUNDLE_IDENTIFIER = com.parseableviewer.ParseableViewerTests;
530+ SDKROOT = macosx;
531+ SWIFT_VERSION = 5.9;
532+ TEST_HOST = "$(BUNDLE_LOADER)";
533+ };
534+ name = Release;
535+ };
536+ DE9928E6B896437015DE8F25 /* Release */ = {
537+ isa = XCBuildConfiguration;
538+ buildSettings = {
539+ ALWAYS_SEARCH_USER_PATHS = NO;
540+ CLANG_ANALYZER_NONNULL = YES;
541+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
542+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
543+ CLANG_CXX_LIBRARY = "libc++";
544+ CLANG_ENABLE_MODULES = YES;
545+ CLANG_ENABLE_OBJC_ARC = YES;
546+ CLANG_ENABLE_OBJC_WEAK = YES;
547+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
548+ CLANG_WARN_BOOL_CONVERSION = YES;
549+ CLANG_WARN_COMMA = YES;
550+ CLANG_WARN_CONSTANT_CONVERSION = YES;
551+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
552+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
553+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
554+ CLANG_WARN_EMPTY_BODY = YES;
555+ CLANG_WARN_ENUM_CONVERSION = YES;
556+ CLANG_WARN_INFINITE_RECURSION = YES;
557+ CLANG_WARN_INT_CONVERSION = YES;
558+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
559+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
560+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
561+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
562+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
563+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
564+ CLANG_WARN_STRICT_PROTOTYPES = YES;
565+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
566+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
567+ CLANG_WARN_UNREACHABLE_CODE = YES;
568+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
569+ COPY_PHASE_STRIP = NO;
570+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
571+ ENABLE_NS_ASSERTIONS = NO;
572+ ENABLE_STRICT_OBJC_MSGSEND = YES;
573+ GCC_C_LANGUAGE_STANDARD = gnu11;
574+ GCC_NO_COMMON_BLOCKS = YES;
575+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
576+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
577+ GCC_WARN_UNDECLARED_SELECTOR = YES;
578+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
579+ GCC_WARN_UNUSED_FUNCTION = YES;
580+ GCC_WARN_UNUSED_VARIABLE = YES;
581+ MACOSX_DEPLOYMENT_TARGET = 14.0;
582+ MTL_ENABLE_DEBUG_INFO = NO;
583+ MTL_FAST_MATH = YES;
584+ PRODUCT_NAME = "$(TARGET_NAME)";
585+ SDKROOT = macosx;
586+ SWIFT_COMPILATION_MODE = wholemodule;
587+ SWIFT_OPTIMIZATION_LEVEL = "-O";
588+ SWIFT_VERSION = 5.0;
589+ };
590+ name = Release;
591+ };
592+/* End XCBuildConfiguration section */
593+
594+/* Begin XCConfigurationList section */
595+ 91629F84D63DA522312E65D6 /* Build configuration list for PBXProject "ParseableViewer" */ = {
596+ isa = XCConfigurationList;
597+ buildConfigurations = (
598+ 1E24A379628C0BC01BF229A8 /* Debug */,
599+ DE9928E6B896437015DE8F25 /* Release */,
600+ );
601+ defaultConfigurationIsVisible = 0;
602+ defaultConfigurationName = Release;
603+ };
604+ 9AA32267150BB93C4ECA4508 /* Build configuration list for PBXNativeTarget "ParseableViewer" */ = {
605+ isa = XCConfigurationList;
606+ buildConfigurations = (
607+ 07794B90ACFADCCF82C3DC80 /* Debug */,
608+ 292D3E9E352934CD0271F677 /* Release */,
609+ );
610+ defaultConfigurationIsVisible = 0;
611+ defaultConfigurationName = Release;
612+ };
613+ E74244CF9426D49C14DD00DF /* Build configuration list for PBXNativeTarget "ParseableViewerTests" */ = {
614+ isa = XCConfigurationList;
615+ buildConfigurations = (
616+ 5C0248843F0B397AF07C25EF /* Debug */,
617+ 8344458AE89EAC72B8A11105 /* Release */,
618+ );
619+ defaultConfigurationIsVisible = 0;
620+ defaultConfigurationName = Release;
621+ };
622+/* End XCConfigurationList section */
623+ };
624+ rootObject = AFB3E6B7F230B722DBCD4143 /* Project object */;
625+}
ParseableViewer.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7
@@ -0,0 +1,7 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Workspace
3+ version = "1.0">
4+ <FileRef
5+ location = "self:">
6+ </FileRef>
7+</Workspace>
ParseableViewer.xcodeproj/xcshareddata/xcschemes/ParseableViewer.xcschemenew file+120
@@ -0,0 +1,120 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Scheme
3+ LastUpgradeVersion = "1620"
4+ version = "1.7">
5+ <BuildAction
6+ parallelizeBuildables = "YES"
7+ buildImplicitDependencies = "YES"
8+ runPostActionsOnFailure = "NO">
9+ <BuildActionEntries>
10+ <BuildActionEntry
11+ buildForTesting = "YES"
12+ buildForRunning = "YES"
13+ buildForProfiling = "YES"
14+ buildForArchiving = "YES"
15+ buildForAnalyzing = "YES">
16+ <BuildableReference
17+ BuildableIdentifier = "primary"
18+ BlueprintIdentifier = "2BFA29C07896DADE27FE8DF9"
19+ BuildableName = "ParseableViewer.app"
20+ BlueprintName = "ParseableViewer"
21+ ReferencedContainer = "container:ParseableViewer.xcodeproj">
22+ </BuildableReference>
23+ </BuildActionEntry>
24+ <BuildActionEntry
25+ buildForTesting = "YES"
26+ buildForRunning = "NO"
27+ buildForProfiling = "NO"
28+ buildForArchiving = "NO"
29+ buildForAnalyzing = "NO">
30+ <BuildableReference
31+ BuildableIdentifier = "primary"
32+ BlueprintIdentifier = "E39BE21B6A9A7720B55CA1FB"
33+ BuildableName = "ParseableViewerTests.xctest"
34+ BlueprintName = "ParseableViewerTests"
35+ ReferencedContainer = "container:ParseableViewer.xcodeproj">
36+ </BuildableReference>
37+ </BuildActionEntry>
38+ </BuildActionEntries>
39+ </BuildAction>
40+ <TestAction
41+ buildConfiguration = "Debug"
42+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
43+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
44+ shouldUseLaunchSchemeArgsEnv = "YES"
45+ onlyGenerateCoverageForSpecifiedTargets = "NO">
46+ <MacroExpansion>
47+ <BuildableReference
48+ BuildableIdentifier = "primary"
49+ BlueprintIdentifier = "2BFA29C07896DADE27FE8DF9"
50+ BuildableName = "ParseableViewer.app"
51+ BlueprintName = "ParseableViewer"
52+ ReferencedContainer = "container:ParseableViewer.xcodeproj">
53+ </BuildableReference>
54+ </MacroExpansion>
55+ <Testables>
56+ <TestableReference
57+ skipped = "NO"
58+ parallelizable = "NO">
59+ <BuildableReference
60+ BuildableIdentifier = "primary"
61+ BlueprintIdentifier = "E39BE21B6A9A7720B55CA1FB"
62+ BuildableName = "ParseableViewerTests.xctest"
63+ BlueprintName = "ParseableViewerTests"
64+ ReferencedContainer = "container:ParseableViewer.xcodeproj">
65+ </BuildableReference>
66+ </TestableReference>
67+ </Testables>
68+ <CommandLineArguments>
69+ </CommandLineArguments>
70+ </TestAction>
71+ <LaunchAction
72+ buildConfiguration = "Debug"
73+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
74+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
75+ launchStyle = "0"
76+ useCustomWorkingDirectory = "NO"
77+ ignoresPersistentStateOnLaunch = "NO"
78+ debugDocumentVersioning = "YES"
79+ debugServiceExtension = "internal"
80+ allowLocationSimulation = "YES">
81+ <BuildableProductRunnable
82+ runnableDebuggingMode = "0">
83+ <BuildableReference
84+ BuildableIdentifier = "primary"
85+ BlueprintIdentifier = "2BFA29C07896DADE27FE8DF9"
86+ BuildableName = "ParseableViewer.app"
87+ BlueprintName = "ParseableViewer"
88+ ReferencedContainer = "container:ParseableViewer.xcodeproj">
89+ </BuildableReference>
90+ </BuildableProductRunnable>
91+ <CommandLineArguments>
92+ </CommandLineArguments>
93+ </LaunchAction>
94+ <ProfileAction
95+ buildConfiguration = "Release"
96+ shouldUseLaunchSchemeArgsEnv = "YES"
97+ savedToolIdentifier = ""
98+ useCustomWorkingDirectory = "NO"
99+ debugDocumentVersioning = "YES">
100+ <BuildableProductRunnable
101+ runnableDebuggingMode = "0">
102+ <BuildableReference
103+ BuildableIdentifier = "primary"
104+ BlueprintIdentifier = "2BFA29C07896DADE27FE8DF9"
105+ BuildableName = "ParseableViewer.app"
106+ BlueprintName = "ParseableViewer"
107+ ReferencedContainer = "container:ParseableViewer.xcodeproj">
108+ </BuildableReference>
109+ </BuildableProductRunnable>
110+ <CommandLineArguments>
111+ </CommandLineArguments>
112+ </ProfileAction>
113+ <AnalyzeAction
114+ buildConfiguration = "Debug">
115+ </AnalyzeAction>
116+ <ArchiveAction
117+ buildConfiguration = "Release"
118+ revealArchiveInOrganizer = "YES">
119+ </ArchiveAction>
120+</Scheme>
ParseableViewer/App/AppState.swift+33−19
@@ -32,10 +32,11 @@ final class AppState {
32 var errorMessage: String?
32 var errorMessage: String?
33 var showError = false
33 var showError = false
34
34
35− // MARK: - Saved Queries
35+ // MARK: - Filters
36− var savedQueries: [SavedQuery] = []
36+ var filters: [ParseableFilter] = []
37+ var isLoadingFilters = false
37− /// Set by sidebar when user clicks a saved query; consumed by QueryView.
38+ /// Set by sidebar when user clicks a filter; consumed by QueryView.
38− var pendingSavedQuery: SavedQuery?
39+ var pendingFilterSQL: String?
39
40
40 // MARK: - Query Refresh
41 // MARK: - Query Refresh
41 /// Incremented by the Cmd+R shortcut; QueryView observes changes to re-execute the current query.
42 /// Incremented by the Cmd+R shortcut; QueryView observes changes to re-execute the current query.
@@ -76,7 +77,6 @@ final class AppState {
76
77
77 init() {
78 init() {
78 connections = ConnectionStore.loadConnections()
79 connections = ConnectionStore.loadConnections()
79− savedQueries = SavedQueryStore.load()
80
80
81 networkMonitor.pathUpdateHandler = { [weak self] path in
81 networkMonitor.pathUpdateHandler = { [weak self] path in
82 Task { @MainActor [weak self] in
82 Task { @MainActor [weak self] in
@@ -127,11 +127,13 @@ final class AppState {
127
127
128 ConnectionStore.saveActiveConnectionID(connection.id)
128 ConnectionStore.saveActiveConnectionID(connection.id)
129
129
130− // Load server info and streams (best-effort; don't fail the connection)
130+ // Load server info, streams, and filters (best-effort; don't fail the connection)
131 async let about = newClient.getAbout()
131 async let about = newClient.getAbout()
132 async let streamList = newClient.listStreams()
132 async let streamList = newClient.listStreams()
133+ async let filterList = newClient.listFilters()
133
134
134 self.serverAbout = try? await about
135 self.serverAbout = try? await about
136+ self.filters = (try? await filterList) ?? []
135 do {
137 do {
136 self.streams = try await streamList
138 self.streams = try await streamList
137 self.streamLoadError = nil
139 self.streamLoadError = nil
@@ -165,6 +167,7 @@ final class AppState {
165 streams = []
167 streams = []
166 selectedStream = nil
168 selectedStream = nil
167 serverAbout = nil
169 serverAbout = nil
170+ filters = []
168 ConnectionStore.saveActiveConnectionID(nil)
171 ConnectionStore.saveActiveConnectionID(nil)
169 }
172 }
170
173
@@ -226,27 +229,38 @@ final class AppState {
226 if selectedStream == name {
229 if selectedStream == name {
227 selectedStream = nil
230 selectedStream = nil
228 }
231 }
229− // Remove saved queries that referenced the deleted stream
232+ await refreshFilters()
230− let orphaned = savedQueries.filter { $0.stream == name }
231− if !orphaned.isEmpty {
232− savedQueries.removeAll { $0.stream == name }
233− SavedQueryStore.save(savedQueries)
234− }
235 await refreshStreams()
233 await refreshStreams()
236 }
234 }
237
235
238− // MARK: - Saved Queries
236+ // MARK: - Filters
239
237
240 @MainActor
238 @MainActor
241− func addSavedQuery(_ query: SavedQuery) {
239+ func refreshFilters() async {
242− savedQueries.append(query)
240+ guard let client else { return }
243− SavedQueryStore.save(savedQueries)
241+ isLoadingFilters = true
242+ filters = (try? await client.listFilters()) ?? []
243+ isLoadingFilters = false
244 }
244 }
245
245
246 @MainActor
246 @MainActor
247− func removeSavedQuery(_ query: SavedQuery) {
247+ func saveFilter(name: String, sql: String, stream: String) async throws {
248+ guard let client else { throw ParseableError.notConnected }
249+ let filter = ParseableFilter(
250+ filterName: name,
251+ streamName: stream,
252+ query: FilterQuery(filterType: "sql", filterQuery: sql)
253+ )
254+ let saved = try await client.createFilter(filter)
255+ filters.append(saved)
256+ }
257+
258+ @MainActor
259+ func deleteFilter(_ filter: ParseableFilter) async throws {
260+ guard let client else { throw ParseableError.notConnected }
261+ guard let filterId = filter.filterId else { return }
262+ try await client.deleteFilter(id: filterId)
248− savedQueries.removeAll { $0.id == query.id }
263+ filters.removeAll { $0.id == filter.id }
249− SavedQueryStore.save(savedQueries)
250 }
264 }
251
265
252 // MARK: - Error Handling
266 // MARK: - Error Handling
ParseableViewer/Models/ParseableFilter.swiftnew file+79
@@ -0,0 +1,79 @@
1+import Foundation
2+
3+struct FilterQuery: Codable, Hashable, Sendable {
4+ var filterType: String
5+ var filterQuery: String?
6+ var filterBuilder: JSONValue?
7+
8+ enum CodingKeys: String, CodingKey {
9+ case filterType = "filter_type"
10+ case filterQuery = "filter_query"
11+ case filterBuilder = "filter_builder"
12+ }
13+
14+ init(filterType: String = "sql", filterQuery: String? = nil, filterBuilder: JSONValue? = nil) {
15+ self.filterType = filterType
16+ self.filterQuery = filterQuery
17+ self.filterBuilder = filterBuilder
18+ }
19+
20+ init(from decoder: Decoder) throws {
21+ let container = try decoder.container(keyedBy: CodingKeys.self)
22+ filterType = try container.decode(String.self, forKey: .filterType)
23+ filterQuery = try? container.decode(String.self, forKey: .filterQuery)
24+ filterBuilder = try? container.decode(JSONValue.self, forKey: .filterBuilder)
25+ }
26+}
27+
28+struct ParseableFilter: Identifiable, Codable, Hashable, Sendable {
29+ var filterId: String?
30+ var filterName: String
31+ var streamName: String
32+ var query: FilterQuery
33+ var version: String?
34+ var userId: String?
35+ var timeFilter: JSONValue?
36+
37+ var id: String {
38+ filterId ?? "\(streamName):\(filterName)"
39+ }
40+
41+ enum CodingKeys: String, CodingKey {
42+ case filterId = "filter_id"
43+ case filterName = "filter_name"
44+ case streamName = "stream_name"
45+ case query
46+ case version
47+ case userId = "user_id"
48+ case timeFilter = "time_filter"
49+ }
50+
51+ init(
52+ filterId: String? = nil,
53+ filterName: String,
54+ streamName: String,
55+ query: FilterQuery,
56+ version: String? = nil,
57+ userId: String? = nil,
58+ timeFilter: JSONValue? = nil
59+ ) {
60+ self.filterId = filterId
61+ self.filterName = filterName
62+ self.streamName = streamName
63+ self.query = query
64+ self.version = version
65+ self.userId = userId
66+ self.timeFilter = timeFilter
67+ }
68+
69+ init(from decoder: Decoder) throws {
70+ let container = try decoder.container(keyedBy: CodingKeys.self)
71+ filterId = try? container.decode(String.self, forKey: .filterId)
72+ filterName = try container.decode(String.self, forKey: .filterName)
73+ streamName = try container.decode(String.self, forKey: .streamName)
74+ query = try container.decode(FilterQuery.self, forKey: .query)
75+ version = try? container.decode(String.self, forKey: .version)
76+ userId = try? container.decode(String.self, forKey: .userId)
77+ timeFilter = try? container.decode(JSONValue.self, forKey: .timeFilter)
78+ }
79+}
ParseableViewer/Services/ConnectionStore.swift−39
@@ -39,42 +39,3 @@ final class ConnectionStore {
39 UserDefaults.standard.set(id?.uuidString, forKey: activeConnectionKey)
39 UserDefaults.standard.set(id?.uuidString, forKey: activeConnectionKey)
40 }
40 }
41}
41}
42−
43−final class SavedQueryStore {
44− private static let storageKey = "parseable_saved_queries"
45−
46− static func load() -> [SavedQuery] {
47− guard let data = UserDefaults.standard.data(forKey: storageKey) else {
48− return []
49− }
50− return (try? JSONDecoder().decode([SavedQuery].self, from: data)) ?? []
51− }
52−
53− static func save(_ queries: [SavedQuery]) {
54− if let data = try? JSONEncoder().encode(queries) {
55− UserDefaults.standard.set(data, forKey: storageKey)
56− }
57− }
58−}
59−
60−struct SavedQuery: Identifiable, Codable, Hashable, Sendable {
61− var id: UUID
62− var name: String
63− var sql: String
64− var stream: String
65− var createdAt: Date
66− var columnOrder: [String]?
67− var hiddenColumns: Set<String>?
68−
69− init(id: UUID = UUID(), name: String, sql: String, stream: String,
70− columnOrder: [String]? = nil, hiddenColumns: Set<String>? = nil,
71− createdAt: Date = Date()) {
72− self.id = id
73− self.name = name
74− self.sql = sql
75− self.stream = stream
76− self.columnOrder = columnOrder
77− self.hiddenColumns = hiddenColumns
78− self.createdAt = createdAt
79− }
80−}
ParseableViewer/Services/ParseableClient.swift+19
@@ -281,4 +281,23 @@ final class ParseableClient: Sendable {
281 let data = try await performRequest(method: "GET", path: "/api/v1/user")
281 let data = try await performRequest(method: "GET", path: "/api/v1/user")
282 return try JSONDecoder().decode([UserInfo].self, from: data)
282 return try JSONDecoder().decode([UserInfo].self, from: data)
283 }
283 }
284+
285+ // MARK: - Filters
286+
287+ func listFilters() async throws -> [ParseableFilter] {
288+ let data = try await performRequest(method: "GET", path: "/api/v1/filters")
289+ if data.isEmpty { return [] }
290+ return try JSONDecoder().decode([ParseableFilter].self, from: data)
291+ }
292+
293+ func createFilter(_ filter: ParseableFilter) async throws -> ParseableFilter {
294+ let body = try JSONEncoder().encode(filter)
295+ let data = try await performRequest(method: "POST", path: "/api/v1/filters", body: body)
296+ return try JSONDecoder().decode(ParseableFilter.self, from: data)
297+ }
298+
299+ func deleteFilter(id: String) async throws {
300+ let encoded = try Self.encodePathComponent(id)
301+ _ = try await performRequest(method: "DELETE", path: "/api/v1/filters/\(encoded)")
302+ }
284}
303}
ParseableViewer/Views/QueryView.swift+40−32
@@ -3,8 +3,10 @@ import SwiftUI
3struct QueryView: View {
3struct QueryView: View {
4 @Environment(AppState.self) private var appState
4 @Environment(AppState.self) private var appState
5 @State private var viewModel = QueryViewModel()
5 @State private var viewModel = QueryViewModel()
6− @State private var showSaveQuerySheet = false
6+ @State private var showSaveFilterSheet = false
7− @State private var saveQueryName = ""
7+ @State private var saveFilterName = ""
8+ @State private var isSavingFilter = false
9+ @State private var saveFilterError: String?
8 @State private var exportError: String?
10 @State private var exportError: String?
9 @State private var showExportError = false
11 @State private var showExportError = false
10 @State private var showColumnPopover = false
12 @State private var showColumnPopover = false
@@ -64,13 +66,13 @@ struct QueryView: View {
64 }
66 }
65
67
66 Button {
68 Button {
67− showSaveQuerySheet = true
69+ showSaveFilterSheet = true
68 } label: {
70 } label: {
69 Text("Save view")
71 Text("Save view")
70 }
72 }
71− .help("Save query")
73+ .help("Save filter")
72 .disabled(viewModel.sqlQuery.isEmpty)
74 .disabled(viewModel.sqlQuery.isEmpty)
73− .accessibilityLabel("Save view")
75+ .accessibilityLabel("Save filter")
74
76
75 Button {
77 Button {
76 exportResults()
78 exportResults()
@@ -305,16 +307,10 @@ struct QueryView: View {
305 )
307 )
306 }
308 }
307 }
309 }
308− .onChange(of: appState.pendingSavedQuery) { _, newQuery in
310+ .onChange(of: appState.pendingFilterSQL) { _, newSQL in
309− if let query = newQuery {
311+ if let sql = newSQL {
310− viewModel.sqlQuery = query.sql
312+ viewModel.sqlQuery = sql
311− if let order = query.columnOrder {
312− viewModel.pendingColumnConfig = QueryViewModel.ColumnConfiguration(
313− order: order,
314− hidden: query.hiddenColumns ?? []
315− )
316− }
317− appState.pendingSavedQuery = nil
313+ appState.pendingFilterSQL = nil
318 }
314 }
319 }
315 }
320 .alert("Export Error", isPresented: $showExportError) {
316 .alert("Export Error", isPresented: $showExportError) {
@@ -322,30 +318,42 @@ struct QueryView: View {
322 } message: {
318 } message: {
323 Text(exportError ?? "Unknown error")
319 Text(exportError ?? "Unknown error")
324 }
320 }
325− .sheet(isPresented: $showSaveQuerySheet) {
321+ .sheet(isPresented: $showSaveFilterSheet) {
326 VStack(spacing: 16) {
322 VStack(spacing: 16) {
327− Text("Save Query")
323+ Text("Save Filter")
328 .font(.headline)
324 .font(.headline)
329− TextField("Query name", text: $saveQueryName)
325+ TextField("Filter name", text: $saveFilterName)
330 .textFieldStyle(.roundedBorder)
326 .textFieldStyle(.roundedBorder)
327+ if let error = saveFilterError {
328+ Text(error)
329+ .font(.caption)
330+ .foregroundStyle(.red)
331+ }
331 HStack {
332 HStack {
332− Button("Cancel") { showSaveQuerySheet = false }
333+ Button("Cancel") {
334+ saveFilterError = nil
335+ showSaveFilterSheet = false
336+ }
333 Spacer()
337 Spacer()
334 Button("Save") {
338 Button("Save") {
335− let colOrder = viewModel.columnOrder.isEmpty ? nil : viewModel.columnOrder
339+ isSavingFilter = true
336− let hidden = viewModel.hiddenColumns.isEmpty ? nil : viewModel.hiddenColumns
340+ saveFilterError = nil
337− let query = SavedQuery(
341+ Task {
338− name: saveQueryName,
342+ do {
343+ try await appState.saveFilter(
344+ name: saveFilterName,
339− sql: viewModel.sqlQuery,
345+ sql: viewModel.sqlQuery,
340− stream: appState.selectedStream ?? "",
346+ stream: appState.selectedStream ?? ""
341− columnOrder: colOrder,
347+ )
342− hiddenColumns: hidden
348+ saveFilterName = ""
343− )
349+ showSaveFilterSheet = false
344− appState.addSavedQuery(query)
350+ } catch {
345− saveQueryName = ""
351+ saveFilterError = ParseableError.userFriendlyMessage(for: error)
346− showSaveQuerySheet = false
352+ }
353+ isSavingFilter = false
354+ }
347 }
355 }
348− .disabled(saveQueryName.isEmpty)
356+ .disabled(saveFilterName.isEmpty || isSavingFilter)
349 .buttonStyle(.borderedProminent)
357 .buttonStyle(.borderedProminent)
350 }
358 }
351 }
359 }
ParseableViewer/Views/SidebarView.swift+38−13
@@ -81,10 +81,10 @@ struct SidebarView: View {
81 }
81 }
82 }
82 }
83
83
84− if !appState.savedQueries.isEmpty {
84+ if !appState.filters.isEmpty {
85− Section("Saved Queries") {
85+ Section("Filters") {
86− ForEach(appState.savedQueries) { query in
86+ ForEach(appState.filters) { filter in
87− SavedQueryRow(query: query)
87+ FilterRow(filter: filter)
88 }
88 }
89 }
89 }
90 }
90 }
@@ -265,32 +265,47 @@ struct StreamRowView: View {
265 }
265 }
266}
266}
267
267
268−struct SavedQueryRow: View {
268+struct FilterRow: View {
269 @Environment(AppState.self) private var appState
269 @Environment(AppState.self) private var appState
270− let query: SavedQuery
270+ let filter: ParseableFilter
271+ @State private var isDeleting = false
271
272
272 private var streamExists: Bool {
273 private var streamExists: Bool {
273− appState.streams.contains { $0.name == query.stream }
274+ appState.streams.contains { $0.name == filter.streamName }
275+ }
276+
277+ private var isSQLFilter: Bool {
278+ filter.query.filterType == "sql"
274 }
279 }
275
280
276 var body: some View {
281 var body: some View {
277 Button {
282 Button {
278− appState.selectedStream = query.stream
283+ appState.selectedStream = filter.streamName
279− appState.pendingSavedQuery = query
284+ if let sql = filter.query.filterQuery {
285+ appState.pendingFilterSQL = sql
286+ }
280 appState.currentTab = .query
287 appState.currentTab = .query
281 } label: {
288 } label: {
282 HStack {
289 HStack {
283 Image(systemName: "bookmark")
290 Image(systemName: "bookmark")
284 .foregroundStyle(streamExists ? .orange : .secondary)
291 .foregroundStyle(streamExists ? .orange : .secondary)
285 VStack(alignment: .leading) {
292 VStack(alignment: .leading) {
286− Text(query.name)
293+ Text(filter.filterName)
287 .lineLimit(1)
294 .lineLimit(1)
288 HStack(spacing: 4) {
295 HStack(spacing: 4) {
289− Text(query.stream)
296+ Text(filter.streamName)
297+ if !isSQLFilter {
298+ Text(filter.query.filterType)
299+ .font(.caption2)
300+ .padding(.horizontal, 4)
301+ .padding(.vertical, 1)
302+ .background(.quaternary)
303+ .clipShape(RoundedRectangle(cornerRadius: 3))
304+ }
290 if !streamExists {
305 if !streamExists {
291 Image(systemName: "exclamationmark.triangle.fill")
306 Image(systemName: "exclamationmark.triangle.fill")
292 .foregroundStyle(.orange)
307 .foregroundStyle(.orange)
293− .help("Stream \"\(query.stream)\" no longer exists")
308+ .help("Stream \"\(filter.streamName)\" no longer exists")
294 }
309 }
295 }
310 }
296 .font(.caption2)
311 .font(.caption2)
@@ -301,8 +316,18 @@ struct SavedQueryRow: View {
301 .buttonStyle(.plain)
316 .buttonStyle(.plain)
302 .contextMenu {
317 .contextMenu {
303 Button("Delete", role: .destructive) {
318 Button("Delete", role: .destructive) {
304− appState.removeSavedQuery(query)
319+ guard !isDeleting else { return }
320+ isDeleting = true
321+ Task {
322+ do {
323+ try await appState.deleteFilter(filter)
324+ } catch {
325+ appState.showErrorMessage(ParseableError.userFriendlyMessage(for: error))
326+ }
327+ isDeleting = false
328+ }
305 }
329 }
330+ .disabled(isDeleting)
306 }
331 }
307 }
332 }
308}
333}
ParseableViewerTests/ConnectionStoreTests.swift−75
@@ -9,7 +9,6 @@ final class ConnectionStoreTests: XCTestCase {
9 // Clean up any test data from UserDefaults
9 // Clean up any test data from UserDefaults
10 UserDefaults.standard.removeObject(forKey: "parseable_connections")
10 UserDefaults.standard.removeObject(forKey: "parseable_connections")
11 UserDefaults.standard.removeObject(forKey: "parseable_active_connection_id")
11 UserDefaults.standard.removeObject(forKey: "parseable_active_connection_id")
12− UserDefaults.standard.removeObject(forKey: "parseable_saved_queries")
13 }
12 }
14
13
15 // MARK: - Active connection ID
14 // MARK: - Active connection ID
@@ -83,78 +82,4 @@ final class ConnectionStoreTests: XCTestCase {
83 }
82 }
84 }
83 }
85
84
86− // MARK: - SavedQueryStore
87−
88− func testSavedQueryStoreLoadEmpty() {
89− UserDefaults.standard.removeObject(forKey: "parseable_saved_queries")
90− let queries = SavedQueryStore.load()
91− XCTAssertTrue(queries.isEmpty)
92− }
93−
94− func testSavedQueryStoreRoundTrip() {
95− let query = SavedQuery(
96− name: "Recent Errors",
97− sql: "SELECT * FROM logs WHERE level = 'error' LIMIT 100",
98− stream: "backend-logs"
99− )
100− SavedQueryStore.save([query])
101− let loaded = SavedQueryStore.load()
102− XCTAssertEqual(loaded.count, 1)
103− XCTAssertEqual(loaded[0].name, "Recent Errors")
104− XCTAssertEqual(loaded[0].sql, "SELECT * FROM logs WHERE level = 'error' LIMIT 100")
105− XCTAssertEqual(loaded[0].stream, "backend-logs")
106− }
107−
108− func testSavedQueryStoreRoundTripWithColumnConfig() {
109− let query = SavedQuery(
110− name: "With Columns",
111− sql: "SELECT * FROM logs",
112− stream: "backend-logs",
113− columnOrder: ["level", "message", "p_timestamp"],
114− hiddenColumns: ["p_metadata"]
115− )
116− SavedQueryStore.save([query])
117− let loaded = SavedQueryStore.load()
118− XCTAssertEqual(loaded.count, 1)
119− XCTAssertEqual(loaded[0].columnOrder, ["level", "message", "p_timestamp"])
120− XCTAssertEqual(loaded[0].hiddenColumns, ["p_metadata"])
121− }
122−
123− func testSavedQueryStoreBackwardCompatibility() {
124− // Simulate a saved query without column fields (pre-feature data)
125− let json = """
126− [{"id":"00000000-0000-0000-0000-000000000001","name":"Old Query","sql":"SELECT 1","stream":"s","createdAt":0}]
127− """
128− UserDefaults.standard.set(json.data(using: .utf8), forKey: "parseable_saved_queries")
129− let loaded = SavedQueryStore.load()
130− XCTAssertEqual(loaded.count, 1)
131− XCTAssertEqual(loaded[0].name, "Old Query")
132− XCTAssertNil(loaded[0].columnOrder)
133− XCTAssertNil(loaded[0].hiddenColumns)
134− }
135−
136− func testSavedQueryStoreMultiple() {
137− let q1 = SavedQuery(name: "Query 1", sql: "SELECT 1", stream: "s1")
138− let q2 = SavedQuery(name: "Query 2", sql: "SELECT 2", stream: "s2")
139− SavedQueryStore.save([q1, q2])
140− let loaded = SavedQueryStore.load()
141− XCTAssertEqual(loaded.count, 2)
142− }
143−
144− // MARK: - SavedQuery model
145−
146− func testSavedQueryIdentifiable() {
147− let q1 = SavedQuery(name: "A", sql: "SELECT 1", stream: "s")
148− let q2 = SavedQuery(name: "B", sql: "SELECT 2", stream: "s")
149− XCTAssertNotEqual(q1.id, q2.id)
150− }
151−
152− func testSavedQueryHashable() {
153− let q1 = SavedQuery(name: "A", sql: "SELECT 1", stream: "s")
154− var set = Set<SavedQuery>()
155− set.insert(q1)
156− XCTAssertEqual(set.count, 1)
157− set.insert(q1) // Same query again
158− XCTAssertEqual(set.count, 1)
159− }
160}
85}
ParseableViewerTests/ParseableClientTests.swift+125
@@ -477,6 +477,131 @@ final class ParseableClientTests: XCTestCase {
477 XCTAssertTrue(error?.lowercased().contains("empty") ?? false)
477 XCTAssertTrue(error?.lowercased().contains("empty") ?? false)
478 }
478 }
479
479
480+ // MARK: - ParseableFilter decoding
481+
482+ func testFilterFullResponse() throws {
483+ let json = """
484+ {
485+ "filter_id": "abc-123",
486+ "filter_name": "Error logs",
487+ "stream_name": "backend",
488+ "query": {"filter_type": "sql", "filter_query": "SELECT * FROM backend WHERE level = 'error'"},
489+ "version": "v1",
490+ "user_id": "admin",
491+ "time_filter": {"start": "2024-01-01T00:00:00Z", "end": "2024-01-02T00:00:00Z"}
492+ }
493+ """.data(using: .utf8)!
494+
495+ let filter = try JSONDecoder().decode(ParseableFilter.self, from: json)
496+ XCTAssertEqual(filter.filterId, "abc-123")
497+ XCTAssertEqual(filter.filterName, "Error logs")
498+ XCTAssertEqual(filter.streamName, "backend")
499+ XCTAssertEqual(filter.query.filterType, "sql")
500+ XCTAssertEqual(filter.query.filterQuery, "SELECT * FROM backend WHERE level = 'error'")
501+ XCTAssertEqual(filter.version, "v1")
502+ XCTAssertEqual(filter.userId, "admin")
503+ XCTAssertNotNil(filter.timeFilter)
504+ }
505+
506+ func testFilterMinimalFields() throws {
507+ let json = """
508+ {
509+ "filter_name": "Quick query",
510+ "stream_name": "logs",
511+ "query": {"filter_type": "sql"}
512+ }
513+ """.data(using: .utf8)!
514+
515+ let filter = try JSONDecoder().decode(ParseableFilter.self, from: json)
516+ XCTAssertNil(filter.filterId)
517+ XCTAssertEqual(filter.filterName, "Quick query")
518+ XCTAssertEqual(filter.streamName, "logs")
519+ XCTAssertEqual(filter.query.filterType, "sql")
520+ XCTAssertNil(filter.query.filterQuery)
521+ XCTAssertNil(filter.version)
522+ XCTAssertNil(filter.userId)
523+ XCTAssertNil(filter.timeFilter)
524+ }
525+
526+ func testFilterArrayDecoding() throws {
527+ let json = """
528+ [
529+ {"filter_name": "A", "stream_name": "s1", "query": {"filter_type": "sql"}},
530+ {"filter_name": "B", "stream_name": "s2", "query": {"filter_type": "filter"}}
531+ ]
532+ """.data(using: .utf8)!
533+
534+ let filters = try JSONDecoder().decode([ParseableFilter].self, from: json)
535+ XCTAssertEqual(filters.count, 2)
536+ XCTAssertEqual(filters[0].filterName, "A")
537+ XCTAssertEqual(filters[1].query.filterType, "filter")
538+ }
539+
540+ func testFilterEncodingForCreate() throws {
541+ let filter = ParseableFilter(
542+ filterName: "My filter",
543+ streamName: "app-logs",
544+ query: FilterQuery(filterType: "sql", filterQuery: "SELECT * FROM \"app-logs\"")
545+ )
546+ let data = try JSONEncoder().encode(filter)
547+ let dict = try JSONSerialization.jsonObject(with: data) as! [String: Any]
548+ XCTAssertEqual(dict["filter_name"] as? String, "My filter")
549+ XCTAssertEqual(dict["stream_name"] as? String, "app-logs")
550+ let query = dict["query"] as! [String: Any]
551+ XCTAssertEqual(query["filter_type"] as? String, "sql")
552+ XCTAssertEqual(query["filter_query"] as? String, "SELECT * FROM \"app-logs\"")
553+ }
554+
555+ func testFilterUnknownFieldsTolerated() throws {
556+ let json = """
557+ {
558+ "filter_id": "xyz",
559+ "filter_name": "Test",
560+ "stream_name": "logs",
561+ "query": {"filter_type": "sql", "filter_query": "SELECT 1", "some_new_field": true},
562+ "brand_new_field": 42
563+ }
564+ """.data(using: .utf8)!
565+
566+ let filter = try JSONDecoder().decode(ParseableFilter.self, from: json)
567+ XCTAssertEqual(filter.filterId, "xyz")
568+ XCTAssertEqual(filter.filterName, "Test")
569+ }
570+
571+ func testFilterQueryWithBuilder() throws {
572+ let json = """
573+ {
574+ "filter_type": "filter",
575+ "filter_query": null,
576+ "filter_builder": {"rules": [{"field": "level", "operator": "=", "value": "error"}]}
577+ }
578+ """.data(using: .utf8)!
579+
580+ let query = try JSONDecoder().decode(FilterQuery.self, from: json)
581+ XCTAssertEqual(query.filterType, "filter")
582+ XCTAssertNil(query.filterQuery)
583+ XCTAssertNotNil(query.filterBuilder)
584+ }
585+
586+ func testFilterIdentifiableWithId() {
587+ let filter = ParseableFilter(
588+ filterId: "server-id-1",
589+ filterName: "Test",
590+ streamName: "logs",
591+ query: FilterQuery(filterType: "sql")
592+ )
593+ XCTAssertEqual(filter.id, "server-id-1")
594+ }
595+
596+ func testFilterIdentifiableFallback() {
597+ let filter = ParseableFilter(
598+ filterName: "Test",
599+ streamName: "logs",
600+ query: FilterQuery(filterType: "sql")
601+ )
602+ XCTAssertEqual(filter.id, "logs:Test")
603+ }
604+
480 // MARK: - URL scheme validation
605 // MARK: - URL scheme validation
481
606
482 func testValidateURLAcceptsHTTPS() {
607 func testValidateURLAcceptsHTTPS() {
project.yml+1
@@ -51,6 +51,7 @@ targets:
51 base:
51 base:
52 SWIFT_VERSION: "5.9"
52 SWIFT_VERSION: "5.9"
53 MACOSX_DEPLOYMENT_TARGET: "14.0"
53 MACOSX_DEPLOYMENT_TARGET: "14.0"
54+ GENERATE_INFOPLIST_FILE: YES
54 BUNDLE_LOADER: "$(BUILT_PRODUCTS_DIR)/Parseable Viewer.app/Contents/MacOS/Parseable Viewer"
55 BUNDLE_LOADER: "$(BUILT_PRODUCTS_DIR)/Parseable Viewer.app/Contents/MacOS/Parseable Viewer"
55 TEST_HOST: "$(BUNDLE_LOADER)"
56 TEST_HOST: "$(BUNDLE_LOADER)"
56 dependencies:
57 dependencies: