Merge lp:~dude-from-by/openlp/my-ios-remote into lp:~danielborges93/openlp/ios-remote
- my-ios-remote
- Merge into ios-remote
Proposed by
andrei
Status: | Needs review |
---|---|
Proposed branch: | lp:~dude-from-by/openlp/my-ios-remote |
Merge into: | lp:~danielborges93/openlp/ios-remote |
Diff against target: |
588 lines (+295/-48) 10 files modified
OLP RemoteTests/SettingsViewModelTests.swift (+77/-0) Podfile (+1/-0) Podfile.lock (+4/-1) Remote.xcodeproj/project.pbxproj (+14/-0) Remote/AppDelegate.swift (+11/-1) Remote/Classes/Controller/Settings/SettingsControllerPresenter.swift (+2/-4) Remote/Classes/Controller/Settings/SettingsViewController.swift (+24/-40) Remote/Classes/Controller/Settings/SettingsViewModel.swift (+73/-0) Remote/Classes/Network/Service.swift (+4/-2) Remote/Classes/Util/CountlyClientSideUserPaths.swift (+85/-0) |
To merge this branch: | bzr merge lp:~dude-from-by/openlp/my-ios-remote |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel Borges | Needs Fixing | ||
Review via email: mp+324257@code.launchpad.net |
This proposal supersedes a proposal from 2017-05-16.
Commit message
Description of the change
Moved SettingsViewCon
Added Countly and events
To post a comment you must log in.
Revision history for this message
Daniel Borges (danielborges93) wrote : | # |
The application works well and the analytics too, but the tests aren't running. Can you take a look about this?
OBS: I put the current events on Wekan card.
review:
Needs Fixing
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'OLP RemoteTests/SettingsViewModelTests.swift' |
2 | --- OLP RemoteTests/SettingsViewModelTests.swift 1970-01-01 00:00:00 +0000 |
3 | +++ OLP RemoteTests/SettingsViewModelTests.swift 2017-05-18 14:05:49 +0000 |
4 | @@ -0,0 +1,77 @@ |
5 | +// |
6 | +// SettingsViewModelTests.swift |
7 | +// Remote |
8 | +// |
9 | +// Created by andrei shender on 5/16/17. |
10 | +// Copyright © 2017 OpenLP. All rights reserved. |
11 | +// |
12 | + |
13 | +import XCTest |
14 | +import InAppSettingsKit |
15 | + |
16 | +class SettingsViewModelTests: XCTestCase { |
17 | + |
18 | + func createModel() -> (defs: UserDefaults, sett: UserSettings, mod: SettingsViewModel) { |
19 | + let userDefaults = UserDefaults() |
20 | + let userSettings = UserSettings(defaults: userDefaults) |
21 | + let settingsViewModel = SettingsViewModel(settings: userSettings) |
22 | + return (userDefaults, userSettings, settingsViewModel) |
23 | + } |
24 | + |
25 | + func testNoAuth() { |
26 | + let (_, _, settingsViewModel) = createModel() |
27 | + XCTAssert(settingsViewModel.needAuth.value == false) |
28 | + } |
29 | + |
30 | + func testNoCrashNotificationWithoutUserInfo() { |
31 | + let (_, _, _) = createModel() |
32 | + NotificationCenter.default.post(Notification(name: NSNotification.Name(rawValue: kIASKAppSettingChanged), object: nil, userInfo: [:])) |
33 | + NotificationCenter.default.post(Notification(name: NSNotification.Name(rawValue: kIASKAppSettingChanged), object: nil, userInfo: nil)) |
34 | + } |
35 | + |
36 | + func testSettingNeedAuth() { |
37 | + let (_, _, settingsViewModel) = createModel() |
38 | + let userInfo = ["auth.needsAuth" : true] |
39 | + NotificationCenter.default.post(Notification(name: NSNotification.Name(rawValue: kIASKAppSettingChanged), object: nil, userInfo: userInfo)) |
40 | + XCTAssert(settingsViewModel.needAuth.value) |
41 | + } |
42 | + |
43 | + func testDismissWithEmptyServerIP() { |
44 | + let (userDefaults, _, settingsViewModel) = createModel() |
45 | + userDefaults.set("", forKey: "server.ip") |
46 | + settingsViewModel.dismiss() |
47 | + XCTAssert(settingsViewModel.alert.value.message == Localizable.Error.validIP) |
48 | + } |
49 | + |
50 | + func testDismissWithInvalidServerIP() { |
51 | + let (userDefaults, _, settingsViewModel) = createModel() |
52 | + userDefaults.set("±±±±", forKey: "server.ip") |
53 | + settingsViewModel.dismiss() |
54 | + XCTAssert(settingsViewModel.alert.value.message == Localizable.Error.validIP) |
55 | + } |
56 | + |
57 | + func testDismissWithNoServerPort() { |
58 | + let (userDefaults, _, settingsViewModel) = createModel() |
59 | + userDefaults.set("1", forKey: "server.ip") |
60 | + userDefaults.set("", forKey: "server.port") |
61 | + settingsViewModel.dismiss() |
62 | + XCTAssert(settingsViewModel.alert.value.message == Localizable.Error.validPort) |
63 | + } |
64 | + |
65 | + func testDismissWithInvalidServerPort() { |
66 | + let (userDefaults, _, settingsViewModel) = createModel() |
67 | + userDefaults.set("1", forKey: "server.ip") |
68 | + userDefaults.set("invalid", forKey: "server.port") |
69 | + settingsViewModel.dismiss() |
70 | + XCTAssert(settingsViewModel.alert.value.message == Localizable.Error.validPort) |
71 | + } |
72 | + |
73 | + func testDismiss() { |
74 | + let (userDefaults, _, settingsViewModel) = createModel() |
75 | + userDefaults.set("1", forKey: "server.ip") |
76 | + userDefaults.set("2", forKey: "server.port") |
77 | + settingsViewModel.dismiss() |
78 | + XCTAssert(settingsViewModel.alert.value.message == "") |
79 | + XCTAssert(settingsViewModel.dismissController.value) |
80 | + } |
81 | + } |
82 | |
83 | === modified file 'Podfile' |
84 | --- Podfile 2017-03-02 07:16:31 +0000 |
85 | +++ Podfile 2017-05-18 14:05:49 +0000 |
86 | @@ -12,6 +12,7 @@ |
87 | pod 'BlockLooper', :git=>'https://github.com/ashender/BlockLooper.git' |
88 | pod 'CWStatusBarNotification' |
89 | pod 'ReactiveCocoa' |
90 | + pod 'Countly' |
91 | end |
92 | |
93 | target 'OLP RemoteTests' do |
94 | |
95 | === modified file 'Podfile.lock' |
96 | --- Podfile.lock 2017-03-02 07:16:31 +0000 |
97 | +++ Podfile.lock 2017-05-18 14:05:49 +0000 |
98 | @@ -4,6 +4,7 @@ |
99 | - Alamofire (~> 4.1) |
100 | - ObjectMapper (~> 2.0) |
101 | - BlockLooper (0.0.3) |
102 | + - Countly (16.12) |
103 | - CWStatusBarNotification (2.3.5) |
104 | - InAppSettingsKit (2.8.1) |
105 | - Nimble (5.1.1) |
106 | @@ -35,6 +36,7 @@ |
107 | - Alamofire |
108 | - AlamofireObjectMapper |
109 | - BlockLooper (from `https://github.com/ashender/BlockLooper.git`) |
110 | + - Countly |
111 | - CWStatusBarNotification |
112 | - InAppSettingsKit |
113 | - Nimble |
114 | @@ -57,6 +59,7 @@ |
115 | Alamofire: 856a113053a7bc9cbe5d6367a555d773fc5cfef7 |
116 | AlamofireObjectMapper: 842d2eed43a4d0593ff0110d54ac86a170c4519c |
117 | BlockLooper: 8bba05fef76734e8e52e39412a0212ac3474776c |
118 | + Countly: 517a5c221463b29854e214b2193620690b5edb1a |
119 | CWStatusBarNotification: 3d2738b25c3207f60cc50201388d3c96182545ff |
120 | InAppSettingsKit: 94d2fba3ccd700fc4e32c6d09fabcc0af2426744 |
121 | Nimble: 415e3aa3267e7bc2c96b05fa814ddea7bb686a29 |
122 | @@ -68,6 +71,6 @@ |
123 | TPKeyboardAvoiding: 0d6af20e95e2850f4c621841225b59ec7d8dd852 |
124 | UITextView+Placeholder: 77680995fcdd07c3f52ec92fe1150874a2ac89ff |
125 | |
126 | -PODFILE CHECKSUM: 8bcb0a7a00d7e61acff50967f2778c47912a0880 |
127 | +PODFILE CHECKSUM: 315619d4b8576ea53bb0e33054782ee9669fb788 |
128 | |
129 | COCOAPODS: 1.1.1 |
130 | |
131 | === modified file 'Remote.xcodeproj/project.pbxproj' |
132 | --- Remote.xcodeproj/project.pbxproj 2017-04-05 15:55:26 +0000 |
133 | +++ Remote.xcodeproj/project.pbxproj 2017-05-18 14:05:49 +0000 |
134 | @@ -83,6 +83,10 @@ |
135 | 759464A51CEB8493001A56BF /* SettingsControllerPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 759464A41CEB8493001A56BF /* SettingsControllerPresenter.swift */; }; |
136 | 759778AE1CF83AC400C2E4B4 /* APITest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 759778AD1CF83AC400C2E4B4 /* APITest.swift */; }; |
137 | 75AF930F1E9297220051EB82 /* SearchResultsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755B3FE11E8CFEE000DA570C /* SearchResultsViewModel.swift */; }; |
138 | + 75BC63271ECDCAB500A21BF2 /* CountlyClientSideUserPaths.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75BC63261ECDCAB500A21BF2 /* CountlyClientSideUserPaths.swift */; }; |
139 | + 75C1BF671ECA1D9C00939F73 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75C1BF661ECA1D9C00939F73 /* SettingsViewModel.swift */; }; |
140 | + 75D100C61ECB2D0B00818402 /* SettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75D100C51ECB2D0B00818402 /* SettingsViewModelTests.swift */; }; |
141 | + 75D100C71ECB2E0000818402 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75C1BF661ECA1D9C00939F73 /* SettingsViewModel.swift */; }; |
142 | 863574EB78CF355A8463055D /* Pods_Remote.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3194E32378DD6BB8E273BB47 /* Pods_Remote.framework */; }; |
143 | /* End PBXBuildFile section */ |
144 | |
145 | @@ -147,6 +151,9 @@ |
146 | 759778A31CF839C200C2E4B4 /* OLP RemoteTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "OLP RemoteTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; |
147 | 759778A71CF839C200C2E4B4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; |
148 | 759778AD1CF83AC400C2E4B4 /* APITest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APITest.swift; sourceTree = "<group>"; }; |
149 | + 75BC63261ECDCAB500A21BF2 /* CountlyClientSideUserPaths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CountlyClientSideUserPaths.swift; sourceTree = "<group>"; }; |
150 | + 75C1BF661ECA1D9C00939F73 /* SettingsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; }; |
151 | + 75D100C51ECB2D0B00818402 /* SettingsViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewModelTests.swift; sourceTree = "<group>"; }; |
152 | BF194B8FEA335C68D241C125 /* Pods-OLP RemoteTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OLP RemoteTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-OLP RemoteTests/Pods-OLP RemoteTests.release.xcconfig"; sourceTree = "<group>"; }; |
153 | /* End PBXFileReference section */ |
154 | |
155 | @@ -217,6 +224,7 @@ |
156 | children = ( |
157 | 759464A41CEB8493001A56BF /* SettingsControllerPresenter.swift */, |
158 | 462395801BE505EC00B90146 /* SettingsViewController.swift */, |
159 | + 75C1BF661ECA1D9C00939F73 /* SettingsViewModel.swift */, |
160 | ); |
161 | path = Settings; |
162 | sourceTree = "<group>"; |
163 | @@ -278,6 +286,7 @@ |
164 | 46A612301C78E75D00B61DC8 /* ColorUtil.swift */, |
165 | 46FD378E1C79867C00DED423 /* ErrorUtil.swift */, |
166 | 46753A9A1E593EAC007872C0 /* LocalizationUtil.swift */, |
167 | + 75BC63261ECDCAB500A21BF2 /* CountlyClientSideUserPaths.swift */, |
168 | ); |
169 | path = Util; |
170 | sourceTree = "<group>"; |
171 | @@ -367,6 +376,7 @@ |
172 | 750831BA1CFF594400CA9299 /* service_response.json */, |
173 | 750831BC1CFF597D00CA9299 /* slides_response.json */, |
174 | 757326FF1E8C06B7001E618B /* SearchViewModelTests.swift */, |
175 | + 75D100C51ECB2D0B00818402 /* SettingsViewModelTests.swift */, |
176 | ); |
177 | path = "OLP RemoteTests"; |
178 | sourceTree = "<group>"; |
179 | @@ -595,10 +605,12 @@ |
180 | 4623B6971C07BC2E007FFE2B /* SlidesViewController.swift in Sources */, |
181 | 46A9EED81BFD89DF00E8D520 /* ServiceAPI.swift in Sources */, |
182 | 46FD37871C7959EF00DED423 /* SearchAPI.swift in Sources */, |
183 | + 75C1BF671ECA1D9C00939F73 /* SettingsViewModel.swift in Sources */, |
184 | 4623B69C1C07C569007FFE2B /* ControllerAPI.swift in Sources */, |
185 | 463A91851C092E6B00D572E6 /* DictionaryUtil.swift in Sources */, |
186 | 462395831BE50C5A00B90146 /* ViewControllerUtil.swift in Sources */, |
187 | 46CC0CED1BE6972300423EB4 /* UserSettings.swift in Sources */, |
188 | + 75BC63271ECDCAB500A21BF2 /* CountlyClientSideUserPaths.swift in Sources */, |
189 | 46FD37891C796C9B00DED423 /* SearchResultViewController.swift in Sources */, |
190 | 463A918D1C093DD500D572E6 /* AlertsViewController.swift in Sources */, |
191 | 755B3FE21E8CFEE000DA570C /* SearchResultsViewModel.swift in Sources */, |
192 | @@ -636,6 +648,7 @@ |
193 | 75667B561CFEE949008FDFE0 /* ItemModel.swift in Sources */, |
194 | 75667B591CFEE949008FDFE0 /* LiveModel.swift in Sources */, |
195 | 75667B521CFEE938008FDFE0 /* AddAPI.swift in Sources */, |
196 | + 75D100C61ECB2D0B00818402 /* SettingsViewModelTests.swift in Sources */, |
197 | 75667B5C1CFEFF7E008FDFE0 /* DictionaryUtil.swift in Sources */, |
198 | 757327001E8C06B7001E618B /* SearchViewModelTests.swift in Sources */, |
199 | 75667B5E1CFEFF86008FDFE0 /* ErrorUtil.swift in Sources */, |
200 | @@ -648,6 +661,7 @@ |
201 | 757326FE1E8C060F001E618B /* LocalizationUtil.swift in Sources */, |
202 | 75667B5F1CFEFFA7008FDFE0 /* DisplayAPI.swift in Sources */, |
203 | 75667B4E1CFEE930008FDFE0 /* PollAPI.swift in Sources */, |
204 | + 75D100C71ECB2E0000818402 /* SettingsViewModel.swift in Sources */, |
205 | 75667B541CFEE93D008FDFE0 /* AlertAPI.swift in Sources */, |
206 | 75667B531CFEE938008FDFE0 /* Service.swift in Sources */, |
207 | 75667B291CFCA8C9008FDFE0 /* UserSettings.swift in Sources */, |
208 | |
209 | === modified file 'Remote/AppDelegate.swift' |
210 | --- Remote/AppDelegate.swift 2017-04-05 15:55:26 +0000 |
211 | +++ Remote/AppDelegate.swift 2017-05-18 14:05:49 +0000 |
212 | @@ -24,6 +24,7 @@ |
213 | |
214 | import UIKit |
215 | import BlockLooper |
216 | +import Countly |
217 | |
218 | @UIApplicationMain |
219 | class AppDelegate: UIResponder, UIApplicationDelegate, UITabBarControllerDelegate, ServiceViewControllerDelegate { |
220 | @@ -42,9 +43,10 @@ |
221 | } |
222 | |
223 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { |
224 | - |
225 | + startAnalytics() |
226 | settings = UserSettings(defaults: UserDefaults.standard) |
227 | settings.registerDefaults() |
228 | + Countly.sharedInstance().applicationStartWithSettings(settings) |
229 | service = Service(settings: settings) |
230 | |
231 | var rootViewControllers = [UINavigationController]() |
232 | @@ -94,6 +96,14 @@ |
233 | useAnimator = false |
234 | } |
235 | |
236 | + func startAnalytics() { |
237 | + let config = CountlyConfig() |
238 | + config.appKey = "f057136e457a5d2da05c3b569a48caaee9a8644b" |
239 | + config.host = "https://stats.openlp.io" |
240 | + config.features = [CLYCrashReporting] |
241 | + Countly.sharedInstance().start(with: config) |
242 | + } |
243 | + |
244 | |
245 | // MARK: - ServiceViewControllerDelegate |
246 | func serviceViewControllerDidSelectItem(_ item: ItemModel) { |
247 | |
248 | === modified file 'Remote/Classes/Controller/Settings/SettingsControllerPresenter.swift' |
249 | --- Remote/Classes/Controller/Settings/SettingsControllerPresenter.swift 2017-02-05 02:25:57 +0000 |
250 | +++ Remote/Classes/Controller/Settings/SettingsControllerPresenter.swift 2017-05-18 14:05:49 +0000 |
251 | @@ -26,13 +26,10 @@ |
252 | import UIKit |
253 | |
254 | class SettingsControllerPresenter: NSObject { |
255 | - |
256 | - let pollAPI: PollAPI |
257 | let settings: UserSettings |
258 | var fromController: UIViewController? |
259 | |
260 | init(settings: UserSettings, pollAPI: PollAPI) { |
261 | - self.pollAPI = pollAPI |
262 | self.settings = settings |
263 | super.init() |
264 | } |
265 | @@ -46,7 +43,8 @@ |
266 | |
267 | |
268 | func presentSettingViewController() { |
269 | - let settingsController = SettingsViewController(settings:settings, pollAPI: pollAPI) |
270 | + let settingsViewModel = SettingsViewModel(settings: settings) |
271 | + let settingsController = SettingsViewController(viewModel: settingsViewModel) |
272 | let navigationController = UINavigationController(rootViewController: settingsController) |
273 | guard let fromController = self.fromController else { return } |
274 | if #available(iOS 9.0, *) { |
275 | |
276 | === modified file 'Remote/Classes/Controller/Settings/SettingsViewController.swift' |
277 | --- Remote/Classes/Controller/Settings/SettingsViewController.swift 2017-02-19 05:21:00 +0000 |
278 | +++ Remote/Classes/Controller/Settings/SettingsViewController.swift 2017-05-18 14:05:49 +0000 |
279 | @@ -24,24 +24,37 @@ |
280 | |
281 | import UIKit |
282 | import InAppSettingsKit |
283 | +import Countly |
284 | |
285 | class SettingsViewController: IASKAppSettingsViewController { |
286 | |
287 | fileprivate let hiddenSettingsKeys = ["auth.userID" as NSObject, "auth.password" as NSObject] as Set<NSObject> |
288 | - fileprivate let pollAPI: PollAPI! |
289 | - fileprivate let settings: UserSettings! |
290 | + fileprivate let viewModel: SettingsViewModel? |
291 | |
292 | - init(settings: UserSettings, pollAPI: PollAPI) { |
293 | - self.pollAPI = pollAPI |
294 | - self.settings = settings |
295 | + init(viewModel: SettingsViewModel) { |
296 | + self.viewModel = viewModel |
297 | super.init(nibName:nil, bundle:nil) |
298 | + viewModel.needAuth.producer.startWithValues { (needAuth) in |
299 | + self.setHiddenKeys(needAuth ? [] : self.hiddenSettingsKeys, animated: true) |
300 | + self.synchronizeSettings() |
301 | + } |
302 | + |
303 | + viewModel.alert.producer.skip(first: 1).startWithValues { (alert) in |
304 | + UIAlertView(title: alert.title, message: alert.message, delegate: nil, cancelButtonTitle: "OK") |
305 | + .show() |
306 | + } |
307 | + |
308 | + viewModel.dismissController.producer.skip(first: 1).startWithValues { (shouldDismiss) in |
309 | + self.synchronizeSettings() |
310 | + self.dismiss(animated: true, completion: nil) |
311 | + } |
312 | + |
313 | self.setupSettingViewController() |
314 | self.setupNavigationBar() |
315 | } |
316 | |
317 | required init?(coder aDecoder: NSCoder) { |
318 | - self.pollAPI = nil |
319 | - self.settings = nil |
320 | + viewModel = nil |
321 | super.init(coder: aDecoder) |
322 | } |
323 | |
324 | @@ -53,55 +66,26 @@ |
325 | self.tableView.delegate = self |
326 | self.tableView.dataSource = self |
327 | } |
328 | - |
329 | + |
330 | // MARK: My methods |
331 | |
332 | fileprivate func setupSettingViewController() { |
333 | self.showDoneButton = false |
334 | self.showCreditsFooter = false |
335 | self.delegate = self |
336 | - |
337 | - let needsAuth = settings.needsAuth |
338 | - self.hiddenKeys = needsAuth ? [] : self.hiddenSettingsKeys |
339 | - |
340 | - NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewController.settingsDidChange(_:)), name: NSNotification.Name(rawValue: kIASKAppSettingChanged), object: nil) |
341 | - } |
342 | - |
343 | - @objc fileprivate func settingsDidChange(_ notification: Notification) { |
344 | - if let object = notification.userInfo { |
345 | - if object["auth.needsAuth"] != nil { |
346 | - let enabled = settings.needsAuth |
347 | - let hiddenKeys = enabled ? [] : self.hiddenSettingsKeys |
348 | - self.setHiddenKeys(hiddenKeys, animated: true) |
349 | - } |
350 | - } |
351 | + |
352 | } |
353 | |
354 | fileprivate func setupNavigationBar() { |
355 | self.navigationItem.title = Localizable.settings |
356 | - |
357 | let strSave = Localizable.ok |
358 | let btSave = UIBarButtonItem(title: strSave, style: .done, target: self, action: #selector(SettingsViewController.dismissViewController)) |
359 | self.navigationItem.rightBarButtonItem = btSave |
360 | } |
361 | - |
362 | + |
363 | @objc fileprivate func dismissViewController() { |
364 | - if !settings.validateServerIP() { |
365 | - let title = Localizable.error |
366 | - let message = Localizable.Error.validIP |
367 | - UIAlertView(title: title, message: message, delegate: nil, cancelButtonTitle: "OK") |
368 | - .show() |
369 | - } else if !settings.validateServerPort() { |
370 | - let title = Localizable.error |
371 | - let message = Localizable.Error.validPort |
372 | - UIAlertView(title: title, message: message, delegate: nil, cancelButtonTitle: "OK") |
373 | - .show() |
374 | - } else { |
375 | - self.synchronizeSettings() |
376 | - self.dismiss(animated: true, completion: nil) |
377 | - } |
378 | + viewModel?.dismiss() |
379 | } |
380 | - |
381 | } |
382 | |
383 | // MARK: - IASKSettingsDelegate |
384 | |
385 | === added file 'Remote/Classes/Controller/Settings/SettingsViewModel.swift' |
386 | --- Remote/Classes/Controller/Settings/SettingsViewModel.swift 1970-01-01 00:00:00 +0000 |
387 | +++ Remote/Classes/Controller/Settings/SettingsViewModel.swift 2017-05-18 14:05:49 +0000 |
388 | @@ -0,0 +1,73 @@ |
389 | +/****************************************************************************** |
390 | + * OpenLP iOS Remote * |
391 | + * --------------------------------------------------------------------------- * |
392 | + * Copyright (c) 2008-2016 OpenLP Developers * |
393 | + * --------------------------------------------------------------------------- * |
394 | + * Permission is hereby granted, free of charge, to any person obtaining a * |
395 | + * copy of this software and associated documentation files (the "Software"), * |
396 | + * to deal in the Software without restriction, including without limitation * |
397 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * |
398 | + * and/or sell copies of the Software, and to permit persons to whom the * |
399 | + * Software is furnished to do so, subject to the following conditions: * |
400 | + * * |
401 | + * The above copyright notice and this permission notice shall be included in * |
402 | + * all copies or substantial portions of the Software. * |
403 | + * * |
404 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * |
405 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * |
406 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * |
407 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * |
408 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * |
409 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * |
410 | + * DEALINGS IN THE SOFTWARE. * |
411 | + ******************************************************************************/ |
412 | + |
413 | + |
414 | +import UIKit |
415 | +import ReactiveCocoa |
416 | +import ReactiveSwift |
417 | +import InAppSettingsKit |
418 | +import Countly |
419 | + |
420 | +struct Alert { |
421 | + let title: String |
422 | + let message: String |
423 | +} |
424 | + |
425 | +class SettingsViewModel: NSObject { |
426 | + let needAuth: MutableProperty<Bool> |
427 | + let settings: UserSettings |
428 | + let alert: MutableProperty<Alert> = MutableProperty<Alert>(Alert(title: "", message: "")) |
429 | + let dismissController = MutableProperty<Bool>(false) |
430 | + |
431 | + init(settings: UserSettings) { |
432 | + self.needAuth = MutableProperty<Bool>(settings.needsAuth) |
433 | + self.settings = settings |
434 | + super.init() |
435 | + NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewModel.settingsDidChange(_:)), name: NSNotification.Name(rawValue: kIASKAppSettingChanged), object: nil) |
436 | + Countly.sharedInstance().settingsScreenOpenedWithUserSettings(settings) |
437 | + } |
438 | + |
439 | + @objc fileprivate func settingsDidChange(_ notification: Notification) { |
440 | + if let object = notification.userInfo, let needAuth = object["auth.needsAuth"] as? Bool { |
441 | + self.needAuth.value = needAuth |
442 | + } |
443 | + } |
444 | + |
445 | + func dismiss() { |
446 | + if !settings.validateServerIP() { |
447 | + let title = Localizable.error |
448 | + let message = Localizable.Error.validIP |
449 | + alert.value = Alert(title: title, message: message) |
450 | + } else if !settings.validateServerPort() { |
451 | + let title = Localizable.error |
452 | + let message = Localizable.Error.validPort |
453 | + alert.value = Alert(title: title, message: message) |
454 | + } else { |
455 | + dismissController.value = true |
456 | + if self.settings.serverIP.characters.count != 0 { |
457 | + Countly.sharedInstance().settingsScreenClosedWithUserSettings(settings) |
458 | + } |
459 | + } |
460 | + } |
461 | +} |
462 | |
463 | === modified file 'Remote/Classes/Network/Service.swift' |
464 | --- Remote/Classes/Network/Service.swift 2017-04-05 15:55:26 +0000 |
465 | +++ Remote/Classes/Network/Service.swift 2017-05-18 14:05:49 +0000 |
466 | @@ -27,6 +27,7 @@ |
467 | import Result |
468 | import ReactiveCocoa |
469 | import BlockLooper |
470 | +import Countly |
471 | |
472 | |
473 | protocol PollAPIServiceDelegate { |
474 | @@ -109,14 +110,14 @@ |
475 | |
476 | |
477 | pollSignal |
478 | - .filter({ (result) -> Bool in |
479 | + .filter() { (result) -> Bool in |
480 | switch result { |
481 | case .success: |
482 | return true |
483 | default: |
484 | return false |
485 | } |
486 | - }) |
487 | + } |
488 | .observe { (event) in |
489 | self.handlePollResponse((event.value?.value)!) |
490 | } |
491 | @@ -157,6 +158,7 @@ |
492 | |
493 | fileprivate func handlePollResponse(_ poll: PollModel?) { |
494 | if let poll = poll { |
495 | + Countly.sharedInstance().serverSentNonEmptyResponse() |
496 | if self.poll.service != poll.service { |
497 | self.updateService({ |
498 | self.serviceDelegate?.updateSelectedItem(itemId: poll.item) |
499 | |
500 | === added file 'Remote/Classes/Util/CountlyClientSideUserPaths.swift' |
501 | --- Remote/Classes/Util/CountlyClientSideUserPaths.swift 1970-01-01 00:00:00 +0000 |
502 | +++ Remote/Classes/Util/CountlyClientSideUserPaths.swift 2017-05-18 14:05:49 +0000 |
503 | @@ -0,0 +1,85 @@ |
504 | +/****************************************************************************** |
505 | + * OpenLP iOS Remote * |
506 | + * --------------------------------------------------------------------------- * |
507 | + * Copyright (c) 2008-2016 OpenLP Developers * |
508 | + * --------------------------------------------------------------------------- * |
509 | + * Permission is hereby granted, free of charge, to any person obtaining a * |
510 | + * copy of this software and associated documentation files (the "Software"), * |
511 | + * to deal in the Software without restriction, including without limitation * |
512 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * |
513 | + * and/or sell copies of the Software, and to permit persons to whom the * |
514 | + * Software is furnished to do so, subject to the following conditions: * |
515 | + * * |
516 | + * The above copyright notice and this permission notice shall be included in * |
517 | + * all copies or substantial portions of the Software. * |
518 | + * * |
519 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * |
520 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * |
521 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * |
522 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * |
523 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * |
524 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * |
525 | + * DEALINGS IN THE SOFTWARE. * |
526 | + ******************************************************************************/ |
527 | + |
528 | +import UIKit |
529 | +import Countly |
530 | + |
531 | +extension Countly { |
532 | + |
533 | + enum UserPathFirstExperienceState : String { |
534 | + case Started = "started" |
535 | + case SettingsOpenedWithEmptyServerAddress = "SettingsOpenedWithEmptyServerAddress" |
536 | + case SettingsClosedWithFilledServerAddress = "SettingsClosedWithFilledServerAddress" |
537 | + case GotNonEmptyResponseFromServer = "GotNonEmptyResponseFromServer" |
538 | + } |
539 | + |
540 | + static let firstExperiencePathStartedKey = "userPath_firstExperienceState" |
541 | + |
542 | + func applicationStartWithSettings(_ settings: UserSettings) { |
543 | + if firstExperiencePathState() == .none, settings.serverIP.characters.count == 0 { |
544 | + startFirstExperienceUserPath() |
545 | + type(of: self).sharedInstance().recordEvent("first app launch start with no server ip") |
546 | + } |
547 | + } |
548 | + |
549 | + func settingsScreenOpenedWithUserSettings(_ settings: UserSettings) { |
550 | + if let state = firstExperiencePathState(), state == .Started, settings.serverIP.characters.count == 0 { |
551 | + updateFirstExperiencePathState(newState: .SettingsOpenedWithEmptyServerAddress) |
552 | + type(of: self).sharedInstance().recordEvent("settings screen was opened without server ip filled") |
553 | + } |
554 | + } |
555 | + |
556 | + func settingsScreenClosedWithUserSettings(_ settings: UserSettings) { |
557 | + if let state = firstExperiencePathState(), state == .SettingsOpenedWithEmptyServerAddress, settings.serverIP.characters.count != 0 { |
558 | + updateFirstExperiencePathState(newState: .SettingsClosedWithFilledServerAddress) |
559 | + type(of: self).sharedInstance().recordEvent("settings screen was closed with server ip filled") |
560 | + } |
561 | + } |
562 | + |
563 | + func serverSentNonEmptyResponse() { |
564 | + if let state = firstExperiencePathState(), state == .SettingsClosedWithFilledServerAddress { |
565 | + updateFirstExperiencePathState(newState: .GotNonEmptyResponseFromServer) |
566 | + type(of: self).sharedInstance().recordEvent("received server response - setup complete") |
567 | + } |
568 | + } |
569 | + |
570 | + // MARK: private |
571 | + |
572 | + fileprivate func startFirstExperienceUserPath() { |
573 | + updateFirstExperiencePathState(newState: .Started) |
574 | + } |
575 | + |
576 | + fileprivate func firstExperiencePathState() -> UserPathFirstExperienceState? { |
577 | + if let state = UserDefaults.standard.object(forKey: type(of: self).firstExperiencePathStartedKey) as? String { |
578 | + return UserPathFirstExperienceState(rawValue:state) |
579 | + } |
580 | + else { |
581 | + return Optional.none |
582 | + } |
583 | + } |
584 | + |
585 | + fileprivate func updateFirstExperiencePathState(newState: UserPathFirstExperienceState) { |
586 | + UserDefaults.standard.set(newState.rawValue, forKey: type(of: self).firstExperiencePathStartedKey) |
587 | + } |
588 | +} |
Events are described in CountlyClientSi deUserPaths. swift
first app launch start with no server ip
settings screen was opened without server ip filled
settings screen was closed with server ip filled
received server response - setup complete