Merge branch 'develop' into feature/accessibilityHandler

This commit is contained in:
Krishna Kishore Bandaru 2023-10-27 19:08:57 +05:30
commit 48efbc73bf
7 changed files with 117 additions and 40 deletions

View File

@ -11,9 +11,19 @@ import Foundation
open class ActionOpenPageHandler: MVMCoreJSONActionHandlerProtocol { open class ActionOpenPageHandler: MVMCoreJSONActionHandlerProtocol {
required public init() {} required public init() {}
func requestParamaters(for model: ActionOpenPageModel) -> MVMCoreRequestParameters {
let requestParameters = model.requestParameters.copy() as! MVMCoreRequestParameters
if let pageType = requestParameters.pageType {
// Re-evaluate required & optional modules as action models might have been generated prior to recent additions to the mapping.
requestParameters.modules = MVMCoreViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType) as? [String]
requestParameters.optionalModules = MVMCoreViewControllerMappingObject.shared()?.modulesOptional(forPageType: pageType) as? [String]
}
return requestParameters
}
open func performAction(with JSON: [AnyHashable : Any], model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws { open func performAction(with JSON: [AnyHashable : Any], model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
guard let model = model as? ActionOpenPageModel else { return } guard let model = model as? ActionOpenPageModel else { return }
let requestParameters: MVMCoreRequestParameters = model.requestParameters.copy() as! MVMCoreRequestParameters let requestParameters = requestParamaters(for: model)
do { do {
if let closure = delegateObject?.actionDelegate?.handleOpenPage { if let closure = delegateObject?.actionDelegate?.handleOpenPage {
// Legacy code will use the old handler function and break the task chain here. // Legacy code will use the old handler function and break the task chain here.

View File

@ -14,15 +14,7 @@ open class ActionShareHandler: MVMCoreActionHandlerProtocol {
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) async throws { open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) async throws {
guard let model = model as? ActionShareModel else { return } guard let model = model as? ActionShareModel else { return }
var shareData: [Any] try await shareWith(activityItems: model.items.map { $0.value }, model: model)
switch model.sharedType {
case .text:
shareData = [model.sharedText]
case .url:
let url = try URL.createURL(with: model.sharedText)
shareData = [url]
}
try await shareWith(activityItems: shareData, model: model)
} }
@MainActor @MainActor
@ -33,17 +25,7 @@ open class ActionShareHandler: MVMCoreActionHandlerProtocol {
controller.completionWithItemsHandler = {(activityType: UIActivity.ActivityType?, completed: Bool, returnedItems: [Any]?, error: Error?) in controller.completionWithItemsHandler = {(activityType: UIActivity.ActivityType?, completed: Bool, returnedItems: [Any]?, error: Error?) in
if completed { if completed {
// Activity was completed, considered finished. // Activity was completed, considered finished.
if activityType == .copyToPasteboard {
// Allow copy
MVMCoreSessionObject.sharedGlobal()?.copyString(toClipboard: model.sharedText)
}
continuation.resume() continuation.resume()
} else if let _ = activityType {
// If a specific type of activity failed, the activity controller is still presented, cannot continue yet.
if let error = error,
let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: MVMCoreActionHandler.getErrorLocation(with: delegateObject?.actionDelegate, actionType: model.actionType)) {
MVMCoreLoggingHandler.addError(toLog: errorObject)
}
} else if let error = error { } else if let error = error {
continuation.resume(throwing: error) continuation.resume(throwing: error)
} else { } else {

View File

@ -6,14 +6,51 @@
// Copyright © 2020 myverizon. All rights reserved. // Copyright © 2020 myverizon. All rights reserved.
// //
public struct ActionShareItemModel: Codable {
public struct ActionShareModel: ActionModelProtocol {
public enum SharedType: String, Codable { public enum SharedType: String, Codable {
case text case text
case url case url
} }
public var type: SharedType
public var value: Any
private enum CodingKeys: String, CodingKey {
case type
case value
}
public init(type: SharedType, value: Any) {
self.type = type
self.value = value
}
public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
type = try typeContainer.decode(SharedType.self, forKey: .type)
switch type {
case .text:
value = try typeContainer.decode(String.self, forKey: .value)
case .url:
value = try typeContainer.decode(URL.self, forKey: .value)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(type, forKey: .type)
switch type {
case .text:
try container.encode(value as! String, forKey: .value)
case .url:
try container.encode(value as! URL, forKey: .value)
}
}
}
public struct ActionShareModel: ActionModelProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -21,8 +58,7 @@ public struct ActionShareModel: ActionModelProtocol {
public static var identifier: String = "share" public static var identifier: String = "share"
public var actionType: String = ActionShareModel.identifier public var actionType: String = ActionShareModel.identifier
public var sharedType: SharedType public var items: [ActionShareItemModel]
public var sharedText: String
public var extraParameters: JSONValueDictionary? public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary? public var analyticsData: JSONValueDictionary?
@ -30,10 +66,54 @@ public struct ActionShareModel: ActionModelProtocol {
// MARK: - Initializer // MARK: - Initializer
//-------------------------------------------------- //--------------------------------------------------
public init(sharedText: String, sharedType: SharedType, _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) { public init(items: [ActionShareItemModel], _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.sharedType = sharedType self.items = items
self.sharedText = sharedText
self.extraParameters = extraParameters self.extraParameters = extraParameters
self.analyticsData = analyticsData self.analyticsData = analyticsData
} }
//--------------------------------------------------
// MARK: - Codable
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case actionType
case items
case sharedType
case sharedText
}
private enum DeprecatedCodingKeys: String, CodingKey {
case sharedType
case sharedText
}
public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
if let items = try typeContainer.decodeIfPresent([ActionShareItemModel].self, forKey: .items) {
self.init(items: items)
} else {
// Legacy
try self.init(deprecatedFrom: decoder)
}
}
private init(deprecatedFrom decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: DeprecatedCodingKeys.self)
let type = try typeContainer.decode(ActionShareItemModel.SharedType.self, forKey: .sharedType)
var value: Any
switch type {
case .url:
value = try typeContainer.decode(URL.self, forKey: .sharedText)
default:
value = try typeContainer.decode(String.self, forKey: .sharedText)
}
items = [ActionShareItemModel(type: type, value: value)]
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(actionType, forKey: .actionType)
try container.encode(items, forKey: .items)
}
} }

View File

@ -299,6 +299,9 @@
if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(getJSONForRequestParameters:)]) { if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(getJSONForRequestParameters:)]) {
NSDictionary *json = [[MVMCoreObject sharedInstance].globalLoadDelegate getJSONForRequestParameters:requestParameters]; NSDictionary *json = [[MVMCoreObject sharedInstance].globalLoadDelegate getJSONForRequestParameters:requestParameters];
if (json) { if (json) {
#if HARD_CODED_RESPONSE_DELAY > 0
[NSThread sleepForTimeInterval:HARD_CODED_RESPONSE_DELAY];
#endif
completionHandler(json); completionHandler(json);
return; return;
} }

View File

@ -104,13 +104,17 @@
timingOutIn: timeout) { (receivedParameter) in timingOutIn: timeout) { (receivedParameter) in
// Queue the results for merge. // Queue the results for merge.
parametersWorkQueue.async { parametersWorkQueue.async {
if (returnedList[index] != nil) { guard !complete else {
MVMCoreLoggingHandler.addError(toLog: MVMCoreErrorObject(title: nil, message: "Client parameter \(parameterType) has already executed. The completion handler should only be called once!", code: ErrorCode.default.rawValue, domain: ErrorDomainNative, location: String(describing: ClientParameterHandler.self))!) MVMCoreLoggingHandler.logDebugMessage(withDelegate: "Client \(parameterType) responded after task completion.")
} else { return
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterFetchComplete(name: parameterType, uuid: requestUUID[index], actionId: actionId))
returnedList[index] = receivedParameter
group.leave() // Leaving is only done after setup (barriered).
} }
guard returnedList[index] == nil else {
MVMCoreLoggingHandler.addError(toLog: MVMCoreErrorObject(title: nil, message: "Client parameter \(parameterType) has already executed. The completion handler should only be called once!", code: ErrorCode.default.rawValue, domain: ErrorDomainNative, location: String(describing: ClientParameterHandler.self))!)
return
}
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterFetchComplete(name: parameterType, uuid: requestUUID[index], actionId: actionId))
returnedList[index] = receivedParameter
group.leave() // Leaving is only done after setup (barriered).
} }
} }
} }

View File

@ -15,7 +15,7 @@ public protocol ClientParameterProtocol: ModelHandlerProtocol {
var clientParameterModel: ClientParameterModelProtocol { get set } var clientParameterModel: ClientParameterModelProtocol { get set }
func fetchClientParameters(requestParameters: [String: Any], timingOutIn timeout: Double, completionHandler:@escaping ([String: AnyHashable]?) -> ()) func fetchClientParameters(requestParameters: [String: Any], timingOutIn timeout: TimeInterval, completionHandler:@escaping ([String: AnyHashable]?) -> ())
/// Default parameter for timeout scenarios. It will use the protocol extension method bydefault. Can override to send custom values. /// Default parameter for timeout scenarios. It will use the protocol extension method bydefault. Can override to send custom values.
func valueOnTimeout() -> [String: AnyHashable] func valueOnTimeout() -> [String: AnyHashable]
@ -28,10 +28,8 @@ public extension ClientParameterProtocol {
} }
/// The handler should call this method to pass the parameter back to the caller. /// The handler should call this method to pass the parameter back to the caller.
func returnParameters(_ isFlatMap: Bool, _ parameter: [String: AnyHashable]?, completionHandler: @escaping ([String: AnyHashable]?) -> ()) { /// If using isFlatMap, you must provide at least 1 element in parameters or it will result in triggering a timeout.
guard let parameter = parameter else { func returnParameters(_ isFlatMap: Bool, _ parameter: [String: AnyHashable], completionHandler: @escaping ([String: AnyHashable]?) -> ()) {
return completionHandler(nil)
}
if isFlatMap { if isFlatMap {
completionHandler(parameter) completionHandler(parameter)
} else { } else {

View File

@ -27,7 +27,7 @@ public extension MVMCoreActionUtility {
- Parameter timemout: the time the operation has to finish before throwing a timeout error. - Parameter timemout: the time the operation has to finish before throwing a timeout error.
- Parameter operation: the operation to perform. - Parameter operation: the operation to perform.
*/ */
static func perform<T>(with timeout: Int = 1, operation: @escaping @Sendable () async throws -> T) async throws -> T { static func perform<T>(withTimeout timeout: TimeInterval, operation: @escaping @Sendable () async throws -> T) async throws -> T {
return try await withCheckedThrowingContinuation { continuation in return try await withCheckedThrowingContinuation { continuation in
Task { Task {
await withThrowingTaskGroup(of: T.self) { group in await withThrowingTaskGroup(of: T.self) { group in
@ -40,7 +40,7 @@ public extension MVMCoreActionUtility {
// Task for time out. // Task for time out.
group.addTask { group.addTask {
try Task.checkCancellation() try Task.checkCancellation()
try await Task.sleep(nanoseconds: UInt64(timeout) * NSEC_PER_SEC) try await Task.sleep(nanoseconds: UInt64(timeout * TimeInterval(NSEC_PER_SEC)))
throw MVMCoreActionUtilityError.timeOut throw MVMCoreActionUtilityError.timeOut
} }