Skip to content

Commit

Permalink
feat: webview discovery implimentation
Browse files Browse the repository at this point in the history
  • Loading branch information
saeedbashir committed Jan 8, 2024
1 parent 3bca8bf commit d656376
Show file tree
Hide file tree
Showing 25 changed files with 805 additions and 26 deletions.
15 changes: 15 additions & 0 deletions Core/Core.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@
E055A53A2B18DC95008D9E5E /* Theme.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E055A5382B18DC95008D9E5E /* Theme.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
E09179FD2B0F204E002AB695 /* ConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E09179FC2B0F204D002AB695 /* ConfigTests.swift */; };
E0D586362B314CD3009B4BA7 /* LogistrationBottomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D586352B314CD3009B4BA7 /* LogistrationBottomView.swift */; };
E0D5861A2B2FF74C009B4BA7 /* DiscoveryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D586192B2FF74C009B4BA7 /* DiscoveryConfig.swift */; };
E0D5861C2B2FF85B009B4BA7 /* RawStringExtactable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D5861B2B2FF85B009B4BA7 /* RawStringExtactable.swift */; };
E0D586362B314CD3009B4BA7 /* LogistrationBottomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D586352B314CD3009B4BA7 /* LogistrationBottomView.swift */; };
E0D5861A2B2FF74C009B4BA7 /* DiscoveryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D586192B2FF74C009B4BA7 /* DiscoveryConfig.swift */; };
E0D5861C2B2FF85B009B4BA7 /* RawStringExtactable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D5861B2B2FF85B009B4BA7 /* RawStringExtactable.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -316,6 +321,11 @@
E055A5382B18DC95008D9E5E /* Theme.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Theme.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E09179FC2B0F204D002AB695 /* ConfigTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigTests.swift; sourceTree = "<group>"; };
E0D586352B314CD3009B4BA7 /* LogistrationBottomView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogistrationBottomView.swift; sourceTree = "<group>"; };
E0D586192B2FF74C009B4BA7 /* DiscoveryConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryConfig.swift; sourceTree = "<group>"; };
E0D5861B2B2FF85B009B4BA7 /* RawStringExtactable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawStringExtactable.swift; sourceTree = "<group>"; };
E0D586352B314CD3009B4BA7 /* LogistrationBottomView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogistrationBottomView.swift; sourceTree = "<group>"; };
E0D586192B2FF74C009B4BA7 /* DiscoveryConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryConfig.swift; sourceTree = "<group>"; };
E0D5861B2B2FF85B009B4BA7 /* RawStringExtactable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawStringExtactable.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -430,6 +440,7 @@
BA8B3A2E2AD546A700D25EF5 /* DebugLog.swift */,
BADB3F5A2AD6EC56004D5CFA /* ResultExtension.swift */,
02D400602B0678190029D168 /* SKStoreReviewControllerExtension.swift */,
E0D5861B2B2FF85B009B4BA7 /* RawStringExtactable.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -710,6 +721,7 @@
BAFB998F2B14B377007D09F9 /* GoogleConfig.swift */,
BAFB99912B14E23D007D09F9 /* AppleSignInConfig.swift */,
A53A32342B233DEC005FE38A /* ThemeConfig.swift */,
E0D586192B2FF74C009B4BA7 /* DiscoveryConfig.swift */,
);
path = Config;
sourceTree = "<group>";
Expand Down Expand Up @@ -996,13 +1008,16 @@
BA30427F2B20B320009B64B7 /* SocialAuthError.swift in Sources */,
0284DBFE28D48C5300830893 /* CourseItem.swift in Sources */,
0248C92329C075EF00DC8402 /* CourseBlockModel.swift in Sources */,
E0D5861A2B2FF74C009B4BA7 /* DiscoveryConfig.swift in Sources */,
DBF6F2412B014ADA0098414B /* FirebaseConfig.swift in Sources */,
072787B628D37A0E002E9142 /* Validator.swift in Sources */,
0236961D28F9A2D200EEF206 /* Data_AuthResponse.swift in Sources */,
02AFCC182AEFDB24000360F0 /* ThirdPartyMailClient.swift in Sources */,
0233D5712AF13EC800BAC8BD /* SelectMailClientView.swift in Sources */,
BAFB99842B0E282E007D09F9 /* MicrosoftConfig.swift in Sources */,
02B2B594295C5C7A00914876 /* Thread.swift in Sources */,
027DB33528D8C8FE002B6862 /* Data_MyCourse.swift in Sources */,
E0D5861C2B2FF85B009B4BA7 /* RawStringExtactable.swift in Sources */,
027BD3BD2909478B00392132 /* UIView+EnclosingScrollView.swift in Sources */,
BA8FA6682AD59A5700EA029A /* SocialAuthButton.swift in Sources */,
02D400612B0678190029D168 /* SKStoreReviewControllerExtension.swift in Sources */,
Expand Down
1 change: 1 addition & 0 deletions Core/Core/Configuration/Config/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public protocol ConfigProtocol {
var features: FeaturesConfig { get }
var theme: ThemeConfig { get }
var uiComponents: UIComponentsConfig { get }
var discovery: DiscoveryConfig { get }
}

public enum TokenType: String {
Expand Down
57 changes: 57 additions & 0 deletions Core/Core/Configuration/Config/DiscoveryConfig.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// DiscoveryConfig.swift
// Core
//
// Created by SaeedBashir on 12/18/23.
//

import Foundation

public enum DiscoveryConfigType: String {
case native
case webview
case none
}

private enum DiscoveryKeys: String, RawStringExtractable {
case discoveryType = "TYPE"
case webview = "WEBVIEW"
case baseURL = "BASE_URL"
case courseDetailTemplate = "COURSE_DETAIL_TEMPLATE"
case programDetailTemplate = "PROGRAM_DETAIL_TEMPLATE"
}

public class DiscoveryWebviewConfig: NSObject {
public let baseURL: String?
public let courseDetailTemplate: String?
public let programDetailTemplate: String?

init(dictionary: [String: AnyObject]) {
baseURL = dictionary[DiscoveryKeys.baseURL] as? String
courseDetailTemplate = dictionary[DiscoveryKeys.courseDetailTemplate] as? String
programDetailTemplate = dictionary[DiscoveryKeys.programDetailTemplate] as? String
}
}

public class DiscoveryConfig: NSObject {
public let type: DiscoveryConfigType
public let webview: DiscoveryWebviewConfig

init(dictionary: [String: AnyObject]) {
type = (dictionary[DiscoveryKeys.discoveryType] as? String).flatMap {
DiscoveryConfigType(rawValue: $0)
} ?? .none
webview = DiscoveryWebviewConfig(dictionary: dictionary[DiscoveryKeys.webview] as? [String: AnyObject] ?? [:])
}

var isEnabled: Bool {
return type != .none
}
}

private let key = "DISCOVERY"
extension Config {
public var discovery: DiscoveryConfig {
DiscoveryConfig(dictionary: self[key] as? [String: AnyObject] ?? [:])
}
}
27 changes: 27 additions & 0 deletions Core/Core/Extensions/RawStringExtactable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// RawStringExtactable.swift
// Core
//
// Created by SaeedBashir on 12/18/23.
//

import Foundation

public protocol RawStringExtractable {
var rawValue: String { get }
}

public protocol DictionaryExtractionExtension {
associatedtype Key
associatedtype Value
subscript(key: Key) -> Value? { get }
}

extension Dictionary: DictionaryExtractionExtension {}

public extension DictionaryExtractionExtension where Self.Key == String {

subscript(key :RawStringExtractable) -> Value? {
return self[key.rawValue]
}
}
2 changes: 2 additions & 0 deletions Core/Core/SwiftGen/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ public enum CoreLocalization {
public enum Alert {
/// Cancel
public static let cancel = CoreLocalization.tr("Localizable", "WEBVIEW.ALERT.CANCEL", fallback: "Cancel")
/// Continue
public static let `continue` = CoreLocalization.tr("Localizable", "WEBVIEW.ALERT.CONTINUE", fallback: "Continue")
/// Ok
public static let ok = CoreLocalization.tr("Localizable", "WEBVIEW.ALERT.OK", fallback: "Ok")
}
Expand Down
19 changes: 12 additions & 7 deletions Core/Core/View/Base/AlertView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public enum AlertViewType: Equatable {
var contentPadding: CGFloat {
switch self {
case .`default`:
return 5
return 16
case .action, .logOut, .leaveProfile:
return 36
}
Expand Down Expand Up @@ -113,6 +113,7 @@ public struct AlertView: View {
Text(alertTitle)
.font(Theme.Fonts.titleLarge)
.padding(.horizontal, 40)
.padding(.top, 10)
Text(alertMessage)
.font(Theme.Fonts.bodyMedium)
.multilineTextAlignment(.center)
Expand Down Expand Up @@ -151,11 +152,15 @@ public struct AlertView: View {
HStack {
switch type {
case let .`default`(positiveAction):
StyledButton(positiveAction, action: { okTapped() })
.frame(maxWidth: 135)
StyledButton(CoreLocalization.Alert.cancel, action: { onCloseTapped() })
.frame(maxWidth: 135)
.saturation(0)
HStack {
StyledButton(positiveAction, action: { okTapped() })
.frame(maxWidth: 135)
StyledButton(CoreLocalization.Alert.cancel, action: { onCloseTapped() })
.frame(maxWidth: 135)
.saturation(0)
}
.padding(.leading, 10)
.padding(.trailing, 10)
case let .action(action, _):
if !isHorizontal {
VStack(spacing: 20) {
Expand Down Expand Up @@ -274,7 +279,7 @@ public struct AlertView: View {
}.padding(.trailing, isHorizontal ? 20 : 0)
}
}
.padding(.top, 5)
.padding(.top, 16)
.padding(.bottom, isHorizontal ? 16 : type.contentPadding)
}
Button(action: {
Expand Down
1 change: 1 addition & 0 deletions Core/Core/View/Base/LogistrationBottomView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum LogistrationSourceScreen: Equatable {
case startup
case discovery
case courseDetail(String, String)
case programDetails(String)
}

public enum LogistrationAction {
Expand Down
55 changes: 52 additions & 3 deletions Core/Core/View/Base/WebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ import WebKit
import SwiftUI
import Theme

public let WebviewReloadNotification = "webviewReloadNotification"

public protocol WebViewNavigationDelegate: AnyObject {
func webView(_ webView: WKWebView, shouldLoad request: URLRequest, navigationAction: WKNavigationAction) -> Bool
}

public struct WebView: UIViewRepresentable {

public class ViewModel: ObservableObject {
Expand All @@ -25,19 +31,30 @@ public struct WebView: UIViewRepresentable {

@ObservedObject var viewModel: ViewModel
@Binding public var isLoading: Bool
weak var webViewNavDelegate: WebViewNavigationDelegate?

var refreshCookies: () async -> Void

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

public class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate {
var parent: WebView

init(_ parent: WebView) {
self.parent = parent
super.init()

addObserver()
}

public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
Expand Down Expand Up @@ -79,6 +96,16 @@ public struct WebView: UIViewRepresentable {

guard let url = navigationAction.request.url else { return .cancel }

let isWebViewDelegateHandled = await (
parent.webViewNavDelegate?.webView(
webView,
shouldLoad: navigationAction.request, navigationAction: navigationAction) ?? false
)

if isWebViewDelegateHandled {
return .cancel
}

let baseURL = await parent.viewModel.baseURL
if !baseURL.isEmpty, !url.absoluteString.starts(with: baseURL) {
if navigationAction.navigationType == .other {
Expand Down Expand Up @@ -117,6 +144,26 @@ public struct WebView: UIViewRepresentable {
}
return .allow
}

private func addObserver() {
NotificationCenter.default.addObserver(
self,
selector: #selector(reload),
name: Notification.Name(WebviewReloadNotification),
object: nil
)
}

fileprivate var webview: WKWebView?

@objc private func reload() {
parent.isLoading = true
webview?.reload()
}

deinit {
NotificationCenter.default.removeObserver(self)
}
}

public func makeCoordinator() -> Coordinator {
Expand All @@ -130,6 +177,8 @@ public struct WebView: UIViewRepresentable {
webView.navigationDelegate = context.coordinator
webView.uiDelegate = context.coordinator

context.coordinator.webview = webView

webView.scrollView.bounces = false
webView.scrollView.alwaysBounceHorizontal = false
webView.scrollView.showsHorizontalScrollIndicator = false
Expand All @@ -139,8 +188,8 @@ public struct WebView: UIViewRepresentable {
webView.backgroundColor = .clear
webView.scrollView.backgroundColor = Theme.Colors.white.uiColor()
webView.scrollView.alwaysBounceVertical = false
webView.scrollView.layer.cornerRadius = 24
webView.scrollView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
// webView.scrollView.layer.cornerRadius = 24
// webView.scrollView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
webView.scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 200, right: 0)

return webView
Expand Down
1 change: 1 addition & 0 deletions Core/Core/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@

"WEBVIEW.ALERT.OK" = "Ok";
"WEBVIEW.ALERT.CANCEL" = "Cancel";
"WEBVIEW.ALERT.CONTINUE" = "Continue";

"REVIEW.VOTE_TITLE" = "Enjoying Open edX?";
"REVIEW.VOTE_DESCRIPTION" = "Your feedback matters to us. Would you take a moment to rate the app by tapping a star below? Thanks for your support!";
Expand Down
1 change: 1 addition & 0 deletions Core/Core/uk.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@

"WEBVIEW.ALERT.OK" = "Так";
"WEBVIEW.ALERT.CANCEL" = "Скасувати";
"WEBVIEW.ALERT.CONTINUE" = "Continue";


"REVIEW.VOTE_TITLE" = "Вам подобається Open edX?";
Expand Down
12 changes: 10 additions & 2 deletions Course/Course/Presentation/Details/CourseDetailsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,11 @@ private struct CourseStateView: View {
case .enrollOpen:
StyledButton(CourseLocalization.Details.enrollNow, action: {
if !viewModel.userloggedIn {
viewModel.router.showRegisterScreen(sourceScreen: .courseDetail(courseDetails.courseID, courseDetails.courseTitle))
viewModel.router.showRegisterScreen(
sourceScreen: .courseDetail(
courseDetails.courseID,
courseDetails.courseTitle)
)
} else {
Task {
await viewModel.enrollToCourse(id: courseDetails.courseID)
Expand All @@ -238,7 +242,11 @@ private struct CourseStateView: View {
case .alreadyEnrolled:
StyledButton(CourseLocalization.Details.viewCourse, action: {
if !viewModel.userloggedIn {
viewModel.router.showRegisterScreen(sourceScreen: .courseDetail(courseDetails.courseID, courseDetails.courseTitle))
viewModel.router.showRegisterScreen(
sourceScreen: .courseDetail(
courseDetails.courseID,
courseDetails.courseTitle)
)
} else {
viewModel.viewCourseClicked(
courseId: courseDetails.courseID,
Expand Down
Loading

0 comments on commit d656376

Please sign in to comment.