Skip to content
This repository has been archived by the owner on Jun 30, 2022. It is now read-only.

Commit

Permalink
Add minimum loading time
Browse files Browse the repository at this point in the history
  • Loading branch information
pg8wood committed Jan 20, 2020
1 parent dcecad4 commit 84f4ab7
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 20 deletions.
24 changes: 24 additions & 0 deletions Common/Extensions/URLError+ShortMessages.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// URLError+ShortCodes.swift
// Dashboard
//
// Created by Patrick Gatewood on 1/20/20.
// Copyright © 2020 Patrick Gatewood. All rights reserved.
//

import Foundation

extension URLError {
var shortLocalizedDescription: String {
switch code {
case .badURL:
return "Invalid URL"
case .cannotFindHost, .cannotConnectToHost:
return "No response"
case .badServerResponse:
return "Invalid response"
default:
return "Unknown error"
}
}
}
4 changes: 4 additions & 0 deletions Dashboard.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
B8AC2CB323D25B1E00558F8D /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8AC2CB223D25B1E00558F8D /* SettingsView.swift */; };
B8AC2CB523D2640C00558F8D /* UserDefaultsPropertyWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8AC2CB423D2640C00558F8D /* UserDefaultsPropertyWrapper.swift */; };
B8AC2CB923D264BF00558F8D /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8AC2CB823D264BF00558F8D /* Settings.swift */; };
B8AC2CBB23D60DC400558F8D /* URLError+ShortMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8AC2CBA23D60DC400558F8D /* URLError+ShortMessages.swift */; };
B8AC722E232EC9DE00074652 /* MockServiceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8AC722D232EC9DE00074652 /* MockServiceModel.swift */; };
B8BEFE2D22E10BDD0074AB51 /* UIViewControllerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BEFE2C22E10BDD0074AB51 /* UIViewControllerExtensions.swift */; };
B8BEFE2E22E10D5A0074AB51 /* UIViewControllerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BEFE2C22E10BDD0074AB51 /* UIViewControllerExtensions.swift */; };
Expand Down Expand Up @@ -200,6 +201,7 @@
B8AC2CB223D25B1E00558F8D /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
B8AC2CB423D2640C00558F8D /* UserDefaultsPropertyWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsPropertyWrapper.swift; sourceTree = "<group>"; };
B8AC2CB823D264BF00558F8D /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
B8AC2CBA23D60DC400558F8D /* URLError+ShortMessages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLError+ShortMessages.swift"; sourceTree = "<group>"; };
B8AC722D232EC9DE00074652 /* MockServiceModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockServiceModel.swift; sourceTree = "<group>"; };
B8BEFE2C22E10BDD0074AB51 /* UIViewControllerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerExtensions.swift; sourceTree = "<group>"; };
B8D04A2C2335446600833CC5 /* EmptyStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyStateView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -367,6 +369,7 @@
B8546C7022DE58520017E2DA /* UIColorExtensions.swift */,
B8BEFE2C22E10BDD0074AB51 /* UIViewControllerExtensions.swift */,
B88D494D234299200058B0D3 /* CodingUserInfoKeyExtensions.swift */,
B8AC2CBA23D60DC400558F8D /* URLError+ShortMessages.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -948,6 +951,7 @@
B8AC2CB923D264BF00558F8D /* Settings.swift in Sources */,
B8BEFE2D22E10BDD0074AB51 /* UIViewControllerExtensions.swift in Sources */,
B8F391632329B16500F62FF8 /* ServiceRow.swift in Sources */,
B8AC2CBB23D60DC400558F8D /* URLError+ShortMessages.swift in Sources */,
B8AC722E232EC9DE00074652 /* MockServiceModel.swift in Sources */,
B881D250221DC94F0038BF81 /* AddServiceViewController.swift in Sources */,
B88D495B2342A4070058B0D3 /* AddServiceView.swift in Sources */,
Expand Down
44 changes: 24 additions & 20 deletions Dashboard/Features/Services/ServiceRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ struct ServiceRow: View {
@State private var disposables = Set<AnyCancellable>()
@State private var serverResponse: Result<Int, URLError> = .failure(URLError(.unknown))

private let accessoryViewWidth: CGFloat = 80

// AnyView type-erasure: https://www.hackingwithswift.com/quick-start/swiftui/how-to-return-different-view-types
private var accessoryView: AnyView {
if isLoading {
return AnyView(
ActivityIndicatorView()
.frame(width: 80, height: 50)
.frame(width: accessoryViewWidth, height: 50)
)
} else {
return AnyView(statusView)
Expand All @@ -47,10 +49,11 @@ struct ServiceRow: View {
if $settings.showErrorCodes.wrappedValue == true {
Text(message)
.font(.caption)
.lineLimit(3)
.lineLimit(2)
.multilineTextAlignment(.center)
}
}
.frame(maxWidth: 80) // TODO might be good to make this a constant in an "errorView" or "accessoryView" class
.frame(maxWidth: accessoryViewWidth)
)
}

Expand All @@ -62,20 +65,7 @@ struct ServiceRow: View {

return AnyView(accessoryImage(from: Image("check")))
case .failure(let error):
let errorMessage: String

switch error.code {
case .badURL:
errorMessage = "Invalid URL"
case .cannotFindHost, .cannotConnectToHost:
errorMessage = "No response"
case .badServerResponse:
errorMessage = "Invalid response"
default:
errorMessage = error.localizedDescription
}

return errorView(message: errorMessage)
return errorView(message: error.shortLocalizedDescription)
}
}

Expand Down Expand Up @@ -111,7 +101,6 @@ struct ServiceRow: View {
.frame(height: 90)
.frame(minWidth: 0, maxWidth: .infinity)
.onTapGesture {
guard self.editMode?.wrappedValue == .inactive else { return }
self.fetchServerStatus()
}
.onAppear {
Expand All @@ -120,22 +109,37 @@ struct ServiceRow: View {
}

func fetchServerStatus() {
guard self.editMode?.wrappedValue == .inactive else { return }

self.isLoading = true
let loadingStartDate = Date()

self.network.fetchServerStatusCode(for: service.url)
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
self.isLoading = false
break
case .failure(let error):
self.serverResponse = .failure(error)
self.isLoading = false
}

self.finishLoading(startingDate: loadingStartDate)
}, receiveValue: { statusCode in
self.serverResponse = .success(statusCode)
})
.store(in: &disposables) // whoops, if we don't retain this cancellable object the network data task will be cancelled
}

private func finishLoading(startingDate: Date) {
let requestEndDate = Date()
let timeSpentLoading = Calendar.current.dateComponents([.second], from: startingDate, to: requestEndDate).second ?? 0
let minimumLoadingTime = 0.25
let secondsToContinueLoading = abs(minimumLoadingTime - Double(timeSpentLoading))

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + secondsToContinueLoading) {
self.isLoading = false
}
}
}

//#if DEBUG
Expand Down

0 comments on commit 84f4ab7

Please sign in to comment.