Skip to content

Commit

Permalink
feat: Program Screen Error Handling (#448)
Browse files Browse the repository at this point in the history
  • Loading branch information
shafqat-muneer authored Jun 26, 2024
1 parent b2539a6 commit ea6e24e
Show file tree
Hide file tree
Showing 11 changed files with 321 additions and 142 deletions.
12 changes: 12 additions & 0 deletions Core/Core.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@
141F1D302B7328D4009E81EB /* WebviewCookiesUpdateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 141F1D2F2B7328D4009E81EB /* WebviewCookiesUpdateProtocol.swift */; };
142EDD6C2B831D1400F9F320 /* BranchSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 142EDD6B2B831D1400F9F320 /* BranchSDK */; };
14769D3C2B9822EE00AB36D4 /* CoreAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14769D3B2B9822EE00AB36D4 /* CoreAnalytics.swift */; };
9784D47E2BF7762800AFEFFF /* FullScreenErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9784D47D2BF7762800AFEFFF /* FullScreenErrorView.swift */; };
A51CDBE72B6D21F2009B6D4E /* SegmentConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51CDBE62B6D21F2009B6D4E /* SegmentConfig.swift */; };
A53A32352B233DEC005FE38A /* ThemeConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53A32342B233DEC005FE38A /* ThemeConfig.swift */; };
A595689B2B6173DF00ED4F90 /* BranchConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A595689A2B6173DF00ED4F90 /* BranchConfig.swift */; };
Expand Down Expand Up @@ -339,6 +340,7 @@
349B90CD6579F7B8D257E515 /* Pods_App_Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App_Core.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3B74C6685E416657F3C5F5A8 /* Pods-App-Core.releaseprod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.releaseprod.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.releaseprod.xcconfig"; sourceTree = "<group>"; };
60153262DBC2F9E660D7E11B /* Pods-App-Core.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.release.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.release.xcconfig"; sourceTree = "<group>"; };
9784D47D2BF7762800AFEFFF /* FullScreenErrorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullScreenErrorView.swift; sourceTree = "<group>"; };
9D5B06CAA99EA5CD49CBE2BB /* Pods-App-Core.debugdev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.debugdev.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.debugdev.xcconfig"; sourceTree = "<group>"; };
A51CDBE62B6D21F2009B6D4E /* SegmentConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentConfig.swift; sourceTree = "<group>"; };
A53A32342B233DEC005FE38A /* ThemeConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeConfig.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -715,6 +717,7 @@
0770DE7728D0C49E006D8A5D /* Base */ = {
isa = PBXGroup;
children = (
9784D47C2BF7761F00AFEFFF /* FullScreenErrorView */,
064987882B4D69FE0071642A /* Webview */,
E0D586352B314CD3009B4BA7 /* LogistrationBottomView.swift */,
02A4833B29B8C57800D33F33 /* DownloadView.swift */,
Expand Down Expand Up @@ -766,6 +769,14 @@
path = Analytics;
sourceTree = "<group>";
};
9784D47C2BF7761F00AFEFFF /* FullScreenErrorView */ = {
isa = PBXGroup;
children = (
9784D47D2BF7762800AFEFFF /* FullScreenErrorView.swift */,
);
path = FullScreenErrorView;
sourceTree = "<group>";
};
BA30427C2B20B235009B64B7 /* SocialAuth */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1124,6 +1135,7 @@
0260E58028FD792800BBBE18 /* WebUnitViewModel.swift in Sources */,
02A4833A29B8A9AB00D33F33 /* DownloadManager.swift in Sources */,
06078B702BA49C3100576798 /* Dictionary+JSON.swift in Sources */,
9784D47E2BF7762800AFEFFF /* FullScreenErrorView.swift in Sources */,
027BD3AE2909475000392132 /* KeyboardScrollerOptions.swift in Sources */,
BAFB99922B14E23D007D09F9 /* AppleSignInConfig.swift in Sources */,
141F1D302B7328D4009E81EB /* WebviewCookiesUpdateProtocol.swift in Sources */,
Expand Down
97 changes: 97 additions & 0 deletions Core/Core/View/Base/FullScreenErrorView/FullScreenErrorView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//
// FullScreenErrorView.swift
// Course
//
// Created by Shafqat Muneer on 5/14/24.
//

import SwiftUI
import Theme

public struct FullScreenErrorView: View {

public enum ErrorType {
case noInternet
case noInternetWithReload
case generic
}

private let errorType: ErrorType
private var action: () -> Void = {}

public init(
type: ErrorType
) {
self.errorType = type
}

public init(
type: ErrorType,
action: @escaping () -> Void
) {
self.errorType = type
self.action = action
}

public var body: some View {
GeometryReader { proxy in
VStack(spacing: 28) {
Spacer()
switch errorType {
case .noInternet, .noInternetWithReload:
CoreAssets.noWifi.swiftUIImage
.renderingMode(.template)
.foregroundStyle(Color.primary)
.scaledToFit()

Text(CoreLocalization.Error.Internet.noInternetTitle)
.font(Theme.Fonts.titleLarge)
.foregroundColor(Theme.Colors.textPrimary)

Text(CoreLocalization.Error.Internet.noInternetDescription)
.font(Theme.Fonts.bodyLarge)
.foregroundColor(Theme.Colors.textPrimary)
.multilineTextAlignment(.center)
.padding(.horizontal, 50)
case .generic:
CoreAssets.notAvaliable.swiftUIImage
.renderingMode(.template)
.foregroundStyle(Color.primary)
.scaledToFit()

Text(CoreLocalization.View.Snackbar.tryAgainBtn)
.font(Theme.Fonts.titleLarge)
.foregroundColor(Theme.Colors.textPrimary)

Text(CoreLocalization.Error.unknownError)
.font(Theme.Fonts.bodyLarge)
.foregroundColor(Theme.Colors.textPrimary)
.multilineTextAlignment(.center)
.padding(.horizontal, 50)
}

if errorType != .noInternet {
UnitButtonView(
type: .reload,
action: {
self.action()
}
)
}
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: proxy.size.height)
.background(
Theme.Colors.background
)
}
}
}

#if DEBUG
struct FullScreenErrorView_Previews: PreviewProvider {
static var previews: some View {
FullScreenErrorView(type: .noInternetWithReload)
}
}
#endif
29 changes: 25 additions & 4 deletions Core/Core/View/Base/Webview/WebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public protocol WebViewNavigationDelegate: AnyObject {
shouldLoad request: URLRequest,
navigationAction: WKNavigationAction
) async -> Bool

func showWebViewError()
}

public struct WebView: UIViewRepresentable {
Expand All @@ -39,17 +41,20 @@ public struct WebView: UIViewRepresentable {
var webViewNavDelegate: WebViewNavigationDelegate?

var refreshCookies: () async -> Void
var webViewType: String?

public init(
viewModel: ViewModel,
isLoading: Binding<Bool>,
refreshCookies: @escaping () async -> Void,
navigationDelegate: WebViewNavigationDelegate? = nil
navigationDelegate: WebViewNavigationDelegate? = nil,
webViewType: String? = nil
) {
self.viewModel = viewModel
self._isLoading = isLoading
self.refreshCookies = refreshCookies
self.webViewNavDelegate = navigationDelegate
self.webViewType = webViewType
}

public class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler {
Expand All @@ -70,6 +75,10 @@ public struct WebView: UIViewRepresentable {

public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
webView.isHidden = false
DispatchQueue.main.async {
self.parent.isLoading = false
self.parent.webViewNavDelegate?.showWebViewError()
}
}

public func webView(
Expand All @@ -78,6 +87,10 @@ public struct WebView: UIViewRepresentable {
withError error: Error
) {
webView.isHidden = false
DispatchQueue.main.async {
self.parent.isLoading = false
self.parent.webViewNavDelegate?.showWebViewError()
}
}

public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
Expand Down Expand Up @@ -172,7 +185,7 @@ public struct WebView: UIViewRepresentable {

private func addObservers() {
cancellables.removeAll()
NotificationCenter.default.publisher(for: .webviewReloadNotification, object: nil)
NotificationCenter.default.publisher(for: Notification.Name(parent.webViewType ?? ""), object: nil)
.sink { [weak self] _ in
self?.reload()
}
Expand All @@ -188,8 +201,16 @@ public struct WebView: UIViewRepresentable {
fileprivate var webview: WKWebView?

@objc private func reload() {
parent.isLoading = true
webview?.reload()
DispatchQueue.main.async {
self.parent.isLoading = true
}
if webview?.url?.absoluteString.isEmpty ?? true,
let url = URL(string: parent.viewModel.url) {
let request = URLRequest(url: url)
webview?.load(request)
} else {
webview?.reload()
}
}

public func userContentController(
Expand Down
14 changes: 11 additions & 3 deletions Course/Course.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,6 @@
02B6B3B528E1D10700232911 /* Domain */,
02EAE2CA28E1F0A700529644 /* Presentation */,
97EA4D822B84EFA900663F58 /* Managers */,
97CA95212B875EA200A9EDEA /* Views */,
02B6B3B428E1C49400232911 /* Localizable.strings */,
02C355372C08DCD700501342 /* Localizable.stringsdict */,
);
Expand Down Expand Up @@ -529,13 +528,20 @@
path = Mock;
sourceTree = "<group>";
};
97CA95212B875EA200A9EDEA /* Views */ = {
9784D4752BF39EEF00AFEFFF /* CalendarSyncProgressView */ = {
isa = PBXGroup;
children = (
97C99C352B9A08FE004EEDE2 /* CalendarSyncProgressView.swift */,
);
path = CalendarSyncProgressView;
sourceTree = "<group>";
};
9784D4762BF39EFD00AFEFFF /* DatesSuccessView */ = {
isa = PBXGroup;
children = (
97CA95242B875EE200A9EDEA /* DatesSuccessView.swift */,
);
path = Views;
path = DatesSuccessView;
sourceTree = "<group>";
};
97EA4D822B84EFA900663F58 /* Managers */ = {
Expand Down Expand Up @@ -586,6 +592,8 @@
BAD9CA482B2C88D500DE790A /* Subviews */ = {
isa = PBXGroup;
children = (
9784D4762BF39EFD00AFEFFF /* DatesSuccessView */,
9784D4752BF39EEF00AFEFFF /* CalendarSyncProgressView */,
02D4FC2C2BBD7C7500C47748 /* MessageSectionView */,
BAC0E0D92B32F0A2006B68A9 /* CourseVideoDownloadBarView */,
BA58CF622B471047005B102E /* VideoDownloadQualityBarView */,
Expand Down
File renamed without changes.
32 changes: 5 additions & 27 deletions Course/Course/Presentation/Unit/CourseUnitView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ public struct CourseUnitView: View {
Spacer(minLength: 150)
}
} else {
NoInternetView()
FullScreenErrorView(type: .noInternet)
}

} else {
Expand Down Expand Up @@ -219,7 +219,7 @@ public struct CourseUnitView: View {
Spacer(minLength: 150)
}
} else {
NoInternetView()
FullScreenErrorView(type: .noInternet)
}
}
// MARK: Web
Expand All @@ -233,7 +233,7 @@ public struct CourseUnitView: View {
)
// not need to add frame limit there because we did that with injection
} else {
NoInternetView()
FullScreenErrorView(type: .noInternet)
}
} else {
EmptyView()
Expand All @@ -247,7 +247,7 @@ public struct CourseUnitView: View {
Spacer()
.frame(minHeight: 100)
} else {
NoInternetView()
FullScreenErrorView(type: .noInternet)
}
} else {
EmptyView()
Expand Down Expand Up @@ -275,7 +275,7 @@ public struct CourseUnitView: View {
//No need iPad paddings there bacause they were added
//to PostsView that placed inside DiscussionView
} else {
NoInternetView()
FullScreenErrorView(type: .noInternet)
}
} else {
EmptyView()
Expand Down Expand Up @@ -586,25 +586,3 @@ struct CourseUnitView_Previews: PreviewProvider {
}
//swiftlint:enable all
#endif

struct NoInternetView: View {

var body: some View {
VStack(spacing: 28) {
Spacer()
CoreAssets.noWifi.swiftUIImage
.renderingMode(.template)
.foregroundStyle(Color.primary)
.scaledToFit()
Text(CoreLocalization.Error.Internet.noInternetTitle)
.font(Theme.Fonts.titleLarge)
.foregroundColor(Theme.Colors.textPrimary)
Text(CoreLocalization.Error.Internet.noInternetDescription)
.font(Theme.Fonts.bodyLarge)
.foregroundColor(Theme.Colors.textPrimary)
.multilineTextAlignment(.center)
.padding(.horizontal, 50)
Spacer()
}.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
Loading

0 comments on commit ea6e24e

Please sign in to comment.