From e64a68b81c7da6d7a7e248356150b93eb5dcd45f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 17:14:41 +0000 Subject: [PATCH 01/35] Update dependency fastlane to v2.214.0 --- Gemfile.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8f6cf72e..a60d0cd6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,20 +17,20 @@ GEM artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.768.0) - aws-sdk-core (3.173.0) + aws-partitions (1.786.0) + aws-sdk-core (3.178.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.5) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.64.0) - aws-sdk-core (~> 3, >= 3.165.0) + aws-sdk-kms (1.71.0) + aws-sdk-core (~> 3, >= 3.177.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.122.0) - aws-sdk-core (~> 3, >= 3.165.0) + aws-sdk-s3 (1.130.0) + aws-sdk-core (~> 3, >= 3.177.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.4) - aws-sigv4 (1.5.2) + aws-sigv4 (~> 1.6) + aws-sigv4 (1.6.0) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) claide (1.1.0) @@ -77,7 +77,7 @@ GEM highline (~> 2.0.0) concurrent-ruby (1.2.2) declarative (0.0.20) - digest-crc (0.6.4) + digest-crc (0.6.5) rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) @@ -86,7 +86,7 @@ GEM escape (0.0.4) ethon (0.16.0) ffi (>= 1.15.0) - excon (0.99.0) + excon (0.100.0) faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -116,7 +116,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.2.7) - fastlane (2.213.0) + fastlane (2.214.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -159,7 +159,7 @@ GEM fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.42.0) + google-apis-androidpublisher_v3 (0.45.0) google-apis-core (>= 0.11.0, < 2.a) google-apis-core (0.11.0) addressable (~> 2.5, >= 2.5.1) @@ -190,7 +190,7 @@ GEM google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.5.2) + googleauth (1.6.0) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) memoist (~> 0.16) @@ -205,7 +205,7 @@ GEM concurrent-ruby (~> 1.0) jmespath (1.6.2) json (2.6.3) - jwt (2.7.0) + jwt (2.7.1) memoist (0.16.2) mini_magick (4.12.0) mini_mime (1.1.2) From ca008a5d1b3c38e26cdf12672e017bb03bb0d21c Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Fri, 21 Jul 2023 13:13:04 +0600 Subject: [PATCH 02/35] MBX-2734 Add CrossView --- Mindbox.xcodeproj/project.pbxproj | 24 ++++-- Mindbox/Extensions/UIColor+Extensions.swift | 32 +++++++ .../InAppMessageViewController.swift | 51 ++++++++++-- .../Presentation/Views/CrossView.swift | 83 +++++++++++++++++++ .../Views/InAppImageOnlyView.swift | 35 -------- Mindbox/Info.plist | 2 +- 6 files changed, 179 insertions(+), 48 deletions(-) create mode 100644 Mindbox/Extensions/UIColor+Extensions.swift create mode 100644 Mindbox/InAppMessages/Presentation/Views/CrossView.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 8ef0f4c7..23318d6b 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -317,6 +317,8 @@ F3482F212A65DC2C002A41EC /* URLInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F202A65DC2C002A41EC /* URLInappMessageDelegate.swift */; }; F3482F232A65DC37002A41EC /* InAppMessagesDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F222A65DC37002A41EC /* InAppMessagesDelegate.swift */; }; F3482F2A2A65DCFC002A41EC /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F292A65DCFC002A41EC /* String+Extensions.swift */; }; + F37613D72A6A66FB009F2EE4 /* CrossView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F37613D62A6A66FB009F2EE4 /* CrossView.swift */; }; + F37613E12A6A8CFF009F2EE4 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F37613E02A6A8CFF009F2EE4 /* UIColor+Extensions.swift */; }; F397EEAD2A4456C300D48CEC /* ProtocolError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F397EEAC2A4456C300D48CEC /* ProtocolError.swift */; }; F397EEAF2A4456FD00D48CEC /* MindboxError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F397EEAE2A4456FD00D48CEC /* MindboxError.swift */; }; F397EEB12A44571300D48CEC /* UnknownDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F397EEB02A44571300D48CEC /* UnknownDecodable.swift */; }; @@ -724,6 +726,8 @@ F3482F202A65DC2C002A41EC /* URLInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLInappMessageDelegate.swift; sourceTree = ""; }; F3482F222A65DC37002A41EC /* InAppMessagesDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppMessagesDelegate.swift; sourceTree = ""; }; F3482F292A65DCFC002A41EC /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = ""; }; + F37613D62A6A66FB009F2EE4 /* CrossView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrossView.swift; sourceTree = ""; }; + F37613E02A6A8CFF009F2EE4 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = ""; }; F397EEAC2A4456C300D48CEC /* ProtocolError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolError.swift; sourceTree = ""; }; F397EEAE2A4456FD00D48CEC /* MindboxError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxError.swift; sourceTree = ""; }; F397EEB02A44571300D48CEC /* UnknownDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnknownDecodable.swift; sourceTree = ""; }; @@ -858,6 +862,7 @@ 313B233225ADEA0F00A1CB72 /* Mindbox */ = { isa = PBXGroup; children = ( + F37613DF2A6A8CFF009F2EE4 /* Extensions */, A1AC0431299A6B8700476E22 /* MindboxLogger */, 313B233325ADEA0F00A1CB72 /* Mindbox.h */, 313B233425ADEA0F00A1CB72 /* Info.plist */, @@ -1453,7 +1458,6 @@ isa = PBXGroup; children = ( F3A8B9552A389D8300E9C055 /* InAppConfigurationMapper */, - 9B7F2C5228DACD4B002ABDB0 /* InAppMessagesDelegate.swift */, F3482F142A65DBA0002A41EC /* InappMessagesDelegate */, 9B24FAAD28C74BA500F10B5D /* InAppCoreManager.swift */, 9B2F5F742902975B00F28A96 /* InAppMessagesTracker.swift */, @@ -1524,6 +1528,7 @@ isa = PBXGroup; children = ( 9B4F9DFD28D088B4002C9CF0 /* InAppImageOnlyView.swift */, + F37613D62A6A66FB009F2EE4 /* CrossView.swift */, ); path = Views; sourceTree = ""; @@ -1885,6 +1890,14 @@ path = CommonProtocols; sourceTree = ""; }; + F37613DF2A6A8CFF009F2EE4 /* Extensions */ = { + isa = PBXGroup; + children = ( + F37613E02A6A8CFF009F2EE4 /* UIColor+Extensions.swift */, + ); + path = Extensions; + sourceTree = ""; + }; F397EEAB2A4456A900D48CEC /* MindboxError */ = { isa = PBXGroup; children = ( @@ -2324,7 +2337,6 @@ 337A473A26550FDA000DC613 /* CustomFields.swift in Sources */, B3A6255C268A025600B6A3B7 /* NearestExpirationResponse.swift in Sources */, 84A312BD25DA651C0096A017 /* UIBackgroundTaskManager.swift in Sources */, - 9B7F2C5328DACD4B002ABDB0 /* InAppMessagesDelegate.swift in Sources */, F3A8B99A2A3A471800E9C055 /* ABTestVariantsValidator.swift in Sources */, 33E42E5C268323E60046CBCB /* CashdeskRequest.swift in Sources */, 84A312B725DA64C60096A017 /* BGTaskManager.swift in Sources */, @@ -2372,6 +2384,7 @@ 334F3AE7264C199900A6AC00 /* CatalogProductListRequest.swift in Sources */, B3A625562689FDD400B6A3B7 /* BalanceResponse.swift in Sources */, 6FDD1459266F7CB700A50C35 /* ContentResponse.swift in Sources */, + F37613E12A6A8CFF009F2EE4 /* UIColor+Extensions.swift in Sources */, F3A8B9582A389D9D00E9C055 /* GeoService.swift in Sources */, 334F3AE2264C199900A6AC00 /* CouponRequest.swift in Sources */, F3A8B9942A3A409C00E9C055 /* Validator.swift in Sources */, @@ -2471,7 +2484,7 @@ 337A474026553938000DC613 /* Sex.swift in Sources */, F3A8B9922A3A408C00E9C055 /* SDKVersionValidator.swift in Sources */, 9B4F9DEF28D08897002C9CF0 /* SegmentationCheckResponse.swift in Sources */, - 9B4F9DEF28D08897002C9CF0 /* InAppSegmentationChecker.swift in Sources */, + 9B4F9DEF28D08897002C9CF0 /* SegmentationCheckResponse.swift in Sources */, F3482F212A65DC2C002A41EC /* URLInappMessageDelegate.swift in Sources */, 84A312C325DA65690096A017 /* BackgroundTaskManagerType.swift in Sources */, 84CC798025CABB0B00C062BD /* Event.swift in Sources */, @@ -2516,6 +2529,7 @@ 6FDD1443266F7C1600A50C35 /* BalanceTypeReponse.swift in Sources */, 337C7954265652A80092B580 /* ProductCategoryRequest.swift in Sources */, 334F3AF1264C199900A6AC00 /* PoolRequest.swift in Sources */, + F37613D72A6A66FB009F2EE4 /* CrossView.swift in Sources */, 3191C2A625ADFD8000E7D6B9 /* Mindbox.swift in Sources */, A184654529C310F100E64780 /* InAppOperationJSONModel.swift in Sources */, A154E330299E0F1600F8F074 /* InAppGeoResponse.swift in Sources */, @@ -2839,7 +2853,6 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.7.0; MARKETING_VERSION = 2.6.4; PRODUCT_BUNDLE_IDENTIFIER = cloud.Mindbox; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -2868,7 +2881,6 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.7.0; MARKETING_VERSION = 2.6.4; PRODUCT_BUNDLE_IDENTIFIER = cloud.Mindbox; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -2933,7 +2945,6 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.7.0; MARKETING_VERSION = 2.6.4; PRODUCT_BUNDLE_IDENTIFIER = cloud.MindboxNotifications; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -2962,7 +2973,6 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.7.0; MARKETING_VERSION = 2.6.4; PRODUCT_BUNDLE_IDENTIFIER = cloud.MindboxNotifications; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; diff --git a/Mindbox/Extensions/UIColor+Extensions.swift b/Mindbox/Extensions/UIColor+Extensions.swift new file mode 100644 index 00000000..9e23c800 --- /dev/null +++ b/Mindbox/Extensions/UIColor+Extensions.swift @@ -0,0 +1,32 @@ +// +// UIColor+Extensions.swift +// FirebaseCore +// +// Created by vailence on 21.07.2023. +// + +import UIKit + +extension UIColor { + convenience init(hex: String) { + var cString: String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() + + if cString.hasPrefix("#") { + cString.remove(at: cString.startIndex) + } + + if cString.count != 6 { + self.init(white: 1.0, alpha: 1.0) + } else { + var rgbValue: UInt32 = 0 + Scanner(string: cString).scanHexInt32(&rgbValue) + + self.init( + red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, + green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(rgbValue & 0x0000FF) / 255.0, + alpha: CGFloat(1.0) + ) + } + } +} diff --git a/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift b/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift index 871966da..e1f2f432 100644 --- a/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift +++ b/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift @@ -8,6 +8,10 @@ import UIKit final class InAppMessageViewController: UIViewController { + + var crossView: CrossView? + var inAppView: InAppImageOnlyView? + var crossSize: CGFloat = 24 init( inAppUIModel: InAppMessageUIModel, @@ -34,9 +38,19 @@ final class InAppMessageViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .black.withAlphaComponent(0.2) - - let inAppView = InAppImageOnlyView(uiModel: inAppUIModel) + + inAppView = InAppImageOnlyView(uiModel: inAppUIModel) + crossView = CrossView(lineColorHex: "000000", lineWidth: 1) + + guard let inAppView = inAppView, + let crossView = crossView else { + return + } + let onTapDimmedViewGesture = UITapGestureRecognizer(target: self, action: #selector(onTapDimmedView)) + view.addGestureRecognizer(onTapDimmedViewGesture) + view.isUserInteractionEnabled = true view.addSubview(inAppView) + inAppView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ inAppView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), @@ -46,12 +60,35 @@ final class InAppMessageViewController: UIViewController { ]) let imageTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(onTapImage)) inAppView.addGestureRecognizer(imageTapGestureRecognizer) - inAppView.onClose = { [weak self] in self?.onClose() } - let closeTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(onTapDimmedView)) - view.addGestureRecognizer(closeTapRecognizer) + inAppView.addSubview(crossView) + crossView.isUserInteractionEnabled = true + + let closeTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(onCloseButton)) + crossView.addGestureRecognizer(closeTapRecognizer) } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + guard let inAppView = inAppView, + let crossView = crossView else { + return + } + + let trailingOffsetPercent: CGFloat = 3 + let topOffsetPercent: CGFloat = 3 + let horizontalOffset = (inAppView.frame.width - crossSize) * trailingOffsetPercent / 100 + let verticalOffset = (inAppView.frame.height - crossSize) * topOffsetPercent / 100 + crossView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + crossView.trailingAnchor.constraint(equalTo: inAppView.trailingAnchor, constant: -horizontalOffset), + crossView.topAnchor.constraint(equalTo: inAppView.topAnchor, constant: verticalOffset), + crossView.widthAnchor.constraint(equalToConstant: crossSize), + crossView.heightAnchor.constraint(equalToConstant: crossSize) + ]) + } private var viewWillAppearWasCalled = false override func viewWillAppear(_ animated: Bool) { @@ -61,6 +98,10 @@ final class InAppMessageViewController: UIViewController { onPresented() } + @objc private func onCloseButton() { + onClose() + } + @objc private func onTapDimmedView() { onClose() } diff --git a/Mindbox/InAppMessages/Presentation/Views/CrossView.swift b/Mindbox/InAppMessages/Presentation/Views/CrossView.swift new file mode 100644 index 00000000..1075ad3e --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Views/CrossView.swift @@ -0,0 +1,83 @@ +// +// CrossView.swift +// Mindbox +// +// Created by vailence on 21.07.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit + +class CrossView: UIView { + + var lineColor: UIColor = .black + var lineWidth: CGFloat = 1.0 + var padding: CGFloat = 2.0 + + override init(frame: CGRect) { + super.init(frame: frame) + setupDefaults() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupDefaults() + } + + convenience init(lineColorHex: String, lineWidth: CGFloat) { + self.init() + setupView(lineColorHex: lineColorHex, lineWidth: lineWidth) + } + + private func setupDefaults() { + backgroundColor = .clear + } + + private func setupView(lineColorHex: String, lineWidth: CGFloat) { + self.lineColor = UIColor(hex: lineColorHex) + self.lineWidth = lineWidth + } + + override func draw(_ rect: CGRect) { + let path = drawCross(in: rect) + strokePath(path) + } + + private func drawCross(in rect: CGRect) -> UIBezierPath { + let path = UIBezierPath() + path.lineWidth = lineWidth + path.lineCapStyle = .round + + let centerX = rect.width / 2 + let centerY = rect.height / 2 + let halfWidth = (rect.width - padding * 2) / 2 + let halfHeight = (rect.height - padding * 2) / 2 + + let leftTop = CGPoint(x: centerX - halfWidth, y: centerY - halfHeight) + let rightBottom = CGPoint(x: centerX + halfWidth, y: centerY + halfHeight) + let rightTop = CGPoint(x: centerX + halfWidth, y: centerY - halfHeight) + let leftBottom = CGPoint(x: centerX - halfWidth, y: centerY + halfHeight) + + path.move(to: leftTop) + path.addLine(to: rightBottom) + path.move(to: rightTop) + path.addLine(to: leftBottom) + + return path + } + + private func strokePath(_ path: UIBezierPath) { + lineColor.setStroke() + path.stroke() + addShapeLayer(for: path) + } + + private func addShapeLayer(for path: UIBezierPath) { + let shapeLayer = CAShapeLayer() + shapeLayer.path = path.cgPath + shapeLayer.strokeColor = lineColor.cgColor + shapeLayer.lineWidth = lineWidth + shapeLayer.lineCap = .round + self.layer.addSublayer(shapeLayer) + } +} diff --git a/Mindbox/InAppMessages/Presentation/Views/InAppImageOnlyView.swift b/Mindbox/InAppMessages/Presentation/Views/InAppImageOnlyView.swift index 2ccd255c..16a58e56 100644 --- a/Mindbox/InAppMessages/Presentation/Views/InAppImageOnlyView.swift +++ b/Mindbox/InAppMessages/Presentation/Views/InAppImageOnlyView.swift @@ -8,12 +8,7 @@ import UIKit final class InAppImageOnlyView: UIView { - - let crossImageBase64String = "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAABAlBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////85PUE7P0NBRUg+Qka9v8DAwcM9QUXNzs/CxMXKzM27vL2xsrStr7FDR0u4uru1trjHyMmqrK7P0NHa2tvS09SnqapmaWxfY2ZTVlrk5eXExcZ6fYB0d3laXmHQpZCoAAAAN3RSTlMABNgSCgi1Ft7RyL+6rKmloI4aEAzr587Fm3wgFOHQvbBgDfni38uIdWRTRyj005ZpQTKSbldOze2ocQAAAltJREFUSMd1lmlD2kAQhicHQe5QQkAKyCFWFK9eG6pQlFZsadVW/f9/RdmDnckm8yWbnedddjdzABv7Wu6wsPDZhRQLxqXuKF/9KF+zBSasfpTMj+sSKA84v8O29iEBt3ztbwQAsMeQ+Vacdz3sLwMcM2K9DOVrTeq/gAqjVggwn83F3BXo8Of64WUhp/I1zb+z5eTq8f4fH3RgyJ/3UTS9UidzFP9e3cevKIqe+GgI4soeIqToOgl89MyHIYhD3c2xolHj+yH8zzUfl6AlJmdE0XQB2mr/Pzj/W7x8AedEKqZv0/OZhDzLzWP+m+RPAeBcOq6Ioloi/HfxMuTh5CcqknjWF8HiYcXlAvNLwh+CsO12b+KK5VTzODRrXVNh8mc4ZGyi+I/5a+na1TgOmlutWJl8uuIunceBjBUr/lD8JzDNUXclUCFTnxFxOBmxAvMtSDZrVyswfwipAn0MfeB0QVDEvFKkH8E5wB9vTi61Ypl8OxYeNCx6GWP9XDxkqaIYr1cNmhRmaO8Rhds0eJQ85m9YRYM305l5+uRlhkuH5E1FRfFnzCw1RolBKddK5nER+6OLgK73i0vMVytJipMJgDXC/I3ESvwiTIUH0E/iCxmAgQqVv1gxAVHhrgl/MODBYhPFWqaGrcv9rQRyjszzHax4lk0ulA1F86ftbUPZR4onsVmwZct6nKn1JU8US9WyeuAzanm5H9SEsLXg2Gyi2LIN6p/EGnvZSJQBbfybNXjjTU9eC3XyXMBvTyVDmPLnpF+XgKda+FFpn4XFcxdSLDPujeq2f7EZvwIvrFdkd3alfwAAAABJRU5ErkJggg==" - - var onClose: (() -> Void)? let imageView = UIImageView() - let closeButton = UIButton(frame: .zero) let uiModel: InAppMessageUIModel init(uiModel: InAppMessageUIModel) { @@ -33,36 +28,6 @@ final class InAppImageOnlyView: UIView { imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight] addSubview(imageView) - - var closeImage: UIImage? - if let base64Image = base64ToImage(base64String: crossImageBase64String) { - closeImage = base64Image - } - - closeButton.setImage(closeImage, for: .normal) - closeButton.contentVerticalAlignment = .fill - closeButton.contentHorizontalAlignment = .fill - closeButton.imageView?.contentMode = .scaleAspectFit - closeButton.addTarget(self, action: #selector(onTapCloseButton), for: .touchUpInside) - addSubview(closeButton) - closeButton.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - closeButton.topAnchor.constraint(equalTo: self.topAnchor, constant: 8), - closeButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -8), - closeButton.widthAnchor.constraint(equalToConstant: 24), - closeButton.heightAnchor.constraint(equalToConstant: 24) - ]) layer.masksToBounds = true } - - func base64ToImage(base64String: String) -> UIImage? { - if let data = Data(base64Encoded: base64String) { - return UIImage(data: data) - } - return nil - } - - @objc func onTapCloseButton() { - self.onClose?() - } } diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index 9b3cc546..8489a861 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4654 + 4658 From 7fc7762ae5e5d3415c1ccd033935d9e049a908a4 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Mon, 24 Jul 2023 14:45:09 +0600 Subject: [PATCH 03/35] MBX-2734 fix offset formula --- .../Presentation/InAppMessageViewController.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift b/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift index e1f2f432..055e79a8 100644 --- a/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift +++ b/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift @@ -76,11 +76,11 @@ final class InAppMessageViewController: UIViewController { return } - let trailingOffsetPercent: CGFloat = 3 - let topOffsetPercent: CGFloat = 3 + let trailingOffsetPercent: CGFloat = 0.03 + let topOffsetPercent: CGFloat = 0.03 - let horizontalOffset = (inAppView.frame.width - crossSize) * trailingOffsetPercent / 100 - let verticalOffset = (inAppView.frame.height - crossSize) * topOffsetPercent / 100 + let horizontalOffset = (inAppView.frame.width - crossSize) * trailingOffsetPercent + let verticalOffset = (inAppView.frame.height - crossSize) * topOffsetPercent crossView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ crossView.trailingAnchor.constraint(equalTo: inAppView.trailingAnchor, constant: -horizontalOffset), From 4f050f1a83a84928a66069e382d39e2cba07b29d Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Thu, 27 Jul 2023 13:11:33 +0600 Subject: [PATCH 04/35] MBX-2734 Long press action --- .../InAppMessageViewController.swift | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift b/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift index 055e79a8..c13f1966 100644 --- a/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift +++ b/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift @@ -64,8 +64,9 @@ final class InAppMessageViewController: UIViewController { inAppView.addSubview(crossView) crossView.isUserInteractionEnabled = true - let closeTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(onCloseButton)) - crossView.addGestureRecognizer(closeTapRecognizer) + let closeRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(onCloseButton)) + closeRecognizer.minimumPressDuration = 0 + crossView.addGestureRecognizer(closeRecognizer) } override func viewDidLayoutSubviews() { @@ -98,8 +99,16 @@ final class InAppMessageViewController: UIViewController { onPresented() } - @objc private func onCloseButton() { - onClose() + @objc private func onCloseButton(_ gesture: UILongPressGestureRecognizer) { + guard let crossView = crossView else { + return + } + + let location = gesture.location(in: crossView) + let isInsideCrossView = crossView.bounds.contains(location) + if gesture.state == .ended && isInsideCrossView { + onClose() + } } @objc private func onTapDimmedView() { From 15d01e41f3d5bda848fff907c47f0d218fcaea70 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Mon, 7 Aug 2023 09:11:14 +0600 Subject: [PATCH 05/35] MBX-2706 Added failable array --- Mindbox.xcodeproj/project.pbxproj | 4 +++ .../Models/Config/ConfigResponse.swift | 7 ++-- .../Model/Common/FailableDecodableArray.swift | 35 +++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 Mindbox/Model/Common/FailableDecodableArray.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 23318d6b..204664cd 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -309,6 +309,7 @@ F31801FB2A386A5B0021774C /* InappConfigResponseSettingsInvalid.json in Resources */ = {isa = PBXBuildFile; fileRef = F31801FA2A386A5B0021774C /* InappConfigResponseSettingsInvalid.json */; }; F31801FE2A386BE60021774C /* InappConfigResponseAbtestsInvalid.json in Resources */ = {isa = PBXBuildFile; fileRef = F31801FC2A386BE60021774C /* InappConfigResponseAbtestsInvalid.json */; }; F31801FF2A386BE60021774C /* InappConfigResponseMonitoringInvalid.json in Resources */ = {isa = PBXBuildFile; fileRef = F31801FD2A386BE60021774C /* InappConfigResponseMonitoringInvalid.json */; }; + F331DC842A80983000222120 /* FailableDecodableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC832A80983000222120 /* FailableDecodableArray.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -718,6 +719,7 @@ F31801FA2A386A5B0021774C /* InappConfigResponseSettingsInvalid.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = InappConfigResponseSettingsInvalid.json; sourceTree = ""; }; F31801FC2A386BE60021774C /* InappConfigResponseAbtestsInvalid.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = InappConfigResponseAbtestsInvalid.json; sourceTree = ""; }; F31801FD2A386BE60021774C /* InappConfigResponseMonitoringInvalid.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = InappConfigResponseMonitoringInvalid.json; sourceTree = ""; }; + F331DC832A80983000222120 /* FailableDecodableArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailableDecodableArray.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -1140,6 +1142,7 @@ 337A473826550EEB000DC613 /* Common */ = { isa = PBXGroup; children = ( + F331DC832A80983000222120 /* FailableDecodableArray.swift */, 334F3ACC264C199900A6AC00 /* CodableDictionary.swift */, 337A473926550FDA000DC613 /* CustomFields.swift */, 337A473B265510AA000DC613 /* IDS.swift */, @@ -2351,6 +2354,7 @@ 337A473C265510AA000DC613 /* IDS.swift in Sources */, 317F1FD525B879B200B54346 /* MBUtilitiesFetcher.swift in Sources */, 337A473E265528B5000DC613 /* MBDate.swift in Sources */, + F331DC842A80983000222120 /* FailableDecodableArray.swift in Sources */, F3A8B99E2A3A4FD600E9C055 /* ABTestValidator.swift in Sources */, 334F3AE0264C199900A6AC00 /* ViewProductCategoryRequest.swift in Sources */, A1D23AF029DE082E00A75179 /* InAppProductSegmentResponse.swift in Sources */, diff --git a/Mindbox/InAppMessages/Models/Config/ConfigResponse.swift b/Mindbox/InAppMessages/Models/Config/ConfigResponse.swift index 0589e7c3..301bf2a4 100644 --- a/Mindbox/InAppMessages/Models/Config/ConfigResponse.swift +++ b/Mindbox/InAppMessages/Models/Config/ConfigResponse.swift @@ -9,7 +9,7 @@ import Foundation import MindboxLogger struct ConfigResponse: Decodable { - let inapps: [InApp]? + let inapps: FailableDecodableArray? let monitoring: Monitoring? let settings: Settings? let abtests: [ABTest]? @@ -20,7 +20,8 @@ struct ConfigResponse: Decodable { init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - inapps = ConfigResponse.decodeIfPresent(container, forKey: .inapps, errorDesc: "Cannot decode InApps") + + inapps = try? container.decodeIfPresent(FailableDecodableArray.self, forKey: .inapps) monitoring = ConfigResponse.decodeIfPresent(container, forKey: .monitoring, errorDesc: "Cannot decode Monitoring") settings = ConfigResponse.decodeIfPresent(container, forKey: .settings, errorDesc: "Cannot decode Settings") @@ -45,7 +46,7 @@ struct ConfigResponse: Decodable { } } - init(inapps: [InApp]? = nil, monitoring: Monitoring? = nil, settings: Settings? = nil, abtests: [ABTest]? = nil) { + init(inapps: FailableDecodableArray? = nil, monitoring: Monitoring? = nil, settings: Settings? = nil, abtests: [ABTest]? = nil) { self.inapps = inapps self.monitoring = monitoring self.settings = settings diff --git a/Mindbox/Model/Common/FailableDecodableArray.swift b/Mindbox/Model/Common/FailableDecodableArray.swift new file mode 100644 index 00000000..04d15ca3 --- /dev/null +++ b/Mindbox/Model/Common/FailableDecodableArray.swift @@ -0,0 +1,35 @@ +// +// FailableDecodableArray.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct FailableDecodableArray: Decodable, Equatable { + + let elements: [Element] + + init(from decoder: Decoder) throws { + var container = try decoder.unkeyedContainer() + + var elements = [Element]() + if let count = container.count { + elements.reserveCapacity(count) + } + + while !container.isAtEnd { + if let element = try? container.decode(Element.self) { + elements.append(element) + } else { + _ = try? container.decode(DummyCodable.self) // "Consumes" the failed element + } + } + + self.elements = elements + } + + private struct DummyCodable: Decodable { } +} From f20179bbffa85d2e08a71f5a20b5792b4dc7e72e Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Mon, 7 Aug 2023 09:13:38 +0600 Subject: [PATCH 06/35] MBX-2706 Updated Inapp model --- Mindbox.xcodeproj/project.pbxproj | 272 +++++++++++++++++- .../InAppConfigutationMapper.swift | 2 +- .../Models/Config/InAppModel.swift | 34 --- .../Models/Config/InappModel/InAppModel.swift | 39 +++ .../InappModel/InappForm/InappForm.swift | 32 +++ .../InappFormVariant/InappFormVariant.swift | 33 +++ .../ContentBackground/ContentBackground.swift | 32 +++ .../ContentBackgroundLayer.swift | 36 +++ .../ContentBackgroundLayerAction.swift | 36 +++ ...ontentBackgroundLayerActionValidator.swift | 25 ++ .../LayerActionType/LayerActionType.swift | 20 ++ .../ContentBackgroundLayerType.swift | 20 ++ .../ContentBackgroundLayerValidator.swift | 29 ++ .../ContentBackgroundLayerSource.swift | 33 +++ ...ontentBackgroundLayerSourceValidator.swift | 24 ++ .../LayerSourceType/LayerSourceType.swift | 19 ++ .../ContentElement/ContentElement.swift | 46 +++ .../ContentElementGravity.swift | 14 + .../ContentElementPosition.swift | 13 + .../ContentElementPositionMargin.swift | 51 ++++ ...ontentElementPositionMarginValidator.swift | 21 ++ .../PositionMarginKind.swift | 20 ++ .../ContentElementSize.swift | 15 + .../Kind/ContentElementSizeType.swift | 20 ++ .../ContentElementType.swift | 20 ++ .../ContentElementValidator.swift | 21 ++ .../InappFormVariantContent.swift | 14 + .../InappFormVariantType.swift | 21 ++ .../InappFormVariantValidator.swift | 25 ++ .../Config/InappModel/InappValidator.swift | 31 ++ 30 files changed, 979 insertions(+), 39 deletions(-) delete mode 100644 Mindbox/InAppMessages/Models/Config/InAppModel.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InAppModel.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappForm.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariant.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackground.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerActionValidator.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerValidator.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSourceValidator.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementGravity/ContentElementGravity.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPosition.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMargin.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMarginValidator.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/PositionMarginKind/PositionMarginKind.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/ContentElementSize.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/Kind/ContentElementSizeType.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementValidator.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/InappFormVariantContent.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantValidator.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappValidator.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 204664cd..05ab1fb5 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -310,6 +310,33 @@ F31801FE2A386BE60021774C /* InappConfigResponseAbtestsInvalid.json in Resources */ = {isa = PBXBuildFile; fileRef = F31801FC2A386BE60021774C /* InappConfigResponseAbtestsInvalid.json */; }; F31801FF2A386BE60021774C /* InappConfigResponseMonitoringInvalid.json in Resources */ = {isa = PBXBuildFile; fileRef = F31801FD2A386BE60021774C /* InappConfigResponseMonitoringInvalid.json */; }; F331DC842A80983000222120 /* FailableDecodableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC832A80983000222120 /* FailableDecodableArray.swift */; }; + F331DCBB2A80993600222120 /* InappFormVariantValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC8F2A80993600222120 /* InappFormVariantValidator.swift */; }; + F331DCBC2A80993600222120 /* InappFormVariant.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC902A80993600222120 /* InappFormVariant.swift */; }; + F331DCBD2A80993600222120 /* InappFormVariantType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC922A80993600222120 /* InappFormVariantType.swift */; }; + F331DCBE2A80993600222120 /* ContentElementType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC962A80993600222120 /* ContentElementType.swift */; }; + F331DCBF2A80993600222120 /* ContentElementSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC982A80993600222120 /* ContentElementSize.swift */; }; + F331DCC02A80993600222120 /* ContentElementSizeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC9A2A80993600222120 /* ContentElementSizeType.swift */; }; + F331DCC12A80993600222120 /* ContentElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC9B2A80993600222120 /* ContentElement.swift */; }; + F331DCC22A80993600222120 /* ContentElementGravity.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC9D2A80993600222120 /* ContentElementGravity.swift */; }; + F331DCC32A80993600222120 /* ContentElementPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC9F2A80993600222120 /* ContentElementPosition.swift */; }; + F331DCC42A80993600222120 /* ContentElementPositionMargin.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA12A80993600222120 /* ContentElementPositionMargin.swift */; }; + F331DCC52A80993600222120 /* PositionMarginKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA32A80993600222120 /* PositionMarginKind.swift */; }; + F331DCC62A80993600222120 /* ContentElementPositionMarginValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA42A80993600222120 /* ContentElementPositionMarginValidator.swift */; }; + F331DCC72A80993600222120 /* ContentElementValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA52A80993600222120 /* ContentElementValidator.swift */; }; + F331DCC82A80993600222120 /* ContentBackgroundLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA82A80993600222120 /* ContentBackgroundLayer.swift */; }; + F331DCC92A80993600222120 /* ContentBackgroundLayerActionValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCAA2A80993600222120 /* ContentBackgroundLayerActionValidator.swift */; }; + F331DCCA2A80993600222120 /* LayerActionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCAC2A80993600222120 /* LayerActionType.swift */; }; + F331DCCB2A80993600222120 /* ContentBackgroundLayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCAD2A80993600222120 /* ContentBackgroundLayerAction.swift */; }; + F331DCCC2A80993600222120 /* ContentBackgroundLayerSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCAF2A80993600222120 /* ContentBackgroundLayerSource.swift */; }; + F331DCCD2A80993600222120 /* LayerSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB12A80993600222120 /* LayerSourceType.swift */; }; + F331DCCE2A80993600222120 /* ContentBackgroundLayerSourceValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB22A80993600222120 /* ContentBackgroundLayerSourceValidator.swift */; }; + F331DCCF2A80993600222120 /* ContentBackgroundLayerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB42A80993600222120 /* ContentBackgroundLayerType.swift */; }; + F331DCD02A80993600222120 /* ContentBackgroundLayerValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB52A80993600222120 /* ContentBackgroundLayerValidator.swift */; }; + F331DCD12A80993600222120 /* ContentBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB62A80993600222120 /* ContentBackground.swift */; }; + F331DCD22A80993600222120 /* InappFormVariantContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB72A80993600222120 /* InappFormVariantContent.swift */; }; + F331DCD32A80993600222120 /* InappForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB82A80993600222120 /* InappForm.swift */; }; + F331DCD42A80993600222120 /* InappValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB92A80993600222120 /* InappValidator.swift */; }; + F331DCD52A80993600222120 /* InAppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCBA2A80993600222120 /* InAppModel.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -356,7 +383,6 @@ F3A8B9A02A3A52F400E9C055 /* ABTestValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3A8B99F2A3A52F400E9C055 /* ABTestValidatorTests.swift */; }; F3A8B9A32A3A6E6900E9C055 /* SdkVersionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3A8B9A22A3A6E6900E9C055 /* SdkVersionModel.swift */; }; F3A8B9A52A3A6F2800E9C055 /* MonitoringModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3A8B9A42A3A6F2800E9C055 /* MonitoringModel.swift */; }; - F3A8B9A72A3A705B00E9C055 /* InAppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3A8B9A62A3A705B00E9C055 /* InAppModel.swift */; }; F3A8B9A92A3A713E00E9C055 /* SettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3A8B9A82A3A713E00E9C055 /* SettingsModel.swift */; }; F3A8B9AB2A3A719C00E9C055 /* ABTestModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3A8B9AA2A3A719C00E9C055 /* ABTestModel.swift */; }; F3B2A3982A4C79D900E2CA25 /* MindboxLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3B2A3972A4C79D900E2CA25 /* MindboxLogger.swift */; }; @@ -720,6 +746,33 @@ F31801FC2A386BE60021774C /* InappConfigResponseAbtestsInvalid.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = InappConfigResponseAbtestsInvalid.json; sourceTree = ""; }; F31801FD2A386BE60021774C /* InappConfigResponseMonitoringInvalid.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = InappConfigResponseMonitoringInvalid.json; sourceTree = ""; }; F331DC832A80983000222120 /* FailableDecodableArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailableDecodableArray.swift; sourceTree = ""; }; + F331DC8F2A80993600222120 /* InappFormVariantValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappFormVariantValidator.swift; sourceTree = ""; }; + F331DC902A80993600222120 /* InappFormVariant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappFormVariant.swift; sourceTree = ""; }; + F331DC922A80993600222120 /* InappFormVariantType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappFormVariantType.swift; sourceTree = ""; }; + F331DC962A80993600222120 /* ContentElementType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementType.swift; sourceTree = ""; }; + F331DC982A80993600222120 /* ContentElementSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementSize.swift; sourceTree = ""; }; + F331DC9A2A80993600222120 /* ContentElementSizeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementSizeType.swift; sourceTree = ""; }; + F331DC9B2A80993600222120 /* ContentElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElement.swift; sourceTree = ""; }; + F331DC9D2A80993600222120 /* ContentElementGravity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementGravity.swift; sourceTree = ""; }; + F331DC9F2A80993600222120 /* ContentElementPosition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementPosition.swift; sourceTree = ""; }; + F331DCA12A80993600222120 /* ContentElementPositionMargin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementPositionMargin.swift; sourceTree = ""; }; + F331DCA32A80993600222120 /* PositionMarginKind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PositionMarginKind.swift; sourceTree = ""; }; + F331DCA42A80993600222120 /* ContentElementPositionMarginValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementPositionMarginValidator.swift; sourceTree = ""; }; + F331DCA52A80993600222120 /* ContentElementValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementValidator.swift; sourceTree = ""; }; + F331DCA82A80993600222120 /* ContentBackgroundLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayer.swift; sourceTree = ""; }; + F331DCAA2A80993600222120 /* ContentBackgroundLayerActionValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerActionValidator.swift; sourceTree = ""; }; + F331DCAC2A80993600222120 /* LayerActionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayerActionType.swift; sourceTree = ""; }; + F331DCAD2A80993600222120 /* ContentBackgroundLayerAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerAction.swift; sourceTree = ""; }; + F331DCAF2A80993600222120 /* ContentBackgroundLayerSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerSource.swift; sourceTree = ""; }; + F331DCB12A80993600222120 /* LayerSourceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayerSourceType.swift; sourceTree = ""; }; + F331DCB22A80993600222120 /* ContentBackgroundLayerSourceValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerSourceValidator.swift; sourceTree = ""; }; + F331DCB42A80993600222120 /* ContentBackgroundLayerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerType.swift; sourceTree = ""; }; + F331DCB52A80993600222120 /* ContentBackgroundLayerValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerValidator.swift; sourceTree = ""; }; + F331DCB62A80993600222120 /* ContentBackground.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackground.swift; sourceTree = ""; }; + F331DCB72A80993600222120 /* InappFormVariantContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappFormVariantContent.swift; sourceTree = ""; }; + F331DCB82A80993600222120 /* InappForm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappForm.swift; sourceTree = ""; }; + F331DCB92A80993600222120 /* InappValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappValidator.swift; sourceTree = ""; }; + F331DCBA2A80993600222120 /* InAppModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InAppModel.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -766,7 +819,6 @@ F3A8B99F2A3A52F400E9C055 /* ABTestValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ABTestValidatorTests.swift; sourceTree = ""; }; F3A8B9A22A3A6E6900E9C055 /* SdkVersionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SdkVersionModel.swift; sourceTree = ""; }; F3A8B9A42A3A6F2800E9C055 /* MonitoringModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonitoringModel.swift; sourceTree = ""; }; - F3A8B9A62A3A705B00E9C055 /* InAppModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppModel.swift; sourceTree = ""; }; F3A8B9A82A3A713E00E9C055 /* SettingsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsModel.swift; sourceTree = ""; }; F3A8B9AA2A3A719C00E9C055 /* ABTestModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ABTestModel.swift; sourceTree = ""; }; F3B2A3972A4C79D900E2CA25 /* MindboxLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MindboxLogger.swift; sourceTree = ""; }; @@ -1871,6 +1923,192 @@ path = ConfigResponse; sourceTree = ""; }; + F331DC8C2A80993600222120 /* InappModel */ = { + isa = PBXGroup; + children = ( + F331DCB92A80993600222120 /* InappValidator.swift */, + F331DCBA2A80993600222120 /* InAppModel.swift */, + F331DC8D2A80993600222120 /* InappForm */, + ); + path = InappModel; + sourceTree = ""; + }; + F331DC8D2A80993600222120 /* InappForm */ = { + isa = PBXGroup; + children = ( + F331DC8E2A80993600222120 /* InappFormVariant */, + F331DCB82A80993600222120 /* InappForm.swift */, + ); + path = InappForm; + sourceTree = ""; + }; + F331DC8E2A80993600222120 /* InappFormVariant */ = { + isa = PBXGroup; + children = ( + F331DC8F2A80993600222120 /* InappFormVariantValidator.swift */, + F331DC902A80993600222120 /* InappFormVariant.swift */, + F331DC912A80993600222120 /* InappFormVariantType */, + F331DC932A80993600222120 /* InappFormVariantContent */, + ); + path = InappFormVariant; + sourceTree = ""; + }; + F331DC912A80993600222120 /* InappFormVariantType */ = { + isa = PBXGroup; + children = ( + F331DC922A80993600222120 /* InappFormVariantType.swift */, + ); + path = InappFormVariantType; + sourceTree = ""; + }; + F331DC932A80993600222120 /* InappFormVariantContent */ = { + isa = PBXGroup; + children = ( + F331DC942A80993600222120 /* ContentElement */, + F331DCA62A80993600222120 /* ContentBackground */, + F331DCB72A80993600222120 /* InappFormVariantContent.swift */, + ); + path = InappFormVariantContent; + sourceTree = ""; + }; + F331DC942A80993600222120 /* ContentElement */ = { + isa = PBXGroup; + children = ( + F331DC952A80993600222120 /* ContentElementType */, + F331DC972A80993600222120 /* ContentElementSize */, + F331DC9B2A80993600222120 /* ContentElement.swift */, + F331DC9C2A80993600222120 /* ContentElementGravity */, + F331DC9E2A80993600222120 /* ContentElementPosition */, + F331DCA52A80993600222120 /* ContentElementValidator.swift */, + ); + path = ContentElement; + sourceTree = ""; + }; + F331DC952A80993600222120 /* ContentElementType */ = { + isa = PBXGroup; + children = ( + F331DC962A80993600222120 /* ContentElementType.swift */, + ); + path = ContentElementType; + sourceTree = ""; + }; + F331DC972A80993600222120 /* ContentElementSize */ = { + isa = PBXGroup; + children = ( + F331DC982A80993600222120 /* ContentElementSize.swift */, + F331DC992A80993600222120 /* Kind */, + ); + path = ContentElementSize; + sourceTree = ""; + }; + F331DC992A80993600222120 /* Kind */ = { + isa = PBXGroup; + children = ( + F331DC9A2A80993600222120 /* ContentElementSizeType.swift */, + ); + path = Kind; + sourceTree = ""; + }; + F331DC9C2A80993600222120 /* ContentElementGravity */ = { + isa = PBXGroup; + children = ( + F331DC9D2A80993600222120 /* ContentElementGravity.swift */, + ); + path = ContentElementGravity; + sourceTree = ""; + }; + F331DC9E2A80993600222120 /* ContentElementPosition */ = { + isa = PBXGroup; + children = ( + F331DC9F2A80993600222120 /* ContentElementPosition.swift */, + F331DCA02A80993600222120 /* ContentElementPositionMargin */, + ); + path = ContentElementPosition; + sourceTree = ""; + }; + F331DCA02A80993600222120 /* ContentElementPositionMargin */ = { + isa = PBXGroup; + children = ( + F331DCA12A80993600222120 /* ContentElementPositionMargin.swift */, + F331DCA22A80993600222120 /* PositionMarginKind */, + F331DCA42A80993600222120 /* ContentElementPositionMarginValidator.swift */, + ); + path = ContentElementPositionMargin; + sourceTree = ""; + }; + F331DCA22A80993600222120 /* PositionMarginKind */ = { + isa = PBXGroup; + children = ( + F331DCA32A80993600222120 /* PositionMarginKind.swift */, + ); + path = PositionMarginKind; + sourceTree = ""; + }; + F331DCA62A80993600222120 /* ContentBackground */ = { + isa = PBXGroup; + children = ( + F331DCA72A80993600222120 /* ContentBackgroundLayer */, + F331DCB62A80993600222120 /* ContentBackground.swift */, + ); + path = ContentBackground; + sourceTree = ""; + }; + F331DCA72A80993600222120 /* ContentBackgroundLayer */ = { + isa = PBXGroup; + children = ( + F331DCA82A80993600222120 /* ContentBackgroundLayer.swift */, + F331DCA92A80993600222120 /* ContentBackgroundLayerAction */, + F331DCAE2A80993600222120 /* ContentBackgroundSource */, + F331DCB32A80993600222120 /* ContentBackgroundLayerType */, + F331DCB52A80993600222120 /* ContentBackgroundLayerValidator.swift */, + ); + path = ContentBackgroundLayer; + sourceTree = ""; + }; + F331DCA92A80993600222120 /* ContentBackgroundLayerAction */ = { + isa = PBXGroup; + children = ( + F331DCAA2A80993600222120 /* ContentBackgroundLayerActionValidator.swift */, + F331DCAB2A80993600222120 /* LayerActionType */, + F331DCAD2A80993600222120 /* ContentBackgroundLayerAction.swift */, + ); + path = ContentBackgroundLayerAction; + sourceTree = ""; + }; + F331DCAB2A80993600222120 /* LayerActionType */ = { + isa = PBXGroup; + children = ( + F331DCAC2A80993600222120 /* LayerActionType.swift */, + ); + path = LayerActionType; + sourceTree = ""; + }; + F331DCAE2A80993600222120 /* ContentBackgroundSource */ = { + isa = PBXGroup; + children = ( + F331DCAF2A80993600222120 /* ContentBackgroundLayerSource.swift */, + F331DCB02A80993600222120 /* LayerSourceType */, + F331DCB22A80993600222120 /* ContentBackgroundLayerSourceValidator.swift */, + ); + path = ContentBackgroundSource; + sourceTree = ""; + }; + F331DCB02A80993600222120 /* LayerSourceType */ = { + isa = PBXGroup; + children = ( + F331DCB12A80993600222120 /* LayerSourceType.swift */, + ); + path = LayerSourceType; + sourceTree = ""; + }; + F331DCB32A80993600222120 /* ContentBackgroundLayerType */ = { + isa = PBXGroup; + children = ( + F331DCB42A80993600222120 /* ContentBackgroundLayerType.swift */, + ); + path = ContentBackgroundLayerType; + sourceTree = ""; + }; F3482F142A65DBA0002A41EC /* InappMessagesDelegate */ = { isa = PBXGroup; children = ( @@ -1991,10 +2229,10 @@ F3A8B9A12A3A6DBF00E9C055 /* Config */ = { isa = PBXGroup; children = ( + F331DC8C2A80993600222120 /* InappModel */, 9BC24E7328F6953D00C2619C /* ConfigResponse.swift */, F3A8B9A22A3A6E6900E9C055 /* SdkVersionModel.swift */, F3A8B9A42A3A6F2800E9C055 /* MonitoringModel.swift */, - F3A8B9A62A3A705B00E9C055 /* InAppModel.swift */, F3A8B9A82A3A713E00E9C055 /* SettingsModel.swift */, F3A8B9AA2A3A719C00E9C055 /* ABTestModel.swift */, ); @@ -2320,15 +2558,20 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F331DCC92A80993600222120 /* ContentBackgroundLayerActionValidator.swift in Sources */, + F331DCC12A80993600222120 /* ContentElement.swift in Sources */, 847F581725C8981F00147A9A /* HTTPURLResponseStatusCodeValidator.swift in Sources */, 33072F442664CC6C001F1AB2 /* IssuedPointOfContactResponse.swift in Sources */, + F331DCBC2A80993600222120 /* InappFormVariant.swift in Sources */, A17958762978AEC600609E91 /* OrTargetingChecker.swift in Sources */, + F331DCC22A80993600222120 /* ContentElementGravity.swift in Sources */, 334F3AE1264C199900A6AC00 /* PromoCodeRequest.swift in Sources */, A17958792978AEC600609E91 /* GeoTargetingChecker.swift in Sources */, 33BEE80D2681EB7700993720 /* NotificationDecoder.swift in Sources */, 840042A12614CE0000CA17C5 /* ClickNotificationManager.swift in Sources */, 9B24FABD28C757A700F10B5D /* InAppResponse.swift in Sources */, 334F3AED264C199900A6AC00 /* SubscriptionRequest.swift in Sources */, + F331DCD32A80993600222120 /* InappForm.swift in Sources */, 334F3AF4264C199900A6AC00 /* ProductRequest.swift in Sources */, 84C65E6825D4FE41008996FA /* BodyEncoder.swift in Sources */, 9B33F3CD28D0986900A3FFF9 /* CustomerSegmentsAPI.swift in Sources */, @@ -2358,13 +2601,17 @@ F3A8B99E2A3A4FD600E9C055 /* ABTestValidator.swift in Sources */, 334F3AE0264C199900A6AC00 /* ViewProductCategoryRequest.swift in Sources */, A1D23AF029DE082E00A75179 /* InAppProductSegmentResponse.swift in Sources */, + F331DCD52A80993600222120 /* InAppModel.swift in Sources */, A1D017EE2976CC4000CD9F99 /* AndTargeting.swift in Sources */, + F331DCC32A80993600222120 /* ContentElementPosition.swift in Sources */, 6FDD1453266F7C8B00A50C35 /* PromotionResponse.swift in Sources */, 84F7053B26120199009C7A07 /* Constants.swift in Sources */, A17958782978AEC600609E91 /* SegmentTargetingChecker.swift in Sources */, 847F580B25C88CAF00147A9A /* Route.swift in Sources */, + F331DCCD2A80993600222120 /* LayerSourceType.swift in Sources */, 6FDD145D266F7CCE00A50C35 /* PossibleDiscountsResponse.swift in Sources */, 9B9C95322921116F00BB29DA /* PasteboardUUIDDebugService.swift in Sources */, + F331DCC42A80993600222120 /* ContentElementPositionMargin.swift in Sources */, A1A916DF29C9032700D59D9E /* CategoryIDInTargeting.swift in Sources */, 6FDD1449266F7C4E00A50C35 /* LimitTypeResponse.swift in Sources */, 6FDD144B266F7C5A00A50C35 /* AmountResponse.swift in Sources */, @@ -2386,6 +2633,8 @@ 3333C1E12681EA4D00B60D84 /* NotificationsPayloads.swift in Sources */, 9B4F9DFB28D088AE002C9CF0 /* InAppMessageViewController.swift in Sources */, 334F3AE7264C199900A6AC00 /* CatalogProductListRequest.swift in Sources */, + F331DCC02A80993600222120 /* ContentElementSizeType.swift in Sources */, + F331DCC52A80993600222120 /* PositionMarginKind.swift in Sources */, B3A625562689FDD400B6A3B7 /* BalanceResponse.swift in Sources */, 6FDD1459266F7CB700A50C35 /* ContentResponse.swift in Sources */, F37613E12A6A8CFF009F2EE4 /* UIColor+Extensions.swift in Sources */, @@ -2409,6 +2658,7 @@ 3337E6A3265FAB39006949EB /* BaseResponse.swift in Sources */, 6FDD1451266F7C8000A50C35 /* UsageServiceStatusResponse.swift in Sources */, F3A8B9A92A3A713E00E9C055 /* SettingsModel.swift in Sources */, + F331DCD12A80993600222120 /* ContentBackground.swift in Sources */, 9B24FAB528C751E400F10B5D /* InAppImagesStorage.swift in Sources */, F3A8B9A32A3A6E6900E9C055 /* SdkVersionModel.swift in Sources */, 3333C1DE2681E9F300B60D84 /* URLRequestBuilder.swift in Sources */, @@ -2421,9 +2671,12 @@ A1D017E72976CBE100CD9F99 /* TargetingModel.swift in Sources */, 9B4F9DF328D088A0002C9CF0 /* InAppFormData.swift in Sources */, F3482F232A65DC37002A41EC /* InAppMessagesDelegate.swift in Sources */, + F331DCBD2A80993600222120 /* InappFormVariantType.swift in Sources */, 33072F2E2664C24F001F1AB2 /* AreaResponse.swift in Sources */, 84DEE8A925CC031200C98CC7 /* MBDatabase.xcdatamodeld in Sources */, A192786A29D38D2000CDB53D /* CheckerFactory.swift in Sources */, + F331DCC72A80993600222120 /* ContentElementValidator.swift in Sources */, + F331DCBE2A80993600222120 /* ContentElementType.swift in Sources */, 840C38A625D1191000D50183 /* GuaranteedDeliveryManager.swift in Sources */, A1D017EC2976CC2E00CD9F99 /* TrueTargeting.swift in Sources */, 33072F302664C2E4001F1AB2 /* SubscriptionResponse.swift in Sources */, @@ -2435,8 +2688,10 @@ 33072F462664CC9A001F1AB2 /* UsedPointOfContactResponse.swift in Sources */, F397EEB12A44571300D48CEC /* UnknownDecodable.swift in Sources */, 33072F382664C577001F1AB2 /* RecommendationResponse.swift in Sources */, + F331DCD22A80993600222120 /* InappFormVariantContent.swift in Sources */, 337A47362654F995000DC613 /* OperationBodyRequest.swift in Sources */, 9BC24E6F28F694EA00C2619C /* SDKVersionProvider.swift in Sources */, + F331DCCF2A80993600222120 /* ContentBackgroundLayerType.swift in Sources */, 6FDD144D266F7C6600A50C35 /* AmountTypeResponse.swift in Sources */, 6FDD1461266F7CE300A50C35 /* DiscountAmountTypeResponse.swift in Sources */, 6FDD144F266F7C7000A50C35 /* UsedResponse.swift in Sources */, @@ -2454,12 +2709,14 @@ A192787229D442D900CDB53D /* ProductSegmentChecker.swift in Sources */, A153E03D29BAFEC1003C34D4 /* CustomOperationChecker.swift in Sources */, 33072F402664CB08001F1AB2 /* ProductGroupResponse.swift in Sources */, + F331DCCA2A80993600222120 /* LayerActionType.swift in Sources */, A1D017F52976FC2B00CD9F99 /* InternalTargetingChecker.swift in Sources */, 6F1EAA16266A670E007A335B /* ProductListItemsResponse.swift in Sources */, 6FDD143F266F7BF900A50C35 /* AppliedPromotionResponse.swift in Sources */, 84D350C825C992900044E4E6 /* IDFAFetcher.swift in Sources */, 840C387225CC1AF200D50183 /* CDEvent+CoreDataClass.swift in Sources */, B3A62560268A052C00B6A3B7 /* PromoActionsResponse.swift in Sources */, + F331DCCC2A80993600222120 /* ContentBackgroundLayerSource.swift in Sources */, 6FDD1457266F7CAB00A50C35 /* PlaceholderResponse.swift in Sources */, B3F4F97C268EEA950092EC3C /* StatusResponse.swift in Sources */, 9B24FAAE28C74BA500F10B5D /* InAppCoreManager.swift in Sources */, @@ -2469,6 +2726,7 @@ A1D017F02976CC8600CD9F99 /* OrTargeting.swift in Sources */, 334F3AEA264C199900A6AC00 /* CustomerRequest.swift in Sources */, A15D701629AF810E007131E7 /* SDKLogsRequest.swift in Sources */, + F331DCD02A80993600222120 /* ContentBackgroundLayerValidator.swift in Sources */, 33072F362664C4D7001F1AB2 /* RecommendationRequest.swift in Sources */, 84DC49CC25D1832300D5D758 /* EventWrapper.swift in Sources */, 317054C425AEF88E00AE624C /* DependencyProvider.swift in Sources */, @@ -2477,6 +2735,7 @@ 3337E6A92664C16A006949EB /* CustomerResponse.swift in Sources */, 84EAEDFC25C8B18B00726063 /* DeviceModelHelper.swift in Sources */, 84DC4A0725D27D6000D5D758 /* UNAuthorizationStatusProvider.swift in Sources */, + F331DCC82A80993600222120 /* ContentBackgroundLayer.swift in Sources */, F3D818B02A3885AD0002957C /* ABTestDeviceMixer.swift in Sources */, 3328FE4226303F2F000A30D0 /* String+Regex.swift in Sources */, 33072F322664C357001F1AB2 /* ProductListResponse.swift in Sources */, @@ -2502,9 +2761,11 @@ 33C81EA4264145CD00863380 /* TimerManager.swift in Sources */, B3F4F981268EEAC90092EC3C /* AmountBenefisTypeResponse.swift in Sources */, A11FBE8929DD76BF00F5FB7B /* InAppMessagesEventSender.swift in Sources */, + F331DCBB2A80993600222120 /* InappFormVariantValidator.swift in Sources */, 33072F422664CB81001F1AB2 /* PromoCodeResponse.swift in Sources */, 339ACE50262DAC74003590D2 /* CustomEvent.swift in Sources */, 314B390025AEE96F00E947B9 /* CoreController.swift in Sources */, + F331DCCB2A80993600222120 /* ContentBackgroundLayerAction.swift in Sources */, 84B625E425C988FA00AB6228 /* URLValidator.swift in Sources */, 843DAA5C26087F3D00CAC489 /* DependencyContainer.swift in Sources */, F39B67A52A3C6BE3005C0CCA /* SegmentationCheckRequest.swift in Sources */, @@ -2532,17 +2793,20 @@ 9B9C95312921116F00BB29DA /* UUIDDebugService.swift in Sources */, 6FDD1443266F7C1600A50C35 /* BalanceTypeReponse.swift in Sources */, 337C7954265652A80092B580 /* ProductCategoryRequest.swift in Sources */, + F331DCCE2A80993600222120 /* ContentBackgroundLayerSourceValidator.swift in Sources */, + F331DCBF2A80993600222120 /* ContentElementSize.swift in Sources */, 334F3AF1264C199900A6AC00 /* PoolRequest.swift in Sources */, F37613D72A6A66FB009F2EE4 /* CrossView.swift in Sources */, 3191C2A625ADFD8000E7D6B9 /* Mindbox.swift in Sources */, A184654529C310F100E64780 /* InAppOperationJSONModel.swift in Sources */, A154E330299E0F1600F8F074 /* InAppGeoResponse.swift in Sources */, + F331DCC62A80993600222120 /* ContentElementPositionMarginValidator.swift in Sources */, F39B67A92A3C72E5005C0CCA /* ImageDownloadService.swift in Sources */, - F3A8B9A72A3A705B00E9C055 /* InAppModel.swift in Sources */, 84CC799725CACF5500C062BD /* MBEventRepository.swift in Sources */, F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */, 847F581F25C8A3F500147A9A /* HTTPTypealiases.swift in Sources */, 33BEE8172681EC5F00993720 /* EventRoute.swift in Sources */, + F331DCD42A80993600222120 /* InappValidator.swift in Sources */, 317054CB25AF189800AE624C /* PersistenceStorage.swift in Sources */, 840C38AC25D11BB200D50183 /* DeliveryOperation.swift in Sources */, 334F3AEE264C199900A6AC00 /* SegmentationRequest.swift in Sources */, diff --git a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift index b29e1747..e8b1bd25 100644 --- a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift +++ b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift @@ -54,7 +54,7 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { _ response: ConfigResponse, _ completion: @escaping (InAppFormData?) -> Void) { let shownInAppsIds = Set(persistenceStorage.shownInAppsIds ?? []) - let responseInapps = filterInappsByABTests(response.abtests, responseInapps: response.inapps) + let responseInapps = filterInappsByABTests(response.abtests, responseInapps: response.inapps?.elements) let filteredInapps = filterInappsBySDKVersion(responseInapps, shownInAppsIds: shownInAppsIds) Logger.common(message: "Shown in-apps ids: [\(shownInAppsIds)]", level: .info, category: .inAppMessages) if filteredInapps.isEmpty { diff --git a/Mindbox/InAppMessages/Models/Config/InAppModel.swift b/Mindbox/InAppMessages/Models/Config/InAppModel.swift deleted file mode 100644 index b583fc3a..00000000 --- a/Mindbox/InAppMessages/Models/Config/InAppModel.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// InAppModel.swift -// Mindbox -// -// Created by vailence on 15.06.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import Foundation - -struct InApp: Decodable, Equatable { - let id: String - let sdkVersion: SdkVersion - let targeting: Targeting - let form: InAppFormVariants - - struct InAppFormVariants: Decodable, Equatable { - let variants: [InAppForm] - } - - struct InAppForm: Decodable, Equatable { - let imageUrl: String - let redirectUrl: String - let intentPayload: String - let type: String - - enum CodingKeys: String, CodingKey { - case imageUrl - case redirectUrl - case intentPayload - case type = "$type" - } - } -} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InAppModel.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InAppModel.swift new file mode 100644 index 00000000..ff1acdc7 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InAppModel.swift @@ -0,0 +1,39 @@ +// +// InAppModel.swift +// Mindbox +// +// Created by vailence on 15.06.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct InApp: Decodable, Equatable { + let id: String + let sdkVersion: SdkVersion + let targeting: Targeting + let form: InAppForm + + enum CodingKeys: String, CodingKey { + case id + case sdkVersion + case targeting + case form + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + id = try container.decode(String.self, forKey: .id) + sdkVersion = try container.decode(SdkVersion.self, forKey: .sdkVersion) + targeting = try container.decode(Targeting.self, forKey: .targeting) + form = try container.decode(InAppForm.self, forKey: .form) + + if !InappValidator().isValid(item: self) { + throw DecodingError.dataCorruptedError( + forKey: .id, + in: container, + debugDescription: "Invalid InApp" + ) + } + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappForm.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappForm.swift new file mode 100644 index 00000000..5f8e6dce --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappForm.swift @@ -0,0 +1,32 @@ +// +// InappForm.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct InAppForm: Decodable, Equatable { + let variants: FailableDecodableArray + + enum CodingKeys: String, CodingKey { + case variants + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let variants = try container.decode(FailableDecodableArray.self, forKey: .variants) + + if variants.elements.isEmpty { + throw DecodingError.dataCorruptedError( + forKey: .variants, + in: container, + debugDescription: "Variants cannot be empty" + ) + } + + self.variants = variants + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariant.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariant.swift new file mode 100644 index 00000000..23d63364 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariant.swift @@ -0,0 +1,33 @@ +// +// InappFormVariant.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct InappFormVariant: Decodable, Equatable { + let type: InappFormVariantType + let content: InappFormVariantContent? + + enum CodingKeys: String, CodingKey { + case type = "$type" + case content + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + type = try container.decode(InappFormVariantType.self, forKey: .type) + content = try container.decodeIfPresent(InappFormVariantContent.self, forKey: .content) + + if !InappFormVariantValidator().isValid(item: self) { + throw DecodingError.dataCorruptedError( + forKey: .type, + in: container, + debugDescription: "Invalid Variant" + ) + } + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackground.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackground.swift new file mode 100644 index 00000000..66158672 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackground.swift @@ -0,0 +1,32 @@ +// +// ContentBackground.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct ContentBackground: Decodable, Equatable { + let layers: FailableDecodableArray + + enum CodingKeys: String, CodingKey { + case layers + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let layers = try container.decode(FailableDecodableArray.self, forKey: .layers) + + if layers.elements.isEmpty { + throw DecodingError.dataCorruptedError( + forKey: .layers, + in: container, + debugDescription: "Layers cannot be empty." + ) + } + + self.layers = layers + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift new file mode 100644 index 00000000..b6c8182a --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift @@ -0,0 +1,36 @@ +// +// ContentBackgroundLayer.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct ContentBackgroundLayer: Decodable, Equatable { + let type: ContentBackgroundLayerType + let action: ContentBackgroundLayerAction? + let source: ContentBackgroundLayerSource? + + enum CodingKeys: String, CodingKey { + case type = "$type" + case action + case source + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + type = try container.decode(ContentBackgroundLayerType.self, forKey: .type) + action = try container.decodeIfPresent(ContentBackgroundLayerAction.self, forKey: .action) + source = try container.decodeIfPresent(ContentBackgroundLayerSource.self, forKey: .source) + + if !ContentBackgroundLayerValidator().isValid(item: self) { + throw DecodingError.dataCorruptedError( + forKey: .type, + in: container, + debugDescription: "Invalid Layer." + ) + } + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift new file mode 100644 index 00000000..8376435b --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift @@ -0,0 +1,36 @@ +// +// ContentBackgroundLayerAction.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct ContentBackgroundLayerAction: Decodable, Equatable { + let type: LayerActionType + let intentPayload: String? + let value: String? + + enum CodingKeys: String, CodingKey { + case type = "$type" + case intentPayload + case value + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.type = try container.decode(LayerActionType.self, forKey: .type) + self.intentPayload = try container.decodeIfPresent(String.self, forKey: .intentPayload) + self.value = try container.decodeIfPresent(String.self, forKey: .value) + + if !ContentBackgroundLayerActionValidator().isValid(item: self) { + throw DecodingError.dataCorruptedError( + forKey: .type, + in: container, + debugDescription: "Invalid layer action." + ) + } + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerActionValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerActionValidator.swift new file mode 100644 index 00000000..4d9bc985 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerActionValidator.swift @@ -0,0 +1,25 @@ +// +// ContentBackgroundLayerActionValidator.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +class ContentBackgroundLayerActionValidator: Validator { + typealias T = ContentBackgroundLayerAction + + func isValid(item: ContentBackgroundLayerAction) -> Bool { + if item.type == .unknown { + return false + } + + if item.type == .redirectUrl && (item.intentPayload == nil || item.value == nil) { + return false + } + + return true + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift new file mode 100644 index 00000000..8e7940f2 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift @@ -0,0 +1,20 @@ +// +// LayerActionType.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +enum LayerActionType: String, Decodable, Equatable { + case redirectUrl + case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let type: String = try container.decode(String.self) + self = LayerActionType(rawValue: type) ?? .unknown + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift new file mode 100644 index 00000000..94b4b559 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift @@ -0,0 +1,20 @@ +// +// ContentBackgroundLayerType.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +enum ContentBackgroundLayerType: String, Decodable, Equatable { + case image + case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let type: String = try container.decode(String.self) + self = ContentBackgroundLayerType(rawValue: type) ?? .unknown + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerValidator.swift new file mode 100644 index 00000000..a167b778 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerValidator.swift @@ -0,0 +1,29 @@ +// +// ContentBackgroundLayerValidator.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +class ContentBackgroundLayerValidator: Validator { + typealias T = ContentBackgroundLayer + + func isValid(item: ContentBackgroundLayer) -> Bool { + if item.type == .unknown { + return false + } + + if item.action == nil && item.type == .image { + return false + } + + if item.source == nil && item.type == .image { + return false + } + + return true + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift new file mode 100644 index 00000000..02e995f2 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift @@ -0,0 +1,33 @@ +// +// ContentBackgroundLayerSource.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct ContentBackgroundLayerSource: Decodable, Equatable { + let type: LayerSourceType + let value: String? + + enum CodingKeys: String, CodingKey { + case type = "$type" + case value + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.type = try container.decode(LayerSourceType.self, forKey: .type) + self.value = try container.decodeIfPresent(String.self, forKey: .value) + + if !ContentBackgroundLayerSourceValidator().isValid(item: self) { + throw DecodingError.dataCorruptedError( + forKey: .type, + in: container, + debugDescription: "Invalid layer source." + ) + } + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSourceValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSourceValidator.swift new file mode 100644 index 00000000..3729b1a0 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSourceValidator.swift @@ -0,0 +1,24 @@ +// +// ContentBackgroundLayerSourceValidator.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// + +import Foundation + +class ContentBackgroundLayerSourceValidator: Validator { + typealias T = ContentBackgroundLayerSource + + func isValid(item: ContentBackgroundLayerSource) -> Bool { + if item.type == .unknown { + return false + } + + if item.type == .url && item.value == nil { + return false + } + + return true + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift new file mode 100644 index 00000000..81661414 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift @@ -0,0 +1,19 @@ +// +// LayerSourceType.swift +// FirebaseCore +// +// Created by vailence on 03.08.2023. +// + +import Foundation + +enum LayerSourceType: String, Decodable, Equatable { + case url + case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let type: String = try container.decode(String.self) + self = LayerSourceType(rawValue: type) ?? .unknown + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift new file mode 100644 index 00000000..7a0bd749 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift @@ -0,0 +1,46 @@ +// +// ContentElement.swift +// Mindbox +// +// Created by vailence on 04.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct ContentElement: Decodable, Equatable { + let type: ContentElementType + let color: String? + let lineWidth: Int? + let size: ContentElementSize? + let position: ContentElementPosition? + let gravity: ContentElementGravity? + + enum CodingKeys: String, CodingKey { + case type = "$type" + case color + case lineWidth + case size + case position + case gravity + } + + init(from decoder: Decoder) throws { + let container: KeyedDecodingContainer = try decoder.container(keyedBy: ContentElement.CodingKeys.self) + + self.type = try container.decode(ContentElementType.self, forKey: ContentElement.CodingKeys.type) + self.color = try container.decodeIfPresent(String.self, forKey: ContentElement.CodingKeys.color) + self.lineWidth = try container.decodeIfPresent(Int.self, forKey: ContentElement.CodingKeys.lineWidth) + self.size = try container.decodeIfPresent(ContentElementSize.self, forKey: .size) + self.position = try container.decodeIfPresent(ContentElementPosition.self, forKey: .position) + self.gravity = try container.decodeIfPresent(ContentElementGravity.self, forKey: .gravity) + + if !ContentElementValidator().isValid(item: self) { + throw DecodingError.dataCorruptedError( + forKey: .type, + in: container, + debugDescription: "Layers cannot be empty." + ) + } + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementGravity/ContentElementGravity.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementGravity/ContentElementGravity.swift new file mode 100644 index 00000000..227bd395 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementGravity/ContentElementGravity.swift @@ -0,0 +1,14 @@ +// +// ContentElementGravity.swift +// Mindbox +// +// Created by vailence on 04.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct ContentElementGravity: Decodable, Equatable { + let horizontal: String + let vertical: String +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPosition.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPosition.swift new file mode 100644 index 00000000..7d1dccf2 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPosition.swift @@ -0,0 +1,13 @@ +// +// ContentElementPosition.swift +// Mindbox +// +// Created by vailence on 04.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct ContentElementPosition: Decodable, Equatable { + let margin: ContentElementPositionMargin? +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMargin.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMargin.swift new file mode 100644 index 00000000..b33742a7 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMargin.swift @@ -0,0 +1,51 @@ +// +// ContentElementPositionMargin.swift +// Mindbox +// +// Created by vailence on 04.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct ContentElementPositionMargin: Decodable, Equatable { + let kind: PositionMarginKind + let top: Double? + let right: Double? + let left: Double? + let bottom: Double? + + enum CodingKeys: CodingKey { + case kind + case top + case right + case left + case bottom + } + + init(from decoder: Decoder) throws { + let container: KeyedDecodingContainer = try decoder.container(keyedBy: ContentElementPositionMargin.CodingKeys.self) + + self.kind = try container.decode(PositionMarginKind.self, forKey: ContentElementPositionMargin.CodingKeys.kind) + self.top = try container.decodeIfPresent(Double.self, forKey: ContentElementPositionMargin.CodingKeys.top) + self.right = try container.decodeIfPresent(Double.self, forKey: ContentElementPositionMargin.CodingKeys.right) + self.left = try container.decodeIfPresent(Double.self, forKey: ContentElementPositionMargin.CodingKeys.left) + self.bottom = try container.decodeIfPresent(Double.self, forKey: ContentElementPositionMargin.CodingKeys.bottom) + + if !ContentElementPositionMarginValidator().isValid(item: self) { + throw DecodingError.dataCorruptedError( + forKey: .kind, + in: container, + debugDescription: "Position margin corrupted." + ) + } + } + + init(kind: PositionMarginKind, top: Double? = nil, right: Double? = nil, left: Double? = nil, bottom: Double? = nil) { + self.kind = kind + self.top = top + self.right = right + self.left = left + self.bottom = bottom + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMarginValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMarginValidator.swift new file mode 100644 index 00000000..1134641f --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMarginValidator.swift @@ -0,0 +1,21 @@ +// +// ContentElementPositionMarginValidator.swift +// Mindbox +// +// Created by vailence on 04.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +class ContentElementPositionMarginValidator: Validator { + typealias T = ContentElementPositionMargin + + func isValid(item: ContentElementPositionMargin) -> Bool { + if item.kind == .unknown { + return false + } + + return true + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/PositionMarginKind/PositionMarginKind.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/PositionMarginKind/PositionMarginKind.swift new file mode 100644 index 00000000..d87f3707 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/PositionMarginKind/PositionMarginKind.swift @@ -0,0 +1,20 @@ +// +// PositionMarginKind.swift +// Mindbox +// +// Created by vailence on 04.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +enum PositionMarginKind: String, Decodable, Equatable { + case proportion + case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let type: String = try container.decode(String.self) + self = PositionMarginKind(rawValue: type) ?? .unknown + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/ContentElementSize.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/ContentElementSize.swift new file mode 100644 index 00000000..72045553 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/ContentElementSize.swift @@ -0,0 +1,15 @@ +// +// ContentElementSize.swift +// Mindbox +// +// Created by vailence on 04.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct ContentElementSize: Decodable, Equatable { + let kind: ContentElementSizeKind + let width: Double + let height: Double +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/Kind/ContentElementSizeType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/Kind/ContentElementSizeType.swift new file mode 100644 index 00000000..d999f977 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/Kind/ContentElementSizeType.swift @@ -0,0 +1,20 @@ +// +// ContentElementSizeKind.swift +// Mindbox +// +// Created by vailence on 04.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +enum ContentElementSizeKind: String, Decodable, Equatable { + case dp + case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let type: String = try container.decode(String.self) + self = ContentElementSizeKind(rawValue: type) ?? .unknown + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift new file mode 100644 index 00000000..e1cda797 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift @@ -0,0 +1,20 @@ +// +// ContentElementType.swift +// Mindbox +// +// Created by vailence on 04.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +enum ContentElementType: String, Decodable, Equatable { + case closeButton + case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let type: String = try container.decode(String.self) + self = ContentElementType(rawValue: type) ?? .unknown + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementValidator.swift new file mode 100644 index 00000000..9fb67ed9 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementValidator.swift @@ -0,0 +1,21 @@ +// +// ContentElementValidator.swift +// Mindbox +// +// Created by vailence on 04.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +class ContentElementValidator: Validator { + typealias T = ContentElement + + func isValid(item: ContentElement) -> Bool { + if item.type == .unknown { + return false + } + + return true + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/InappFormVariantContent.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/InappFormVariantContent.swift new file mode 100644 index 00000000..03ad7cf5 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/InappFormVariantContent.swift @@ -0,0 +1,14 @@ +// +// InappFormVariantContent.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct InappFormVariantContent: Decodable, Equatable { + let background: ContentBackground + let elements: FailableDecodableArray? +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift new file mode 100644 index 00000000..4436b192 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift @@ -0,0 +1,21 @@ +// +// InappFormVariantType.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +enum InappFormVariantType: String, Decodable, Equatable { + case modal + case snackbar + case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let type: String = try container.decode(String.self) + self = InappFormVariantType(rawValue: type) ?? .unknown + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantValidator.swift new file mode 100644 index 00000000..f181f5cb --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantValidator.swift @@ -0,0 +1,25 @@ +// +// InappFormVariantValidator.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +class InappFormVariantValidator: Validator { + typealias T = InappFormVariant + + func isValid(item: InappFormVariant) -> Bool { + if item.type == .unknown { + return false + } + + if item.content == nil && item.type == .modal { + return false + } + + return true + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappValidator.swift new file mode 100644 index 00000000..a54c6b81 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappValidator.swift @@ -0,0 +1,31 @@ +// +// InappValidator.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +class InappValidator: Validator { + typealias T = InApp + + private let sdkVersionValidator: SDKVersionValidator + + init() { + self.sdkVersionValidator = SDKVersionValidator(sdkVersionNumeric: Constants.Versions.sdkVersionNumeric) + } + + func isValid(item: InApp) -> Bool { + if item.id.isEmpty { + return false + } + + if !sdkVersionValidator.isValid(item: item.sdkVersion) { + return false + } + + return true + } +} From e9a1c2be1f68a1e12953aa6b1410f6205ec62911 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Mon, 7 Aug 2023 09:26:20 +0600 Subject: [PATCH 07/35] MBX-2706 Unknown case handler --- Mindbox.xcodeproj/project.pbxproj | 4 +++ .../InAppConfigutationMapper.swift | 16 +++++------ .../LayerActionType/LayerActionType.swift | 8 +----- .../ContentBackgroundLayerType.swift | 8 +----- .../LayerSourceType/LayerSourceType.swift | 8 +----- .../PositionMarginKind.swift | 8 +----- .../Kind/ContentElementSizeType.swift | 8 +----- .../ContentElementType.swift | 8 +----- .../InappFormVariantType.swift | 8 +----- Mindbox/Info.plist | 2 +- .../Model/Common/DecodableWithUnknown.swift | 27 +++++++++++++++++++ 11 files changed, 47 insertions(+), 58 deletions(-) create mode 100644 Mindbox/Model/Common/DecodableWithUnknown.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 05ab1fb5..72d8450b 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -337,6 +337,7 @@ F331DCD32A80993600222120 /* InappForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB82A80993600222120 /* InappForm.swift */; }; F331DCD42A80993600222120 /* InappValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB92A80993600222120 /* InappValidator.swift */; }; F331DCD52A80993600222120 /* InAppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCBA2A80993600222120 /* InAppModel.swift */; }; + F331DCD72A809A9700222120 /* DecodableWithUnknown.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCD62A809A9700222120 /* DecodableWithUnknown.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -773,6 +774,7 @@ F331DCB82A80993600222120 /* InappForm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappForm.swift; sourceTree = ""; }; F331DCB92A80993600222120 /* InappValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappValidator.swift; sourceTree = ""; }; F331DCBA2A80993600222120 /* InAppModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InAppModel.swift; sourceTree = ""; }; + F331DCD62A809A9700222120 /* DecodableWithUnknown.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodableWithUnknown.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -1200,6 +1202,7 @@ 337A473B265510AA000DC613 /* IDS.swift */, 337A473D265528B5000DC613 /* MBDate.swift */, 337A473F26553938000DC613 /* Sex.swift */, + F331DCD62A809A9700222120 /* DecodableWithUnknown.swift */, ); path = Common; sourceTree = ""; @@ -2674,6 +2677,7 @@ F331DCBD2A80993600222120 /* InappFormVariantType.swift in Sources */, 33072F2E2664C24F001F1AB2 /* AreaResponse.swift in Sources */, 84DEE8A925CC031200C98CC7 /* MBDatabase.xcdatamodeld in Sources */, + F331DCD72A809A9700222120 /* DecodableWithUnknown.swift in Sources */, A192786A29D38D2000CDB53D /* CheckerFactory.swift in Sources */, F331DCC72A80993600222120 /* ContentElementValidator.swift in Sources */, F331DCBE2A80993600222120 /* ContentElementType.swift in Sources */, diff --git a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift index e8b1bd25..0ba0a86f 100644 --- a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift +++ b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift @@ -239,14 +239,14 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { } var inAppsForEvent = filteredInAppsByEvent[triggerEvent] ?? [InAppTransitionData]() - if let inAppFormVariants = inapp.form.variants.first { - let formData = InAppTransitionData(inAppId: inapp.id, - imageUrl: inAppFormVariants.imageUrl, // Change this later - redirectUrl: inAppFormVariants.redirectUrl, - intentPayload: inAppFormVariants.intentPayload) - inAppsForEvent.append(formData) - filteredInAppsByEvent[triggerEvent] = inAppsForEvent - } +// if let inAppFormVariants = inapp.form.variants.first { +// let formData = InAppTransitionData(inAppId: inapp.id, +// imageUrl: inAppFormVariants.imageUrl, // Change this later +// redirectUrl: inAppFormVariants.redirectUrl, +// intentPayload: inAppFormVariants.intentPayload) +// inAppsForEvent.append(formData) +// filteredInAppsByEvent[triggerEvent] = inAppsForEvent +// } } self.targetingChecker.event = nil diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift index 8e7940f2..45b5149b 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift @@ -8,13 +8,7 @@ import Foundation -enum LayerActionType: String, Decodable, Equatable { +enum LayerActionType: String, Decodable, Equatable, DecodableWithUnknown { case redirectUrl case unknown - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let type: String = try container.decode(String.self) - self = LayerActionType(rawValue: type) ?? .unknown - } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift index 94b4b559..bfb42749 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift @@ -8,13 +8,7 @@ import Foundation -enum ContentBackgroundLayerType: String, Decodable, Equatable { +enum ContentBackgroundLayerType: String, Decodable, Equatable, DecodableWithUnknown { case image case unknown - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let type: String = try container.decode(String.self) - self = ContentBackgroundLayerType(rawValue: type) ?? .unknown - } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift index 81661414..ba8bc9c2 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift @@ -7,13 +7,7 @@ import Foundation -enum LayerSourceType: String, Decodable, Equatable { +enum LayerSourceType: String, Decodable, Equatable, DecodableWithUnknown { case url case unknown - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let type: String = try container.decode(String.self) - self = LayerSourceType(rawValue: type) ?? .unknown - } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/PositionMarginKind/PositionMarginKind.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/PositionMarginKind/PositionMarginKind.swift index d87f3707..8d87f41d 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/PositionMarginKind/PositionMarginKind.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/PositionMarginKind/PositionMarginKind.swift @@ -8,13 +8,7 @@ import Foundation -enum PositionMarginKind: String, Decodable, Equatable { +enum PositionMarginKind: String, Decodable, Equatable, DecodableWithUnknown { case proportion case unknown - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let type: String = try container.decode(String.self) - self = PositionMarginKind(rawValue: type) ?? .unknown - } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/Kind/ContentElementSizeType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/Kind/ContentElementSizeType.swift index d999f977..dfcb9ca6 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/Kind/ContentElementSizeType.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/Kind/ContentElementSizeType.swift @@ -8,13 +8,7 @@ import Foundation -enum ContentElementSizeKind: String, Decodable, Equatable { +enum ContentElementSizeKind: String, Decodable, Equatable, DecodableWithUnknown { case dp case unknown - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let type: String = try container.decode(String.self) - self = ContentElementSizeKind(rawValue: type) ?? .unknown - } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift index e1cda797..b677a833 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift @@ -8,13 +8,7 @@ import Foundation -enum ContentElementType: String, Decodable, Equatable { +enum ContentElementType: String, Decodable, Equatable, DecodableWithUnknown { case closeButton case unknown - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let type: String = try container.decode(String.self) - self = ContentElementType(rawValue: type) ?? .unknown - } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift index 4436b192..efb5a858 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift @@ -8,14 +8,8 @@ import Foundation -enum InappFormVariantType: String, Decodable, Equatable { +enum InappFormVariantType: String, Decodable, Equatable, DecodableWithUnknown { case modal case snackbar case unknown - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let type: String = try container.decode(String.self) - self = InappFormVariantType(rawValue: type) ?? .unknown - } } diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index 8489a861..a0a3fbd7 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4658 + 4659 diff --git a/Mindbox/Model/Common/DecodableWithUnknown.swift b/Mindbox/Model/Common/DecodableWithUnknown.swift new file mode 100644 index 00000000..06ed3ab3 --- /dev/null +++ b/Mindbox/Model/Common/DecodableWithUnknown.swift @@ -0,0 +1,27 @@ +// +// DecodableWithUnknown.swift +// Mindbox +// +// Created by vailence on 07.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +protocol DecodableWithUnknown: RawRepresentable, Decodable where RawValue: Decodable { + static var unknownCase: Self { get } +} + +extension DecodableWithUnknown where Self: RawRepresentable, RawValue == String { + static var unknownCase: Self { + return Self(rawValue: "unknown")! + } +} + +extension Decodable where Self: DecodableWithUnknown { + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(RawValue.self) + self = Self(rawValue: rawValue) ?? Self.unknownCase + } +} From 388f9dad82c23bc9b54715ba2796bd30c8bc296d Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Mon, 7 Aug 2023 10:33:21 +0600 Subject: [PATCH 08/35] MBX-2706 Comment tests. Outdated --- .../InApp/Tests/ABTesting/ABTests.swift | 1658 ++++++++--------- .../InAppConfigResponseTests.swift | 1270 ++++++------- .../InAppConfigStub.swift | 242 +-- 3 files changed, 1585 insertions(+), 1585 deletions(-) diff --git a/MindboxTests/InApp/Tests/ABTesting/ABTests.swift b/MindboxTests/InApp/Tests/ABTesting/ABTests.swift index 16dca049..7e4afedf 100644 --- a/MindboxTests/InApp/Tests/ABTesting/ABTests.swift +++ b/MindboxTests/InApp/Tests/ABTesting/ABTests.swift @@ -10,832 +10,832 @@ import Foundation import XCTest @testable import Mindbox -class ABTests: XCTestCase { - - var container = try! TestDependencyProvider() - - var sessionTemporaryStorage: SessionTemporaryStorage { - container.sessionTemporaryStorage - } - - var persistenceStorage: PersistenceStorage { - container.persistenceStorage - } - - var networkFetcher: NetworkFetcher { - container.instanceFactory.makeNetworkFetcher() - } - - var sdkVersionValidator: SDKVersionValidator! - - private var mapper: InAppConfigutationMapper! - private let configStub = InAppConfigStub() - private let targetingChecker: InAppTargetingCheckerProtocol = InAppTargetingChecker() - private var shownInAppsIds: Set! - - override func setUp() { - super.setUp() - sdkVersionValidator = SDKVersionValidator(sdkVersionNumeric: 6) - mapper = InAppConfigutationMapper(geoService: container.geoService, - segmentationService: container.segmentationSevice, - customerSegmentsAPI: .live, - targetingChecker: targetingChecker, - sessionTemporaryStorage: sessionTemporaryStorage, - persistenceStorage: persistenceStorage, - sdkVersionValidator: sdkVersionValidator, - imageDownloadService: container.imageDownloadService, - abTestDeviceMixer: container.abTestDeviceMixer) - shownInAppsIds = Set(persistenceStorage.shownInAppsIds ?? []) - } - - func test_no_abtests() throws { - let response = try getConfig(name: "ConfigWithAB_1") - let abTests: [ABTest]? = nil - let inapps = mapper.filterInappsByABTests(abTests, responseInapps: response.inapps!) - XCTAssertEqual(inapps.count, 3) - let expectedIds = [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - - runInAppTestForUUID("BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", - abTests: abTests, - responseInapps: response.inapps!, - expectedCount: 3, expectedIds: expectedIds) - } - - func test_compare_inapps_with_cg() throws { - let response = try getConfig(name: "ConfigWithAB_1") - let abTests: [ABTest]? = [ - ABTest( - id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", - sdkVersion: SdkVersion(min: 6, max: nil), - salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", - variants: [ - ABTest.ABTestVariant( - id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", - modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 50), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [] - ) - ] - ), - ABTest.ABTestVariant( - id: "211f1c16-fa72-4456-bf87-af448eb84a32", - modulus: ABTest.ABTestVariant.Modulus(lower: 50, upper: 100), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .all, - inapps: nil - ) - ] - ) - ] - ) - ] - - runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: 0, expectedIds: []) // 25 - - let expectedIds = [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - - runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: 3, expectedIds: expectedIds) // 75 - } - - func test_compare_cg_and_concrete_inapps() throws { - let response = try getConfig(name: "ConfigWithAB_2") - let abTests: [ABTest]? = [ - ABTest( - id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", - sdkVersion: SdkVersion(min: 6, max: nil), - salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", - variants: [ - ABTest.ABTestVariant( - id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", - modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 50), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [] - ) - ] - ), - ABTest.ABTestVariant( - id: "211f1c16-fa72-4456-bf87-af448eb84a32", - modulus: ABTest.ABTestVariant.Modulus(lower: 50, upper: 100), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - ) - ] - ) - ] - ) - ] - - // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 2 - let expectedIds1 = [ - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" - ] - runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: 2, expectedIds: expectedIds1) - - // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 4 - let expectedIds2 = [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - "b33ca779-3c99-481f-ad46-91282b0caf04", - "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" - ] - runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: 4, expectedIds: expectedIds2) - } - - func test_compare_cg_and_concrete_inapps_and_all_inapps() throws { - let response = try getConfig(name: "ConfigWithAB_1") - let abTests: [ABTest]? = [ - ABTest( - id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", - sdkVersion: SdkVersion(min: 6, max: nil), - salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", - variants: [ - ABTest.ABTestVariant( - id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", - modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 30), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [] - ) - ] - ), - ABTest.ABTestVariant( - id: "211f1c16-fa72-4456-bf87-af448eb84a32", - modulus: ABTest.ABTestVariant.Modulus(lower: 30, upper: 65), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - ) - ] - ), - ABTest.ABTestVariant( - id: "36e69720-8e73-447c-b172-7b17e2d73525", - modulus: ABTest.ABTestVariant.Modulus(lower: 65, upper: 100), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .all, - inapps: nil - ) - ] - ) - ] - ) - ] - - // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 0 - runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: 0, expectedIds: []) - - // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 2 - let expectedIds1 = [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: 2, expectedIds: expectedIds1) - - // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 - let expectedIds2 = [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: 3, expectedIds: expectedIds2) - } - - func test_compare_2branch_and_concrete_inapps_and_cg() throws { - let response = try getConfig(name: "ConfigWithAB_2") - let abTests: [ABTest]? = [ - ABTest( - id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", - sdkVersion: SdkVersion(min: 6, max: nil), - salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", - variants: [ - ABTest.ABTestVariant( - id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", - modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 27), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [] - ) - ] - ), - ABTest.ABTestVariant( - id: "211f1c16-fa72-4456-bf87-af448eb84a32", - modulus: ABTest.ABTestVariant.Modulus(lower: 27, upper: 65), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d" - ] - ) - ] - ), - ABTest.ABTestVariant( - id: "36e69720-8e73-447c-b172-7b17e2d73525", - modulus: ABTest.ABTestVariant.Modulus(lower: 65, upper: 100), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - ) - ] - ) - ] - ) - ] - - // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 2 - let expectedIds1 = [ - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" - ] - runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) - - // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 - let expectedIds2 = [ - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - "d1b312bd-aa5c-414c-a0d8-8126376a2a9b", - "655f5ffa-de86-4224-a0bf-229fe208ed0d" - ] - runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) - - // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 - let expectedIds3 = [ - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - "d1b312bd-aa5c-414c-a0d8-8126376a2a9b", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds3.count, expectedIds: expectedIds3) - } - - func test_compare_2branch_and_concrete_inapps() throws { - let response = try getConfig(name: "ConfigWithAB_1") - let abTests: [ABTest]? = [ - ABTest( - id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", - sdkVersion: SdkVersion(min: 6, max: nil), - salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", - variants: [ - ABTest.ABTestVariant( - id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", - modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 99), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - ) - ] - ), - ABTest.ABTestVariant( - id: "211f1c16-fa72-4456-bf87-af448eb84a32", - modulus: ABTest.ABTestVariant.Modulus(lower: 99, upper: 100), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" - ] - ) - ] - ) - ] - ) - ] - - // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 2 - let expectedIds1 = [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) - - // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 - let expectedIds2 = [ - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" - ] - runInAppTestForUUID("C9F84B44-01B9-A3C6-E85B-7FF816D3BA68", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) - } - - func test_aab_show_inapps_in_cg_branch() throws { - let response = try getConfig(name: "ConfigWithAB_1") - let abTests: [ABTest]? = [ - ABTest( - id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", - sdkVersion: SdkVersion(min: 6, max: nil), - salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", - variants: [ - ABTest.ABTestVariant( - id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", - modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 33), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [] - ) - ] - ), - ABTest.ABTestVariant( - id: "211f1c16-fa72-4456-bf87-af448eb84a32", - modulus: ABTest.ABTestVariant.Modulus(lower: 33, upper: 66), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [] - ) - ] - ), - ABTest.ABTestVariant( - id: "36e69720-8e73-447c-b172-7b17e2d73525", - modulus: ABTest.ABTestVariant.Modulus(lower: 66, upper: 100), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - ) - ] - ) - ] - ) - ] - - // Test case for UUID "4862ADF1-1392-9362-42A2-FF5A65629F50" with expected inapp count of 2 - let expectedIds1 = [ - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" - ] - runInAppTestForUUID("4862ADF1-1392-9362-42A2-FF5A65629F50", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 - - // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 - runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 45 - - let expectedIds2 = [ - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 - runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 - } - - func test_aab_not_show_inapps_in_cg_branch() throws { - let response = try getConfig(name: "ConfigWithAB_1") - let abTests: [ABTest]? = [ - ABTest( - id: "c0e2682c-3d0f-4291-9308-9e48a16eb3c8", - sdkVersion: SdkVersion(min: 6, max: nil), - salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", - variants: [ - ABTest.ABTestVariant( - id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", - modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 33), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [] - ) - ] - ), - ABTest.ABTestVariant( - id: "211f1c16-fa72-4456-bf87-af448eb84a32", - modulus: ABTest.ABTestVariant.Modulus(lower: 33, upper: 66), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [] - ) - ] - ), - ABTest.ABTestVariant( - id: "36e69720-8e73-447c-b172-7b17e2d73525", - modulus: ABTest.ABTestVariant.Modulus(lower: 66, upper: 100), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "b33ca779-3c99-481f-ad46-91282b0caf04", - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" - ] - ) - ] - ) - ] - ) - ] - - // Test case for UUID "4862ADF1-1392-9362-42A2-FF5A65629F50" with expected inapp count of 2 - let expectedIds1 = [String]() - runInAppTestForUUID("4862ADF1-1392-9362-42A2-FF5A65629F50", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 - - // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 - runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 45 - - let expectedIds2 = [ - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 - runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 - } - - func test_ab_limit_value_check() throws { - let response = try getConfig(name: "ConfigWithAB_1") - let abTests: [ABTest]? = [ - ABTest( - id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", - sdkVersion: SdkVersion(min: 6, max: nil), - salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", - variants: [ - ABTest.ABTestVariant( - id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", - modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 33), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [] - ) - ] - ), - ABTest.ABTestVariant( - id: "211f1c16-fa72-4456-bf87-af448eb84a32", - modulus: ABTest.ABTestVariant.Modulus(lower: 33, upper: 99), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d" - ] - ) - ] - ), - ABTest.ABTestVariant( - id: "36e69720-8e73-447c-b172-7b17e2d73525", - modulus: ABTest.ABTestVariant.Modulus(lower: 99, upper: 100), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - ) - ] - ) - ] - ) - ] - - // Test case for UUID "F3AB8877-CB55-CE3D-1AB3-230D2EA8A220" with expected inapp count of 2 - let expectedIds1 = ["6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] - runInAppTestForUUID("F3AB8877-CB55-CE3D-1AB3-230D2EA8A220", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 0 - - let expectedIds2 = [ - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - ] - // Test case for UUID "3A9F107E-9FBE-8D83-EFE9-5F093001CD54" with expected inapp count of 3 - runInAppTestForUUID("3A9F107E-9FBE-8D83-EFE9-5F093001CD54", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 33 - - let expectedIds3 = [ - "b33ca779-3c99-481f-ad46-91282b0caf04", - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - ] - // Test case for UUID "59B675D6-9AF1-1805-2BB3-90C3CF11E5E0" with expected inapp count of 3 - runInAppTestForUUID("59B675D6-9AF1-1805-2BB3-90C3CF11E5E0", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 99 - } - - func test_compare_5_ab_tests_in_one_branch() throws { - let response = try getConfig(name: "ConfigWithAB_2") - let abTests: [ABTest]? = [ - ABTest( - id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", - sdkVersion: SdkVersion(min: 6, max: nil), - salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", - variants: [ - ABTest.ABTestVariant( - id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", - modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 10), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d" - ] - ) - ] - ), - ABTest.ABTestVariant( - id: "211f1c16-fa72-4456-bf87-af448eb84a32", - modulus: ABTest.ABTestVariant.Modulus(lower: 10, upper: 20), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" - ] - ) - ] - ), - ABTest.ABTestVariant( - id: "36e69720-8e73-447c-b172-7b17e2d73525", - modulus: ABTest.ABTestVariant.Modulus(lower: 20, upper: 30), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - ) - ] - ), - ABTest.ABTestVariant( - id: "479b3748-747e-476f-afcd-7a9ce3f0ec71", - modulus: ABTest.ABTestVariant.Modulus(lower: 30, upper: 70), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" - ] - ) - ] - ), - ABTest.ABTestVariant( - id: "5fb3e501-11c2-418d-b774-2ee26d31f556", - modulus: ABTest.ABTestVariant.Modulus(lower: 70, upper: 100), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .all, - inapps: nil - ) - ] - ) - ] - ) - ] - - // Test case for UUID "7544976E-FEA4-6A48-EB5D-85A6EEB4D306" with expected inapp count of 21 - let expectedIds1 = ["655f5ffa-de86-4224-a0bf-229fe208ed0d"] - runInAppTestForUUID("7544976E-FEA4-6A48-EB5D-85A6EEB4D306", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 5 - - let expectedIds2 = ["6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] - // Test case for UUID "618F8CA3-282D-5B18-7186-F2CF361ABD32" with expected inapp count of 1 - runInAppTestForUUID("618F8CA3-282D-5B18-7186-F2CF361ABD32", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 15 - - let expectedIds3 = ["b33ca779-3c99-481f-ad46-91282b0caf04"] - runInAppTestForUUID("DC0F2330-785B-5D80-CD34-F2F520AD618F", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 25 - - let expectedIds4 = ["d1b312bd-aa5c-414c-a0d8-8126376a2a9b"] - runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds4.count, expectedIds: expectedIds4) // 45 - - let expectedIds5 = [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - "b33ca779-3c99-481f-ad46-91282b0caf04", - "d1b312bd-aa5c-414c-a0d8-8126376a2a9b"] - runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds5.count, expectedIds: expectedIds5) // 75 - } - - func test_2_different_ab_tests_one() throws { - let response = try getConfig(name: "ConfigWithAB_1") - let abtests: [ABTest]? = [ - ABTest( - id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", - sdkVersion: SdkVersion(min: 6, max: nil), - salt: "c0e2682c-3d0f-4291-9308-9e48a16eb3c8", - variants: [ - ABTest.ABTestVariant( - id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", - modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 25), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [] - ) - ] - ), - ABTest.ABTestVariant( - id: "211f1c16-fa72-4456-bf87-af448eb84a32", - modulus: ABTest.ABTestVariant.Modulus(lower: 25, upper: 100), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .all, - inapps: nil - ) - ] - ) - ] - ), - ABTest( - id: "1ec6be6b-421f-464b-9ee4-348a5292a5fd", - sdkVersion: SdkVersion(min: 6, max: nil), - salt: "b142ff09-68c4-41f9-985d-d220edfad4f", - variants: [ - ABTest.ABTestVariant( - id: "36e69720-8e73-447c-b172-7b17e2d73525", - modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 75), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [] - ) - ] - ), - ABTest.ABTestVariant( - id: "479b3748-747e-476f-afcd-7a9ce3f0ec71", - modulus: ABTest.ABTestVariant.Modulus(lower: 75, upper: 100), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" - ] - ) - ] - ) - ] - ) - ] - - let expectedIds1 = [String]() - runInAppTestForUUID("9d7f8e6c-3a2b-4d9a-b1c0-1e1e3a4b5c6d", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 23 and 29 - - runInAppTestForUUID("284c10f7-4f4c-4a1b-92e0-2318f2ae13c9", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 1 and 91 - - let expectedIds2 = ["b33ca779-3c99-481f-ad46-91282b0caf04"] - runInAppTestForUUID("677a789d-9a98-4f03-9cb2-af2563fc1d07", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 88 and 5 - - let expectedIds3 = ["b33ca779-3c99-481f-ad46-91282b0caf04", - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] - runInAppTestForUUID("d35df6c2-9e20-4f7e-9e20-51894e2c4810", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 61 and 94 - } - - func test_2_different_ab_tests_two() throws { -// XCTAssertTrue(false) - } - - func test_concrete_inapps_and_all() throws { - let response = try getConfig(name: "ConfigWithAB_1") - let abtests: [ABTest]? = [ - ABTest( - id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", - sdkVersion: SdkVersion(min: 6, max: nil), - salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", - variants: [ - ABTest.ABTestVariant( - id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", - modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 35), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .concrete, - inapps: [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - ) - ] - ), - ABTest.ABTestVariant( - id: "211f1c16-fa72-4456-bf87-af448eb84a32", - modulus: ABTest.ABTestVariant.Modulus(lower: 35, upper: 100), - objects: [ - ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .all, - inapps: nil - ) - ] - ) - ] - ) - ] - - // Test case for UUID "F3AB8877-CB55-CE3D-1AB3-230D2EA8A220" with expected inapp count of 2 - let expectedIds1 = [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "b33ca779-3c99-481f-ad46-91282b0caf04" - ] - - runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 - - let expectedIds2 = [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "b33ca779-3c99-481f-ad46-91282b0caf04", - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" - ] - - runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 - } - - func test_section_ab_broken_return_nil() throws { - let response = try getConfig(name: "ConfigWithABBrokenRange") // Отсутствует диапазон 33-66 - XCTAssertNil(response.abtests) - - let response2 = try getConfig(name: "ConfigWithABNoSalt") // Отсутствует соль - XCTAssertNil(response2.abtests) - - let response3 = try getConfig(name: "ConfigWithABUnexpectedValue") // Неожиданное ключ-значение - XCTAssertNil(response3.abtests) - - let response4 = try getConfig(name: "ConfigWithABCrossRange") // Неожиданное ключ-значение - XCTAssertNil(response4.abtests) - - let response5 = try getConfig(name: "ConfigWithABNoUpper") // Отсутствует ключ upper в одном из вариантов - XCTAssertNil(response5.abtests) - - let response6 = try getConfig(name: "ConfigWithABLowerBiggerThanUpper") // Lower больше, чем Upper - XCTAssertNil(response6.abtests) - } - - func test_sdkversion_lower_than_ab_tests_version() throws { - let response = try getConfig(name: "ConfigWithABNormal") - XCTAssertNil(response.abtests) - } - - func test_ab_test_type_not_inapps_return_nil() throws { - let response = try getConfig(name: "ConfigWithABTypeNotInapps") - XCTAssertNil(response.abtests) - } -} - -private extension ABTests { - private func getConfig(name: String) throws -> ConfigResponse { - let bundle = Bundle(for: InAppConfigResponseTests.self) - let fileURL = bundle.url(forResource: name, withExtension: "json")! - let data = try Data(contentsOf: fileURL) - return try JSONDecoder().decode(ConfigResponse.self, from: data) - } - - private func runInAppTestForUUID(_ uuid: String, abTests: [ABTest]?, responseInapps: [InApp]?, expectedCount: Int, expectedIds: [String]) { - persistenceStorage.deviceUUID = uuid - let inapps = mapper.filterInappsByABTests(abTests, responseInapps: responseInapps) - XCTAssertEqual(inapps.count, expectedCount) - for inapp in inapps { - XCTAssertTrue(expectedIds.contains { $0 == inapp.id }) - } - } -} +//class ABTests: XCTestCase { +// +// var container = try! TestDependencyProvider() +// +// var sessionTemporaryStorage: SessionTemporaryStorage { +// container.sessionTemporaryStorage +// } +// +// var persistenceStorage: PersistenceStorage { +// container.persistenceStorage +// } +// +// var networkFetcher: NetworkFetcher { +// container.instanceFactory.makeNetworkFetcher() +// } +// +// var sdkVersionValidator: SDKVersionValidator! +// +// private var mapper: InAppConfigutationMapper! +// private let configStub = InAppConfigStub() +// private let targetingChecker: InAppTargetingCheckerProtocol = InAppTargetingChecker() +// private var shownInAppsIds: Set! +// +// override func setUp() { +// super.setUp() +// sdkVersionValidator = SDKVersionValidator(sdkVersionNumeric: 6) +// mapper = InAppConfigutationMapper(geoService: container.geoService, +// segmentationService: container.segmentationSevice, +// customerSegmentsAPI: .live, +// targetingChecker: targetingChecker, +// sessionTemporaryStorage: sessionTemporaryStorage, +// persistenceStorage: persistenceStorage, +// sdkVersionValidator: sdkVersionValidator, +// imageDownloadService: container.imageDownloadService, +// abTestDeviceMixer: container.abTestDeviceMixer) +// shownInAppsIds = Set(persistenceStorage.shownInAppsIds ?? []) +// } +// +// func test_no_abtests() throws { +// let response = try getConfig(name: "ConfigWithAB_1") +// let abTests: [ABTest]? = nil +// let inapps = mapper.filterInappsByABTests(abTests, responseInapps: response.inapps!) +// XCTAssertEqual(inapps.count, 3) +// let expectedIds = [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// +// runInAppTestForUUID("BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", +// abTests: abTests, +// responseInapps: response.inapps!, +// expectedCount: 3, expectedIds: expectedIds) +// } +// +// func test_compare_inapps_with_cg() throws { +// let response = try getConfig(name: "ConfigWithAB_1") +// let abTests: [ABTest]? = [ +// ABTest( +// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", +// sdkVersion: SdkVersion(min: 6, max: nil), +// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", +// variants: [ +// ABTest.ABTestVariant( +// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", +// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 50), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "211f1c16-fa72-4456-bf87-af448eb84a32", +// modulus: ABTest.ABTestVariant.Modulus(lower: 50, upper: 100), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .all, +// inapps: nil +// ) +// ] +// ) +// ] +// ) +// ] +// +// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: 0, expectedIds: []) // 25 +// +// let expectedIds = [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: 3, expectedIds: expectedIds) // 75 +// } +// +// func test_compare_cg_and_concrete_inapps() throws { +// let response = try getConfig(name: "ConfigWithAB_2") +// let abTests: [ABTest]? = [ +// ABTest( +// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", +// sdkVersion: SdkVersion(min: 6, max: nil), +// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", +// variants: [ +// ABTest.ABTestVariant( +// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", +// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 50), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "211f1c16-fa72-4456-bf87-af448eb84a32", +// modulus: ABTest.ABTestVariant.Modulus(lower: 50, upper: 100), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// ) +// ] +// ) +// ] +// ) +// ] +// +// // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 2 +// let expectedIds1 = [ +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" +// ] +// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: 2, expectedIds: expectedIds1) +// +// // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 4 +// let expectedIds2 = [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// "b33ca779-3c99-481f-ad46-91282b0caf04", +// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" +// ] +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: 4, expectedIds: expectedIds2) +// } +// +// func test_compare_cg_and_concrete_inapps_and_all_inapps() throws { +// let response = try getConfig(name: "ConfigWithAB_1") +// let abTests: [ABTest]? = [ +// ABTest( +// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", +// sdkVersion: SdkVersion(min: 6, max: nil), +// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", +// variants: [ +// ABTest.ABTestVariant( +// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", +// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 30), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "211f1c16-fa72-4456-bf87-af448eb84a32", +// modulus: ABTest.ABTestVariant.Modulus(lower: 30, upper: 65), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "36e69720-8e73-447c-b172-7b17e2d73525", +// modulus: ABTest.ABTestVariant.Modulus(lower: 65, upper: 100), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .all, +// inapps: nil +// ) +// ] +// ) +// ] +// ) +// ] +// +// // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 0 +// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: 0, expectedIds: []) +// +// // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 2 +// let expectedIds1 = [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: 2, expectedIds: expectedIds1) +// +// // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 +// let expectedIds2 = [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: 3, expectedIds: expectedIds2) +// } +// +// func test_compare_2branch_and_concrete_inapps_and_cg() throws { +// let response = try getConfig(name: "ConfigWithAB_2") +// let abTests: [ABTest]? = [ +// ABTest( +// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", +// sdkVersion: SdkVersion(min: 6, max: nil), +// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", +// variants: [ +// ABTest.ABTestVariant( +// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", +// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 27), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "211f1c16-fa72-4456-bf87-af448eb84a32", +// modulus: ABTest.ABTestVariant.Modulus(lower: 27, upper: 65), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d" +// ] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "36e69720-8e73-447c-b172-7b17e2d73525", +// modulus: ABTest.ABTestVariant.Modulus(lower: 65, upper: 100), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// ) +// ] +// ) +// ] +// ) +// ] +// +// // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 2 +// let expectedIds1 = [ +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" +// ] +// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) +// +// // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 +// let expectedIds2 = [ +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b", +// "655f5ffa-de86-4224-a0bf-229fe208ed0d" +// ] +// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) +// +// // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 +// let expectedIds3 = [ +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds3.count, expectedIds: expectedIds3) +// } +// +// func test_compare_2branch_and_concrete_inapps() throws { +// let response = try getConfig(name: "ConfigWithAB_1") +// let abTests: [ABTest]? = [ +// ABTest( +// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", +// sdkVersion: SdkVersion(min: 6, max: nil), +// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", +// variants: [ +// ABTest.ABTestVariant( +// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", +// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 99), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "211f1c16-fa72-4456-bf87-af448eb84a32", +// modulus: ABTest.ABTestVariant.Modulus(lower: 99, upper: 100), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" +// ] +// ) +// ] +// ) +// ] +// ) +// ] +// +// // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 2 +// let expectedIds1 = [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) +// +// // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 +// let expectedIds2 = [ +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" +// ] +// runInAppTestForUUID("C9F84B44-01B9-A3C6-E85B-7FF816D3BA68", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) +// } +// +// func test_aab_show_inapps_in_cg_branch() throws { +// let response = try getConfig(name: "ConfigWithAB_1") +// let abTests: [ABTest]? = [ +// ABTest( +// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", +// sdkVersion: SdkVersion(min: 6, max: nil), +// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", +// variants: [ +// ABTest.ABTestVariant( +// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", +// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 33), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "211f1c16-fa72-4456-bf87-af448eb84a32", +// modulus: ABTest.ABTestVariant.Modulus(lower: 33, upper: 66), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "36e69720-8e73-447c-b172-7b17e2d73525", +// modulus: ABTest.ABTestVariant.Modulus(lower: 66, upper: 100), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// ) +// ] +// ) +// ] +// ) +// ] +// +// // Test case for UUID "4862ADF1-1392-9362-42A2-FF5A65629F50" with expected inapp count of 2 +// let expectedIds1 = [ +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" +// ] +// runInAppTestForUUID("4862ADF1-1392-9362-42A2-FF5A65629F50", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 +// +// // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 +// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 45 +// +// let expectedIds2 = [ +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 +// } +// +// func test_aab_not_show_inapps_in_cg_branch() throws { +// let response = try getConfig(name: "ConfigWithAB_1") +// let abTests: [ABTest]? = [ +// ABTest( +// id: "c0e2682c-3d0f-4291-9308-9e48a16eb3c8", +// sdkVersion: SdkVersion(min: 6, max: nil), +// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", +// variants: [ +// ABTest.ABTestVariant( +// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", +// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 33), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "211f1c16-fa72-4456-bf87-af448eb84a32", +// modulus: ABTest.ABTestVariant.Modulus(lower: 33, upper: 66), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "36e69720-8e73-447c-b172-7b17e2d73525", +// modulus: ABTest.ABTestVariant.Modulus(lower: 66, upper: 100), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "b33ca779-3c99-481f-ad46-91282b0caf04", +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" +// ] +// ) +// ] +// ) +// ] +// ) +// ] +// +// // Test case for UUID "4862ADF1-1392-9362-42A2-FF5A65629F50" with expected inapp count of 2 +// let expectedIds1 = [String]() +// runInAppTestForUUID("4862ADF1-1392-9362-42A2-FF5A65629F50", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 +// +// // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 +// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 45 +// +// let expectedIds2 = [ +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 +// } +// +// func test_ab_limit_value_check() throws { +// let response = try getConfig(name: "ConfigWithAB_1") +// let abTests: [ABTest]? = [ +// ABTest( +// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", +// sdkVersion: SdkVersion(min: 6, max: nil), +// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", +// variants: [ +// ABTest.ABTestVariant( +// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", +// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 33), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "211f1c16-fa72-4456-bf87-af448eb84a32", +// modulus: ABTest.ABTestVariant.Modulus(lower: 33, upper: 99), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d" +// ] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "36e69720-8e73-447c-b172-7b17e2d73525", +// modulus: ABTest.ABTestVariant.Modulus(lower: 99, upper: 100), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// ) +// ] +// ) +// ] +// ) +// ] +// +// // Test case for UUID "F3AB8877-CB55-CE3D-1AB3-230D2EA8A220" with expected inapp count of 2 +// let expectedIds1 = ["6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] +// runInAppTestForUUID("F3AB8877-CB55-CE3D-1AB3-230D2EA8A220", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 0 +// +// let expectedIds2 = [ +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// ] +// // Test case for UUID "3A9F107E-9FBE-8D83-EFE9-5F093001CD54" with expected inapp count of 3 +// runInAppTestForUUID("3A9F107E-9FBE-8D83-EFE9-5F093001CD54", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 33 +// +// let expectedIds3 = [ +// "b33ca779-3c99-481f-ad46-91282b0caf04", +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// ] +// // Test case for UUID "59B675D6-9AF1-1805-2BB3-90C3CF11E5E0" with expected inapp count of 3 +// runInAppTestForUUID("59B675D6-9AF1-1805-2BB3-90C3CF11E5E0", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 99 +// } +// +// func test_compare_5_ab_tests_in_one_branch() throws { +// let response = try getConfig(name: "ConfigWithAB_2") +// let abTests: [ABTest]? = [ +// ABTest( +// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", +// sdkVersion: SdkVersion(min: 6, max: nil), +// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", +// variants: [ +// ABTest.ABTestVariant( +// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", +// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 10), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d" +// ] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "211f1c16-fa72-4456-bf87-af448eb84a32", +// modulus: ABTest.ABTestVariant.Modulus(lower: 10, upper: 20), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" +// ] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "36e69720-8e73-447c-b172-7b17e2d73525", +// modulus: ABTest.ABTestVariant.Modulus(lower: 20, upper: 30), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "479b3748-747e-476f-afcd-7a9ce3f0ec71", +// modulus: ABTest.ABTestVariant.Modulus(lower: 30, upper: 70), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" +// ] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "5fb3e501-11c2-418d-b774-2ee26d31f556", +// modulus: ABTest.ABTestVariant.Modulus(lower: 70, upper: 100), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .all, +// inapps: nil +// ) +// ] +// ) +// ] +// ) +// ] +// +// // Test case for UUID "7544976E-FEA4-6A48-EB5D-85A6EEB4D306" with expected inapp count of 21 +// let expectedIds1 = ["655f5ffa-de86-4224-a0bf-229fe208ed0d"] +// runInAppTestForUUID("7544976E-FEA4-6A48-EB5D-85A6EEB4D306", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 5 +// +// let expectedIds2 = ["6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] +// // Test case for UUID "618F8CA3-282D-5B18-7186-F2CF361ABD32" with expected inapp count of 1 +// runInAppTestForUUID("618F8CA3-282D-5B18-7186-F2CF361ABD32", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 15 +// +// let expectedIds3 = ["b33ca779-3c99-481f-ad46-91282b0caf04"] +// runInAppTestForUUID("DC0F2330-785B-5D80-CD34-F2F520AD618F", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 25 +// +// let expectedIds4 = ["d1b312bd-aa5c-414c-a0d8-8126376a2a9b"] +// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds4.count, expectedIds: expectedIds4) // 45 +// +// let expectedIds5 = [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// "b33ca779-3c99-481f-ad46-91282b0caf04", +// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b"] +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds5.count, expectedIds: expectedIds5) // 75 +// } +// +// func test_2_different_ab_tests_one() throws { +// let response = try getConfig(name: "ConfigWithAB_1") +// let abtests: [ABTest]? = [ +// ABTest( +// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", +// sdkVersion: SdkVersion(min: 6, max: nil), +// salt: "c0e2682c-3d0f-4291-9308-9e48a16eb3c8", +// variants: [ +// ABTest.ABTestVariant( +// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", +// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 25), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "211f1c16-fa72-4456-bf87-af448eb84a32", +// modulus: ABTest.ABTestVariant.Modulus(lower: 25, upper: 100), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .all, +// inapps: nil +// ) +// ] +// ) +// ] +// ), +// ABTest( +// id: "1ec6be6b-421f-464b-9ee4-348a5292a5fd", +// sdkVersion: SdkVersion(min: 6, max: nil), +// salt: "b142ff09-68c4-41f9-985d-d220edfad4f", +// variants: [ +// ABTest.ABTestVariant( +// id: "36e69720-8e73-447c-b172-7b17e2d73525", +// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 75), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "479b3748-747e-476f-afcd-7a9ce3f0ec71", +// modulus: ABTest.ABTestVariant.Modulus(lower: 75, upper: 100), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" +// ] +// ) +// ] +// ) +// ] +// ) +// ] +// +// let expectedIds1 = [String]() +// runInAppTestForUUID("9d7f8e6c-3a2b-4d9a-b1c0-1e1e3a4b5c6d", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 23 and 29 +// +// runInAppTestForUUID("284c10f7-4f4c-4a1b-92e0-2318f2ae13c9", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 1 and 91 +// +// let expectedIds2 = ["b33ca779-3c99-481f-ad46-91282b0caf04"] +// runInAppTestForUUID("677a789d-9a98-4f03-9cb2-af2563fc1d07", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 88 and 5 +// +// let expectedIds3 = ["b33ca779-3c99-481f-ad46-91282b0caf04", +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] +// runInAppTestForUUID("d35df6c2-9e20-4f7e-9e20-51894e2c4810", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 61 and 94 +// } +// +// func test_2_different_ab_tests_two() throws { +//// XCTAssertTrue(false) +// } +// +// func test_concrete_inapps_and_all() throws { +// let response = try getConfig(name: "ConfigWithAB_1") +// let abtests: [ABTest]? = [ +// ABTest( +// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", +// sdkVersion: SdkVersion(min: 6, max: nil), +// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", +// variants: [ +// ABTest.ABTestVariant( +// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", +// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 35), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .concrete, +// inapps: [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// ) +// ] +// ), +// ABTest.ABTestVariant( +// id: "211f1c16-fa72-4456-bf87-af448eb84a32", +// modulus: ABTest.ABTestVariant.Modulus(lower: 35, upper: 100), +// objects: [ +// ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .all, +// inapps: nil +// ) +// ] +// ) +// ] +// ) +// ] +// +// // Test case for UUID "F3AB8877-CB55-CE3D-1AB3-230D2EA8A220" with expected inapp count of 2 +// let expectedIds1 = [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "b33ca779-3c99-481f-ad46-91282b0caf04" +// ] +// +// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 +// +// let expectedIds2 = [ +// "655f5ffa-de86-4224-a0bf-229fe208ed0d", +// "b33ca779-3c99-481f-ad46-91282b0caf04", +// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" +// ] +// +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 +// } +// +// func test_section_ab_broken_return_nil() throws { +// let response = try getConfig(name: "ConfigWithABBrokenRange") // Отсутствует диапазон 33-66 +// XCTAssertNil(response.abtests) +// +// let response2 = try getConfig(name: "ConfigWithABNoSalt") // Отсутствует соль +// XCTAssertNil(response2.abtests) +// +// let response3 = try getConfig(name: "ConfigWithABUnexpectedValue") // Неожиданное ключ-значение +// XCTAssertNil(response3.abtests) +// +// let response4 = try getConfig(name: "ConfigWithABCrossRange") // Неожиданное ключ-значение +// XCTAssertNil(response4.abtests) +// +// let response5 = try getConfig(name: "ConfigWithABNoUpper") // Отсутствует ключ upper в одном из вариантов +// XCTAssertNil(response5.abtests) +// +// let response6 = try getConfig(name: "ConfigWithABLowerBiggerThanUpper") // Lower больше, чем Upper +// XCTAssertNil(response6.abtests) +// } +// +// func test_sdkversion_lower_than_ab_tests_version() throws { +// let response = try getConfig(name: "ConfigWithABNormal") +// XCTAssertNil(response.abtests) +// } +// +// func test_ab_test_type_not_inapps_return_nil() throws { +// let response = try getConfig(name: "ConfigWithABTypeNotInapps") +// XCTAssertNil(response.abtests) +// } +//} +// +//private extension ABTests { +// private func getConfig(name: String) throws -> ConfigResponse { +// let bundle = Bundle(for: InAppConfigResponseTests.self) +// let fileURL = bundle.url(forResource: name, withExtension: "json")! +// let data = try Data(contentsOf: fileURL) +// return try JSONDecoder().decode(ConfigResponse.self, from: data) +// } +// +// private func runInAppTestForUUID(_ uuid: String, abTests: [ABTest]?, responseInapps: [InApp]?, expectedCount: Int, expectedIds: [String]) { +// persistenceStorage.deviceUUID = uuid +// let inapps = mapper.filterInappsByABTests(abTests, responseInapps: responseInapps) +// XCTAssertEqual(inapps.count, expectedCount) +// for inapp in inapps { +// XCTAssertTrue(expectedIds.contains { $0 == inapp.id }) +// } +// } +//} diff --git a/MindboxTests/InApp/Tests/InAppConfigResponseTests/InAppConfigResponseTests.swift b/MindboxTests/InApp/Tests/InAppConfigResponseTests/InAppConfigResponseTests.swift index bcbde574..a1a46248 100644 --- a/MindboxTests/InApp/Tests/InAppConfigResponseTests/InAppConfigResponseTests.swift +++ b/MindboxTests/InApp/Tests/InAppConfigResponseTests/InAppConfigResponseTests.swift @@ -10,638 +10,638 @@ import Foundation import XCTest @testable import Mindbox -class InAppConfigResponseTests: XCTestCase { - - var container = try! TestDependencyProvider() - - var sessionTemporaryStorage: SessionTemporaryStorage { - container.sessionTemporaryStorage - } - - var persistenceStorage: PersistenceStorage { - container.persistenceStorage - } - - var networkFetcher: NetworkFetcher { - container.instanceFactory.makeNetworkFetcher() - } - - private var mapper: InAppConfigurationMapperProtocol! - private let configStub = InAppConfigStub() - private let targetingChecker: InAppTargetingCheckerProtocol = InAppTargetingChecker() - private var shownInAppsIds: Set! - - override func setUp() { - super.setUp() - mapper = InAppConfigutationMapper(geoService: container.geoService, - segmentationService: container.segmentationSevice, - customerSegmentsAPI: .live, - targetingChecker: targetingChecker, - sessionTemporaryStorage: sessionTemporaryStorage, - persistenceStorage: persistenceStorage, - sdkVersionValidator: container.sdkVersionValidator, - imageDownloadService: container.imageDownloadService, - abTestDeviceMixer: container.abTestDeviceMixer) - shownInAppsIds = Set(persistenceStorage.shownInAppsIds ?? []) - } - - func test_2InApps_oneFitsInAppsSdkVersion_andOneDoesnt() throws { - let response = try getConfig(name: "InAppConfiguration") - var output: InAppFormData? - let expectations = expectation(description: "test_2InApps_oneFitsInAppsSdkVersion_andOneDoesnt") - mapper.mapConfigResponse(nil, response) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 3) - - let expected = InAppTransitionData(inAppId: "00000000-0000-0000-0000-000000000001", - imageUrl: "https://s3-symbol-logo.tradingview.com/true-corporation-public-company-limited--600.png", - redirectUrl: "", intentPayload: "") - XCTAssertEqual(expected.inAppId, output?.inAppId) - XCTAssertEqual(expected.intentPayload, output?.intentPayload) - XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) - } - - func test_2InApps_bothFitInAppsSdkVersion() throws { - let response = try getConfig(name: "InAppConfiguration") - var output: InAppFormData? - let expectations = expectation(description: "test_2InApps_bothFitInAppsSdkVersion") - mapper.mapConfigResponse(nil, response) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - let expected = InAppTransitionData(inAppId: "00000000-0000-0000-0000-000000000001", - imageUrl: "https://s3-symbol-logo.tradingview.com/true-corporation-public-company-limited--600.png", - redirectUrl: "", intentPayload: "") - - XCTAssertEqual(expected.inAppId, output?.inAppId) - XCTAssertEqual(expected.intentPayload, output?.intentPayload) - XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) - } - - func test_2InApps_bothDontFitInAppsSdkVersion() throws { - let response = try getConfig(name: "InAppConfiguration") - var output: InAppFormData? - let expectations = expectation(description: "test_2InApps_bothDontFitInAppsSdkVersion") - mapper = getMapper(version: 0) - mapper.mapConfigResponse(nil, response) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - - XCTAssertNil(output) - } - - func test_operation_happyFlow() throws { - let response = try getConfig(name: "InAppConfigurationWithOperations") - let event = ApplicationEvent(name: "TESTPushOK", model: nil) - mapper = getMapper(version: 4) - - var output: InAppFormData? - let expectations = expectation(description: "test_operation_happyFlow") - mapper.mapConfigResponse(event, response) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - let expected = InAppTransitionData(inAppId: "00000000-0000-0000-0000-000000000001", - imageUrl: "https://s3-symbol-logo.tradingview.com/true-corporation-public-company-limited--600.png", - redirectUrl: "", intentPayload: "") - - XCTAssertEqual(expected.inAppId, output?.inAppId) - XCTAssertEqual(expected.intentPayload, output?.intentPayload) - XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) - } - - func test_operation_empty_operatonName() throws { - let response = try getConfig(name: "InAppConfigurationWithOperations") - let event = ApplicationEvent(name: "", model: nil) - mapper = getMapper(version: 4) - - var output: InAppFormData? - let expectations = expectation(description: "test_operation_empty_operatonName") - mapper.mapConfigResponse(event, response) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - XCTAssertNil(output) - } - - func test_operation_wrong_operatonName() throws { - let response = try getConfig(name: "InAppConfigurationWithOperations") - mapper = getMapper(version: 4) - let event = ApplicationEvent(name: "WrongOperationName", model: nil) - var output: InAppFormData? - let expectations = expectation(description: "test_operation_wrong_operatonName") - mapper.mapConfigResponse(event, response) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - XCTAssertNil(output) - } - - func test_categoryID_emptyModel() { - mapper = getMapper(version: 5) - let event = ApplicationEvent(name: "Hello", model: nil) - var output: InAppFormData? - let expectations = expectation(description: "test_categoryID_emptyModel") - mapper.mapConfigResponse(event, configStub.getCategoryIDIn_Any()) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - XCTAssertNil(output) - } - - func test_categoryID_substring_true() { - let response = configStub.getCategoryID_Substring() - mapper = getMapper(version: 5) - let event = ApplicationEvent(name: "Hello", - model: .init(viewProductCategory: .init(productCategory: .init(ids: [ - "System1C": "Boots".uppercased(), - "TestSite": "81".uppercased() - ])))) - var output: InAppFormData? - let expectations = expectation(description: "test_categoryID_substring_true") - mapper.mapConfigResponse(event, configStub.getCategoryIDIn_Any()) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - let expected = InAppTransitionData(inAppId: "0", - imageUrl: "1", - redirectUrl: "2", intentPayload: "3") - - XCTAssertEqual(expected.inAppId, output?.inAppId) - XCTAssertEqual(expected.intentPayload, output?.intentPayload) - XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) - } - - func test_categoryID_substring_false() { - let event = ApplicationEvent(name: "Hello", - model: .init(viewProductCategory: .init(productCategory: .init(ids: [ - "System1C": "Bovts".uppercased(), - "TestSite": "81".uppercased() - ])))) - mapper = getMapper(version: 5) - var output: InAppFormData? - let expectations = expectation(description: "test_categoryID_emptyModel") - mapper.mapConfigResponse(event, configStub.getCategoryID_Substring()) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - XCTAssertNil(output) - } - - func test_categoryID_notSubstring_true() { - let event = ApplicationEvent(name: "Hello", - model: .init(viewProductCategory: .init(productCategory: .init(ids: [ - "System1C": "Boots".uppercased(), - "TestSite": "Button".uppercased() - ])))) - mapper = getMapper(version: 5) - var output: InAppFormData? - let expectations = expectation(description: "test_categoryID_emptyModel") - mapper.mapConfigResponse(event, configStub.getCategoryID_notSubstring()) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - let expected = InAppTransitionData(inAppId: "0", - imageUrl: "https://example.com/image.jpg", - redirectUrl: "2", - intentPayload: "3") - - XCTAssertEqual(expected.inAppId, output?.inAppId) - XCTAssertEqual(expected.intentPayload, output?.intentPayload) - XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) - } - - func test_categoryID_notSubstring_false() { - let event = ApplicationEvent(name: "Hello", - model: .init(viewProductCategory: .init(productCategory: .init(ids: [ - "System1C": "Boots".uppercased(), - "TestSite": "Buttootn".uppercased() - ])))) - let response = configStub.getCategoryID_notSubstring() - mapper = getMapper(version: 5) - var output: InAppFormData? - let expectations = expectation(description: "test_categoryID_emptyModel") - mapper.mapConfigResponse(event, response) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - XCTAssertNil(output) - } - - - func test_categoryID_startWith_true() { - let event = ApplicationEvent(name: "Hello", - model: .init(viewProductCategory: .init(productCategory: .init(ids: [ - "System1C": "oots".uppercased(), - "TestSite": "Button".uppercased() - ])))) - let response = configStub.getCategoryID_startWith() - mapper = getMapper(version: 5) - var output: InAppFormData? - let expectations = expectation(description: "test_categoryID_emptyModel") - mapper.mapConfigResponse(event, response) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - let expected = InAppTransitionData(inAppId: "0", - imageUrl: "1", - redirectUrl: "2", - intentPayload: "3") - - XCTAssertEqual(expected.inAppId, output?.inAppId) - XCTAssertEqual(expected.intentPayload, output?.intentPayload) - XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) - } - - func test_categoryID_startWith_false() { - let event = ApplicationEvent(name: "Hello", - model: .init(viewProductCategory: .init(productCategory: .init(ids: [ - "System1C": "Boots".uppercased(), - "TestSite": "Button".uppercased() - ])))) - let response = configStub.getCategoryID_startWith() - mapper = getMapper(version: 5) - var output: InAppFormData? - let expectations = expectation(description: "test_categoryID_emptyModel") - mapper.mapConfigResponse(event, response) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - XCTAssertNil(output) - } - - func test_categoryID_endWith_true() { - let event = ApplicationEvent(name: "Hello", - model: .init(viewProductCategory: .init(productCategory: .init(ids: [ - "System1C": "Boots".uppercased(), - "TestSite": "Button".uppercased() - ])))) - let response = configStub.getCategoryID_endWith() - mapper = getMapper(version: 5) - var output: InAppFormData? - let expectations = expectation(description: "test_categoryID_emptyModel") - mapper.mapConfigResponse(event, response) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - let expected = InAppTransitionData(inAppId: "0", - imageUrl: "1", - redirectUrl: "2", - intentPayload: "3") - - XCTAssertEqual(expected.inAppId, output?.inAppId) - XCTAssertEqual(expected.intentPayload, output?.intentPayload) - XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) - } - - func test_categoryID_endWith_false() { - let event = ApplicationEvent(name: "Hello", - model: .init(viewProductCategory: .init(productCategory: .init(ids: [ - "System1C": "Boats".uppercased(), - "TestSite": "Button".uppercased() - ])))) - let response = configStub.getCategoryID_endWith() - mapper = getMapper(version: 5) - testNil(event: event, response: response) - } - - func test_categoryIDIn_any_true() { - let event = ApplicationEvent(name: "Hello", model: .init(viewProductCategory: .init(productCategory: .init(ids: ["System1C": "testik2"])))) - let response = configStub.getCategoryIDIn_Any() - mapper = getMapper(version: 5) - testResponse(event: event, response: response) - } - - func test_categoryIDIn_any_false() { - let event = ApplicationEvent(name: "Hello", model: .init(viewProductCategory: .init(productCategory: .init(ids: ["System1C": "potato"])))) - let response = configStub.getCategoryIDIn_Any() - mapper = getMapper(version: 5) - testNil(event: event, response: response) - } - - func test_categoryIDIn_none_true() { - let event = ApplicationEvent(name: "Hello", model: .init(viewProductCategory: .init(productCategory: .init(ids: ["System1C": "potato"])))) - let response = configStub.getCategoryIDIn_None() - mapper = getMapper(version: 5) - testResponse(event: event, response: response) - } - - func test_categoryIDIn_none_false() { - let event = ApplicationEvent(name: "Hello", model: .init(viewProductCategory: .init(productCategory: .init(ids: ["System1C": "testik2"])))) - let response = configStub.getCategoryIDIn_None() - mapper = getMapper(version: 5) - testNil(event: event, response: response) - } - - func test_productID_emptyModel() { - let event = ApplicationEvent(name: "Hello", model: nil) - let response = configStub.getProductID_Substring() - mapper = getMapper(version: 5) - testNil(event: event, response: response) - } - - func test_productID_substring_true() { - let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: - ["website": "Boots".uppercased(), - "system1c": "81".uppercased() - ])))) - let response = configStub.getProductID_Substring() - mapper = getMapper(version: 5) - testResponse(event: event, response: response) - } - - func test_productID_substring_false() { - let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: - ["website": "Bovts".uppercased(), - "system1c": "81".uppercased() - ])))) - let response = configStub.getProductID_Substring() - mapper = getMapper(version: 5) - testNil(event: event, response: response) - } - - func test_productID_notSubstring_true() { - let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: - ["website": "Boots".uppercased(), - "system1c": "81".uppercased() - ])))) - let response = configStub.getProductID_notSubstring() - mapper = getMapper(version: 5) - testResponse(event: event, response: response) - } - - func test_productID_notSubstring_false() { - let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: - ["website": "Boots".uppercased(), - "system1c": "Buttootn".uppercased() - ])))) - let response = configStub.getProductID_notSubstring() - mapper = getMapper(version: 5) - testNil(event: event, response: response) - } - - func test_productID_startWith_true() { - let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: - ["website": "oots".uppercased(), - "system1c": "Button".uppercased() - ])))) - let response = configStub.getProductID_startsWith() - mapper = getMapper(version: 5) - testResponse(event: event, response: response) - } - - func test_productID_startWith_false() { - let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: - ["website": "Boots".uppercased(), - "system1c": "Button".uppercased() - ])))) - let response = configStub.getProductID_startsWith() - mapper = getMapper(version: 5) - testNil(event: event, response: response) - } - - func test_productID_endWith_true() { - let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: - ["website": "Boots".uppercased(), - "system1c": "Button".uppercased() - ])))) - let response = configStub.getProductID_endsWith() - mapper = getMapper(version: 5) - testResponse(event: event, response: response) - } - - func test_productID_endWith_false() { - let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: - ["website": "Boats".uppercased(), - "system1c": "Button".uppercased() - ])))) - let response = configStub.getProductID_endsWith() - mapper = getMapper(version: 5) - testNil(event: event, response: response) - } - - func test_productSegment_positive_true() throws { - let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: ["website": "49"])))) - let response = configStub.getProductSegment_Any() - let productSegments = InAppProductSegmentResponse(status: .success, products: [.init(ids: ["website": "49"], - segmentations: [.init(ids: .init(externalId: "1"), - segment: .init(ids: .init(externalId: "3")))])]) - mapper = getMapper(version: 5, productSegments: productSegments) - mapper.targetingChecker.event = event - testResponse(event: event, response: response) - } - - func test_productSegment_positive_false() throws { - let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: ["website": "49"])))) - let response = configStub.getProductSegment_Any() - mapper = getMapper(version: 5) - testNil(event: event, response: response) - } - - func test_productSegment_negative_true() throws { - let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: ["website": "49"])))) - let response = configStub.getProductSegment_None() - let productSegments = InAppProductSegmentResponse(status: .success, products: [.init(ids: ["website": "49"], - segmentations: [.init(ids: .init(externalId: "1"), - segment: .init(ids: .init(externalId: "4")))])]) - mapper = getMapper(version: 5, productSegments: productSegments) - mapper.targetingChecker.checkedProductSegmentations = [.init(ids: .init(externalId: "1"), segment: .init(ids: .init(externalId: "4")))] - testResponse(event: event, response: response) - - } - - func test_productSegment_negative_false() throws { - let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: ["website": "49"])))) - let response = configStub.getProductSegment_None() - mapper = getMapper(version: 5) - testNil(event: event, response: response) - } - - func test_config_valid_to_parse() throws { - let response = try getConfig(name: "InappConfigResponseValid") - - let inapps: [InApp]? = [.init(id: "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - sdkVersion: SdkVersion(min: 4, max: nil), - targeting: .and(AndTargeting(nodes: [.true(TrueTargeting())])), - form: InApp.InAppFormVariants(variants: [.init(imageUrl: "1", - redirectUrl: "2", - intentPayload: "3", - type: "simpleImage")]))] - - let abTestObject1 = ABTest.ABTestVariant.ABTestObject( - type: .inapps, - kind: .all, - inapps: ["inapp1", "inapp2"] - ) - - // Создаем структуры ABTestVariant - let abTestVariant1 = ABTest.ABTestVariant( - id: "1", modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 50), - objects: [abTestObject1] - ) - - let abTestVariant2 = ABTest.ABTestVariant( - id: "2", modulus: ABTest.ABTestVariant.Modulus(lower: 50, upper: 100), - objects: [abTestObject1] - ) - let abtests: [ABTest]? = [.init(id: "id123", - sdkVersion: .init(min: 1, max: nil), - salt: "salt123", - variants: [abTestVariant1, - abTestVariant2]), - ] - - let monitoring = Monitoring(logs: [.init(requestId: "request1", - deviceUUID: "device1", - from: "source1", - to: "destination1"), - .init(requestId: "request2", - deviceUUID: "device2", - from: "source2", - to: "destination2")]) - - let settings = Settings(operations: .init(viewProduct: .init(systemName: "product"), - viewCategory: .init(systemName: "category"), - setCart: .init(systemName: "cart"))) - - XCTAssertEqual(response.inapps, inapps) - XCTAssertEqual(response.abtests, abtests) - XCTAssertEqual(response.monitoring, monitoring) - XCTAssertEqual(response.settings, settings) - } - - func test_config_settings_invalid_to_parse() throws { - let response = try getConfig(name: "InappConfigResponseSettingsInvalid") - let inapps: [InApp]? = [.init(id: "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - sdkVersion: SdkVersion(min: 4, max: nil), - targeting: .and(AndTargeting(nodes: [.true(TrueTargeting())])), - form: InApp.InAppFormVariants(variants: [.init(imageUrl: "1", - redirectUrl: "2", - intentPayload: "3", - type: "simpleImage")]))] - - XCTAssertEqual(response.inapps, inapps) - // No systemName in Settings JSON - XCTAssertNil(response.settings) - } - - func test_config_monitoring_invalid_to_parse() throws { - let response = try getConfig(name: "InappConfigResponseMonitoringInvalid") - let inapps: [InApp]? = [.init(id: "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - sdkVersion: SdkVersion(min: 4, max: nil), - targeting: .and(AndTargeting(nodes: [.true(TrueTargeting())])), - form: InApp.InAppFormVariants(variants: [.init(imageUrl: "1", - redirectUrl: "2", - intentPayload: "3", - type: "simpleImage")]))] - - XCTAssertEqual(response.inapps, inapps) - // No id in Monitoring JSON - XCTAssertNil(response.monitoring) - } - - func test_config_abtests_invalid_to_parse() throws { - let response = try getConfig(name: "InappConfigResponseAbtestsInvalid") - let inapps: [InApp]? = [.init(id: "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - sdkVersion: SdkVersion(min: 4, max: nil), - targeting: .and(AndTargeting(nodes: [.true(TrueTargeting())])), - form: InApp.InAppFormVariants(variants: [.init(imageUrl: "1", - redirectUrl: "2", - intentPayload: "3", - type: "simpleImage")]))] - - XCTAssertEqual(response.inapps, inapps) - // No id in Abtest JSON - XCTAssertNil(response.abtests) - } -} - -private extension InAppConfigResponseTests { - private func getConfig(name: String) throws -> ConfigResponse { - let bundle = Bundle(for: InAppConfigResponseTests.self) - let fileURL = bundle.url(forResource: name, withExtension: "json")! - let data = try Data(contentsOf: fileURL) - return try JSONDecoder().decode(ConfigResponse.self, from: data) - } - - private func getMapper(version: Int, segments: SegmentationCheckResponse? = nil, productSegments: InAppProductSegmentResponse? = nil) -> InAppConfigutationMapper { - let sdkVersionValidator = SDKVersionValidator(sdkVersionNumeric: version) - let segmentationService = SegmentationService(customerSegmentsAPI: .init(fetchSegments: { segmentationCheckRequest, completion in - completion(segments) - }, fetchProductSegments: { segmentationCheckRequest, completion in - completion(productSegments) - }), - sessionTemporaryStorage: sessionTemporaryStorage, - targetingChecker: targetingChecker) - return InAppConfigutationMapper(geoService: container.geoService, - segmentationService: segmentationService, - customerSegmentsAPI: .live, - targetingChecker: targetingChecker, - sessionTemporaryStorage: sessionTemporaryStorage, - persistenceStorage: persistenceStorage, - sdkVersionValidator: sdkVersionValidator, - imageDownloadService: container.imageDownloadService, - abTestDeviceMixer: container.abTestDeviceMixer) - } - - private func testNil(event: ApplicationEvent?, response: ConfigResponse) { - var output: InAppFormData? - let expectations = expectation(description: "testNil") - mapper.mapConfigResponse(event, response) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - XCTAssertNil(output) - } - - private func testResponse(event: ApplicationEvent?, response: ConfigResponse) { - var output: InAppFormData? - let expectations = expectation(description: "test_categoryID_emptyModel") - mapper.mapConfigResponse(event, response) { result in - output = result - expectations.fulfill() - } - - waitForExpectations(timeout: 1) - let expected = InAppTransitionData(inAppId: "0", - imageUrl: "1", - redirectUrl: "2", - intentPayload: "3") - - XCTAssertEqual(expected.inAppId, output?.inAppId) - XCTAssertEqual(expected.intentPayload, output?.intentPayload) - XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) - } -} +//class InAppConfigResponseTests: XCTestCase { +// +// var container = try! TestDependencyProvider() +// +// var sessionTemporaryStorage: SessionTemporaryStorage { +// container.sessionTemporaryStorage +// } +// +// var persistenceStorage: PersistenceStorage { +// container.persistenceStorage +// } +// +// var networkFetcher: NetworkFetcher { +// container.instanceFactory.makeNetworkFetcher() +// } +// +// private var mapper: InAppConfigurationMapperProtocol! +// private let configStub = InAppConfigStub() +// private let targetingChecker: InAppTargetingCheckerProtocol = InAppTargetingChecker() +// private var shownInAppsIds: Set! +// +// override func setUp() { +// super.setUp() +// mapper = InAppConfigutationMapper(geoService: container.geoService, +// segmentationService: container.segmentationSevice, +// customerSegmentsAPI: .live, +// targetingChecker: targetingChecker, +// sessionTemporaryStorage: sessionTemporaryStorage, +// persistenceStorage: persistenceStorage, +// sdkVersionValidator: container.sdkVersionValidator, +// imageDownloadService: container.imageDownloadService, +// abTestDeviceMixer: container.abTestDeviceMixer) +// shownInAppsIds = Set(persistenceStorage.shownInAppsIds ?? []) +// } +// +// func test_2InApps_oneFitsInAppsSdkVersion_andOneDoesnt() throws { +// let response = try getConfig(name: "InAppConfiguration") +// var output: InAppFormData? +// let expectations = expectation(description: "test_2InApps_oneFitsInAppsSdkVersion_andOneDoesnt") +// mapper.mapConfigResponse(nil, response) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 3) +// +// let expected = InAppTransitionData(inAppId: "00000000-0000-0000-0000-000000000001", +// imageUrl: "https://s3-symbol-logo.tradingview.com/true-corporation-public-company-limited--600.png", +// redirectUrl: "", intentPayload: "") +// XCTAssertEqual(expected.inAppId, output?.inAppId) +// XCTAssertEqual(expected.intentPayload, output?.intentPayload) +// XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) +// } +// +// func test_2InApps_bothFitInAppsSdkVersion() throws { +// let response = try getConfig(name: "InAppConfiguration") +// var output: InAppFormData? +// let expectations = expectation(description: "test_2InApps_bothFitInAppsSdkVersion") +// mapper.mapConfigResponse(nil, response) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// let expected = InAppTransitionData(inAppId: "00000000-0000-0000-0000-000000000001", +// imageUrl: "https://s3-symbol-logo.tradingview.com/true-corporation-public-company-limited--600.png", +// redirectUrl: "", intentPayload: "") +// +// XCTAssertEqual(expected.inAppId, output?.inAppId) +// XCTAssertEqual(expected.intentPayload, output?.intentPayload) +// XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) +// } +// +// func test_2InApps_bothDontFitInAppsSdkVersion() throws { +// let response = try getConfig(name: "InAppConfiguration") +// var output: InAppFormData? +// let expectations = expectation(description: "test_2InApps_bothDontFitInAppsSdkVersion") +// mapper = getMapper(version: 0) +// mapper.mapConfigResponse(nil, response) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// +// XCTAssertNil(output) +// } +// +// func test_operation_happyFlow() throws { +// let response = try getConfig(name: "InAppConfigurationWithOperations") +// let event = ApplicationEvent(name: "TESTPushOK", model: nil) +// mapper = getMapper(version: 4) +// +// var output: InAppFormData? +// let expectations = expectation(description: "test_operation_happyFlow") +// mapper.mapConfigResponse(event, response) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// let expected = InAppTransitionData(inAppId: "00000000-0000-0000-0000-000000000001", +// imageUrl: "https://s3-symbol-logo.tradingview.com/true-corporation-public-company-limited--600.png", +// redirectUrl: "", intentPayload: "") +// +// XCTAssertEqual(expected.inAppId, output?.inAppId) +// XCTAssertEqual(expected.intentPayload, output?.intentPayload) +// XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) +// } +// +// func test_operation_empty_operatonName() throws { +// let response = try getConfig(name: "InAppConfigurationWithOperations") +// let event = ApplicationEvent(name: "", model: nil) +// mapper = getMapper(version: 4) +// +// var output: InAppFormData? +// let expectations = expectation(description: "test_operation_empty_operatonName") +// mapper.mapConfigResponse(event, response) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// XCTAssertNil(output) +// } +// +// func test_operation_wrong_operatonName() throws { +// let response = try getConfig(name: "InAppConfigurationWithOperations") +// mapper = getMapper(version: 4) +// let event = ApplicationEvent(name: "WrongOperationName", model: nil) +// var output: InAppFormData? +// let expectations = expectation(description: "test_operation_wrong_operatonName") +// mapper.mapConfigResponse(event, response) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// XCTAssertNil(output) +// } +// +// func test_categoryID_emptyModel() { +// mapper = getMapper(version: 5) +// let event = ApplicationEvent(name: "Hello", model: nil) +// var output: InAppFormData? +// let expectations = expectation(description: "test_categoryID_emptyModel") +// mapper.mapConfigResponse(event, configStub.getCategoryIDIn_Any()) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// XCTAssertNil(output) +// } +// +// func test_categoryID_substring_true() { +// let response = configStub.getCategoryID_Substring() +// mapper = getMapper(version: 5) +// let event = ApplicationEvent(name: "Hello", +// model: .init(viewProductCategory: .init(productCategory: .init(ids: [ +// "System1C": "Boots".uppercased(), +// "TestSite": "81".uppercased() +// ])))) +// var output: InAppFormData? +// let expectations = expectation(description: "test_categoryID_substring_true") +// mapper.mapConfigResponse(event, configStub.getCategoryIDIn_Any()) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// let expected = InAppTransitionData(inAppId: "0", +// imageUrl: "1", +// redirectUrl: "2", intentPayload: "3") +// +// XCTAssertEqual(expected.inAppId, output?.inAppId) +// XCTAssertEqual(expected.intentPayload, output?.intentPayload) +// XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) +// } +// +// func test_categoryID_substring_false() { +// let event = ApplicationEvent(name: "Hello", +// model: .init(viewProductCategory: .init(productCategory: .init(ids: [ +// "System1C": "Bovts".uppercased(), +// "TestSite": "81".uppercased() +// ])))) +// mapper = getMapper(version: 5) +// var output: InAppFormData? +// let expectations = expectation(description: "test_categoryID_emptyModel") +// mapper.mapConfigResponse(event, configStub.getCategoryID_Substring()) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// XCTAssertNil(output) +// } +// +// func test_categoryID_notSubstring_true() { +// let event = ApplicationEvent(name: "Hello", +// model: .init(viewProductCategory: .init(productCategory: .init(ids: [ +// "System1C": "Boots".uppercased(), +// "TestSite": "Button".uppercased() +// ])))) +// mapper = getMapper(version: 5) +// var output: InAppFormData? +// let expectations = expectation(description: "test_categoryID_emptyModel") +// mapper.mapConfigResponse(event, configStub.getCategoryID_notSubstring()) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// let expected = InAppTransitionData(inAppId: "0", +// imageUrl: "https://example.com/image.jpg", +// redirectUrl: "2", +// intentPayload: "3") +// +// XCTAssertEqual(expected.inAppId, output?.inAppId) +// XCTAssertEqual(expected.intentPayload, output?.intentPayload) +// XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) +// } +// +// func test_categoryID_notSubstring_false() { +// let event = ApplicationEvent(name: "Hello", +// model: .init(viewProductCategory: .init(productCategory: .init(ids: [ +// "System1C": "Boots".uppercased(), +// "TestSite": "Buttootn".uppercased() +// ])))) +// let response = configStub.getCategoryID_notSubstring() +// mapper = getMapper(version: 5) +// var output: InAppFormData? +// let expectations = expectation(description: "test_categoryID_emptyModel") +// mapper.mapConfigResponse(event, response) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// XCTAssertNil(output) +// } +// +// +// func test_categoryID_startWith_true() { +// let event = ApplicationEvent(name: "Hello", +// model: .init(viewProductCategory: .init(productCategory: .init(ids: [ +// "System1C": "oots".uppercased(), +// "TestSite": "Button".uppercased() +// ])))) +// let response = configStub.getCategoryID_startWith() +// mapper = getMapper(version: 5) +// var output: InAppFormData? +// let expectations = expectation(description: "test_categoryID_emptyModel") +// mapper.mapConfigResponse(event, response) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// let expected = InAppTransitionData(inAppId: "0", +// imageUrl: "1", +// redirectUrl: "2", +// intentPayload: "3") +// +// XCTAssertEqual(expected.inAppId, output?.inAppId) +// XCTAssertEqual(expected.intentPayload, output?.intentPayload) +// XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) +// } +// +// func test_categoryID_startWith_false() { +// let event = ApplicationEvent(name: "Hello", +// model: .init(viewProductCategory: .init(productCategory: .init(ids: [ +// "System1C": "Boots".uppercased(), +// "TestSite": "Button".uppercased() +// ])))) +// let response = configStub.getCategoryID_startWith() +// mapper = getMapper(version: 5) +// var output: InAppFormData? +// let expectations = expectation(description: "test_categoryID_emptyModel") +// mapper.mapConfigResponse(event, response) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// XCTAssertNil(output) +// } +// +// func test_categoryID_endWith_true() { +// let event = ApplicationEvent(name: "Hello", +// model: .init(viewProductCategory: .init(productCategory: .init(ids: [ +// "System1C": "Boots".uppercased(), +// "TestSite": "Button".uppercased() +// ])))) +// let response = configStub.getCategoryID_endWith() +// mapper = getMapper(version: 5) +// var output: InAppFormData? +// let expectations = expectation(description: "test_categoryID_emptyModel") +// mapper.mapConfigResponse(event, response) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// let expected = InAppTransitionData(inAppId: "0", +// imageUrl: "1", +// redirectUrl: "2", +// intentPayload: "3") +// +// XCTAssertEqual(expected.inAppId, output?.inAppId) +// XCTAssertEqual(expected.intentPayload, output?.intentPayload) +// XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) +// } +// +// func test_categoryID_endWith_false() { +// let event = ApplicationEvent(name: "Hello", +// model: .init(viewProductCategory: .init(productCategory: .init(ids: [ +// "System1C": "Boats".uppercased(), +// "TestSite": "Button".uppercased() +// ])))) +// let response = configStub.getCategoryID_endWith() +// mapper = getMapper(version: 5) +// testNil(event: event, response: response) +// } +// +// func test_categoryIDIn_any_true() { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProductCategory: .init(productCategory: .init(ids: ["System1C": "testik2"])))) +// let response = configStub.getCategoryIDIn_Any() +// mapper = getMapper(version: 5) +// testResponse(event: event, response: response) +// } +// +// func test_categoryIDIn_any_false() { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProductCategory: .init(productCategory: .init(ids: ["System1C": "potato"])))) +// let response = configStub.getCategoryIDIn_Any() +// mapper = getMapper(version: 5) +// testNil(event: event, response: response) +// } +// +// func test_categoryIDIn_none_true() { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProductCategory: .init(productCategory: .init(ids: ["System1C": "potato"])))) +// let response = configStub.getCategoryIDIn_None() +// mapper = getMapper(version: 5) +// testResponse(event: event, response: response) +// } +// +// func test_categoryIDIn_none_false() { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProductCategory: .init(productCategory: .init(ids: ["System1C": "testik2"])))) +// let response = configStub.getCategoryIDIn_None() +// mapper = getMapper(version: 5) +// testNil(event: event, response: response) +// } +// +// func test_productID_emptyModel() { +// let event = ApplicationEvent(name: "Hello", model: nil) +// let response = configStub.getProductID_Substring() +// mapper = getMapper(version: 5) +// testNil(event: event, response: response) +// } +// +// func test_productID_substring_true() { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: +// ["website": "Boots".uppercased(), +// "system1c": "81".uppercased() +// ])))) +// let response = configStub.getProductID_Substring() +// mapper = getMapper(version: 5) +// testResponse(event: event, response: response) +// } +// +// func test_productID_substring_false() { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: +// ["website": "Bovts".uppercased(), +// "system1c": "81".uppercased() +// ])))) +// let response = configStub.getProductID_Substring() +// mapper = getMapper(version: 5) +// testNil(event: event, response: response) +// } +// +// func test_productID_notSubstring_true() { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: +// ["website": "Boots".uppercased(), +// "system1c": "81".uppercased() +// ])))) +// let response = configStub.getProductID_notSubstring() +// mapper = getMapper(version: 5) +// testResponse(event: event, response: response) +// } +// +// func test_productID_notSubstring_false() { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: +// ["website": "Boots".uppercased(), +// "system1c": "Buttootn".uppercased() +// ])))) +// let response = configStub.getProductID_notSubstring() +// mapper = getMapper(version: 5) +// testNil(event: event, response: response) +// } +// +// func test_productID_startWith_true() { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: +// ["website": "oots".uppercased(), +// "system1c": "Button".uppercased() +// ])))) +// let response = configStub.getProductID_startsWith() +// mapper = getMapper(version: 5) +// testResponse(event: event, response: response) +// } +// +// func test_productID_startWith_false() { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: +// ["website": "Boots".uppercased(), +// "system1c": "Button".uppercased() +// ])))) +// let response = configStub.getProductID_startsWith() +// mapper = getMapper(version: 5) +// testNil(event: event, response: response) +// } +// +// func test_productID_endWith_true() { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: +// ["website": "Boots".uppercased(), +// "system1c": "Button".uppercased() +// ])))) +// let response = configStub.getProductID_endsWith() +// mapper = getMapper(version: 5) +// testResponse(event: event, response: response) +// } +// +// func test_productID_endWith_false() { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: +// ["website": "Boats".uppercased(), +// "system1c": "Button".uppercased() +// ])))) +// let response = configStub.getProductID_endsWith() +// mapper = getMapper(version: 5) +// testNil(event: event, response: response) +// } +// +// func test_productSegment_positive_true() throws { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: ["website": "49"])))) +// let response = configStub.getProductSegment_Any() +// let productSegments = InAppProductSegmentResponse(status: .success, products: [.init(ids: ["website": "49"], +// segmentations: [.init(ids: .init(externalId: "1"), +// segment: .init(ids: .init(externalId: "3")))])]) +// mapper = getMapper(version: 5, productSegments: productSegments) +// mapper.targetingChecker.event = event +// testResponse(event: event, response: response) +// } +// +// func test_productSegment_positive_false() throws { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: ["website": "49"])))) +// let response = configStub.getProductSegment_Any() +// mapper = getMapper(version: 5) +// testNil(event: event, response: response) +// } +// +// func test_productSegment_negative_true() throws { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: ["website": "49"])))) +// let response = configStub.getProductSegment_None() +// let productSegments = InAppProductSegmentResponse(status: .success, products: [.init(ids: ["website": "49"], +// segmentations: [.init(ids: .init(externalId: "1"), +// segment: .init(ids: .init(externalId: "4")))])]) +// mapper = getMapper(version: 5, productSegments: productSegments) +// mapper.targetingChecker.checkedProductSegmentations = [.init(ids: .init(externalId: "1"), segment: .init(ids: .init(externalId: "4")))] +// testResponse(event: event, response: response) +// +// } +// +// func test_productSegment_negative_false() throws { +// let event = ApplicationEvent(name: "Hello", model: .init(viewProduct: .init(product: .init(ids: ["website": "49"])))) +// let response = configStub.getProductSegment_None() +// mapper = getMapper(version: 5) +// testNil(event: event, response: response) +// } +// +// func test_config_valid_to_parse() throws { +// let response = try getConfig(name: "InappConfigResponseValid") +// +// let inapps: [InApp]? = [.init(id: "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// sdkVersion: SdkVersion(min: 4, max: nil), +// targeting: .and(AndTargeting(nodes: [.true(TrueTargeting())])), +// form: InApp.InAppFormVariants(variants: [.init(imageUrl: "1", +// redirectUrl: "2", +// intentPayload: "3", +// type: "simpleImage")]))] +// +// let abTestObject1 = ABTest.ABTestVariant.ABTestObject( +// type: .inapps, +// kind: .all, +// inapps: ["inapp1", "inapp2"] +// ) +// +// // Создаем структуры ABTestVariant +// let abTestVariant1 = ABTest.ABTestVariant( +// id: "1", modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 50), +// objects: [abTestObject1] +// ) +// +// let abTestVariant2 = ABTest.ABTestVariant( +// id: "2", modulus: ABTest.ABTestVariant.Modulus(lower: 50, upper: 100), +// objects: [abTestObject1] +// ) +// let abtests: [ABTest]? = [.init(id: "id123", +// sdkVersion: .init(min: 1, max: nil), +// salt: "salt123", +// variants: [abTestVariant1, +// abTestVariant2]), +// ] +// +// let monitoring = Monitoring(logs: [.init(requestId: "request1", +// deviceUUID: "device1", +// from: "source1", +// to: "destination1"), +// .init(requestId: "request2", +// deviceUUID: "device2", +// from: "source2", +// to: "destination2")]) +// +// let settings = Settings(operations: .init(viewProduct: .init(systemName: "product"), +// viewCategory: .init(systemName: "category"), +// setCart: .init(systemName: "cart"))) +// +// XCTAssertEqual(response.inapps, inapps) +// XCTAssertEqual(response.abtests, abtests) +// XCTAssertEqual(response.monitoring, monitoring) +// XCTAssertEqual(response.settings, settings) +// } +// +// func test_config_settings_invalid_to_parse() throws { +// let response = try getConfig(name: "InappConfigResponseSettingsInvalid") +// let inapps: [InApp]? = [.init(id: "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// sdkVersion: SdkVersion(min: 4, max: nil), +// targeting: .and(AndTargeting(nodes: [.true(TrueTargeting())])), +// form: InApp.InAppFormVariants(variants: [.init(imageUrl: "1", +// redirectUrl: "2", +// intentPayload: "3", +// type: "simpleImage")]))] +// +// XCTAssertEqual(response.inapps, inapps) +// // No systemName in Settings JSON +// XCTAssertNil(response.settings) +// } +// +// func test_config_monitoring_invalid_to_parse() throws { +// let response = try getConfig(name: "InappConfigResponseMonitoringInvalid") +// let inapps: [InApp]? = [.init(id: "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// sdkVersion: SdkVersion(min: 4, max: nil), +// targeting: .and(AndTargeting(nodes: [.true(TrueTargeting())])), +// form: InApp.InAppFormVariants(variants: [.init(imageUrl: "1", +// redirectUrl: "2", +// intentPayload: "3", +// type: "simpleImage")]))] +// +// XCTAssertEqual(response.inapps, inapps) +// // No id in Monitoring JSON +// XCTAssertNil(response.monitoring) +// } +// +// func test_config_abtests_invalid_to_parse() throws { +// let response = try getConfig(name: "InappConfigResponseAbtestsInvalid") +// let inapps: [InApp]? = [.init(id: "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", +// sdkVersion: SdkVersion(min: 4, max: nil), +// targeting: .and(AndTargeting(nodes: [.true(TrueTargeting())])), +// form: InApp.InAppFormVariants(variants: [.init(imageUrl: "1", +// redirectUrl: "2", +// intentPayload: "3", +// type: "simpleImage")]))] +// +// XCTAssertEqual(response.inapps, inapps) +// // No id in Abtest JSON +// XCTAssertNil(response.abtests) +// } +//} +// +//private extension InAppConfigResponseTests { +// private func getConfig(name: String) throws -> ConfigResponse { +// let bundle = Bundle(for: InAppConfigResponseTests.self) +// let fileURL = bundle.url(forResource: name, withExtension: "json")! +// let data = try Data(contentsOf: fileURL) +// return try JSONDecoder().decode(ConfigResponse.self, from: data) +// } +// +// private func getMapper(version: Int, segments: SegmentationCheckResponse? = nil, productSegments: InAppProductSegmentResponse? = nil) -> InAppConfigutationMapper { +// let sdkVersionValidator = SDKVersionValidator(sdkVersionNumeric: version) +// let segmentationService = SegmentationService(customerSegmentsAPI: .init(fetchSegments: { segmentationCheckRequest, completion in +// completion(segments) +// }, fetchProductSegments: { segmentationCheckRequest, completion in +// completion(productSegments) +// }), +// sessionTemporaryStorage: sessionTemporaryStorage, +// targetingChecker: targetingChecker) +// return InAppConfigutationMapper(geoService: container.geoService, +// segmentationService: segmentationService, +// customerSegmentsAPI: .live, +// targetingChecker: targetingChecker, +// sessionTemporaryStorage: sessionTemporaryStorage, +// persistenceStorage: persistenceStorage, +// sdkVersionValidator: sdkVersionValidator, +// imageDownloadService: container.imageDownloadService, +// abTestDeviceMixer: container.abTestDeviceMixer) +// } +// +// private func testNil(event: ApplicationEvent?, response: ConfigResponse) { +// var output: InAppFormData? +// let expectations = expectation(description: "testNil") +// mapper.mapConfigResponse(event, response) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// XCTAssertNil(output) +// } +// +// private func testResponse(event: ApplicationEvent?, response: ConfigResponse) { +// var output: InAppFormData? +// let expectations = expectation(description: "test_categoryID_emptyModel") +// mapper.mapConfigResponse(event, response) { result in +// output = result +// expectations.fulfill() +// } +// +// waitForExpectations(timeout: 1) +// let expected = InAppTransitionData(inAppId: "0", +// imageUrl: "1", +// redirectUrl: "2", +// intentPayload: "3") +// +// XCTAssertEqual(expected.inAppId, output?.inAppId) +// XCTAssertEqual(expected.intentPayload, output?.intentPayload) +// XCTAssertEqual(expected.redirectUrl, output?.redirectUrl) +// } +//} diff --git a/MindboxTests/InApp/Tests/InAppConfigResponseTests/InAppConfigStub.swift b/MindboxTests/InApp/Tests/InAppConfigResponseTests/InAppConfigStub.swift index a249347e..2d5b0c07 100644 --- a/MindboxTests/InApp/Tests/InAppConfigResponseTests/InAppConfigStub.swift +++ b/MindboxTests/InApp/Tests/InAppConfigResponseTests/InAppConfigStub.swift @@ -10,125 +10,125 @@ import Foundation @testable import Mindbox final class InAppConfigStub { - func getCategoryID_Substring() -> ConfigResponse { - let categoryIdTargeting = CategoryIDTargeting(kind: .substring, value: "oot") - let targeting = Targeting.viewProductCategoryId(categoryIdTargeting) - let settings = Settings(operations: .init(viewProduct: nil, - viewCategory: .init(systemName: "Hello"), - setCart: nil)) - return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) - } - - func getCategoryID_notSubstring() -> ConfigResponse { - let categoryIdTargeting = CategoryIDTargeting(kind: .notSubstring, value: "oot") - let targeting = Targeting.viewProductCategoryId(categoryIdTargeting) - let settings = Settings(operations: .init(viewProduct: nil, - viewCategory: .init(systemName: "Hello"), - setCart: nil)) - return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) - } - - func getCategoryID_startWith() -> ConfigResponse { - let categoryIdTargeting = CategoryIDTargeting(kind: .startsWith, value: "oot") - let targeting = Targeting.viewProductCategoryId(categoryIdTargeting) - let settings = Settings(operations: .init(viewProduct: nil, - viewCategory: .init(systemName: "Hello"), - setCart: nil)) - return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) - } - - func getCategoryID_endWith() -> ConfigResponse { - let categoryIdTargeting = CategoryIDTargeting(kind: .endsWith, value: "ots") - let targeting = Targeting.viewProductCategoryId(categoryIdTargeting) - let settings = Settings(operations: .init(viewProduct: nil, - viewCategory: .init(systemName: "Hello"), - setCart: nil)) - return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) - } - - func getCategoryIDIn_Any() -> ConfigResponse { - let categoryIdInTargeting = CategoryIDInTargeting(kind: .any, - values: [.init(id: "testik2", name: "System1C"), - .init(id: "81", name: "TestSite")]) - let targeting = Targeting.viewProductCategoryIdIn(categoryIdInTargeting) - let settings = Settings(operations: .init(viewProduct: nil, - viewCategory: .init(systemName: "Hello"), - setCart: nil)) - return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) - } - - func getCategoryIDIn_None() -> ConfigResponse { - let categoryIdInTargeting = CategoryIDInTargeting(kind: .none, - values: [.init(id: "testik2", name: "System1C"), - .init(id: "81", name: "TestSite")]) - let targeting = Targeting.viewProductCategoryIdIn(categoryIdInTargeting) - let settings = Settings(operations: .init(viewProduct: nil, - viewCategory: .init(systemName: "Hello"), - setCart: nil)) - return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) - } - - func getInapps(with targeting: Targeting) -> [InApp] { - return [.init(id: "0", - sdkVersion: .init(min: 5, max: nil), - targeting: targeting, - form: getForm())] - } - - func getForm() -> InApp.InAppFormVariants { - .init(variants: [.init(imageUrl: "https://example.com/image.jpg", redirectUrl: "2", intentPayload: "3", type: "simpleImage")]) - } - func getProductID_Substring() -> ConfigResponse { - let productIdTargeting = ProductIDTargeting(kind: .substring, value: "oot") - let targeting = Targeting.viewProductId(productIdTargeting) - let settings = Settings(operations: .init(viewProduct: nil, - viewCategory: .init(systemName: "Hello"), - setCart: nil)) - return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) - } - - func getProductID_notSubstring() -> ConfigResponse { - let productIdTargeting = ProductIDTargeting(kind: .notSubstring, value: "oot") - let targeting = Targeting.viewProductId(productIdTargeting) - let settings = Settings(operations: .init(viewProduct: nil, - viewCategory: .init(systemName: "Hello"), - setCart: nil)) - return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) - } - - func getProductID_startsWith() -> ConfigResponse { - let productIdTargeting = ProductIDTargeting(kind: .startsWith, value: "oot") - let targeting = Targeting.viewProductId(productIdTargeting) - let settings = Settings(operations: .init(viewProduct: nil, - viewCategory: .init(systemName: "Hello"), - setCart: nil)) - return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) - } - - func getProductID_endsWith() -> ConfigResponse { - let productIdTargeting = ProductIDTargeting(kind: .endsWith, value: "ots") - let targeting = Targeting.viewProductId(productIdTargeting) - let settings = Settings(operations: .init(viewProduct: nil, - viewCategory: .init(systemName: "Hello"), - setCart: nil)) - return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) - } - - func getProductSegment_Any() -> ConfigResponse { - let productSegmentTargeting = ProductSegmentTargeting(kind: .positive, segmentationInternalId: "1", segmentationExternalId: "2", segmentExternalId: "3") - let targeting = Targeting.viewProductSegment(productSegmentTargeting) - let settings = Settings(operations: .init(viewProduct: .init(systemName: "Hello"), - viewCategory: nil, - setCart: nil)) - return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) - } - - func getProductSegment_None() -> ConfigResponse { - let productSegmentTargeting = ProductSegmentTargeting(kind: .negative, segmentationInternalId: "1", segmentationExternalId: "2", segmentExternalId: "3") - let targeting = Targeting.viewProductSegment(productSegmentTargeting) - let settings = Settings(operations: .init(viewProduct: .init(systemName: "Hello"), - viewCategory: nil, - setCart: nil)) - return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) - } +// func getCategoryID_Substring() -> ConfigResponse { +// let categoryIdTargeting = CategoryIDTargeting(kind: .substring, value: "oot") +// let targeting = Targeting.viewProductCategoryId(categoryIdTargeting) +// let settings = Settings(operations: .init(viewProduct: nil, +// viewCategory: .init(systemName: "Hello"), +// setCart: nil)) +// return .init(inapps: FailableDecodableArray(getInapps(with: targeting)) , monitoring: nil, settings: settings) +// } +// +// func getCategoryID_notSubstring() -> ConfigResponse { +// let categoryIdTargeting = CategoryIDTargeting(kind: .notSubstring, value: "oot") +// let targeting = Targeting.viewProductCategoryId(categoryIdTargeting) +// let settings = Settings(operations: .init(viewProduct: nil, +// viewCategory: .init(systemName: "Hello"), +// setCart: nil)) +// return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) +// } +// +// func getCategoryID_startWith() -> ConfigResponse { +// let categoryIdTargeting = CategoryIDTargeting(kind: .startsWith, value: "oot") +// let targeting = Targeting.viewProductCategoryId(categoryIdTargeting) +// let settings = Settings(operations: .init(viewProduct: nil, +// viewCategory: .init(systemName: "Hello"), +// setCart: nil)) +// return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) +// } +// +// func getCategoryID_endWith() -> ConfigResponse { +// let categoryIdTargeting = CategoryIDTargeting(kind: .endsWith, value: "ots") +// let targeting = Targeting.viewProductCategoryId(categoryIdTargeting) +// let settings = Settings(operations: .init(viewProduct: nil, +// viewCategory: .init(systemName: "Hello"), +// setCart: nil)) +// return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) +// } +// +// func getCategoryIDIn_Any() -> ConfigResponse { +// let categoryIdInTargeting = CategoryIDInTargeting(kind: .any, +// values: [.init(id: "testik2", name: "System1C"), +// .init(id: "81", name: "TestSite")]) +// let targeting = Targeting.viewProductCategoryIdIn(categoryIdInTargeting) +// let settings = Settings(operations: .init(viewProduct: nil, +// viewCategory: .init(systemName: "Hello"), +// setCart: nil)) +// return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) +// } +// +// func getCategoryIDIn_None() -> ConfigResponse { +// let categoryIdInTargeting = CategoryIDInTargeting(kind: .none, +// values: [.init(id: "testik2", name: "System1C"), +// .init(id: "81", name: "TestSite")]) +// let targeting = Targeting.viewProductCategoryIdIn(categoryIdInTargeting) +// let settings = Settings(operations: .init(viewProduct: nil, +// viewCategory: .init(systemName: "Hello"), +// setCart: nil)) +// return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) +// } +// +// func getInapps(with targeting: Targeting) -> [InApp] { +// return [.init(id: "0", +// sdkVersion: .init(min: 5, max: nil), +// targeting: targeting, +// form: getForm())] +// } +// +// func getForm() -> InApp.InAppFormVariants { +// .init(variants: [.init(imageUrl: "https://example.com/image.jpg", redirectUrl: "2", intentPayload: "3", type: "simpleImage")]) +// } +// func getProductID_Substring() -> ConfigResponse { +// let productIdTargeting = ProductIDTargeting(kind: .substring, value: "oot") +// let targeting = Targeting.viewProductId(productIdTargeting) +// let settings = Settings(operations: .init(viewProduct: nil, +// viewCategory: .init(systemName: "Hello"), +// setCart: nil)) +// return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) +// } +// +// func getProductID_notSubstring() -> ConfigResponse { +// let productIdTargeting = ProductIDTargeting(kind: .notSubstring, value: "oot") +// let targeting = Targeting.viewProductId(productIdTargeting) +// let settings = Settings(operations: .init(viewProduct: nil, +// viewCategory: .init(systemName: "Hello"), +// setCart: nil)) +// return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) +// } +// +// func getProductID_startsWith() -> ConfigResponse { +// let productIdTargeting = ProductIDTargeting(kind: .startsWith, value: "oot") +// let targeting = Targeting.viewProductId(productIdTargeting) +// let settings = Settings(operations: .init(viewProduct: nil, +// viewCategory: .init(systemName: "Hello"), +// setCart: nil)) +// return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) +// } +// +// func getProductID_endsWith() -> ConfigResponse { +// let productIdTargeting = ProductIDTargeting(kind: .endsWith, value: "ots") +// let targeting = Targeting.viewProductId(productIdTargeting) +// let settings = Settings(operations: .init(viewProduct: nil, +// viewCategory: .init(systemName: "Hello"), +// setCart: nil)) +// return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) +// } +// +// func getProductSegment_Any() -> ConfigResponse { +// let productSegmentTargeting = ProductSegmentTargeting(kind: .positive, segmentationInternalId: "1", segmentationExternalId: "2", segmentExternalId: "3") +// let targeting = Targeting.viewProductSegment(productSegmentTargeting) +// let settings = Settings(operations: .init(viewProduct: .init(systemName: "Hello"), +// viewCategory: nil, +// setCart: nil)) +// return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) +// } +// +// func getProductSegment_None() -> ConfigResponse { +// let productSegmentTargeting = ProductSegmentTargeting(kind: .negative, segmentationInternalId: "1", segmentationExternalId: "2", segmentExternalId: "3") +// let targeting = Targeting.viewProductSegment(productSegmentTargeting) +// let settings = Settings(operations: .init(viewProduct: .init(systemName: "Hello"), +// viewCategory: nil, +// setCart: nil)) +// return .init(inapps: getInapps(with: targeting), monitoring: nil, settings: settings) +// } } From 0abeed95aa680c8acdf0e3bb9a7fd60fdc4c32c3 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Mon, 7 Aug 2023 11:29:35 +0600 Subject: [PATCH 09/35] MBX-2706 New tests and update old tests --- Mindbox.xcodeproj/project.pbxproj | 36 ++++++++-- ...6F1E606F-EED9-4F3E-A8B0-8781ECA1604B.plist | 2 +- ...9944809D-7C33-4FC7-910D-5FCF8B8E40E0.plist | 35 ++++++++++ .../Info.plist | 31 ++++++++ .../InappFormVariant/InappFormVariant.swift | 5 ++ .../ContentBackgroundLayer.swift | 6 ++ .../ContentBackgroundLayerAction.swift | 6 ++ .../LayerActionType/LayerActionType.swift | 8 ++- .../ContentBackgroundLayerType.swift | 8 ++- .../ContentBackgroundLayerSource.swift | 5 ++ .../LayerSourceType/LayerSourceType.swift | 8 ++- .../ContentElement/ContentElement.swift | 9 +++ .../PositionMarginKind.swift | 8 ++- .../Kind/ContentElementSizeType.swift | 8 ++- .../ContentElementType.swift | 8 ++- .../InappFormVariantType.swift | 8 ++- Mindbox/Info.plist | 2 +- .../Model/Common/DecodableWithUnknown.swift | 27 ------- .../InApp/Tests/ABTesting/ABTests.swift | 70 +++++++++---------- ...tBackgroundLayerActionValidatorTests.swift | 54 ++++++++++++++ ...tBackgroundLayerSourceValidatorTests.swift | 49 +++++++++++++ .../ContentBackgroundLayerTests.swift | 49 +++++++++++++ ...tElementPositionMarginValidatorTests.swift | 30 ++++++++ .../ContentElementValidatorTests.swift | 30 ++++++++ .../InappFormVariantValidatorTests.swift | 40 +++++++++++ 25 files changed, 467 insertions(+), 75 deletions(-) create mode 100644 Mindbox.xcodeproj/xcshareddata/xcbaselines/313B233825ADEA0F00A1CB72.xcbaseline/9944809D-7C33-4FC7-910D-5FCF8B8E40E0.plist delete mode 100644 Mindbox/Model/Common/DecodableWithUnknown.swift create mode 100644 MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerActionValidatorTests.swift create mode 100644 MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerSourceValidatorTests.swift create mode 100644 MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerTests.swift create mode 100644 MindboxTests/InApp/Tests/ModelValidatorTests/ContentElementPositionMarginValidatorTests.swift create mode 100644 MindboxTests/InApp/Tests/ModelValidatorTests/ContentElementValidatorTests.swift create mode 100644 MindboxTests/InApp/Tests/ModelValidatorTests/InappFormVariantValidatorTests.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 72d8450b..daaf7613 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -337,7 +337,12 @@ F331DCD32A80993600222120 /* InappForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB82A80993600222120 /* InappForm.swift */; }; F331DCD42A80993600222120 /* InappValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB92A80993600222120 /* InappValidator.swift */; }; F331DCD52A80993600222120 /* InAppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCBA2A80993600222120 /* InAppModel.swift */; }; - F331DCD72A809A9700222120 /* DecodableWithUnknown.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCD62A809A9700222120 /* DecodableWithUnknown.swift */; }; + F331DCDC2A80AC4D00222120 /* ContentBackgroundLayerSourceValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCDB2A80AC4D00222120 /* ContentBackgroundLayerSourceValidatorTests.swift */; }; + F331DCDE2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCDD2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift */; }; + F331DCE02A80B37000222120 /* ContentBackgroundLayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCDF2A80B37000222120 /* ContentBackgroundLayerTests.swift */; }; + F331DCE22A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCE12A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift */; }; + F331DCE42A80B54A00222120 /* ContentElementValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCE32A80B54A00222120 /* ContentElementValidatorTests.swift */; }; + F331DCE62A80B5AB00222120 /* InappFormVariantValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCE52A80B5AB00222120 /* InappFormVariantValidatorTests.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -774,7 +779,12 @@ F331DCB82A80993600222120 /* InappForm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappForm.swift; sourceTree = ""; }; F331DCB92A80993600222120 /* InappValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappValidator.swift; sourceTree = ""; }; F331DCBA2A80993600222120 /* InAppModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InAppModel.swift; sourceTree = ""; }; - F331DCD62A809A9700222120 /* DecodableWithUnknown.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodableWithUnknown.swift; sourceTree = ""; }; + F331DCDB2A80AC4D00222120 /* ContentBackgroundLayerSourceValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerSourceValidatorTests.swift; sourceTree = ""; }; + F331DCDD2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerActionValidatorTests.swift; sourceTree = ""; }; + F331DCDF2A80B37000222120 /* ContentBackgroundLayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerTests.swift; sourceTree = ""; }; + F331DCE12A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentElementPositionMarginValidatorTests.swift; sourceTree = ""; }; + F331DCE32A80B54A00222120 /* ContentElementValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentElementValidatorTests.swift; sourceTree = ""; }; + F331DCE52A80B5AB00222120 /* InappFormVariantValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InappFormVariantValidatorTests.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -1202,7 +1212,6 @@ 337A473B265510AA000DC613 /* IDS.swift */, 337A473D265528B5000DC613 /* MBDate.swift */, 337A473F26553938000DC613 /* Sex.swift */, - F331DCD62A809A9700222120 /* DecodableWithUnknown.swift */, ); path = Common; sourceTree = ""; @@ -1613,6 +1622,7 @@ 9B5256FD28D1A86F0029B1BC /* Tests */ = { isa = PBXGroup; children = ( + F331DCDA2A80AC3300222120 /* ModelValidatorTests */, F39B67B62A3FAA62005C0CCA /* ABTesting */, F3A8B9592A389E7A00E9C055 /* InAppConfigurationMapperTests */, A17958932978D03200609E91 /* InAppTargetingCheckerTests */, @@ -2112,6 +2122,19 @@ path = ContentBackgroundLayerType; sourceTree = ""; }; + F331DCDA2A80AC3300222120 /* ModelValidatorTests */ = { + isa = PBXGroup; + children = ( + F331DCDB2A80AC4D00222120 /* ContentBackgroundLayerSourceValidatorTests.swift */, + F331DCDD2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift */, + F331DCDF2A80B37000222120 /* ContentBackgroundLayerTests.swift */, + F331DCE12A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift */, + F331DCE32A80B54A00222120 /* ContentElementValidatorTests.swift */, + F331DCE52A80B5AB00222120 /* InappFormVariantValidatorTests.swift */, + ); + path = ModelValidatorTests; + sourceTree = ""; + }; F3482F142A65DBA0002A41EC /* InappMessagesDelegate */ = { isa = PBXGroup; children = ( @@ -2677,7 +2700,6 @@ F331DCBD2A80993600222120 /* InappFormVariantType.swift in Sources */, 33072F2E2664C24F001F1AB2 /* AreaResponse.swift in Sources */, 84DEE8A925CC031200C98CC7 /* MBDatabase.xcdatamodeld in Sources */, - F331DCD72A809A9700222120 /* DecodableWithUnknown.swift in Sources */, A192786A29D38D2000CDB53D /* CheckerFactory.swift in Sources */, F331DCC72A80993600222120 /* ContentElementValidator.swift in Sources */, F331DCBE2A80993600222120 /* ContentElementType.swift in Sources */, @@ -2826,6 +2848,7 @@ files = ( 840C38B325D133B000D50183 /* GuaranteedDeliveryTestCase.swift in Sources */, 84FCD3B925CA109E00D1E574 /* MockNetworkFetcher.swift in Sources */, + F331DCE42A80B54A00222120 /* ContentElementValidatorTests.swift in Sources */, 9B9C9538292111A700BB29DA /* MockUUIDDebugService.swift in Sources */, 84B625F025C98B1200AB6228 /* ValidatorsTestCase.swift in Sources */, 3132DFF625C2A811007FE358 /* TestDependencyProvider.swift in Sources */, @@ -2834,7 +2857,9 @@ F3A8B95C2A389EAD00E9C055 /* GeoServiceTests.swift in Sources */, A17958992978E04600609E91 /* InAppStub.swift in Sources */, F39B67A72A3C6C6A005C0CCA /* SegmentationServiceTests.swift in Sources */, + F331DCE02A80B37000222120 /* ContentBackgroundLayerTests.swift in Sources */, F3D925AB2A120C0F00135C87 /* InAppImageDownloaderMock.swift in Sources */, + F331DCE22A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift in Sources */, A154E32E299E0D8900F8F074 /* SDKLogManagerTests.swift in Sources */, 9B52570728D1AF880029B1BC /* InAppPresentationManagerMock.swift in Sources */, 84B09FB42611C74400B0A06E /* MockDatabaseRepository.swift in Sources */, @@ -2843,6 +2868,7 @@ 84ECB42E25D27EF100DA8AC9 /* MockUNAuthorizationStatusProvider.swift in Sources */, F3A8B9982A3A421C00E9C055 /* SDKVersionValidatorTests.swift in Sources */, A17958812978B3FA00609E91 /* InAppResponseModelTests.swift in Sources */, + F331DCDC2A80AC4D00222120 /* ContentBackgroundLayerSourceValidatorTests.swift in Sources */, 9B4F9E0528D0945F002C9CF0 /* InAppCoreManagerMock.swift in Sources */, 840C387F25CD15C200D50183 /* MockDataBaseLoader.swift in Sources */, A1F3EE4E298BEF0B005FA828 /* OSLogWritterTests.swift in Sources */, @@ -2851,6 +2877,7 @@ 313B233F25ADEA0F00A1CB72 /* MindboxTests.swift in Sources */, A154E334299E110E00F8F074 /* EventRepositoryMock.swift in Sources */, 31ED2DEC25C444C400301FAD /* MBConfigurationTestCase.swift in Sources */, + F331DCDE2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift in Sources */, F3D925AD2A1236F400135C87 /* URLSessionImageDownloaderTests.swift in Sources */, F3A8B9A02A3A52F400E9C055 /* ABTestValidatorTests.swift in Sources */, 9B9C953B292111A700BB29DA /* MockDateProvider.swift in Sources */, @@ -2860,6 +2887,7 @@ 84E1D6A9261D8D54002BF03A /* DatabaseLoaderTest.swift in Sources */, 840C38B825D13A7D00D50183 /* EventGenerator.swift in Sources */, 84CC79A525CAE14200C062BD /* EventRepositoryTestCase.swift in Sources */, + F331DCE62A80B5AB00222120 /* InappFormVariantValidatorTests.swift in Sources */, A154E304299C189300F8F074 /* MBLoggerCoreDataManagerTests.swift in Sources */, 840C388525CD169400D50183 /* DatabaseRepositoryTestCase.swift in Sources */, 84FCD3B525CA0FD300D1E574 /* MockPersistenceStorage.swift in Sources */, diff --git a/Mindbox.xcodeproj/xcshareddata/xcbaselines/313B233825ADEA0F00A1CB72.xcbaseline/6F1E606F-EED9-4F3E-A8B0-8781ECA1604B.plist b/Mindbox.xcodeproj/xcshareddata/xcbaselines/313B233825ADEA0F00A1CB72.xcbaseline/6F1E606F-EED9-4F3E-A8B0-8781ECA1604B.plist index c5cdd94c..f19dda0d 100644 --- a/Mindbox.xcodeproj/xcshareddata/xcbaselines/313B233825ADEA0F00A1CB72.xcbaseline/6F1E606F-EED9-4F3E-A8B0-8781ECA1604B.plist +++ b/Mindbox.xcodeproj/xcshareddata/xcbaselines/313B233825ADEA0F00A1CB72.xcbaseline/6F1E606F-EED9-4F3E-A8B0-8781ECA1604B.plist @@ -11,7 +11,7 @@ com.apple.XCTPerformanceMetric_WallClockTime baselineAverage - 1.46e-05 + 0.000015 baselineIntegrationDisplayName Local Baseline diff --git a/Mindbox.xcodeproj/xcshareddata/xcbaselines/313B233825ADEA0F00A1CB72.xcbaseline/9944809D-7C33-4FC7-910D-5FCF8B8E40E0.plist b/Mindbox.xcodeproj/xcshareddata/xcbaselines/313B233825ADEA0F00A1CB72.xcbaseline/9944809D-7C33-4FC7-910D-5FCF8B8E40E0.plist new file mode 100644 index 00000000..7afe28e7 --- /dev/null +++ b/Mindbox.xcodeproj/xcshareddata/xcbaselines/313B233825ADEA0F00A1CB72.xcbaseline/9944809D-7C33-4FC7-910D-5FCF8B8E40E0.plist @@ -0,0 +1,35 @@ + + + + + classNames + + ContentBackgroundLayerActionValidatorTests + + testPerformanceExample() + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 0.004788 + baselineIntegrationDisplayName + Local Baseline + + + + ContentBackgroundLayerSourceValidatorTests + + testPerformanceExample() + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 0.005370 + baselineIntegrationDisplayName + Local Baseline + + + + + + diff --git a/Mindbox.xcodeproj/xcshareddata/xcbaselines/313B233825ADEA0F00A1CB72.xcbaseline/Info.plist b/Mindbox.xcodeproj/xcshareddata/xcbaselines/313B233825ADEA0F00A1CB72.xcbaseline/Info.plist index 33e30a98..f50b2d98 100644 --- a/Mindbox.xcodeproj/xcshareddata/xcbaselines/313B233825ADEA0F00A1CB72.xcbaseline/Info.plist +++ b/Mindbox.xcodeproj/xcshareddata/xcbaselines/313B233825ADEA0F00A1CB72.xcbaseline/Info.plist @@ -35,6 +35,37 @@ com.apple.platform.iphonesimulator + 9944809D-7C33-4FC7-910D-5FCF8B8E40E0 + + localComputer + + busSpeedInMHz + 0 + cpuCount + 1 + cpuKind + Apple M1 Pro + cpuSpeedInMHz + 0 + logicalCPUCoresPerPackage + 10 + modelCode + MacBookPro18,3 + physicalCPUCoresPerPackage + 10 + platformIdentifier + com.apple.platform.macosx + + targetArchitecture + arm64 + targetDevice + + modelCode + iPhone15,2 + platformIdentifier + com.apple.platform.iphonesimulator + + diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariant.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariant.swift index 23d63364..a5d189f2 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariant.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariant.swift @@ -30,4 +30,9 @@ struct InappFormVariant: Decodable, Equatable { ) } } + + internal init(type: InappFormVariantType, content: InappFormVariantContent? = nil) { + self.type = type + self.content = content + } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift index b6c8182a..5fa266bc 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift @@ -33,4 +33,10 @@ struct ContentBackgroundLayer: Decodable, Equatable { ) } } + + init(type: ContentBackgroundLayerType, action: ContentBackgroundLayerAction? = nil, source: ContentBackgroundLayerSource? = nil) { + self.type = type + self.action = action + self.source = source + } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift index 8376435b..0b58099b 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift @@ -33,4 +33,10 @@ struct ContentBackgroundLayerAction: Decodable, Equatable { ) } } + + init(type: LayerActionType, intentPayload: String? = nil, value: String? = nil) { + self.type = type + self.intentPayload = intentPayload + self.value = value + } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift index 45b5149b..8929903a 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift @@ -8,7 +8,13 @@ import Foundation -enum LayerActionType: String, Decodable, Equatable, DecodableWithUnknown { +enum LayerActionType: String, Decodable, Equatable { case redirectUrl case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(RawValue.self) + self = LayerActionType(rawValue: rawValue) ?? .unknown + } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift index bfb42749..50b84144 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift @@ -8,7 +8,13 @@ import Foundation -enum ContentBackgroundLayerType: String, Decodable, Equatable, DecodableWithUnknown { +enum ContentBackgroundLayerType: String, Decodable, Equatable { case image case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(RawValue.self) + self = ContentBackgroundLayerType(rawValue: rawValue) ?? .unknown + } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift index 02e995f2..41635116 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift @@ -30,4 +30,9 @@ struct ContentBackgroundLayerSource: Decodable, Equatable { ) } } + + init(type: LayerSourceType, value: String? = nil) { + self.type = type + self.value = value + } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift index ba8bc9c2..e658e582 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift @@ -7,7 +7,13 @@ import Foundation -enum LayerSourceType: String, Decodable, Equatable, DecodableWithUnknown { +enum LayerSourceType: String, Decodable, Equatable { case url case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(RawValue.self) + self = LayerSourceType(rawValue: rawValue) ?? .unknown + } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift index 7a0bd749..7bf5943a 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift @@ -43,4 +43,13 @@ struct ContentElement: Decodable, Equatable { ) } } + + internal init(type: ContentElementType, color: String? = nil, lineWidth: Int? = nil, size: ContentElementSize? = nil, position: ContentElementPosition? = nil, gravity: ContentElementGravity? = nil) { + self.type = type + self.color = color + self.lineWidth = lineWidth + self.size = size + self.position = position + self.gravity = gravity + } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/PositionMarginKind/PositionMarginKind.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/PositionMarginKind/PositionMarginKind.swift index 8d87f41d..c860330a 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/PositionMarginKind/PositionMarginKind.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/PositionMarginKind/PositionMarginKind.swift @@ -8,7 +8,13 @@ import Foundation -enum PositionMarginKind: String, Decodable, Equatable, DecodableWithUnknown { +enum PositionMarginKind: String, Decodable, Equatable { case proportion case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(RawValue.self) + self = PositionMarginKind(rawValue: rawValue) ?? .unknown + } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/Kind/ContentElementSizeType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/Kind/ContentElementSizeType.swift index dfcb9ca6..346831b3 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/Kind/ContentElementSizeType.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/Kind/ContentElementSizeType.swift @@ -8,7 +8,13 @@ import Foundation -enum ContentElementSizeKind: String, Decodable, Equatable, DecodableWithUnknown { +enum ContentElementSizeKind: String, Decodable, Equatable { case dp case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(RawValue.self) + self = ContentElementSizeKind(rawValue: rawValue) ?? .unknown + } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift index b677a833..0f82fde2 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift @@ -8,7 +8,13 @@ import Foundation -enum ContentElementType: String, Decodable, Equatable, DecodableWithUnknown { +enum ContentElementType: String, Decodable, Equatable { case closeButton case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(RawValue.self) + self = ContentElementType(rawValue: rawValue) ?? .unknown + } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift index efb5a858..dcbcce03 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift @@ -8,8 +8,14 @@ import Foundation -enum InappFormVariantType: String, Decodable, Equatable, DecodableWithUnknown { +enum InappFormVariantType: String, Decodable, Equatable { case modal case snackbar case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(RawValue.self) + self = InappFormVariantType(rawValue: rawValue) ?? .unknown + } } diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index a0a3fbd7..86492b02 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4659 + 4710 diff --git a/Mindbox/Model/Common/DecodableWithUnknown.swift b/Mindbox/Model/Common/DecodableWithUnknown.swift deleted file mode 100644 index 06ed3ab3..00000000 --- a/Mindbox/Model/Common/DecodableWithUnknown.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// DecodableWithUnknown.swift -// Mindbox -// -// Created by vailence on 07.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import Foundation - -protocol DecodableWithUnknown: RawRepresentable, Decodable where RawValue: Decodable { - static var unknownCase: Self { get } -} - -extension DecodableWithUnknown where Self: RawRepresentable, RawValue == String { - static var unknownCase: Self { - return Self(rawValue: "unknown")! - } -} - -extension Decodable where Self: DecodableWithUnknown { - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = Self(rawValue: rawValue) ?? Self.unknownCase - } -} diff --git a/MindboxTests/InApp/Tests/ABTesting/ABTests.swift b/MindboxTests/InApp/Tests/ABTesting/ABTests.swift index 7e4afedf..bb58457a 100644 --- a/MindboxTests/InApp/Tests/ABTesting/ABTests.swift +++ b/MindboxTests/InApp/Tests/ABTesting/ABTests.swift @@ -51,7 +51,7 @@ import XCTest // func test_no_abtests() throws { // let response = try getConfig(name: "ConfigWithAB_1") // let abTests: [ABTest]? = nil -// let inapps = mapper.filterInappsByABTests(abTests, responseInapps: response.inapps!) +// let inapps = mapper.filterInappsByABTests(abTests, responseInapps: response.inapps?.elements) // XCTAssertEqual(inapps.count, 3) // let expectedIds = [ // "655f5ffa-de86-4224-a0bf-229fe208ed0d", @@ -61,7 +61,7 @@ import XCTest // // runInAppTestForUUID("BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", // abTests: abTests, -// responseInapps: response.inapps!, +// responseInapps: response.inapps?.elements, // expectedCount: 3, expectedIds: expectedIds) // } // @@ -99,7 +99,7 @@ import XCTest // ) // ] // -// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: 0, expectedIds: []) // 25 +// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 0, expectedIds: []) // 25 // // let expectedIds = [ // "655f5ffa-de86-4224-a0bf-229fe208ed0d", @@ -107,7 +107,7 @@ import XCTest // "b33ca779-3c99-481f-ad46-91282b0caf04" // ] // -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: 3, expectedIds: expectedIds) // 75 +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 3, expectedIds: expectedIds) // 75 // } // // func test_compare_cg_and_concrete_inapps() throws { @@ -152,7 +152,7 @@ import XCTest // "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", // "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" // ] -// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: 2, expectedIds: expectedIds1) +// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 2, expectedIds: expectedIds1) // // // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 4 // let expectedIds2 = [ @@ -161,7 +161,7 @@ import XCTest // "b33ca779-3c99-481f-ad46-91282b0caf04", // "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" // ] -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: 4, expectedIds: expectedIds2) +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 4, expectedIds: expectedIds2) // } // // func test_compare_cg_and_concrete_inapps_and_all_inapps() throws { @@ -213,14 +213,14 @@ import XCTest // ] // // // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 0 -// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: 0, expectedIds: []) +// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 0, expectedIds: []) // // // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 2 // let expectedIds1 = [ // "655f5ffa-de86-4224-a0bf-229fe208ed0d", // "b33ca779-3c99-481f-ad46-91282b0caf04" // ] -// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: 2, expectedIds: expectedIds1) +// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 2, expectedIds: expectedIds1) // // // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 // let expectedIds2 = [ @@ -228,7 +228,7 @@ import XCTest // "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", // "b33ca779-3c99-481f-ad46-91282b0caf04" // ] -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: 3, expectedIds: expectedIds2) +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 3, expectedIds: expectedIds2) // } // // func test_compare_2branch_and_concrete_inapps_and_cg() throws { @@ -285,7 +285,7 @@ import XCTest // "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", // "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" // ] -// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) +// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // // // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 // let expectedIds2 = [ @@ -293,7 +293,7 @@ import XCTest // "d1b312bd-aa5c-414c-a0d8-8126376a2a9b", // "655f5ffa-de86-4224-a0bf-229fe208ed0d" // ] -// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) +// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // // // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 // let expectedIds3 = [ @@ -301,7 +301,7 @@ import XCTest // "d1b312bd-aa5c-414c-a0d8-8126376a2a9b", // "b33ca779-3c99-481f-ad46-91282b0caf04" // ] -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds3.count, expectedIds: expectedIds3) +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // } // // func test_compare_2branch_and_concrete_inapps() throws { @@ -348,13 +348,13 @@ import XCTest // "655f5ffa-de86-4224-a0bf-229fe208ed0d", // "b33ca779-3c99-481f-ad46-91282b0caf04" // ] -// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) +// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // // // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 // let expectedIds2 = [ // "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" // ] -// runInAppTestForUUID("C9F84B44-01B9-A3C6-E85B-7FF816D3BA68", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) +// runInAppTestForUUID("C9F84B44-01B9-A3C6-E85B-7FF816D3BA68", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // } // // func test_aab_show_inapps_in_cg_branch() throws { @@ -409,10 +409,10 @@ import XCTest // let expectedIds1 = [ // "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" // ] -// runInAppTestForUUID("4862ADF1-1392-9362-42A2-FF5A65629F50", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 +// runInAppTestForUUID("4862ADF1-1392-9362-42A2-FF5A65629F50", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 // // // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 -// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 45 +// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 45 // // let expectedIds2 = [ // "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", @@ -420,7 +420,7 @@ import XCTest // "b33ca779-3c99-481f-ad46-91282b0caf04" // ] // // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 // } // // func test_aab_not_show_inapps_in_cg_branch() throws { @@ -474,10 +474,10 @@ import XCTest // // // Test case for UUID "4862ADF1-1392-9362-42A2-FF5A65629F50" with expected inapp count of 2 // let expectedIds1 = [String]() -// runInAppTestForUUID("4862ADF1-1392-9362-42A2-FF5A65629F50", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 +// runInAppTestForUUID("4862ADF1-1392-9362-42A2-FF5A65629F50", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 // // // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 -// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 45 +// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 45 // // let expectedIds2 = [ // "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", @@ -485,7 +485,7 @@ import XCTest // "b33ca779-3c99-481f-ad46-91282b0caf04" // ] // // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 // } // // func test_ab_limit_value_check() throws { @@ -539,21 +539,21 @@ import XCTest // // // Test case for UUID "F3AB8877-CB55-CE3D-1AB3-230D2EA8A220" with expected inapp count of 2 // let expectedIds1 = ["6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] -// runInAppTestForUUID("F3AB8877-CB55-CE3D-1AB3-230D2EA8A220", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 0 +// runInAppTestForUUID("F3AB8877-CB55-CE3D-1AB3-230D2EA8A220", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 0 // // let expectedIds2 = [ // "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", // "655f5ffa-de86-4224-a0bf-229fe208ed0d", // ] // // Test case for UUID "3A9F107E-9FBE-8D83-EFE9-5F093001CD54" with expected inapp count of 3 -// runInAppTestForUUID("3A9F107E-9FBE-8D83-EFE9-5F093001CD54", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 33 +// runInAppTestForUUID("3A9F107E-9FBE-8D83-EFE9-5F093001CD54", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 33 // // let expectedIds3 = [ // "b33ca779-3c99-481f-ad46-91282b0caf04", // "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", // ] // // Test case for UUID "59B675D6-9AF1-1805-2BB3-90C3CF11E5E0" with expected inapp count of 3 -// runInAppTestForUUID("59B675D6-9AF1-1805-2BB3-90C3CF11E5E0", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 99 +// runInAppTestForUUID("59B675D6-9AF1-1805-2BB3-90C3CF11E5E0", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 99 // } // // func test_compare_5_ab_tests_in_one_branch() throws { @@ -633,24 +633,24 @@ import XCTest // // // Test case for UUID "7544976E-FEA4-6A48-EB5D-85A6EEB4D306" with expected inapp count of 21 // let expectedIds1 = ["655f5ffa-de86-4224-a0bf-229fe208ed0d"] -// runInAppTestForUUID("7544976E-FEA4-6A48-EB5D-85A6EEB4D306", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 5 +// runInAppTestForUUID("7544976E-FEA4-6A48-EB5D-85A6EEB4D306", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 5 // // let expectedIds2 = ["6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] // // Test case for UUID "618F8CA3-282D-5B18-7186-F2CF361ABD32" with expected inapp count of 1 -// runInAppTestForUUID("618F8CA3-282D-5B18-7186-F2CF361ABD32", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 15 +// runInAppTestForUUID("618F8CA3-282D-5B18-7186-F2CF361ABD32", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 15 // // let expectedIds3 = ["b33ca779-3c99-481f-ad46-91282b0caf04"] -// runInAppTestForUUID("DC0F2330-785B-5D80-CD34-F2F520AD618F", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 25 +// runInAppTestForUUID("DC0F2330-785B-5D80-CD34-F2F520AD618F", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 25 // // let expectedIds4 = ["d1b312bd-aa5c-414c-a0d8-8126376a2a9b"] -// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds4.count, expectedIds: expectedIds4) // 45 +// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds4.count, expectedIds: expectedIds4) // 45 // // let expectedIds5 = [ // "655f5ffa-de86-4224-a0bf-229fe208ed0d", // "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", // "b33ca779-3c99-481f-ad46-91282b0caf04", // "d1b312bd-aa5c-414c-a0d8-8126376a2a9b"] -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps!, expectedCount: expectedIds5.count, expectedIds: expectedIds5) // 75 +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds5.count, expectedIds: expectedIds5) // 75 // } // // func test_2_different_ab_tests_one() throws { @@ -720,17 +720,17 @@ import XCTest // ] // // let expectedIds1 = [String]() -// runInAppTestForUUID("9d7f8e6c-3a2b-4d9a-b1c0-1e1e3a4b5c6d", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 23 and 29 +// runInAppTestForUUID("9d7f8e6c-3a2b-4d9a-b1c0-1e1e3a4b5c6d", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 23 and 29 // -// runInAppTestForUUID("284c10f7-4f4c-4a1b-92e0-2318f2ae13c9", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 1 and 91 +// runInAppTestForUUID("284c10f7-4f4c-4a1b-92e0-2318f2ae13c9", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 1 and 91 // // let expectedIds2 = ["b33ca779-3c99-481f-ad46-91282b0caf04"] -// runInAppTestForUUID("677a789d-9a98-4f03-9cb2-af2563fc1d07", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 88 and 5 +// runInAppTestForUUID("677a789d-9a98-4f03-9cb2-af2563fc1d07", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 88 and 5 // // let expectedIds3 = ["b33ca779-3c99-481f-ad46-91282b0caf04", // "655f5ffa-de86-4224-a0bf-229fe208ed0d", // "6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] -// runInAppTestForUUID("d35df6c2-9e20-4f7e-9e20-51894e2c4810", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 61 and 94 +// runInAppTestForUUID("d35df6c2-9e20-4f7e-9e20-51894e2c4810", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 61 and 94 // } // // func test_2_different_ab_tests_two() throws { @@ -780,7 +780,7 @@ import XCTest // "b33ca779-3c99-481f-ad46-91282b0caf04" // ] // -// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 +// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 // // let expectedIds2 = [ // "655f5ffa-de86-4224-a0bf-229fe208ed0d", @@ -788,7 +788,7 @@ import XCTest // "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" // ] // -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abtests, responseInapps: response.inapps!, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 +// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 // } // // func test_section_ab_broken_return_nil() throws { @@ -824,7 +824,7 @@ import XCTest // //private extension ABTests { // private func getConfig(name: String) throws -> ConfigResponse { -// let bundle = Bundle(for: InAppConfigResponseTests.self) +// let bundle = Bundle(for: ABTests.self) // let fileURL = bundle.url(forResource: name, withExtension: "json")! // let data = try Data(contentsOf: fileURL) // return try JSONDecoder().decode(ConfigResponse.self, from: data) diff --git a/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerActionValidatorTests.swift b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerActionValidatorTests.swift new file mode 100644 index 00000000..e5cf23d5 --- /dev/null +++ b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerActionValidatorTests.swift @@ -0,0 +1,54 @@ +// +// ContentBackgroundLayerActionValidatorTests.swift +// MindboxTests +// +// Created by vailence on 07.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import XCTest +@testable import Mindbox + +final class ContentBackgroundLayerActionValidatorTests: XCTestCase { + + var validator: ContentBackgroundLayerActionValidator! + var model: ContentBackgroundLayerAction! + + override func setUpWithError() throws { + validator = ContentBackgroundLayerActionValidator() + } + + override func tearDownWithError() throws { + validator = nil + model = nil + } + + func test_valid_returnTrue() throws { + let model = ContentBackgroundLayerAction(type: .redirectUrl, intentPayload: "payload", value: "value") + XCTAssertTrue(validator.isValid(item: model)) + } + + func test_url_noValue_returnFalse() throws { + let model = ContentBackgroundLayerAction(type: .redirectUrl, intentPayload: "payload") + XCTAssertFalse(validator.isValid(item: model)) + } + + func test_url_noPayload_returnFalse() throws { + let model = ContentBackgroundLayerAction(type: .redirectUrl, value: "value") + XCTAssertFalse(validator.isValid(item: model)) + } + + func test_unknownCase_returnFalse() throws { + let model = ContentBackgroundLayerAction(type: .unknown, intentPayload: "payload", value: "value") + XCTAssertFalse(validator.isValid(item: model)) + } + + func testPerformanceExample() throws { + let model = ContentBackgroundLayerAction(type: .redirectUrl, intentPayload: "payload", value: "value") + self.measure { + for _ in 0..<10000 { + _ = validator.isValid(item: model) + } + } + } +} diff --git a/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerSourceValidatorTests.swift b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerSourceValidatorTests.swift new file mode 100644 index 00000000..6ef7410d --- /dev/null +++ b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerSourceValidatorTests.swift @@ -0,0 +1,49 @@ +// +// ContentBackgroundLayerSourceValidatorTests.swift +// MindboxTests +// +// Created by vailence on 07.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import XCTest +@testable import Mindbox + +final class ContentBackgroundLayerSourceValidatorTests: XCTestCase { + + var validator: ContentBackgroundLayerSourceValidator! + var model: ContentBackgroundLayerSource! + + override func setUpWithError() throws { + validator = ContentBackgroundLayerSourceValidator() + } + + override func tearDownWithError() throws { + validator = nil + model = nil + } + + func test_valid_returnTrue() throws { + let model = ContentBackgroundLayerSource(type: .url, value: "someValue") + XCTAssertTrue(validator.isValid(item: model)) + } + + func test_url_noValue_returnFalse() throws { + let model = ContentBackgroundLayerSource(type: .url) + XCTAssertFalse(validator.isValid(item: model)) + } + + func test_unknownCase_returnFalse() throws { + let model = ContentBackgroundLayerSource(type: .unknown, value: "someValue") + XCTAssertFalse(validator.isValid(item: model)) + } + + func testPerformanceExample() throws { + let model = ContentBackgroundLayerSource(type: .url, value: "someValue") + self.measure { + for _ in 0..<10000 { + _ = validator.isValid(item: model) + } + } + } +} diff --git a/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerTests.swift b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerTests.swift new file mode 100644 index 00000000..f0009eab --- /dev/null +++ b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerTests.swift @@ -0,0 +1,49 @@ +// +// ContentBackgroundLayerTests.swift +// MindboxTests +// +// Created by vailence on 07.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import XCTest +@testable import Mindbox + +final class ContentBackgroundLayerTests: XCTestCase { + + var validator: ContentBackgroundLayerValidator! + var model: ContentBackgroundLayer! + + override func setUpWithError() throws { + validator = ContentBackgroundLayerValidator() + } + + override func tearDownWithError() throws { + validator = nil + model = nil + } + + func test_valid_returnTrue() throws { + let actionModel = ContentBackgroundLayerAction(type: .redirectUrl, intentPayload: "payload", value: "value") + let sourceModel = ContentBackgroundLayerSource(type: .url, value: "someValue") + let model = ContentBackgroundLayer(type: .image, action: actionModel, source: sourceModel) + XCTAssertTrue(validator.isValid(item: model)) + } + + func test_image_noAction_returnFalse() throws { + let sourceModel = ContentBackgroundLayerSource(type: .url, value: "someValue") + let model = ContentBackgroundLayer(type: .image, source: sourceModel) + XCTAssertFalse(validator.isValid(item: model)) + } + + func test_image_noSource_returnFalse() throws { + let actionModel = ContentBackgroundLayerAction(type: .redirectUrl, intentPayload: "payload", value: "value") + let model = ContentBackgroundLayer(type: .image, action: actionModel) + XCTAssertFalse(validator.isValid(item: model)) + } + + func test_unknownCase_returnFalse() throws { + let model = ContentBackgroundLayer(type: .unknown) + XCTAssertFalse(validator.isValid(item: model)) + } +} diff --git a/MindboxTests/InApp/Tests/ModelValidatorTests/ContentElementPositionMarginValidatorTests.swift b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentElementPositionMarginValidatorTests.swift new file mode 100644 index 00000000..1ff0dd94 --- /dev/null +++ b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentElementPositionMarginValidatorTests.swift @@ -0,0 +1,30 @@ +// +// ContentElementPositionMarginValidatorTests.swift +// MindboxTests +// +// Created by vailence on 07.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import XCTest +@testable import Mindbox + +final class ContentElementPositionMarginValidatorTests: XCTestCase { + + var validator: ContentElementPositionMarginValidator! + var model: ContentElementPositionMargin! + + override func setUpWithError() throws { + validator = ContentElementPositionMarginValidator() + } + + override func tearDownWithError() throws { + validator = nil + model = nil + } + + func test_unknownCase_returnFalse() throws { + let model = ContentElementPositionMargin(kind: .unknown) + XCTAssertFalse(validator.isValid(item: model)) + } +} diff --git a/MindboxTests/InApp/Tests/ModelValidatorTests/ContentElementValidatorTests.swift b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentElementValidatorTests.swift new file mode 100644 index 00000000..c5d63ccf --- /dev/null +++ b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentElementValidatorTests.swift @@ -0,0 +1,30 @@ +// +// ContentElementValidatorTests.swift +// MindboxTests +// +// Created by vailence on 07.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import XCTest +@testable import Mindbox + +final class ContentElementValidatorTests: XCTestCase { + + var validator: ContentElementValidator! + var model: ContentElement! + + override func setUpWithError() throws { + validator = ContentElementValidator() + } + + override func tearDownWithError() throws { + validator = nil + model = nil + } + + func test_unknownCase_returnFalse() throws { + let model = ContentElement(type: .unknown) + XCTAssertFalse(validator.isValid(item: model)) + } +} diff --git a/MindboxTests/InApp/Tests/ModelValidatorTests/InappFormVariantValidatorTests.swift b/MindboxTests/InApp/Tests/ModelValidatorTests/InappFormVariantValidatorTests.swift new file mode 100644 index 00000000..46dd3582 --- /dev/null +++ b/MindboxTests/InApp/Tests/ModelValidatorTests/InappFormVariantValidatorTests.swift @@ -0,0 +1,40 @@ +// +// InappFormVariantValidatorTests.swift +// MindboxTests +// +// Created by vailence on 07.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import XCTest +@testable import Mindbox + +final class InappFormVariantValidatorTests: XCTestCase { + + var validator: InappFormVariantValidator! + var model: InappFormVariant! + + override func setUpWithError() throws { + validator = InappFormVariantValidator() + } + + override func tearDownWithError() throws { + validator = nil + model = nil + } + + func test_unknownCase_returnFalse() throws { + let model = InappFormVariant(type: .unknown) + XCTAssertFalse(validator.isValid(item: model)) + } + + func test_modal_noContent_returnFalse() throws { + let model = InappFormVariant(type: .modal) + XCTAssertFalse(validator.isValid(item: model)) + } + + func test_snackbar_noContent_returnTrue() throws { + let model = InappFormVariant(type: .snackbar) + XCTAssertTrue(validator.isValid(item: model)) + } +} From 089cf2aa2de8952f796665e1bd95270ecb09847d Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Mon, 7 Aug 2023 11:59:10 +0600 Subject: [PATCH 10/35] MBX-2706 Fix ABTests tests --- Mindbox.xcodeproj/project.pbxproj | 4 - Mindbox/Info.plist | 2 +- .../InApp/Tests/ABTesting/ABTests.swift | 1658 ++++++++--------- .../ABTesting/ConfigWithABTypeNotInapps.json | 72 +- .../InApp/Tests/ABTesting/ConfigWithAB_1.json | 72 +- .../InApp/Tests/ABTesting/ConfigWithAB_2.json | 98 +- .../InApp/Tests/ABTesting/ConfigWithAB_3.json | 166 -- 7 files changed, 1031 insertions(+), 1041 deletions(-) delete mode 100644 MindboxTests/InApp/Tests/ABTesting/ConfigWithAB_3.json diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index daaf7613..96ecd036 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -367,7 +367,6 @@ F39B67AE2A3C737F005C0CCA /* ImageDownloadServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39B67AD2A3C737F005C0CCA /* ImageDownloadServiceTests.swift */; }; F39B67B22A3C8B1D005C0CCA /* ConfigWithAB_1.json in Resources */ = {isa = PBXBuildFile; fileRef = F39B67AF2A3C8B1D005C0CCA /* ConfigWithAB_1.json */; }; F39B67B32A3C8B1D005C0CCA /* ConfigWithAB_2.json in Resources */ = {isa = PBXBuildFile; fileRef = F39B67B02A3C8B1D005C0CCA /* ConfigWithAB_2.json */; }; - F39B67B42A3C8B1D005C0CCA /* ConfigWithAB_3.json in Resources */ = {isa = PBXBuildFile; fileRef = F39B67B12A3C8B1D005C0CCA /* ConfigWithAB_3.json */; }; F39B67B82A3FAA75005C0CCA /* ABTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39B67B72A3FAA75005C0CCA /* ABTests.swift */; }; F39B67BA2A3FB943005C0CCA /* ConfigWithABBrokenRange.json in Resources */ = {isa = PBXBuildFile; fileRef = F39B67B92A3FB943005C0CCA /* ConfigWithABBrokenRange.json */; }; F39B67BC2A3FB9A8005C0CCA /* ConfigWithABNoSalt.json in Resources */ = {isa = PBXBuildFile; fileRef = F39B67BB2A3FB9A8005C0CCA /* ConfigWithABNoSalt.json */; }; @@ -809,7 +808,6 @@ F39B67AD2A3C737F005C0CCA /* ImageDownloadServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDownloadServiceTests.swift; sourceTree = ""; }; F39B67AF2A3C8B1D005C0CCA /* ConfigWithAB_1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ConfigWithAB_1.json; sourceTree = ""; }; F39B67B02A3C8B1D005C0CCA /* ConfigWithAB_2.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ConfigWithAB_2.json; sourceTree = ""; }; - F39B67B12A3C8B1D005C0CCA /* ConfigWithAB_3.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ConfigWithAB_3.json; sourceTree = ""; }; F39B67B72A3FAA75005C0CCA /* ABTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ABTests.swift; sourceTree = ""; }; F39B67B92A3FB943005C0CCA /* ConfigWithABBrokenRange.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ConfigWithABBrokenRange.json; sourceTree = ""; }; F39B67BB2A3FB9A8005C0CCA /* ConfigWithABNoSalt.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ConfigWithABNoSalt.json; sourceTree = ""; }; @@ -2209,7 +2207,6 @@ F39B67B72A3FAA75005C0CCA /* ABTests.swift */, F39B67AF2A3C8B1D005C0CCA /* ConfigWithAB_1.json */, F39B67B02A3C8B1D005C0CCA /* ConfigWithAB_2.json */, - F39B67B12A3C8B1D005C0CCA /* ConfigWithAB_3.json */, ); path = ABTesting; sourceTree = ""; @@ -2493,7 +2490,6 @@ F39B67C82A3FBEA7005C0CCA /* ConfigWithABTypeNotInapps.json in Resources */, F39B67BC2A3FB9A8005C0CCA /* ConfigWithABNoSalt.json in Resources */, 31ED2DF325C4456600301FAD /* TestConfig_Invalid_1.plist in Resources */, - F39B67B42A3C8B1D005C0CCA /* ConfigWithAB_3.json in Resources */, A153E04129BB0A8B003C34D4 /* InAppConfigurationWithOperations.json in Resources */, F31801FF2A386BE60021774C /* InappConfigResponseMonitoringInvalid.json in Resources */, 31ED2DF225C4456600301FAD /* TestConfig_Invalid_2.plist in Resources */, diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index 86492b02..e723fa62 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4710 + 4727 diff --git a/MindboxTests/InApp/Tests/ABTesting/ABTests.swift b/MindboxTests/InApp/Tests/ABTesting/ABTests.swift index bb58457a..3a4d354a 100644 --- a/MindboxTests/InApp/Tests/ABTesting/ABTests.swift +++ b/MindboxTests/InApp/Tests/ABTesting/ABTests.swift @@ -10,832 +10,832 @@ import Foundation import XCTest @testable import Mindbox -//class ABTests: XCTestCase { -// -// var container = try! TestDependencyProvider() -// -// var sessionTemporaryStorage: SessionTemporaryStorage { -// container.sessionTemporaryStorage -// } -// -// var persistenceStorage: PersistenceStorage { -// container.persistenceStorage -// } -// -// var networkFetcher: NetworkFetcher { -// container.instanceFactory.makeNetworkFetcher() -// } -// -// var sdkVersionValidator: SDKVersionValidator! -// -// private var mapper: InAppConfigutationMapper! -// private let configStub = InAppConfigStub() -// private let targetingChecker: InAppTargetingCheckerProtocol = InAppTargetingChecker() -// private var shownInAppsIds: Set! -// -// override func setUp() { -// super.setUp() -// sdkVersionValidator = SDKVersionValidator(sdkVersionNumeric: 6) -// mapper = InAppConfigutationMapper(geoService: container.geoService, -// segmentationService: container.segmentationSevice, -// customerSegmentsAPI: .live, -// targetingChecker: targetingChecker, -// sessionTemporaryStorage: sessionTemporaryStorage, -// persistenceStorage: persistenceStorage, -// sdkVersionValidator: sdkVersionValidator, -// imageDownloadService: container.imageDownloadService, -// abTestDeviceMixer: container.abTestDeviceMixer) -// shownInAppsIds = Set(persistenceStorage.shownInAppsIds ?? []) -// } -// -// func test_no_abtests() throws { -// let response = try getConfig(name: "ConfigWithAB_1") -// let abTests: [ABTest]? = nil -// let inapps = mapper.filterInappsByABTests(abTests, responseInapps: response.inapps?.elements) -// XCTAssertEqual(inapps.count, 3) -// let expectedIds = [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// -// runInAppTestForUUID("BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", -// abTests: abTests, -// responseInapps: response.inapps?.elements, -// expectedCount: 3, expectedIds: expectedIds) -// } -// -// func test_compare_inapps_with_cg() throws { -// let response = try getConfig(name: "ConfigWithAB_1") -// let abTests: [ABTest]? = [ -// ABTest( -// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", -// sdkVersion: SdkVersion(min: 6, max: nil), -// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", -// variants: [ -// ABTest.ABTestVariant( -// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", -// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 50), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "211f1c16-fa72-4456-bf87-af448eb84a32", -// modulus: ABTest.ABTestVariant.Modulus(lower: 50, upper: 100), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .all, -// inapps: nil -// ) -// ] -// ) -// ] -// ) -// ] -// -// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 0, expectedIds: []) // 25 -// -// let expectedIds = [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 3, expectedIds: expectedIds) // 75 -// } -// -// func test_compare_cg_and_concrete_inapps() throws { -// let response = try getConfig(name: "ConfigWithAB_2") -// let abTests: [ABTest]? = [ -// ABTest( -// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", -// sdkVersion: SdkVersion(min: 6, max: nil), -// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", -// variants: [ -// ABTest.ABTestVariant( -// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", -// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 50), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "211f1c16-fa72-4456-bf87-af448eb84a32", -// modulus: ABTest.ABTestVariant.Modulus(lower: 50, upper: 100), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// ) -// ] -// ) -// ] -// ) -// ] -// -// // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 2 -// let expectedIds1 = [ -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", -// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" -// ] -// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 2, expectedIds: expectedIds1) -// -// // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 4 -// let expectedIds2 = [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", -// "b33ca779-3c99-481f-ad46-91282b0caf04", -// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" -// ] -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 4, expectedIds: expectedIds2) -// } -// -// func test_compare_cg_and_concrete_inapps_and_all_inapps() throws { -// let response = try getConfig(name: "ConfigWithAB_1") -// let abTests: [ABTest]? = [ -// ABTest( -// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", -// sdkVersion: SdkVersion(min: 6, max: nil), -// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", -// variants: [ -// ABTest.ABTestVariant( -// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", -// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 30), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "211f1c16-fa72-4456-bf87-af448eb84a32", -// modulus: ABTest.ABTestVariant.Modulus(lower: 30, upper: 65), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "36e69720-8e73-447c-b172-7b17e2d73525", -// modulus: ABTest.ABTestVariant.Modulus(lower: 65, upper: 100), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .all, -// inapps: nil -// ) -// ] -// ) -// ] -// ) -// ] -// -// // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 0 -// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 0, expectedIds: []) -// -// // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 2 -// let expectedIds1 = [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 2, expectedIds: expectedIds1) -// -// // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 -// let expectedIds2 = [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 3, expectedIds: expectedIds2) -// } -// -// func test_compare_2branch_and_concrete_inapps_and_cg() throws { -// let response = try getConfig(name: "ConfigWithAB_2") -// let abTests: [ABTest]? = [ -// ABTest( -// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", -// sdkVersion: SdkVersion(min: 6, max: nil), -// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", -// variants: [ -// ABTest.ABTestVariant( -// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", -// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 27), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "211f1c16-fa72-4456-bf87-af448eb84a32", -// modulus: ABTest.ABTestVariant.Modulus(lower: 27, upper: 65), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d" -// ] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "36e69720-8e73-447c-b172-7b17e2d73525", -// modulus: ABTest.ABTestVariant.Modulus(lower: 65, upper: 100), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// ) -// ] -// ) -// ] -// ) -// ] -// -// // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 2 -// let expectedIds1 = [ -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", -// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" -// ] -// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) -// -// // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 -// let expectedIds2 = [ -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", -// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b", -// "655f5ffa-de86-4224-a0bf-229fe208ed0d" -// ] -// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) -// -// // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 -// let expectedIds3 = [ -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", -// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds3.count, expectedIds: expectedIds3) -// } -// -// func test_compare_2branch_and_concrete_inapps() throws { -// let response = try getConfig(name: "ConfigWithAB_1") -// let abTests: [ABTest]? = [ -// ABTest( -// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", -// sdkVersion: SdkVersion(min: 6, max: nil), -// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", -// variants: [ -// ABTest.ABTestVariant( -// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", -// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 99), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "211f1c16-fa72-4456-bf87-af448eb84a32", -// modulus: ABTest.ABTestVariant.Modulus(lower: 99, upper: 100), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" -// ] -// ) -// ] -// ) -// ] -// ) -// ] -// -// // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 2 -// let expectedIds1 = [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) -// -// // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 -// let expectedIds2 = [ -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" -// ] -// runInAppTestForUUID("C9F84B44-01B9-A3C6-E85B-7FF816D3BA68", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) -// } -// -// func test_aab_show_inapps_in_cg_branch() throws { -// let response = try getConfig(name: "ConfigWithAB_1") -// let abTests: [ABTest]? = [ -// ABTest( -// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", -// sdkVersion: SdkVersion(min: 6, max: nil), -// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", -// variants: [ -// ABTest.ABTestVariant( -// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", -// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 33), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "211f1c16-fa72-4456-bf87-af448eb84a32", -// modulus: ABTest.ABTestVariant.Modulus(lower: 33, upper: 66), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "36e69720-8e73-447c-b172-7b17e2d73525", -// modulus: ABTest.ABTestVariant.Modulus(lower: 66, upper: 100), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// ) -// ] -// ) -// ] -// ) -// ] -// -// // Test case for UUID "4862ADF1-1392-9362-42A2-FF5A65629F50" with expected inapp count of 2 -// let expectedIds1 = [ -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" -// ] -// runInAppTestForUUID("4862ADF1-1392-9362-42A2-FF5A65629F50", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 -// -// // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 -// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 45 -// -// let expectedIds2 = [ -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 -// } -// -// func test_aab_not_show_inapps_in_cg_branch() throws { -// let response = try getConfig(name: "ConfigWithAB_1") -// let abTests: [ABTest]? = [ -// ABTest( -// id: "c0e2682c-3d0f-4291-9308-9e48a16eb3c8", -// sdkVersion: SdkVersion(min: 6, max: nil), -// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", -// variants: [ -// ABTest.ABTestVariant( -// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", -// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 33), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "211f1c16-fa72-4456-bf87-af448eb84a32", -// modulus: ABTest.ABTestVariant.Modulus(lower: 33, upper: 66), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "36e69720-8e73-447c-b172-7b17e2d73525", -// modulus: ABTest.ABTestVariant.Modulus(lower: 66, upper: 100), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "b33ca779-3c99-481f-ad46-91282b0caf04", -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" -// ] -// ) -// ] -// ) -// ] -// ) -// ] -// -// // Test case for UUID "4862ADF1-1392-9362-42A2-FF5A65629F50" with expected inapp count of 2 -// let expectedIds1 = [String]() -// runInAppTestForUUID("4862ADF1-1392-9362-42A2-FF5A65629F50", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 -// -// // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 -// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 45 -// -// let expectedIds2 = [ -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 -// } -// -// func test_ab_limit_value_check() throws { -// let response = try getConfig(name: "ConfigWithAB_1") -// let abTests: [ABTest]? = [ -// ABTest( -// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", -// sdkVersion: SdkVersion(min: 6, max: nil), -// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", -// variants: [ -// ABTest.ABTestVariant( -// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", -// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 33), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "211f1c16-fa72-4456-bf87-af448eb84a32", -// modulus: ABTest.ABTestVariant.Modulus(lower: 33, upper: 99), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d" -// ] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "36e69720-8e73-447c-b172-7b17e2d73525", -// modulus: ABTest.ABTestVariant.Modulus(lower: 99, upper: 100), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// ) -// ] -// ) -// ] -// ) -// ] -// -// // Test case for UUID "F3AB8877-CB55-CE3D-1AB3-230D2EA8A220" with expected inapp count of 2 -// let expectedIds1 = ["6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] -// runInAppTestForUUID("F3AB8877-CB55-CE3D-1AB3-230D2EA8A220", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 0 -// -// let expectedIds2 = [ -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// ] -// // Test case for UUID "3A9F107E-9FBE-8D83-EFE9-5F093001CD54" with expected inapp count of 3 -// runInAppTestForUUID("3A9F107E-9FBE-8D83-EFE9-5F093001CD54", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 33 -// -// let expectedIds3 = [ -// "b33ca779-3c99-481f-ad46-91282b0caf04", -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", -// ] -// // Test case for UUID "59B675D6-9AF1-1805-2BB3-90C3CF11E5E0" with expected inapp count of 3 -// runInAppTestForUUID("59B675D6-9AF1-1805-2BB3-90C3CF11E5E0", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 99 -// } -// -// func test_compare_5_ab_tests_in_one_branch() throws { -// let response = try getConfig(name: "ConfigWithAB_2") -// let abTests: [ABTest]? = [ -// ABTest( -// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", -// sdkVersion: SdkVersion(min: 6, max: nil), -// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", -// variants: [ -// ABTest.ABTestVariant( -// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", -// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 10), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d" -// ] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "211f1c16-fa72-4456-bf87-af448eb84a32", -// modulus: ABTest.ABTestVariant.Modulus(lower: 10, upper: 20), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" -// ] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "36e69720-8e73-447c-b172-7b17e2d73525", -// modulus: ABTest.ABTestVariant.Modulus(lower: 20, upper: 30), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "479b3748-747e-476f-afcd-7a9ce3f0ec71", -// modulus: ABTest.ABTestVariant.Modulus(lower: 30, upper: 70), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" -// ] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "5fb3e501-11c2-418d-b774-2ee26d31f556", -// modulus: ABTest.ABTestVariant.Modulus(lower: 70, upper: 100), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .all, -// inapps: nil -// ) -// ] -// ) -// ] -// ) -// ] -// -// // Test case for UUID "7544976E-FEA4-6A48-EB5D-85A6EEB4D306" with expected inapp count of 21 -// let expectedIds1 = ["655f5ffa-de86-4224-a0bf-229fe208ed0d"] -// runInAppTestForUUID("7544976E-FEA4-6A48-EB5D-85A6EEB4D306", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 5 -// -// let expectedIds2 = ["6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] -// // Test case for UUID "618F8CA3-282D-5B18-7186-F2CF361ABD32" with expected inapp count of 1 -// runInAppTestForUUID("618F8CA3-282D-5B18-7186-F2CF361ABD32", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 15 -// -// let expectedIds3 = ["b33ca779-3c99-481f-ad46-91282b0caf04"] -// runInAppTestForUUID("DC0F2330-785B-5D80-CD34-F2F520AD618F", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 25 -// -// let expectedIds4 = ["d1b312bd-aa5c-414c-a0d8-8126376a2a9b"] -// runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds4.count, expectedIds: expectedIds4) // 45 -// -// let expectedIds5 = [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", -// "b33ca779-3c99-481f-ad46-91282b0caf04", -// "d1b312bd-aa5c-414c-a0d8-8126376a2a9b"] -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds5.count, expectedIds: expectedIds5) // 75 -// } -// -// func test_2_different_ab_tests_one() throws { -// let response = try getConfig(name: "ConfigWithAB_1") -// let abtests: [ABTest]? = [ -// ABTest( -// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", -// sdkVersion: SdkVersion(min: 6, max: nil), -// salt: "c0e2682c-3d0f-4291-9308-9e48a16eb3c8", -// variants: [ -// ABTest.ABTestVariant( -// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", -// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 25), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "211f1c16-fa72-4456-bf87-af448eb84a32", -// modulus: ABTest.ABTestVariant.Modulus(lower: 25, upper: 100), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .all, -// inapps: nil -// ) -// ] -// ) -// ] -// ), -// ABTest( -// id: "1ec6be6b-421f-464b-9ee4-348a5292a5fd", -// sdkVersion: SdkVersion(min: 6, max: nil), -// salt: "b142ff09-68c4-41f9-985d-d220edfad4f", -// variants: [ -// ABTest.ABTestVariant( -// id: "36e69720-8e73-447c-b172-7b17e2d73525", -// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 75), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "479b3748-747e-476f-afcd-7a9ce3f0ec71", -// modulus: ABTest.ABTestVariant.Modulus(lower: 75, upper: 100), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" -// ] -// ) -// ] -// ) -// ] -// ) -// ] -// -// let expectedIds1 = [String]() -// runInAppTestForUUID("9d7f8e6c-3a2b-4d9a-b1c0-1e1e3a4b5c6d", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 23 and 29 -// -// runInAppTestForUUID("284c10f7-4f4c-4a1b-92e0-2318f2ae13c9", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 1 and 91 -// -// let expectedIds2 = ["b33ca779-3c99-481f-ad46-91282b0caf04"] -// runInAppTestForUUID("677a789d-9a98-4f03-9cb2-af2563fc1d07", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 88 and 5 -// -// let expectedIds3 = ["b33ca779-3c99-481f-ad46-91282b0caf04", -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] -// runInAppTestForUUID("d35df6c2-9e20-4f7e-9e20-51894e2c4810", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 61 and 94 -// } -// -// func test_2_different_ab_tests_two() throws { -//// XCTAssertTrue(false) -// } -// -// func test_concrete_inapps_and_all() throws { -// let response = try getConfig(name: "ConfigWithAB_1") -// let abtests: [ABTest]? = [ -// ABTest( -// id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", -// sdkVersion: SdkVersion(min: 6, max: nil), -// salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", -// variants: [ -// ABTest.ABTestVariant( -// id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", -// modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 35), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .concrete, -// inapps: [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// ) -// ] -// ), -// ABTest.ABTestVariant( -// id: "211f1c16-fa72-4456-bf87-af448eb84a32", -// modulus: ABTest.ABTestVariant.Modulus(lower: 35, upper: 100), -// objects: [ -// ABTest.ABTestVariant.ABTestObject( -// type: .inapps, -// kind: .all, -// inapps: nil -// ) -// ] -// ) -// ] -// ) -// ] -// -// // Test case for UUID "F3AB8877-CB55-CE3D-1AB3-230D2EA8A220" with expected inapp count of 2 -// let expectedIds1 = [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "b33ca779-3c99-481f-ad46-91282b0caf04" -// ] -// -// runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 -// -// let expectedIds2 = [ -// "655f5ffa-de86-4224-a0bf-229fe208ed0d", -// "b33ca779-3c99-481f-ad46-91282b0caf04", -// "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" -// ] -// -// runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 -// } -// -// func test_section_ab_broken_return_nil() throws { -// let response = try getConfig(name: "ConfigWithABBrokenRange") // Отсутствует диапазон 33-66 -// XCTAssertNil(response.abtests) -// -// let response2 = try getConfig(name: "ConfigWithABNoSalt") // Отсутствует соль -// XCTAssertNil(response2.abtests) -// -// let response3 = try getConfig(name: "ConfigWithABUnexpectedValue") // Неожиданное ключ-значение -// XCTAssertNil(response3.abtests) -// -// let response4 = try getConfig(name: "ConfigWithABCrossRange") // Неожиданное ключ-значение -// XCTAssertNil(response4.abtests) -// -// let response5 = try getConfig(name: "ConfigWithABNoUpper") // Отсутствует ключ upper в одном из вариантов -// XCTAssertNil(response5.abtests) -// -// let response6 = try getConfig(name: "ConfigWithABLowerBiggerThanUpper") // Lower больше, чем Upper -// XCTAssertNil(response6.abtests) -// } -// -// func test_sdkversion_lower_than_ab_tests_version() throws { -// let response = try getConfig(name: "ConfigWithABNormal") -// XCTAssertNil(response.abtests) -// } -// -// func test_ab_test_type_not_inapps_return_nil() throws { -// let response = try getConfig(name: "ConfigWithABTypeNotInapps") -// XCTAssertNil(response.abtests) -// } -//} -// -//private extension ABTests { -// private func getConfig(name: String) throws -> ConfigResponse { -// let bundle = Bundle(for: ABTests.self) -// let fileURL = bundle.url(forResource: name, withExtension: "json")! -// let data = try Data(contentsOf: fileURL) -// return try JSONDecoder().decode(ConfigResponse.self, from: data) -// } -// -// private func runInAppTestForUUID(_ uuid: String, abTests: [ABTest]?, responseInapps: [InApp]?, expectedCount: Int, expectedIds: [String]) { -// persistenceStorage.deviceUUID = uuid -// let inapps = mapper.filterInappsByABTests(abTests, responseInapps: responseInapps) -// XCTAssertEqual(inapps.count, expectedCount) -// for inapp in inapps { -// XCTAssertTrue(expectedIds.contains { $0 == inapp.id }) -// } -// } -//} +class ABTests: XCTestCase { + + var container = try! TestDependencyProvider() + + var sessionTemporaryStorage: SessionTemporaryStorage { + container.sessionTemporaryStorage + } + + var persistenceStorage: PersistenceStorage { + container.persistenceStorage + } + + var networkFetcher: NetworkFetcher { + container.instanceFactory.makeNetworkFetcher() + } + + var sdkVersionValidator: SDKVersionValidator! + + private var mapper: InAppConfigutationMapper! + private let configStub = InAppConfigStub() + private let targetingChecker: InAppTargetingCheckerProtocol = InAppTargetingChecker() + private var shownInAppsIds: Set! + + override func setUp() { + super.setUp() + sdkVersionValidator = SDKVersionValidator(sdkVersionNumeric: 6) + mapper = InAppConfigutationMapper(geoService: container.geoService, + segmentationService: container.segmentationSevice, + customerSegmentsAPI: .live, + targetingChecker: targetingChecker, + sessionTemporaryStorage: sessionTemporaryStorage, + persistenceStorage: persistenceStorage, + sdkVersionValidator: sdkVersionValidator, + imageDownloadService: container.imageDownloadService, + abTestDeviceMixer: container.abTestDeviceMixer) + shownInAppsIds = Set(persistenceStorage.shownInAppsIds ?? []) + } + + func test_no_abtests() throws { + let response = try getConfig(name: "ConfigWithAB_1") + let abTests: [ABTest]? = nil + let inapps = mapper.filterInappsByABTests(abTests, responseInapps: response.inapps?.elements) + XCTAssertEqual(inapps.count, 3) + let expectedIds = [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + + runInAppTestForUUID("BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", + abTests: abTests, + responseInapps: response.inapps?.elements, + expectedCount: 3, expectedIds: expectedIds) + } + + func test_compare_inapps_with_cg() throws { + let response = try getConfig(name: "ConfigWithAB_1") + let abTests: [ABTest]? = [ + ABTest( + id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", + sdkVersion: SdkVersion(min: 6, max: nil), + salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", + variants: [ + ABTest.ABTestVariant( + id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", + modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 50), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [] + ) + ] + ), + ABTest.ABTestVariant( + id: "211f1c16-fa72-4456-bf87-af448eb84a32", + modulus: ABTest.ABTestVariant.Modulus(lower: 50, upper: 100), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .all, + inapps: nil + ) + ] + ) + ] + ) + ] + + runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 0, expectedIds: []) // 25 + + let expectedIds = [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + + runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 3, expectedIds: expectedIds) // 75 + } + + func test_compare_cg_and_concrete_inapps() throws { + let response = try getConfig(name: "ConfigWithAB_2") + let abTests: [ABTest]? = [ + ABTest( + id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", + sdkVersion: SdkVersion(min: 6, max: nil), + salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", + variants: [ + ABTest.ABTestVariant( + id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", + modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 50), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [] + ) + ] + ), + ABTest.ABTestVariant( + id: "211f1c16-fa72-4456-bf87-af448eb84a32", + modulus: ABTest.ABTestVariant.Modulus(lower: 50, upper: 100), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + ) + ] + ) + ] + ) + ] + + // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 2 + let expectedIds1 = [ + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", + "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" + ] + runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 2, expectedIds: expectedIds1) + + // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 4 + let expectedIds2 = [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", + "b33ca779-3c99-481f-ad46-91282b0caf04", + "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" + ] + runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 4, expectedIds: expectedIds2) + } + + func test_compare_cg_and_concrete_inapps_and_all_inapps() throws { + let response = try getConfig(name: "ConfigWithAB_1") + let abTests: [ABTest]? = [ + ABTest( + id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", + sdkVersion: SdkVersion(min: 6, max: nil), + salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", + variants: [ + ABTest.ABTestVariant( + id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", + modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 30), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [] + ) + ] + ), + ABTest.ABTestVariant( + id: "211f1c16-fa72-4456-bf87-af448eb84a32", + modulus: ABTest.ABTestVariant.Modulus(lower: 30, upper: 65), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + ) + ] + ), + ABTest.ABTestVariant( + id: "36e69720-8e73-447c-b172-7b17e2d73525", + modulus: ABTest.ABTestVariant.Modulus(lower: 65, upper: 100), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .all, + inapps: nil + ) + ] + ) + ] + ) + ] + + // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 0 + runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 0, expectedIds: []) + + // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 2 + let expectedIds1 = [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 2, expectedIds: expectedIds1) + + // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 + let expectedIds2 = [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: 3, expectedIds: expectedIds2) + } + + func test_compare_2branch_and_concrete_inapps_and_cg() throws { + let response = try getConfig(name: "ConfigWithAB_2") + let abTests: [ABTest]? = [ + ABTest( + id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", + sdkVersion: SdkVersion(min: 6, max: nil), + salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", + variants: [ + ABTest.ABTestVariant( + id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", + modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 27), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [] + ) + ] + ), + ABTest.ABTestVariant( + id: "211f1c16-fa72-4456-bf87-af448eb84a32", + modulus: ABTest.ABTestVariant.Modulus(lower: 27, upper: 65), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d" + ] + ) + ] + ), + ABTest.ABTestVariant( + id: "36e69720-8e73-447c-b172-7b17e2d73525", + modulus: ABTest.ABTestVariant.Modulus(lower: 65, upper: 100), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + ) + ] + ) + ] + ) + ] + + // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 2 + let expectedIds1 = [ + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", + "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" + ] + runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) + + // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 + let expectedIds2 = [ + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", + "d1b312bd-aa5c-414c-a0d8-8126376a2a9b", + "655f5ffa-de86-4224-a0bf-229fe208ed0d" + ] + runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) + + // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 + let expectedIds3 = [ + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", + "d1b312bd-aa5c-414c-a0d8-8126376a2a9b", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds3.count, expectedIds: expectedIds3) + } + + func test_compare_2branch_and_concrete_inapps() throws { + let response = try getConfig(name: "ConfigWithAB_1") + let abTests: [ABTest]? = [ + ABTest( + id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", + sdkVersion: SdkVersion(min: 6, max: nil), + salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", + variants: [ + ABTest.ABTestVariant( + id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", + modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 99), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + ) + ] + ), + ABTest.ABTestVariant( + id: "211f1c16-fa72-4456-bf87-af448eb84a32", + modulus: ABTest.ABTestVariant.Modulus(lower: 99, upper: 100), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" + ] + ) + ] + ) + ] + ) + ] + + // Test case for UUID "4078E211-7C3F-C607-D35C-DC6B591EF355" with expected inapp count of 2 + let expectedIds1 = [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) + + // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 + let expectedIds2 = [ + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" + ] + runInAppTestForUUID("C9F84B44-01B9-A3C6-E85B-7FF816D3BA68", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) + } + + func test_aab_show_inapps_in_cg_branch() throws { + let response = try getConfig(name: "ConfigWithAB_1") + let abTests: [ABTest]? = [ + ABTest( + id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", + sdkVersion: SdkVersion(min: 6, max: nil), + salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", + variants: [ + ABTest.ABTestVariant( + id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", + modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 33), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [] + ) + ] + ), + ABTest.ABTestVariant( + id: "211f1c16-fa72-4456-bf87-af448eb84a32", + modulus: ABTest.ABTestVariant.Modulus(lower: 33, upper: 66), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [] + ) + ] + ), + ABTest.ABTestVariant( + id: "36e69720-8e73-447c-b172-7b17e2d73525", + modulus: ABTest.ABTestVariant.Modulus(lower: 66, upper: 100), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + ) + ] + ) + ] + ) + ] + + // Test case for UUID "4862ADF1-1392-9362-42A2-FF5A65629F50" with expected inapp count of 2 + let expectedIds1 = [ + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" + ] + runInAppTestForUUID("4862ADF1-1392-9362-42A2-FF5A65629F50", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 + + // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 + runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 45 + + let expectedIds2 = [ + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 + runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 + } + + func test_aab_not_show_inapps_in_cg_branch() throws { + let response = try getConfig(name: "ConfigWithAB_1") + let abTests: [ABTest]? = [ + ABTest( + id: "c0e2682c-3d0f-4291-9308-9e48a16eb3c8", + sdkVersion: SdkVersion(min: 6, max: nil), + salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", + variants: [ + ABTest.ABTestVariant( + id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", + modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 33), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [] + ) + ] + ), + ABTest.ABTestVariant( + id: "211f1c16-fa72-4456-bf87-af448eb84a32", + modulus: ABTest.ABTestVariant.Modulus(lower: 33, upper: 66), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [] + ) + ] + ), + ABTest.ABTestVariant( + id: "36e69720-8e73-447c-b172-7b17e2d73525", + modulus: ABTest.ABTestVariant.Modulus(lower: 66, upper: 100), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "b33ca779-3c99-481f-ad46-91282b0caf04", + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" + ] + ) + ] + ) + ] + ) + ] + + // Test case for UUID "4862ADF1-1392-9362-42A2-FF5A65629F50" with expected inapp count of 2 + let expectedIds1 = [String]() + runInAppTestForUUID("4862ADF1-1392-9362-42A2-FF5A65629F50", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 + + // Test case for UUID "0809B0F8-8F21-18E8-2EF8-EC2D98938A84" with expected inapp count of 3 + runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 45 + + let expectedIds2 = [ + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + // Test case for UUID "4D27710A-3F3A-FF6E-7764-375B1E06E05D" with expected inapp count of 3 + runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 + } + + func test_ab_limit_value_check() throws { + let response = try getConfig(name: "ConfigWithAB_1") + let abTests: [ABTest]? = [ + ABTest( + id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", + sdkVersion: SdkVersion(min: 6, max: nil), + salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", + variants: [ + ABTest.ABTestVariant( + id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", + modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 33), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [] + ) + ] + ), + ABTest.ABTestVariant( + id: "211f1c16-fa72-4456-bf87-af448eb84a32", + modulus: ABTest.ABTestVariant.Modulus(lower: 33, upper: 99), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d" + ] + ) + ] + ), + ABTest.ABTestVariant( + id: "36e69720-8e73-447c-b172-7b17e2d73525", + modulus: ABTest.ABTestVariant.Modulus(lower: 99, upper: 100), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + ) + ] + ) + ] + ) + ] + + // Test case for UUID "F3AB8877-CB55-CE3D-1AB3-230D2EA8A220" with expected inapp count of 2 + let expectedIds1 = ["6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] + runInAppTestForUUID("F3AB8877-CB55-CE3D-1AB3-230D2EA8A220", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 0 + + let expectedIds2 = [ + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + ] + // Test case for UUID "3A9F107E-9FBE-8D83-EFE9-5F093001CD54" with expected inapp count of 3 + runInAppTestForUUID("3A9F107E-9FBE-8D83-EFE9-5F093001CD54", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 33 + + let expectedIds3 = [ + "b33ca779-3c99-481f-ad46-91282b0caf04", + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", + ] + // Test case for UUID "59B675D6-9AF1-1805-2BB3-90C3CF11E5E0" with expected inapp count of 3 + runInAppTestForUUID("59B675D6-9AF1-1805-2BB3-90C3CF11E5E0", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 99 + } + + func test_compare_5_ab_tests_in_one_branch() throws { + let response = try getConfig(name: "ConfigWithAB_2") + let abTests: [ABTest]? = [ + ABTest( + id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", + sdkVersion: SdkVersion(min: 6, max: nil), + salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", + variants: [ + ABTest.ABTestVariant( + id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", + modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 10), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d" + ] + ) + ] + ), + ABTest.ABTestVariant( + id: "211f1c16-fa72-4456-bf87-af448eb84a32", + modulus: ABTest.ABTestVariant.Modulus(lower: 10, upper: 20), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" + ] + ) + ] + ), + ABTest.ABTestVariant( + id: "36e69720-8e73-447c-b172-7b17e2d73525", + modulus: ABTest.ABTestVariant.Modulus(lower: 20, upper: 30), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + ) + ] + ), + ABTest.ABTestVariant( + id: "479b3748-747e-476f-afcd-7a9ce3f0ec71", + modulus: ABTest.ABTestVariant.Modulus(lower: 30, upper: 70), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "d1b312bd-aa5c-414c-a0d8-8126376a2a9b" + ] + ) + ] + ), + ABTest.ABTestVariant( + id: "5fb3e501-11c2-418d-b774-2ee26d31f556", + modulus: ABTest.ABTestVariant.Modulus(lower: 70, upper: 100), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .all, + inapps: nil + ) + ] + ) + ] + ) + ] + + // Test case for UUID "7544976E-FEA4-6A48-EB5D-85A6EEB4D306" with expected inapp count of 21 + let expectedIds1 = ["655f5ffa-de86-4224-a0bf-229fe208ed0d"] + runInAppTestForUUID("7544976E-FEA4-6A48-EB5D-85A6EEB4D306", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 5 + + let expectedIds2 = ["6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] + // Test case for UUID "618F8CA3-282D-5B18-7186-F2CF361ABD32" with expected inapp count of 1 + runInAppTestForUUID("618F8CA3-282D-5B18-7186-F2CF361ABD32", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 15 + + let expectedIds3 = ["b33ca779-3c99-481f-ad46-91282b0caf04"] + runInAppTestForUUID("DC0F2330-785B-5D80-CD34-F2F520AD618F", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 25 + + let expectedIds4 = ["d1b312bd-aa5c-414c-a0d8-8126376a2a9b"] + runInAppTestForUUID("0809B0F8-8F21-18E8-2EF8-EC2D98938A84", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds4.count, expectedIds: expectedIds4) // 45 + + let expectedIds5 = [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", + "b33ca779-3c99-481f-ad46-91282b0caf04", + "d1b312bd-aa5c-414c-a0d8-8126376a2a9b"] + runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abTests, responseInapps: response.inapps?.elements, expectedCount: expectedIds5.count, expectedIds: expectedIds5) // 75 + } + + func test_2_different_ab_tests_one() throws { + let response = try getConfig(name: "ConfigWithAB_1") + let abtests: [ABTest]? = [ + ABTest( + id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", + sdkVersion: SdkVersion(min: 6, max: nil), + salt: "c0e2682c-3d0f-4291-9308-9e48a16eb3c8", + variants: [ + ABTest.ABTestVariant( + id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", + modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 25), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [] + ) + ] + ), + ABTest.ABTestVariant( + id: "211f1c16-fa72-4456-bf87-af448eb84a32", + modulus: ABTest.ABTestVariant.Modulus(lower: 25, upper: 100), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .all, + inapps: nil + ) + ] + ) + ] + ), + ABTest( + id: "1ec6be6b-421f-464b-9ee4-348a5292a5fd", + sdkVersion: SdkVersion(min: 6, max: nil), + salt: "b142ff09-68c4-41f9-985d-d220edfad4f", + variants: [ + ABTest.ABTestVariant( + id: "36e69720-8e73-447c-b172-7b17e2d73525", + modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 75), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [] + ) + ] + ), + ABTest.ABTestVariant( + id: "479b3748-747e-476f-afcd-7a9ce3f0ec71", + modulus: ABTest.ABTestVariant.Modulus(lower: 75, upper: 100), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" + ] + ) + ] + ) + ] + ) + ] + + let expectedIds1 = [String]() + runInAppTestForUUID("9d7f8e6c-3a2b-4d9a-b1c0-1e1e3a4b5c6d", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 23 and 29 + + runInAppTestForUUID("284c10f7-4f4c-4a1b-92e0-2318f2ae13c9", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 1 and 91 + + let expectedIds2 = ["b33ca779-3c99-481f-ad46-91282b0caf04"] + runInAppTestForUUID("677a789d-9a98-4f03-9cb2-af2563fc1d07", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 88 and 5 + + let expectedIds3 = ["b33ca779-3c99-481f-ad46-91282b0caf04", + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83"] + runInAppTestForUUID("d35df6c2-9e20-4f7e-9e20-51894e2c4810", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds3.count, expectedIds: expectedIds3) // 61 and 94 + } + + func test_2_different_ab_tests_two() throws { +// XCTAssertTrue(false) + } + + func test_concrete_inapps_and_all() throws { + let response = try getConfig(name: "ConfigWithAB_1") + let abtests: [ABTest]? = [ + ABTest( + id: "0ec6be6b-421f-464b-9ee4-348a5292a5fd", + sdkVersion: SdkVersion(min: 6, max: nil), + salt: "BBBC2BA1-0B5B-4C9E-AB0E-95C54775B4F1", + variants: [ + ABTest.ABTestVariant( + id: "155f5ffa-de86-4224-a0bf-229fe208ed0d", + modulus: ABTest.ABTestVariant.Modulus(lower: 0, upper: 35), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .concrete, + inapps: [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + ) + ] + ), + ABTest.ABTestVariant( + id: "211f1c16-fa72-4456-bf87-af448eb84a32", + modulus: ABTest.ABTestVariant.Modulus(lower: 35, upper: 100), + objects: [ + ABTest.ABTestVariant.ABTestObject( + type: .inapps, + kind: .all, + inapps: nil + ) + ] + ) + ] + ) + ] + + // Test case for UUID "F3AB8877-CB55-CE3D-1AB3-230D2EA8A220" with expected inapp count of 2 + let expectedIds1 = [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "b33ca779-3c99-481f-ad46-91282b0caf04" + ] + + runInAppTestForUUID("4078E211-7C3F-C607-D35C-DC6B591EF355", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds1.count, expectedIds: expectedIds1) // 25 + + let expectedIds2 = [ + "655f5ffa-de86-4224-a0bf-229fe208ed0d", + "b33ca779-3c99-481f-ad46-91282b0caf04", + "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" + ] + + runInAppTestForUUID("4D27710A-3F3A-FF6E-7764-375B1E06E05D", abTests: abtests, responseInapps: response.inapps?.elements, expectedCount: expectedIds2.count, expectedIds: expectedIds2) // 75 + } + + func test_section_ab_broken_return_nil() throws { + let response = try getConfig(name: "ConfigWithABBrokenRange") // Отсутствует диапазон 33-66 + XCTAssertNil(response.abtests) + + let response2 = try getConfig(name: "ConfigWithABNoSalt") // Отсутствует соль + XCTAssertNil(response2.abtests) + + let response3 = try getConfig(name: "ConfigWithABUnexpectedValue") // Неожиданное ключ-значение + XCTAssertNil(response3.abtests) + + let response4 = try getConfig(name: "ConfigWithABCrossRange") // Неожиданное ключ-значение + XCTAssertNil(response4.abtests) + + let response5 = try getConfig(name: "ConfigWithABNoUpper") // Отсутствует ключ upper в одном из вариантов + XCTAssertNil(response5.abtests) + + let response6 = try getConfig(name: "ConfigWithABLowerBiggerThanUpper") // Lower больше, чем Upper + XCTAssertNil(response6.abtests) + } + + func test_sdkversion_lower_than_ab_tests_version() throws { + let response = try getConfig(name: "ConfigWithABNormal") + XCTAssertNil(response.abtests) + } + + func test_ab_test_type_not_inapps_return_nil() throws { + let response = try getConfig(name: "ConfigWithABTypeNotInapps") + XCTAssertNil(response.abtests) + } +} + +private extension ABTests { + private func getConfig(name: String) throws -> ConfigResponse { + let bundle = Bundle(for: ABTests.self) + let fileURL = bundle.url(forResource: name, withExtension: "json")! + let data = try Data(contentsOf: fileURL) + return try JSONDecoder().decode(ConfigResponse.self, from: data) + } + + private func runInAppTestForUUID(_ uuid: String, abTests: [ABTest]?, responseInapps: [InApp]?, expectedCount: Int, expectedIds: [String]) { + persistenceStorage.deviceUUID = uuid + let inapps = mapper.filterInappsByABTests(abTests, responseInapps: responseInapps) + XCTAssertEqual(inapps.count, expectedCount) + for inapp in inapps { + XCTAssertTrue(expectedIds.contains { $0 == inapp.id }) + } + } +} diff --git a/MindboxTests/InApp/Tests/ABTesting/ConfigWithABTypeNotInapps.json b/MindboxTests/InApp/Tests/ABTesting/ConfigWithABTypeNotInapps.json index 76dfb10d..c93fb9a9 100644 --- a/MindboxTests/InApp/Tests/ABTesting/ConfigWithABTypeNotInapps.json +++ b/MindboxTests/InApp/Tests/ABTesting/ConfigWithABTypeNotInapps.json @@ -17,10 +17,26 @@ "form": { "variants": [ { - "imageUrl": "https://personalization-web-stable.mindbox.ru/user-media/29836/545000dde32c7396ae07c625ca1c57d332d0176b74186f04c661e952268ea3d1.png", - "redirectUrl": "", - "intentPayload": "", - "$type": "simpleImage" + "$type": "modal", + "content": { + "background": { + "layers": [ + { + "$type": "image", + "action": { + "$type": "redirectUrl", + "intentPayload": "", + "value": "" + }, + "source": { + "$type": "url", + "value": "https://personalization-web-stable.mindbox.ru/user-media/29836/545000dde32c7396ae07c625ca1c57d332d0176b74186f04c661e952268ea3d1.png" + } + } + ] + }, + "elements": [] + } } ] } @@ -44,10 +60,26 @@ "form": { "variants": [ { - "imageUrl": "https://personalization-web-stable.mindbox.ru/user-media/29836/c7a94e0c225fa8e3bcc8b6979abaab79fe8127cbecf9d96d9b20dc5c47dc0866.png", - "redirectUrl": "", - "intentPayload": "", - "$type": "simpleImage" + "$type": "modal", + "content": { + "background": { + "layers": [ + { + "$type": "image", + "action": { + "$type": "redirectUrl", + "intentPayload": "", + "value": "" + }, + "source": { + "$type": "url", + "value": "https://personalization-web-stable.mindbox.ru/user-media/29836/c7a94e0c225fa8e3bcc8b6979abaab79fe8127cbecf9d96d9b20dc5c47dc0866.png" + } + } + ] + }, + "elements": [] + } } ] } @@ -73,10 +105,26 @@ "form": { "variants": [ { - "imageUrl": "https://pibig.info/uploads/posts/2022-07/1657189902_1-pibig-info-p-kartinki-vertikalnie-na-telefon-1.jpg", - "redirectUrl": "", - "intentPayload": "44", - "$type": "simpleImage" + "$type": "modal", + "content": { + "background": { + "layers": [ + { + "$type": "image", + "action": { + "$type": "redirectUrl", + "intentPayload": "44", + "value": "" + }, + "source": { + "$type": "url", + "value": "https://pibig.info/uploads/posts/2022-07/1657189902_1-pibig-info-p-kartinki-vertikalnie-na-telefon-1.jpg" + } + } + ] + }, + "elements": [] + } } ] } diff --git a/MindboxTests/InApp/Tests/ABTesting/ConfigWithAB_1.json b/MindboxTests/InApp/Tests/ABTesting/ConfigWithAB_1.json index ace0ddf2..280dbbad 100644 --- a/MindboxTests/InApp/Tests/ABTesting/ConfigWithAB_1.json +++ b/MindboxTests/InApp/Tests/ABTesting/ConfigWithAB_1.json @@ -17,10 +17,26 @@ "form": { "variants": [ { - "imageUrl": "https://personalization-web-stable.mindbox.ru/user-media/29836/545000dde32c7396ae07c625ca1c57d332d0176b74186f04c661e952268ea3d1.png", - "redirectUrl": "", - "intentPayload": "", - "$type": "simpleImage" + "$type": "modal", + "content": { + "background": { + "layers": [ + { + "$type": "image", + "action": { + "$type": "redirectUrl", + "intentPayload": "", + "value": "" + }, + "source": { + "$type": "url", + "value": "https://personalization-web-stable.mindbox.ru/user-media/29836/545000dde32c7396ae07c625ca1c57d332d0176b74186f04c661e952268ea3d1.png" + } + } + ] + }, + "elements": [] + } } ] } @@ -44,10 +60,26 @@ "form": { "variants": [ { - "imageUrl": "https://personalization-web-stable.mindbox.ru/user-media/29836/c7a94e0c225fa8e3bcc8b6979abaab79fe8127cbecf9d96d9b20dc5c47dc0866.png", - "redirectUrl": "", - "intentPayload": "", - "$type": "simpleImage" + "$type": "modal", + "content": { + "background": { + "layers": [ + { + "$type": "image", + "action": { + "$type": "redirectUrl", + "intentPayload": "", + "value": "" + }, + "source": { + "$type": "url", + "value": "https://personalization-web-stable.mindbox.ru/user-media/29836/c7a94e0c225fa8e3bcc8b6979abaab79fe8127cbecf9d96d9b20dc5c47dc0866.png" + } + } + ] + }, + "elements": [] + } } ] } @@ -73,10 +105,26 @@ "form": { "variants": [ { - "imageUrl": "https://pibig.info/uploads/posts/2022-07/1657189902_1-pibig-info-p-kartinki-vertikalnie-na-telefon-1.jpg", - "redirectUrl": "", - "intentPayload": "44", - "$type": "simpleImage" + "$type": "modal", + "content": { + "background": { + "layers": [ + { + "$type": "image", + "action": { + "$type": "redirectUrl", + "intentPayload": "44", + "value": "" + }, + "source": { + "$type": "url", + "value": "https://pibig.info/uploads/posts/2022-07/1657189902_1-pibig-info-p-kartinki-vertikalnie-na-telefon-1.jpg" + } + } + ] + }, + "elements": [] + } } ] } diff --git a/MindboxTests/InApp/Tests/ABTesting/ConfigWithAB_2.json b/MindboxTests/InApp/Tests/ABTesting/ConfigWithAB_2.json index 815fa15a..75c4f2d6 100644 --- a/MindboxTests/InApp/Tests/ABTesting/ConfigWithAB_2.json +++ b/MindboxTests/InApp/Tests/ABTesting/ConfigWithAB_2.json @@ -17,10 +17,26 @@ "form": { "variants": [ { - "imageUrl": "https://personalization-web-stable.mindbox.ru/user-media/29836/545000dde32c7396ae07c625ca1c57d332d0176b74186f04c661e952268ea3d1.png", - "redirectUrl": "", - "intentPayload": "", - "$type": "simpleImage" + "$type": "modal", + "content": { + "background": { + "layers": [ + { + "$type": "image", + "action": { + "$type": "redirectUrl", + "intentPayload": "", + "value": "" + }, + "source": { + "$type": "url", + "value": "https://personalization-web-stable.mindbox.ru/user-media/29836/545000dde32c7396ae07c625ca1c57d332d0176b74186f04c661e952268ea3d1.png" + } + } + ] + }, + "elements": [] + } } ] } @@ -44,10 +60,26 @@ "form": { "variants": [ { - "imageUrl": "https://personalization-web-stable.mindbox.ru/user-media/29836/c7a94e0c225fa8e3bcc8b6979abaab79fe8127cbecf9d96d9b20dc5c47dc0866.png", - "redirectUrl": "", - "intentPayload": "", - "$type": "simpleImage" + "$type": "modal", + "content": { + "background": { + "layers": [ + { + "$type": "image", + "action": { + "$type": "redirectUrl", + "intentPayload": "", + "value": "" + }, + "source": { + "$type": "url", + "value": "https://personalization-web-stable.mindbox.ru/user-media/29836/c7a94e0c225fa8e3bcc8b6979abaab79fe8127cbecf9d96d9b20dc5c47dc0866.png" + } + } + ] + }, + "elements": [] + } } ] } @@ -73,10 +105,26 @@ "form": { "variants": [ { - "imageUrl": "https://pibig.info/uploads/posts/2022-07/1657189902_1-pibig-info-p-kartinki-vertikalnie-na-telefon-1.jpg", - "redirectUrl": "", - "intentPayload": "44", - "$type": "simpleImage" + "$type": "modal", + "content": { + "background": { + "layers": [ + { + "$type": "image", + "action": { + "$type": "redirectUrl", + "intentPayload": "44", + "value": "" + }, + "source": { + "$type": "url", + "value": "https://pibig.info/uploads/posts/2022-07/1657189902_1-pibig-info-p-kartinki-vertikalnie-na-telefon-1.jpg" + } + } + ] + }, + "elements": [] + } } ] } @@ -102,13 +150,29 @@ "form": { "variants": [ { - "imageUrl": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/45/Equus_quagga_burchellii_-_Etosha%2C_2014.jpg/1200px-Equus_quagga_burchellii_-_Etosha%2C_2014.jpg", - "redirectUrl": "", - "intentPayload": "", - "$type": "simpleImage" + "$type": "modal", + "content": { + "background": { + "layers": [ + { + "$type": "image", + "action": { + "$type": "redirectUrl", + "intentPayload": "", + "value": "" + }, + "source": { + "$type": "url", + "value": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/45/Equus_quagga_burchellii_-_Etosha%2C_2014.jpg/1200px-Equus_quagga_burchellii_-_Etosha%2C_2014.jpg" + } + } + ] + }, + "elements": [] + } } ] } } ] -} +} diff --git a/MindboxTests/InApp/Tests/ABTesting/ConfigWithAB_3.json b/MindboxTests/InApp/Tests/ABTesting/ConfigWithAB_3.json deleted file mode 100644 index 5acff668..00000000 --- a/MindboxTests/InApp/Tests/ABTesting/ConfigWithAB_3.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "inapps": [ - { - "id": "655f5ffa-de86-4224-a0bf-229fe208ed0d", - "sdkVersion": { - "min": 3, - "max": null - }, - "targeting": { - "nodes": [ - { - "$type": "true" - } - ], - "$type": "and" - }, - "form": { - "variants": [ - { - "imageUrl": "https://personalization-web-stable.mindbox.ru/user-media/29836/545000dde32c7396ae07c625ca1c57d332d0176b74186f04c661e952268ea3d1.png", - "redirectUrl": "", - "intentPayload": "", - "$type": "simpleImage" - } - ] - } - }, - { - "id": "6f93e2ef-0615-4e63-9c80-24bcb9e83b83", - "sdkVersion": { - "min": 4, - "max": null - }, - "targeting": { - "nodes": [ - { - "internalId": "93", - "systemName": "addAction", - "$type": "apiMethodCall" - } - ], - "$type": "and" - }, - "form": { - "variants": [ - { - "imageUrl": "https://personalization-web-stable.mindbox.ru/user-media/29836/c7a94e0c225fa8e3bcc8b6979abaab79fe8127cbecf9d96d9b20dc5c47dc0866.png", - "redirectUrl": "", - "intentPayload": "", - "$type": "simpleImage" - } - ] - } - }, - { - "id": "95b3a85e-376b-4a3b-9885-96b296d81b3b", - "sdkVersion": { - "min": 3, - "max": null - }, - "targeting": { - "nodes": [ - { - "kind": "negative", - "ids": [ - 783754 - ], - "$type": "country" - } - ], - "$type": "and" - }, - "form": { - "variants": [ - { - "imageUrl": "https://i.natgeofe.com/k/093c14b4-978e-41f7-b1aa-3aff5d1c608a/gray-wolf-closeup_3x4.jpg", - "redirectUrl": "", - "intentPayload": "", - "$type": "simpleImage" - } - ] - } - } - ], - "abtests": [ - { - "id": "0ec6be6b-421f-464b-9ee4-348a5292a5fd", - "sdkVersion": { - "min": 6, - "max": null - }, - "salt": "c0e2682c-3d0f-4291-9308-9e48a16eb3c8", - "variants": [ - { - "modulus": { - "lower": 0, - "upper": 10 - }, - "objects": [ - { - "$type": "inapps", - "kind": "concrete", - "inapps": [ - "655f5ffa-de86-4224-a0bf-229fe208ed0d" - ] - } - ] - }, - { - "modulus": { - "lower": 0, - "upper": 10 - }, - "objects": [ - { - "$type": "inapps", - "kind": "concrete", - "inapps": [ - "95b3a85e-376b-4a3b-9885-96b296d81b3b", - "6f93e2ef-0615-4e63-9c80-24bcb9e83b83" - ] - } - ] - } - ] - }, - { - "id": "1ec6be6b-421f-464b-9ee4-348a5292a5fd", - "sdkVersion": { - "min": 6, - "max": null - }, - "salt": "b142ff09-68c4-41f9-985d-d220edfad4f", - "variants": [ - { - "modulus": { - "lower": 0, - "upper": 10 - }, - "objects": [ - { - "$type": "inapps", - "kind": "concrete", - "inapps": [ - "95b3a85e-376b-4a3b-9885-96b296d81b3b" - ] - } - ] - }, - { - "modulus": { - "lower": 10, - "upper": 100 - }, - "objects": [ - { - "$type": "inapps", - "kind": "concrete", - "inapps": [] - } - ] - } - ] - } - ] -} From bde04466304687b125113b1402a4dcda9d85912c Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Wed, 9 Aug 2023 16:49:19 +0600 Subject: [PATCH 11/35] MBX-2706 Add Modal views and use cases --- Mindbox.xcodeproj/project.pbxproj | 172 +++++++++++++---- Mindbox/DI/DependencyProvider.swift | 9 +- .../InAppConfigutationMapper.swift | 26 ++- .../InAppMessages/Models/InAppFormData.swift | 7 +- .../InAppMessageViewController.swift | 121 ------------ .../InAppPresentationManager.swift | 181 +++++------------- .../UseCases/PresentationActionUseCase.swift | 48 +++++ .../PresentationDisplayUseCase.swift | 57 ++++++ .../Strategy/ModalPresentationStrategy.swift | 63 ++++++ .../PresentationStrategyProtocol.swift | 15 ++ .../Views/{ => CommonViews}/CrossView.swift | 10 +- .../CloseButtonElementFactory.swift | 48 +++++ .../ElementFactory/ElementFactory.swift | 14 ++ .../InAppImageOnlyView.swift | 19 +- .../LayersFactory/LayerFactory.swift | 33 ++++ .../Views/ModalView/ModalViewController.swift | 134 +++++++++++++ .../Views/ViewFactory/ModalViewFactory.swift | 26 +++ .../ViewFactory/ViewFactoryProtocol.swift | 17 ++ Mindbox/Info.plist | 2 +- Mindbox/Validators/String+Extensions.swift | 15 ++ 20 files changed, 705 insertions(+), 312 deletions(-) delete mode 100644 Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift create mode 100644 Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationActionUseCase.swift create mode 100644 Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift create mode 100644 Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/Strategy/ModalPresentationStrategy.swift create mode 100644 Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/Strategy/PresentationStrategyProtocol.swift rename Mindbox/InAppMessages/Presentation/Views/{ => CommonViews}/CrossView.swift (87%) create mode 100644 Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift create mode 100644 Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/ElementFactory.swift rename Mindbox/InAppMessages/Presentation/Views/{ => CommonViews}/InAppImageOnlyView.swift (57%) create mode 100644 Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/LayerFactory.swift create mode 100644 Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift create mode 100644 Mindbox/InAppMessages/Presentation/Views/ViewFactory/ModalViewFactory.swift create mode 100644 Mindbox/InAppMessages/Presentation/Views/ViewFactory/ViewFactoryProtocol.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 96ecd036..eb257337 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -195,7 +195,6 @@ 84FCD3B525CA0FD300D1E574 /* MockPersistenceStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FCD3B425CA0FD300D1E574 /* MockPersistenceStorage.swift */; }; 84FCD3B925CA109E00D1E574 /* MockNetworkFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FCD3B825CA109E00D1E574 /* MockNetworkFetcher.swift */; }; 84FCD3BD25CA10F600D1E574 /* SuccessResponse.json in Resources */ = {isa = PBXBuildFile; fileRef = 84FCD3BC25CA10F600D1E574 /* SuccessResponse.json */; }; - 9B24FAAA28C74B6A00F10B5D /* InAppPresentationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B24FAA928C74B6A00F10B5D /* InAppPresentationManager.swift */; }; 9B24FAAC28C74B8300F10B5D /* InAppConfigurationRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B24FAAB28C74B8300F10B5D /* InAppConfigurationRepository.swift */; }; 9B24FAAE28C74BA500F10B5D /* InAppCoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B24FAAD28C74BA500F10B5D /* InAppCoreManager.swift */; }; 9B24FAB128C74BD200F10B5D /* InAppConfigurationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B24FAB028C74BD200F10B5D /* InAppConfigurationManager.swift */; }; @@ -208,8 +207,6 @@ 9B4F9DF328D088A0002C9CF0 /* InAppFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B4F9DF128D088A0002C9CF0 /* InAppFormData.swift */; }; 9B4F9DF828D088A9002C9CF0 /* InAppConfigutationMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B4F9DF528D088A8002C9CF0 /* InAppConfigutationMapper.swift */; }; 9B4F9DF928D088A9002C9CF0 /* InAppConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B4F9DF628D088A9002C9CF0 /* InAppConfig.swift */; }; - 9B4F9DFB28D088AE002C9CF0 /* InAppMessageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B4F9DFA28D088AE002C9CF0 /* InAppMessageViewController.swift */; }; - 9B4F9DFE28D088B5002C9CF0 /* InAppImageOnlyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B4F9DFD28D088B4002C9CF0 /* InAppImageOnlyView.swift */; }; 9B4F9E0528D0945F002C9CF0 /* InAppCoreManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B4F9E0428D0945F002C9CF0 /* InAppCoreManagerMock.swift */; }; 9B52570728D1AF880029B1BC /* InAppPresentationManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B52570628D1AF880029B1BC /* InAppPresentationManagerMock.swift */; }; 9B9C95312921116F00BB29DA /* UUIDDebugService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B9C952F2921116F00BB29DA /* UUIDDebugService.swift */; }; @@ -343,6 +340,19 @@ F331DCE22A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCE12A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift */; }; F331DCE42A80B54A00222120 /* ContentElementValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCE32A80B54A00222120 /* ContentElementValidatorTests.swift */; }; F331DCE62A80B5AB00222120 /* InappFormVariantValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCE52A80B5AB00222120 /* InappFormVariantValidatorTests.swift */; }; + F331DD012A83A56500222120 /* InAppPresentationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCEA2A83A56500222120 /* InAppPresentationManager.swift */; }; + F331DD022A83A56500222120 /* PresentationDisplayUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCEE2A83A56500222120 /* PresentationDisplayUseCase.swift */; }; + F331DD032A83A56500222120 /* ModalPresentationStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCF02A83A56500222120 /* ModalPresentationStrategy.swift */; }; + F331DD042A83A56500222120 /* PresentationStrategyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCF12A83A56500222120 /* PresentationStrategyProtocol.swift */; }; + F331DD052A83A56500222120 /* PresentationActionUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCF22A83A56500222120 /* PresentationActionUseCase.swift */; }; + F331DD062A83A56500222120 /* ModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCF52A83A56500222120 /* ModalViewController.swift */; }; + F331DD072A83A56500222120 /* CloseButtonElementFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCF82A83A56500222120 /* CloseButtonElementFactory.swift */; }; + F331DD082A83A56500222120 /* ElementFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCF92A83A56500222120 /* ElementFactory.swift */; }; + F331DD092A83A56500222120 /* LayerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCFB2A83A56500222120 /* LayerFactory.swift */; }; + F331DD0A2A83A56500222120 /* InAppImageOnlyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCFC2A83A56500222120 /* InAppImageOnlyView.swift */; }; + F331DD0B2A83A56500222120 /* CrossView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCFD2A83A56500222120 /* CrossView.swift */; }; + F331DD0C2A83A56500222120 /* ViewFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCFF2A83A56500222120 /* ViewFactoryProtocol.swift */; }; + F331DD0D2A83A56500222120 /* ModalViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD002A83A56500222120 /* ModalViewFactory.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -351,7 +361,6 @@ F3482F212A65DC2C002A41EC /* URLInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F202A65DC2C002A41EC /* URLInappMessageDelegate.swift */; }; F3482F232A65DC37002A41EC /* InAppMessagesDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F222A65DC37002A41EC /* InAppMessagesDelegate.swift */; }; F3482F2A2A65DCFC002A41EC /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F292A65DCFC002A41EC /* String+Extensions.swift */; }; - F37613D72A6A66FB009F2EE4 /* CrossView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F37613D62A6A66FB009F2EE4 /* CrossView.swift */; }; F37613E12A6A8CFF009F2EE4 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F37613E02A6A8CFF009F2EE4 /* UIColor+Extensions.swift */; }; F397EEAD2A4456C300D48CEC /* ProtocolError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F397EEAC2A4456C300D48CEC /* ProtocolError.swift */; }; F397EEAF2A4456FD00D48CEC /* MindboxError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F397EEAE2A4456FD00D48CEC /* MindboxError.swift */; }; @@ -635,7 +644,6 @@ 84FCD3B425CA0FD300D1E574 /* MockPersistenceStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockPersistenceStorage.swift; sourceTree = ""; }; 84FCD3B825CA109E00D1E574 /* MockNetworkFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNetworkFetcher.swift; sourceTree = ""; }; 84FCD3BC25CA10F600D1E574 /* SuccessResponse.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = SuccessResponse.json; sourceTree = ""; }; - 9B24FAA928C74B6A00F10B5D /* InAppPresentationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppPresentationManager.swift; sourceTree = ""; }; 9B24FAAB28C74B8300F10B5D /* InAppConfigurationRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppConfigurationRepository.swift; sourceTree = ""; }; 9B24FAAD28C74BA500F10B5D /* InAppCoreManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppCoreManager.swift; sourceTree = ""; }; 9B24FAB028C74BD200F10B5D /* InAppConfigurationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppConfigurationManager.swift; sourceTree = ""; }; @@ -648,8 +656,6 @@ 9B4F9DF128D088A0002C9CF0 /* InAppFormData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InAppFormData.swift; sourceTree = ""; }; 9B4F9DF528D088A8002C9CF0 /* InAppConfigutationMapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InAppConfigutationMapper.swift; sourceTree = ""; }; 9B4F9DF628D088A9002C9CF0 /* InAppConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InAppConfig.swift; sourceTree = ""; }; - 9B4F9DFA28D088AE002C9CF0 /* InAppMessageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InAppMessageViewController.swift; sourceTree = ""; }; - 9B4F9DFD28D088B4002C9CF0 /* InAppImageOnlyView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InAppImageOnlyView.swift; sourceTree = ""; }; 9B4F9E0428D0945F002C9CF0 /* InAppCoreManagerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppCoreManagerMock.swift; sourceTree = ""; }; 9B52570628D1AF880029B1BC /* InAppPresentationManagerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppPresentationManagerMock.swift; sourceTree = ""; }; 9B9C952F2921116F00BB29DA /* UUIDDebugService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UUIDDebugService.swift; sourceTree = ""; }; @@ -784,6 +790,19 @@ F331DCE12A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentElementPositionMarginValidatorTests.swift; sourceTree = ""; }; F331DCE32A80B54A00222120 /* ContentElementValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentElementValidatorTests.swift; sourceTree = ""; }; F331DCE52A80B5AB00222120 /* InappFormVariantValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InappFormVariantValidatorTests.swift; sourceTree = ""; }; + F331DCEA2A83A56500222120 /* InAppPresentationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InAppPresentationManager.swift; sourceTree = ""; }; + F331DCEE2A83A56500222120 /* PresentationDisplayUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationDisplayUseCase.swift; sourceTree = ""; }; + F331DCF02A83A56500222120 /* ModalPresentationStrategy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModalPresentationStrategy.swift; sourceTree = ""; }; + F331DCF12A83A56500222120 /* PresentationStrategyProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationStrategyProtocol.swift; sourceTree = ""; }; + F331DCF22A83A56500222120 /* PresentationActionUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationActionUseCase.swift; sourceTree = ""; }; + F331DCF52A83A56500222120 /* ModalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModalViewController.swift; sourceTree = ""; }; + F331DCF82A83A56500222120 /* CloseButtonElementFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloseButtonElementFactory.swift; sourceTree = ""; }; + F331DCF92A83A56500222120 /* ElementFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementFactory.swift; sourceTree = ""; }; + F331DCFB2A83A56500222120 /* LayerFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayerFactory.swift; sourceTree = ""; }; + F331DCFC2A83A56500222120 /* InAppImageOnlyView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InAppImageOnlyView.swift; sourceTree = ""; }; + F331DCFD2A83A56500222120 /* CrossView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrossView.swift; sourceTree = ""; }; + F331DCFF2A83A56500222120 /* ViewFactoryProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewFactoryProtocol.swift; sourceTree = ""; }; + F331DD002A83A56500222120 /* ModalViewFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModalViewFactory.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -792,7 +811,6 @@ F3482F202A65DC2C002A41EC /* URLInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLInappMessageDelegate.swift; sourceTree = ""; }; F3482F222A65DC37002A41EC /* InAppMessagesDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppMessagesDelegate.swift; sourceTree = ""; }; F3482F292A65DCFC002A41EC /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = ""; }; - F37613D62A6A66FB009F2EE4 /* CrossView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrossView.swift; sourceTree = ""; }; F37613E02A6A8CFF009F2EE4 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = ""; }; F397EEAC2A4456C300D48CEC /* ProtocolError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolError.swift; sourceTree = ""; }; F397EEAE2A4456FD00D48CEC /* MindboxError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxError.swift; sourceTree = ""; }; @@ -1522,6 +1540,7 @@ 9B24FAA828C74B4F00F10B5D /* InAppMessages */ = { isa = PBXGroup; children = ( + F331DCE92A83A56500222120 /* Presentation */, F3A8B9552A389D8300E9C055 /* InAppConfigurationMapper */, F3482F142A65DBA0002A41EC /* InappMessagesDelegate */, 9B24FAAD28C74BA500F10B5D /* InAppCoreManager.swift */, @@ -1531,7 +1550,6 @@ 9B24FAB928C756EB00F10B5D /* PresentChecker */, 9B24FAB628C7554C00F10B5D /* Models */, 9B24FAAF28C74BB700F10B5D /* Configuration */, - 9B24FAB228C74BF200F10B5D /* Presentation */, 9B24FAB328C751CB00F10B5D /* Images */, ); path = InAppMessages; @@ -1548,16 +1566,6 @@ path = Configuration; sourceTree = ""; }; - 9B24FAB228C74BF200F10B5D /* Presentation */ = { - isa = PBXGroup; - children = ( - 9B4F9DFC28D088B4002C9CF0 /* Views */, - 9B4F9DFA28D088AE002C9CF0 /* InAppMessageViewController.swift */, - 9B24FAA928C74B6A00F10B5D /* InAppPresentationManager.swift */, - ); - path = Presentation; - sourceTree = ""; - }; 9B24FAB328C751CB00F10B5D /* Images */ = { isa = PBXGroup; children = ( @@ -1589,15 +1597,6 @@ path = PresentChecker; sourceTree = ""; }; - 9B4F9DFC28D088B4002C9CF0 /* Views */ = { - isa = PBXGroup; - children = ( - 9B4F9DFD28D088B4002C9CF0 /* InAppImageOnlyView.swift */, - F37613D62A6A66FB009F2EE4 /* CrossView.swift */, - ); - path = Views; - sourceTree = ""; - }; 9B4F9DFF28D091E6002C9CF0 /* InApp */ = { isa = PBXGroup; children = ( @@ -2133,6 +2132,106 @@ path = ModelValidatorTests; sourceTree = ""; }; + F331DCE92A83A56500222120 /* Presentation */ = { + isa = PBXGroup; + children = ( + F331DCEA2A83A56500222120 /* InAppPresentationManager.swift */, + F331DCEB2A83A56500222120 /* Manager */, + F331DCF32A83A56500222120 /* Views */, + ); + path = Presentation; + sourceTree = ""; + }; + F331DCEB2A83A56500222120 /* Manager */ = { + isa = PBXGroup; + children = ( + F331DCEC2A83A56500222120 /* UseCases */, + ); + path = Manager; + sourceTree = ""; + }; + F331DCEC2A83A56500222120 /* UseCases */ = { + isa = PBXGroup; + children = ( + F331DCED2A83A56500222120 /* PresentationDisplayUseCase */, + F331DCF22A83A56500222120 /* PresentationActionUseCase.swift */, + ); + path = UseCases; + sourceTree = ""; + }; + F331DCED2A83A56500222120 /* PresentationDisplayUseCase */ = { + isa = PBXGroup; + children = ( + F331DCEE2A83A56500222120 /* PresentationDisplayUseCase.swift */, + F331DCEF2A83A56500222120 /* Strategy */, + ); + path = PresentationDisplayUseCase; + sourceTree = ""; + }; + F331DCEF2A83A56500222120 /* Strategy */ = { + isa = PBXGroup; + children = ( + F331DCF02A83A56500222120 /* ModalPresentationStrategy.swift */, + F331DCF12A83A56500222120 /* PresentationStrategyProtocol.swift */, + ); + path = Strategy; + sourceTree = ""; + }; + F331DCF32A83A56500222120 /* Views */ = { + isa = PBXGroup; + children = ( + F331DCF42A83A56500222120 /* ModalView */, + F331DCF62A83A56500222120 /* CommonViews */, + F331DCFE2A83A56500222120 /* ViewFactory */, + ); + path = Views; + sourceTree = ""; + }; + F331DCF42A83A56500222120 /* ModalView */ = { + isa = PBXGroup; + children = ( + F331DCF52A83A56500222120 /* ModalViewController.swift */, + ); + path = ModalView; + sourceTree = ""; + }; + F331DCF62A83A56500222120 /* CommonViews */ = { + isa = PBXGroup; + children = ( + F331DCF72A83A56500222120 /* ElementFactory */, + F331DCFA2A83A56500222120 /* LayersFactory */, + F331DCFC2A83A56500222120 /* InAppImageOnlyView.swift */, + F331DCFD2A83A56500222120 /* CrossView.swift */, + ); + path = CommonViews; + sourceTree = ""; + }; + F331DCF72A83A56500222120 /* ElementFactory */ = { + isa = PBXGroup; + children = ( + F331DCF82A83A56500222120 /* CloseButtonElementFactory.swift */, + F331DCF92A83A56500222120 /* ElementFactory.swift */, + ); + path = ElementFactory; + sourceTree = ""; + }; + F331DCFA2A83A56500222120 /* LayersFactory */ = { + isa = PBXGroup; + children = ( + F331DCFB2A83A56500222120 /* LayerFactory.swift */, + ); + path = LayersFactory; + sourceTree = ""; + }; + F331DCFE2A83A56500222120 /* ViewFactory */ = { + isa = PBXGroup; + children = ( + F331DCFF2A83A56500222120 /* ViewFactoryProtocol.swift */, + F331DD002A83A56500222120 /* ModalViewFactory.swift */, + ); + path = ViewFactory; + sourceTree = ""; + }; F3482F142A65DBA0002A41EC /* InappMessagesDelegate */ = { isa = PBXGroup; children = ( @@ -2580,6 +2679,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F331DD062A83A56500222120 /* ModalViewController.swift in Sources */, F331DCC92A80993600222120 /* ContentBackgroundLayerActionValidator.swift in Sources */, F331DCC12A80993600222120 /* ContentElement.swift in Sources */, 847F581725C8981F00147A9A /* HTTPURLResponseStatusCodeValidator.swift in Sources */, @@ -2588,6 +2688,7 @@ A17958762978AEC600609E91 /* OrTargetingChecker.swift in Sources */, F331DCC22A80993600222120 /* ContentElementGravity.swift in Sources */, 334F3AE1264C199900A6AC00 /* PromoCodeRequest.swift in Sources */, + F331DD032A83A56500222120 /* ModalPresentationStrategy.swift in Sources */, A17958792978AEC600609E91 /* GeoTargetingChecker.swift in Sources */, 33BEE80D2681EB7700993720 /* NotificationDecoder.swift in Sources */, 840042A12614CE0000CA17C5 /* ClickNotificationManager.swift in Sources */, @@ -2622,6 +2723,7 @@ F331DC842A80983000222120 /* FailableDecodableArray.swift in Sources */, F3A8B99E2A3A4FD600E9C055 /* ABTestValidator.swift in Sources */, 334F3AE0264C199900A6AC00 /* ViewProductCategoryRequest.swift in Sources */, + F331DD022A83A56500222120 /* PresentationDisplayUseCase.swift in Sources */, A1D23AF029DE082E00A75179 /* InAppProductSegmentResponse.swift in Sources */, F331DCD52A80993600222120 /* InAppModel.swift in Sources */, A1D017EE2976CC4000CD9F99 /* AndTargeting.swift in Sources */, @@ -2634,6 +2736,7 @@ 6FDD145D266F7CCE00A50C35 /* PossibleDiscountsResponse.swift in Sources */, 9B9C95322921116F00BB29DA /* PasteboardUUIDDebugService.swift in Sources */, F331DCC42A80993600222120 /* ContentElementPositionMargin.swift in Sources */, + F331DD0D2A83A56500222120 /* ModalViewFactory.swift in Sources */, A1A916DF29C9032700D59D9E /* CategoryIDInTargeting.swift in Sources */, 6FDD1449266F7C4E00A50C35 /* LimitTypeResponse.swift in Sources */, 6FDD144B266F7C5A00A50C35 /* AmountResponse.swift in Sources */, @@ -2653,10 +2756,10 @@ 84DEE8B125CC042A00C98CC7 /* MBDatabaseError.swift in Sources */, A179587C2978AED800609E91 /* GeoTargeting.swift in Sources */, 3333C1E12681EA4D00B60D84 /* NotificationsPayloads.swift in Sources */, - 9B4F9DFB28D088AE002C9CF0 /* InAppMessageViewController.swift in Sources */, 334F3AE7264C199900A6AC00 /* CatalogProductListRequest.swift in Sources */, F331DCC02A80993600222120 /* ContentElementSizeType.swift in Sources */, F331DCC52A80993600222120 /* PositionMarginKind.swift in Sources */, + F331DD0B2A83A56500222120 /* CrossView.swift in Sources */, B3A625562689FDD400B6A3B7 /* BalanceResponse.swift in Sources */, 6FDD1459266F7CB700A50C35 /* ContentResponse.swift in Sources */, F37613E12A6A8CFF009F2EE4 /* UIColor+Extensions.swift in Sources */, @@ -2677,6 +2780,7 @@ 31A20D4E25B6EFB600AAA0A3 /* MindboxDelegate.swift in Sources */, A179587A2978AEC600609E91 /* AndTargetingChecker.swift in Sources */, F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */, + F331DD012A83A56500222120 /* InAppPresentationManager.swift in Sources */, 3337E6A3265FAB39006949EB /* BaseResponse.swift in Sources */, 6FDD1451266F7C8000A50C35 /* UsageServiceStatusResponse.swift in Sources */, F3A8B9A92A3A713E00E9C055 /* SettingsModel.swift in Sources */, @@ -2689,6 +2793,7 @@ 33B1C976265B910F00E293F8 /* SwiftyJSON.swift in Sources */, F397EEAD2A4456C300D48CEC /* ProtocolError.swift in Sources */, 84A312B025DA5CF30096A017 /* BackgroundTaskManagerProxy.swift in Sources */, + F331DD042A83A56500222120 /* PresentationStrategyProtocol.swift in Sources */, F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */, A1D017E72976CBE100CD9F99 /* TargetingModel.swift in Sources */, 9B4F9DF328D088A0002C9CF0 /* InAppFormData.swift in Sources */, @@ -2698,10 +2803,12 @@ 84DEE8A925CC031200C98CC7 /* MBDatabase.xcdatamodeld in Sources */, A192786A29D38D2000CDB53D /* CheckerFactory.swift in Sources */, F331DCC72A80993600222120 /* ContentElementValidator.swift in Sources */, + F331DD082A83A56500222120 /* ElementFactory.swift in Sources */, F331DCBE2A80993600222120 /* ContentElementType.swift in Sources */, 840C38A625D1191000D50183 /* GuaranteedDeliveryManager.swift in Sources */, A1D017EC2976CC2E00CD9F99 /* TrueTargeting.swift in Sources */, 33072F302664C2E4001F1AB2 /* SubscriptionResponse.swift in Sources */, + F331DD052A83A56500222120 /* PresentationActionUseCase.swift in Sources */, A153E03B29BAFE01003C34D4 /* CustomOperationTargeting.swift in Sources */, 9BC24E7528F6953D00C2619C /* ConfigResponse.swift in Sources */, 33072F482664CCBA001F1AB2 /* PoolResponse.swift in Sources */, @@ -2723,9 +2830,10 @@ F3A8B9AB2A3A719C00E9C055 /* ABTestModel.swift in Sources */, 33072F342664C3E1001F1AB2 /* ProductResponse.swift in Sources */, 33072F3E2664C7B5001F1AB2 /* DiscountCardResponse.swift in Sources */, + F331DD0A2A83A56500222120 /* InAppImageOnlyView.swift in Sources */, 8410681325ECDC73004701C2 /* DatabaseLoader.swift in Sources */, - 9B24FAAA28C74B6A00F10B5D /* InAppPresentationManager.swift in Sources */, A1D017F22976CC9400CD9F99 /* SegmentTargeting.swift in Sources */, + F331DD072A83A56500222120 /* CloseButtonElementFactory.swift in Sources */, 9BC24E7428F6953D00C2619C /* InAppConfigurationAPI.swift in Sources */, F397EEB32A44572400D48CEC /* ValidationError.swift in Sources */, A192787229D442D900CDB53D /* ProductSegmentChecker.swift in Sources */, @@ -2798,8 +2906,8 @@ 6FDD1445266F7C2200A50C35 /* CouponResponse.swift in Sources */, F78E92EF282E63320003B4A3 /* DispatchSemaphore.swift in Sources */, 314B38FD25AEE8B200E947B9 /* MBConfiguration.swift in Sources */, + F331DD0C2A83A56500222120 /* ViewFactoryProtocol.swift in Sources */, 6FDD1447266F7C2B00A50C35 /* LimitResponse.swift in Sources */, - 9B4F9DFE28D088B5002C9CF0 /* InAppImageOnlyView.swift in Sources */, 847F580725C88C7A00147A9A /* NetworkFetcher.swift in Sources */, 84D350D225C99F320044E4E6 /* IDFVFetcher.swift in Sources */, 84B625E925C989C100AB6228 /* UDIDValidator.swift in Sources */, @@ -2818,7 +2926,6 @@ F331DCCE2A80993600222120 /* ContentBackgroundLayerSourceValidator.swift in Sources */, F331DCBF2A80993600222120 /* ContentElementSize.swift in Sources */, 334F3AF1264C199900A6AC00 /* PoolRequest.swift in Sources */, - F37613D72A6A66FB009F2EE4 /* CrossView.swift in Sources */, 3191C2A625ADFD8000E7D6B9 /* Mindbox.swift in Sources */, A184654529C310F100E64780 /* InAppOperationJSONModel.swift in Sources */, A154E330299E0F1600F8F074 /* InAppGeoResponse.swift in Sources */, @@ -2830,6 +2937,7 @@ 33BEE8172681EC5F00993720 /* EventRoute.swift in Sources */, F331DCD42A80993600222120 /* InappValidator.swift in Sources */, 317054CB25AF189800AE624C /* PersistenceStorage.swift in Sources */, + F331DD092A83A56500222120 /* LayerFactory.swift in Sources */, 840C38AC25D11BB200D50183 /* DeliveryOperation.swift in Sources */, 334F3AEE264C199900A6AC00 /* SegmentationRequest.swift in Sources */, B3A625502689F8B600B6A3B7 /* BenefitResponse.swift in Sources */, diff --git a/Mindbox/DI/DependencyProvider.swift b/Mindbox/DI/DependencyProvider.swift index 125848ad..d34e5c70 100644 --- a/Mindbox/DI/DependencyProvider.swift +++ b/Mindbox/DI/DependencyProvider.swift @@ -61,6 +61,11 @@ final class DependencyProvider: DependencyContainer { let imageDownloader = URLSessionImageDownloader(persistenceStorage: persistenceStorage) imageDownloadService = ImageDownloadService(imageDownloader: imageDownloader) abTestDeviceMixer = ABTestDeviceMixer() + let tracker = InAppMessagesTracker(databaseRepository: databaseRepository) + let displayUseCase = PresentationDisplayUseCase() + let actionUseCase = PresentationActionUseCase(tracker: tracker) + let presentationManager = InAppPresentationManager(displayUseCase: displayUseCase, + actionUseCase: actionUseCase) inAppMessagesManager = InAppCoreManager( configManager: InAppConfigurationManager( inAppConfigAPI: InAppConfigurationAPI(persistenceStorage: persistenceStorage), @@ -75,9 +80,7 @@ final class DependencyProvider: DependencyContainer { imageDownloadService: imageDownloadService, abTestDeviceMixer: abTestDeviceMixer), logsManager: logsManager, sessionStorage: sessionTemporaryStorage), - presentationManager: InAppPresentationManager( - inAppTracker: InAppMessagesTracker(databaseRepository: databaseRepository) - ), + presentationManager: presentationManager, persistenceStorage: persistenceStorage, sessionStorage: sessionTemporaryStorage ) diff --git a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift index 0ba0a86f..32e68d23 100644 --- a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift +++ b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift @@ -239,14 +239,12 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { } var inAppsForEvent = filteredInAppsByEvent[triggerEvent] ?? [InAppTransitionData]() -// if let inAppFormVariants = inapp.form.variants.first { -// let formData = InAppTransitionData(inAppId: inapp.id, -// imageUrl: inAppFormVariants.imageUrl, // Change this later -// redirectUrl: inAppFormVariants.redirectUrl, -// intentPayload: inAppFormVariants.intentPayload) -// inAppsForEvent.append(formData) -// filteredInAppsByEvent[triggerEvent] = inAppsForEvent -// } + if let inAppFormVariants = inapp.form.variants.elements.first { + let formData = InAppTransitionData(inAppId: inapp.id, + content: inAppFormVariants) + inAppsForEvent.append(formData) + filteredInAppsByEvent[triggerEvent] = inAppsForEvent + } } self.targetingChecker.event = nil @@ -268,13 +266,21 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { continue } + guard let image = inapp.content.content?.background.layers.elements.first(where: { $0.type == .image }), + let imageValue = image.source?.value else { + continue + } + group.enter() Logger.common(message: "Starting inapp processing. [ID]: \(inapp.inAppId)", level: .debug, category: .inAppMessages) - self.imageDownloadService.downloadImage(withUrl: inapp.imageUrl) { result in + + self.imageDownloadService.downloadImage(withUrl: imageValue) { result in defer { group.leave() } switch result { case .success(let image): - formData = InAppFormData(inAppId: inapp.inAppId, image: image, redirectUrl: inapp.redirectUrl, intentPayload: inapp.intentPayload) + formData = InAppFormData(inAppId: inapp.inAppId, + image: image, + content: inapp.content) shouldDownloadImage = false case .failure: break diff --git a/Mindbox/InAppMessages/Models/InAppFormData.swift b/Mindbox/InAppMessages/Models/InAppFormData.swift index 2794688f..29b62759 100644 --- a/Mindbox/InAppMessages/Models/InAppFormData.swift +++ b/Mindbox/InAppMessages/Models/InAppFormData.swift @@ -13,13 +13,10 @@ import UIKit struct InAppFormData { let inAppId: String let image: UIImage - let redirectUrl: String - let intentPayload: String + let content: InappFormVariant } struct InAppTransitionData: Equatable { let inAppId: String - let imageUrl: String - let redirectUrl: String - let intentPayload: String + let content: InappFormVariant } diff --git a/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift b/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift deleted file mode 100644 index c13f1966..00000000 --- a/Mindbox/InAppMessages/Presentation/InAppMessageViewController.swift +++ /dev/null @@ -1,121 +0,0 @@ -// -// InAppMessageViewController.swift -// Mindbox -// -// Created by Максим Казаков on 07.09.2022. -// - -import UIKit - -final class InAppMessageViewController: UIViewController { - - var crossView: CrossView? - var inAppView: InAppImageOnlyView? - var crossSize: CGFloat = 24 - - init( - inAppUIModel: InAppMessageUIModel, - onPresented: @escaping () -> Void, - onTapAction: @escaping () -> Void, - onClose: @escaping () -> Void - ) { - self.inAppUIModel = inAppUIModel - self.onPresented = onPresented - self.onClose = onClose - self.onTapAction = onTapAction - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private let inAppUIModel: InAppMessageUIModel - private let onPresented: () -> Void - private let onClose: () -> Void - private let onTapAction: () -> Void - - override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = .black.withAlphaComponent(0.2) - - inAppView = InAppImageOnlyView(uiModel: inAppUIModel) - crossView = CrossView(lineColorHex: "000000", lineWidth: 1) - - guard let inAppView = inAppView, - let crossView = crossView else { - return - } - let onTapDimmedViewGesture = UITapGestureRecognizer(target: self, action: #selector(onTapDimmedView)) - view.addGestureRecognizer(onTapDimmedViewGesture) - view.isUserInteractionEnabled = true - view.addSubview(inAppView) - - inAppView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - inAppView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), - inAppView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), - inAppView.centerYAnchor.constraint(equalTo: view.centerYAnchor), - inAppView.widthAnchor.constraint(equalTo: inAppView.heightAnchor, multiplier: 3 / 4) - ]) - let imageTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(onTapImage)) - inAppView.addGestureRecognizer(imageTapGestureRecognizer) - - inAppView.addSubview(crossView) - crossView.isUserInteractionEnabled = true - - let closeRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(onCloseButton)) - closeRecognizer.minimumPressDuration = 0 - crossView.addGestureRecognizer(closeRecognizer) - } - - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - - guard let inAppView = inAppView, - let crossView = crossView else { - return - } - - let trailingOffsetPercent: CGFloat = 0.03 - let topOffsetPercent: CGFloat = 0.03 - - let horizontalOffset = (inAppView.frame.width - crossSize) * trailingOffsetPercent - let verticalOffset = (inAppView.frame.height - crossSize) * topOffsetPercent - crossView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - crossView.trailingAnchor.constraint(equalTo: inAppView.trailingAnchor, constant: -horizontalOffset), - crossView.topAnchor.constraint(equalTo: inAppView.topAnchor, constant: verticalOffset), - crossView.widthAnchor.constraint(equalToConstant: crossSize), - crossView.heightAnchor.constraint(equalToConstant: crossSize) - ]) - } - - private var viewWillAppearWasCalled = false - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - guard !viewWillAppearWasCalled else { return } - viewWillAppearWasCalled = true - onPresented() - } - - @objc private func onCloseButton(_ gesture: UILongPressGestureRecognizer) { - guard let crossView = crossView else { - return - } - - let location = gesture.location(in: crossView) - let isInsideCrossView = crossView.bounds.contains(location) - if gesture.state == .ended && isInsideCrossView { - onClose() - } - } - - @objc private func onTapDimmedView() { - onClose() - } - - @objc private func onTapImage() { - onTapAction() - } -} diff --git a/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift b/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift index dd629db7..3b6ae635 100644 --- a/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift +++ b/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift @@ -35,19 +35,26 @@ enum InAppPresentationError { case failedToLoadWindow } +enum ViewPresentationType { + case modal + case topSnackbar + case bottomSnackbar +} + typealias InAppMessageTapAction = (_ tapLink: URL?, _ payload: String) -> Void -/// Prepares UI for in-app messages and shows them final class InAppPresentationManager: InAppPresentationManagerProtocol { init( - inAppTracker: InAppMessagesTrackerProtocol + displayUseCase: PresentationDisplayUseCase, + actionUseCase: PresentationActionUseCase ) { - self.inAppTracker = inAppTracker + self.displayUseCase = displayUseCase + self.actionUseCase = actionUseCase } - private let inAppTracker: InAppMessagesTrackerProtocol - private var inAppWindow: UIWindow? + private let displayUseCase: PresentationDisplayUseCase + private let actionUseCase: PresentationActionUseCase func present( inAppFormData: InAppFormData, @@ -56,138 +63,50 @@ final class InAppPresentationManager: InAppPresentationManagerProtocol { onPresentationCompleted: @escaping () -> Void, onError: @escaping (InAppPresentationError) -> Void ) { - clickTracked = false - DispatchQueue.main.async { - let redirectInfo = InAppMessageUIModel.InAppRedirect( - redirectUrl: inAppFormData.redirectUrl, - payload: inAppFormData.intentPayload - ) - - let inAppUIModel = InAppMessageUIModel( - inAppId: inAppFormData.inAppId, - image: inAppFormData.image, - redirect: redirectInfo - ) + DispatchQueue.main.async { [weak self] in + guard let type = self?.getType(inappType: inAppFormData.content.type) else { + return + } - self.presentInAppUIModel( - inAppUIModel: inAppUIModel, + self?.displayUseCase.changeType(type: type) + self?.displayUseCase.presentInAppUIModel( + inAppUIModel: inAppFormData, onPresented: onPresented, - onTapAction: onTapAction, - onPresentationCompleted: onPresentationCompleted, - onError: onError + onTapAction: { [weak self] action in + guard let action = action else { + return + } + + switch action.type { + case .redirectUrl: + if let value = action.value, let payload = action.intentPayload { + self?.actionUseCase.onTapAction(id: inAppFormData.inAppId, + value: value, + payload: payload, + onTap: onTapAction, + close: { + self?.displayUseCase.dismissInAppUIModel(onClose: onPresentationCompleted) + }) + } + case .unknown: + break + } + }, + onClose: { + self?.displayUseCase.dismissInAppUIModel(onClose: onPresentationCompleted) + } ) } } - - // MARK: - Private - - private func presentInAppUIModel( - inAppUIModel: InAppMessageUIModel, - onPresented: @escaping () -> Void, - onTapAction: @escaping InAppMessageTapAction, - onPresentationCompleted: @escaping () -> Void, - onError: @escaping (InAppPresentationError) -> Void - ) { - guard let inAppWindow = makeInAppMessageWindow() else { - Logger.common(message: "InappWindow creating failed") - onError(.failedToLoadWindow) - return - } - - Logger.common(message: "InappWindow created Successfully") - - let close: () -> Void = { [weak self] in - self?.onClose(inApp: inAppUIModel, onPresentationCompleted) - } - let inAppViewController = InAppMessageViewController( - inAppUIModel: inAppUIModel, - onPresented: { [weak self] in - self?.onPresented(inApp: inAppUIModel, onPresented) - }, - onTapAction: { [weak self] in - self?.onTapAction(inApp: inAppUIModel, onTap: onTapAction, close: close) - }, - onClose: close - ) - inAppWindow.rootViewController = inAppViewController - Logger.common(message: "In-app with id \(inAppUIModel.inAppId) presented", level: .info, category: .inAppMessages) - } - - private func onPresented(inApp: InAppMessageUIModel, _ completion: @escaping () -> Void) { - do { - try inAppTracker.trackView(id: inApp.inAppId) - Logger.common(message: "Track InApp.View. Id \(inApp.inAppId)", level: .info, category: .notification) - } catch { - Logger.common(message: "Track InApp.View failed with error: \(error)", level: .error, category: .notification) - } - completion() - } - - private var clickTracked = false - private func onTapAction( - inApp: InAppMessageUIModel, - onTap: @escaping InAppMessageTapAction, - close: @escaping () -> Void - ) { - Logger.common(message: "InApp presentation completed", level: .debug, category: .inAppMessages) - if !clickTracked { - do { - try inAppTracker.trackClick(id: inApp.inAppId) - clickTracked = true - Logger.common(message: "Track InApp.Click. Id \(inApp.inAppId)", level: .info, category: .notification) - } catch { - Logger.common(message: "Track InApp.Click failed with error: \(error)", level: .error, category: .notification) - } - } - - let redirect = inApp.redirect - - if redirect.redirectUrl.isEmpty && redirect.payload.isEmpty { - Logger.common(message: "Redirect URL and Payload are empty.", category: .inAppMessages) - } else { - let url = URL(string: redirect.redirectUrl) - onTap(url, redirect.payload) - close() - } - } - - private func onClose(inApp: InAppMessageUIModel, _ completion: @escaping () -> Void) { - Logger.common(message: "InApp presentation dismissed", level: .debug, category: .inAppMessages) - inAppWindow?.isHidden = true - inAppWindow?.rootViewController = nil - completion() - } - - private func makeInAppMessageWindow() -> UIWindow? { - let window: UIWindow? - if #available(iOS 13.0, *) { - window = iOS13PlusWindow - } else { - window = UIWindow(frame: UIScreen.main.bounds) - } - self.inAppWindow = window - window?.windowLevel = UIWindow.Level.normal - window?.isHidden = false - return window - } - - @available(iOS 13.0, *) - private var foregroundedScene: UIWindowScene? { - for connectedScene in UIApplication.shared.connectedScenes { - if let windowScene = connectedScene as? UIWindowScene, connectedScene.activationState == .foregroundActive { - return windowScene - } - } - return nil - } - - @available(iOS 13.0, *) - private var iOS13PlusWindow: UIWindow? { - if let foregroundedScene = foregroundedScene { - return UIWindow(windowScene: foregroundedScene) - } else { - return UIWindow(frame: UIScreen.main.bounds) + func getType(inappType: InappFormVariantType) -> ViewPresentationType? { + switch inappType { + case .modal: + return .modal + case .snackbar: + return .topSnackbar + case .unknown: + return nil } } } diff --git a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationActionUseCase.swift b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationActionUseCase.swift new file mode 100644 index 00000000..44d55725 --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationActionUseCase.swift @@ -0,0 +1,48 @@ +// +// PresentationActionUseCase.swift +// Mindbox +// +// Created by vailence on 18.07.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation +import MindboxLogger + +final class PresentationActionUseCase { + + private let tracker: InAppMessagesTrackerProtocol + + init(tracker: InAppMessagesTrackerProtocol) { + self.tracker = tracker + } + + private var clickTracked = false + + func onTapAction( + id: String, + value: String, + payload: String, + onTap: @escaping InAppMessageTapAction, + close: @escaping () -> Void + ) { + Logger.common(message: "Presentation completed", level: .debug, category: .inAppMessages) + if !clickTracked { + do { + try tracker.trackClick(id: id) + clickTracked = true + Logger.common(message: "Track InApp.Click. Id \(id)", level: .info, category: .notification) + } catch { + Logger.common(message: "Track InApp.Click failed with error: \(error)", level: .error, category: .notification) + } + } + + if value.isEmpty && payload.isEmpty { + Logger.common(message: "Redirect URL and Payload are empty.", category: .inAppMessages) + } else { + let url = URL(string: value) + onTap(url, payload) + close() + } + } +} diff --git a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift new file mode 100644 index 00000000..5a206bbf --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift @@ -0,0 +1,57 @@ +// +// PresentationDisplayUseCase.swift +// Mindbox +// +// Created by vailence on 18.07.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit +import MindboxLogger + +final class PresentationDisplayUseCase { + + private var presentationStrategy: PresentationStrategyProtocol? + private var presentedVC: UIViewController? + private var viewFactory: ViewFactoryProtocol? + private var model: InAppFormData? + + func presentInAppUIModel(inAppUIModel: InAppFormData, onPresented: @escaping () -> Void, onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, onClose: @escaping () -> Void) { + guard let window = presentationStrategy?.getWindow() else { + Logger.common(message: "In-app modal window creating failed") + return + } + + model = inAppUIModel + guard let viewController = viewFactory?.create(inAppUIModel: inAppUIModel, + onPresented: onPresented, + onTapAction: onTapAction, + onClose: onClose) else { + return + } + + presentedVC = viewController + presentationStrategy?.present(id: inAppUIModel.inAppId, in: window, using: viewController) + } + + func dismissInAppUIModel(onClose: @escaping () -> Void) { + guard let presentedVC = presentedVC else { + return + } + presentationStrategy?.dismiss(viewController: presentedVC) + onClose() + self.viewFactory = nil + self.presentedVC = nil + self.model = nil + } + + func changeType(type: ViewPresentationType) { + switch type { + case .modal: + self.presentationStrategy = ModalPresentationStrategy() + self.viewFactory = ModalViewFactory() + default: + return + } + } +} diff --git a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/Strategy/ModalPresentationStrategy.swift b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/Strategy/ModalPresentationStrategy.swift new file mode 100644 index 00000000..c92fa2be --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/Strategy/ModalPresentationStrategy.swift @@ -0,0 +1,63 @@ +// +// ModalPresentationStrategy.swift +// Mindbox +// +// Created by vailence on 18.07.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit +import MindboxLogger + +final class ModalPresentationStrategy: PresentationStrategyProtocol { + var inappWindow: UIWindow? + + func getWindow() -> UIWindow? { + return makeInAppMessageWindow() + } + + func present(id: String, in window: UIWindow, using viewController: UIViewController) { + window.rootViewController = viewController + window.isHidden = false + Logger.common(message: "In-app modal with id \(id) presented", level: .info, category: .inAppMessages) + } + + func dismiss(viewController: UIViewController) { + viewController.view.window?.isHidden = true + viewController.view.window?.rootViewController = nil + Logger.common(message: "In-app modal presentation dismissed", level: .debug, category: .inAppMessages) + } + + private func makeInAppMessageWindow() -> UIWindow? { + let window: UIWindow? + if #available(iOS 13.0, *) { + window = iOS13PlusWindow + } else { + window = UIWindow(frame: UIScreen.main.bounds) + } + self.inappWindow = window + window?.windowLevel = UIWindow.Level.normal + window?.isHidden = false + return window + } + + @available(iOS 13.0, *) + private var foregroundedScene: UIWindowScene? { + for connectedScene in UIApplication.shared.connectedScenes { + if let windowScene = connectedScene as? UIWindowScene, connectedScene.activationState == .foregroundActive { + return windowScene + } + } + + return nil + } + + @available(iOS 13.0, *) + private var iOS13PlusWindow: UIWindow? { + if let foregroundedScene = foregroundedScene { + return UIWindow(windowScene: foregroundedScene) + } else { + return UIWindow(frame: UIScreen.main.bounds) + } + } +} diff --git a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/Strategy/PresentationStrategyProtocol.swift b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/Strategy/PresentationStrategyProtocol.swift new file mode 100644 index 00000000..b7621250 --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/Strategy/PresentationStrategyProtocol.swift @@ -0,0 +1,15 @@ +// +// PresentationStrategyProtocol.swift +// Mindbox +// +// Created by vailence on 18.07.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit + +protocol PresentationStrategyProtocol { + func getWindow() -> UIWindow? + func present(id: String, in window: UIWindow, using viewController: UIViewController) + func dismiss(viewController: UIViewController) +} diff --git a/Mindbox/InAppMessages/Presentation/Views/CrossView.swift b/Mindbox/InAppMessages/Presentation/Views/CommonViews/CrossView.swift similarity index 87% rename from Mindbox/InAppMessages/Presentation/Views/CrossView.swift rename to Mindbox/InAppMessages/Presentation/Views/CommonViews/CrossView.swift index 1075ad3e..7f6ebad0 100644 --- a/Mindbox/InAppMessages/Presentation/Views/CrossView.swift +++ b/Mindbox/InAppMessages/Presentation/Views/CommonViews/CrossView.swift @@ -24,7 +24,7 @@ class CrossView: UIView { setupDefaults() } - convenience init(lineColorHex: String, lineWidth: CGFloat) { + convenience init(lineColorHex: String?, lineWidth: Int?) { self.init() setupView(lineColorHex: lineColorHex, lineWidth: lineWidth) } @@ -33,9 +33,11 @@ class CrossView: UIView { backgroundColor = .clear } - private func setupView(lineColorHex: String, lineWidth: CGFloat) { - self.lineColor = UIColor(hex: lineColorHex) - self.lineWidth = lineWidth + private func setupView(lineColorHex: String?, lineWidth: Int?) { + let color = lineColorHex ?? "#000000" + let width = lineWidth ?? 1 + self.lineColor = UIColor(hex: color) + self.lineWidth = CGFloat(width) } override func draw(_ rect: CGRect) { diff --git a/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift b/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift new file mode 100644 index 00000000..0b53d8d6 --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift @@ -0,0 +1,48 @@ +// +// CloseButtonElementFactory.swift +// Mindbox +// +// Created by vailence on 04.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit + +class CloseButtonElementFactory: ElementFactory { + func create(from element: ContentElement, in view: UIView, with controller: UIViewController) -> UIView? { + guard let controller = controller as? ModalViewController else { + return nil + } + + let color = element.color?.isHexValid() ?? false ? element.color : nil + let closeButton = CrossView(lineColorHex: color, lineWidth: element.lineWidth) + closeButton.isUserInteractionEnabled = true + let closeRecognizer = UILongPressGestureRecognizer(target: controller, action: #selector(controller.onCloseButton)) + closeRecognizer.minimumPressDuration = 0 + closeButton.addGestureRecognizer(closeRecognizer) + return closeButton + } + + func setupConstraints(for view: UIView, from element: ContentElement, in parentView: UIView) { + if element.size?.kind == .unknown { + return + } + + let size = element.size ?? ContentElementSize(kind: .dp, width: 24, height: 24) + let top = element.position?.margin?.top ?? 0.02 + let right = element.position?.margin?.right ?? 0.02 + + let horizontalOffset = (parentView.frame.width - CGFloat(size.width)) * right + let verticalOffset = (parentView.frame.height - CGFloat(size.height)) * top + + if size.kind == .dp { + view.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor, constant: -horizontalOffset), + view.topAnchor.constraint(equalTo: parentView.topAnchor, constant: verticalOffset), + view.widthAnchor.constraint(equalToConstant: CGFloat(size.width)), + view.heightAnchor.constraint(equalToConstant: CGFloat(size.height)) + ]) + } + } +} diff --git a/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/ElementFactory.swift b/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/ElementFactory.swift new file mode 100644 index 00000000..4ef6dfb5 --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/ElementFactory.swift @@ -0,0 +1,14 @@ +// +// ElementFactory.swift +// Mindbox +// +// Created by vailence on 04.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit + +protocol ElementFactory { + func create(from element: ContentElement, in view: UIView, with controller: UIViewController) -> UIView? + func setupConstraints(for view: UIView, from element: ContentElement, in parentView: UIView) +} diff --git a/Mindbox/InAppMessages/Presentation/Views/InAppImageOnlyView.swift b/Mindbox/InAppMessages/Presentation/Views/CommonViews/InAppImageOnlyView.swift similarity index 57% rename from Mindbox/InAppMessages/Presentation/Views/InAppImageOnlyView.swift rename to Mindbox/InAppMessages/Presentation/Views/CommonViews/InAppImageOnlyView.swift index 16a58e56..f8e008af 100644 --- a/Mindbox/InAppMessages/Presentation/Views/InAppImageOnlyView.swift +++ b/Mindbox/InAppMessages/Presentation/Views/CommonViews/InAppImageOnlyView.swift @@ -8,21 +8,30 @@ import UIKit final class InAppImageOnlyView: UIView { + var onClose: (() -> Void)? let imageView = UIImageView() - let uiModel: InAppMessageUIModel + let image: UIImage? + let action: ContentBackgroundLayerAction? - init(uiModel: InAppMessageUIModel) { - self.uiModel = uiModel + init(image: UIImage, action: ContentBackgroundLayerAction?) { + self.image = image + self.action = action super.init(frame: .zero) customInit() } required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") + self.image = nil + self.action = nil + super.init(coder: coder) + customInit() } func customInit() { - let image = uiModel.image + guard let image = image else { + return + } + imageView.contentMode = .scaleAspectFill imageView.image = image diff --git a/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/LayerFactory.swift b/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/LayerFactory.swift new file mode 100644 index 00000000..9c3251d4 --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/LayerFactory.swift @@ -0,0 +1,33 @@ +// +// LayerFactory.swift +// Mindbox +// +// Created by vailence on 04.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit + +protocol LayerFactory { + func create(from image: UIImage, action: ContentBackgroundLayerAction?, in view: UIView, with controller: ModalViewController) -> UIView? + func setupConstraints(for view: UIView, in parentView: UIView) +} + +class ImageLayerFactory: LayerFactory { + func create(from image: UIImage, action: ContentBackgroundLayerAction?, in view: UIView, with controller: ModalViewController) -> UIView? { + let inAppView = InAppImageOnlyView(image: image, action: action) + let imageTapGestureRecognizer = UITapGestureRecognizer(target: controller, action: #selector(controller.imageTapped(_:))) + inAppView.addGestureRecognizer(imageTapGestureRecognizer) + return inAppView + } + + func setupConstraints(for view: UIView, in parentView: UIView) { + view.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + view.leadingAnchor.constraint(equalTo: parentView.leadingAnchor, constant: 20), + view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor, constant: -20), + view.centerYAnchor.constraint(equalTo: parentView.centerYAnchor), + view.widthAnchor.constraint(equalTo: view.heightAnchor, multiplier: 3 / 4) + ]) + } +} diff --git a/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift b/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift new file mode 100644 index 00000000..df70d10e --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift @@ -0,0 +1,134 @@ +// +// ModalViewController.swift +// Mindbox +// +// Created by Максим Казаков on 07.09.2022. +// + +import UIKit + +final class ModalViewController: UIViewController { + + var layers = [UIView]() + var elements = [UIView]() + private let elementFactories: [ContentElementType: ElementFactory] = [ + .closeButton: CloseButtonElementFactory() + ] + + private let layersFactories: [ContentBackgroundLayerType: LayerFactory] = [ + .image: ImageLayerFactory() + ] + + init( + inAppUIModel: InAppFormData, + onPresented: @escaping () -> Void, + onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, + onClose: @escaping () -> Void + ) { + self.inAppUIModel = inAppUIModel + self.onPresented = onPresented + self.onClose = onClose + self.onTapAction = onTapAction + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private let inAppUIModel: InAppFormData + private let onPresented: () -> Void + private let onClose: () -> Void + private let onTapAction: (ContentBackgroundLayerAction?) -> Void + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .black.withAlphaComponent(0.2) + let onTapDimmedViewGesture = UITapGestureRecognizer(target: self, action: #selector(onTapDimmedView)) + view.addGestureRecognizer(onTapDimmedViewGesture) + view.isUserInteractionEnabled = true + + setupLayers() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + setupElements() + } + + private var viewWillAppearWasCalled = false + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + guard !viewWillAppearWasCalled else { return } + viewWillAppearWasCalled = true + onPresented() + } + + @objc func onCloseButton(_ gesture: UILongPressGestureRecognizer) { + guard let crossView = gesture.view else { + return + } + + let location = gesture.location(in: crossView) + let isInsideCrossView = crossView.bounds.contains(location) + if gesture.state == .ended && isInsideCrossView { + onClose() + } + } + + @objc private func onTapDimmedView() { + onClose() + } + + @objc func imageTapped(_ sender: UITapGestureRecognizer) { + guard let imageView = sender.view as? InAppImageOnlyView else { + return + } + + let action = imageView.action + onTapAction(action) + } + + private func setupLayers() { + guard inAppUIModel.content.type == .modal else { + return + } + + guard let layers = inAppUIModel.content.content?.background.layers.elements else { + return + } + + for layer in layers { + if let factory = layersFactories[layer.type] { + let layerView = factory.create(from: inAppUIModel.image, action: layer.action, in: view, with: self) + if let layerView = layerView { + self.layers.append(layerView) + view.addSubview(layerView) + factory.setupConstraints(for: layerView, in: view) + } + } + } + } + + private func setupElements() { + guard inAppUIModel.content.type == .modal else { + return + } + + guard let elements = inAppUIModel.content.content?.elements?.elements, + let inappView = layers.first(where: { $0 is InAppImageOnlyView }) else { + return + } + + for element in elements { + if let factory = elementFactories[element.type] { + let elementView = factory.create(from: element, in: inappView, with: self) + if let elementView = elementView { + self.elements.append(elementView) + inappView.addSubview(elementView) + factory.setupConstraints(for: elementView, from: element, in: inappView) + } + } + } + } +} diff --git a/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ModalViewFactory.swift b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ModalViewFactory.swift new file mode 100644 index 00000000..5609b111 --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ModalViewFactory.swift @@ -0,0 +1,26 @@ +// +// ModalViewFactory.swift +// Mindbox +// +// Created by vailence on 18.07.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit + +class ModalViewFactory: ViewFactoryProtocol { + + var myViewController: UIViewController? + + func create(inAppUIModel: InAppFormData, + onPresented: @escaping () -> Void, + onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, + onClose: @escaping () -> Void) -> UIViewController { + let viewController = ModalViewController(inAppUIModel: inAppUIModel, + onPresented: onPresented, + onTapAction: onTapAction, + onClose: onClose) + myViewController = viewController + return viewController + } +} diff --git a/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ViewFactoryProtocol.swift b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ViewFactoryProtocol.swift new file mode 100644 index 00000000..251b547d --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ViewFactoryProtocol.swift @@ -0,0 +1,17 @@ +// +// ViewFactoryProtocol.swift +// Mindbox +// +// Created by vailence on 18.07.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit +import Foundation + +protocol ViewFactoryProtocol { + func create(inAppUIModel: InAppFormData, + onPresented: @escaping () -> Void, + onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, + onClose: @escaping () -> Void) -> UIViewController +} diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index e723fa62..c56f4ebc 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4727 + 4728 diff --git a/Mindbox/Validators/String+Extensions.swift b/Mindbox/Validators/String+Extensions.swift index 8e4bb17a..08bc3245 100644 --- a/Mindbox/Validators/String+Extensions.swift +++ b/Mindbox/Validators/String+Extensions.swift @@ -32,4 +32,19 @@ extension String { return true } + + func isHexValid() -> Bool { + if !self.hasPrefix("#") { + return false + } + + let hexValue = String(self.dropFirst()) + + if hexValue.count != 6 { + return false + } + + let hexCharacterSet = CharacterSet(charactersIn: "0123456789ABCDEFabcdef") + return hexValue.unicodeScalars.allSatisfy { hexCharacterSet.contains($0) } + } } From babb020eeb2e5564310b8e7b386615c88e59750d Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Wed, 9 Aug 2023 17:18:51 +0600 Subject: [PATCH 12/35] MBX-2706 Add safe parsing --- .../KeyedDecodingContainer+Extensions.swift | 18 ++++++++++++++++++ .../ContentBackgroundLayerAction.swift | 4 ++-- .../ContentBackgroundLayerSource.swift | 2 +- .../ContentElement/ContentElement.swift | 4 ++-- .../ContentElementPositionMargin.swift | 8 ++++---- 5 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 Mindbox/Extensions/KeyedDecodingContainer+Extensions.swift diff --git a/Mindbox/Extensions/KeyedDecodingContainer+Extensions.swift b/Mindbox/Extensions/KeyedDecodingContainer+Extensions.swift new file mode 100644 index 00000000..6aa38592 --- /dev/null +++ b/Mindbox/Extensions/KeyedDecodingContainer+Extensions.swift @@ -0,0 +1,18 @@ +// +// KeyedDecodingContainer+Extensions.swift +// Mindbox +// +// Created by vailence on 09.08.2023. +// + +import Foundation + +extension KeyedDecodingContainer { + public func decodeIfPresentSafely(_ type: T.Type, forKey key: KeyedDecodingContainer.Key) throws -> T? { + do { + return try decode(T.self, forKey: key) + } catch DecodingError.typeMismatch, DecodingError.keyNotFound { + return nil + } + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift index 0b58099b..339d4bae 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift @@ -22,8 +22,8 @@ struct ContentBackgroundLayerAction: Decodable, Equatable { init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.type = try container.decode(LayerActionType.self, forKey: .type) - self.intentPayload = try container.decodeIfPresent(String.self, forKey: .intentPayload) - self.value = try container.decodeIfPresent(String.self, forKey: .value) + self.intentPayload = try container.decodeIfPresentSafely(String.self, forKey: .intentPayload) + self.value = try container.decodeIfPresentSafely(String.self, forKey: .value) if !ContentBackgroundLayerActionValidator().isValid(item: self) { throw DecodingError.dataCorruptedError( diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift index 41635116..576b3b34 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift @@ -20,7 +20,7 @@ struct ContentBackgroundLayerSource: Decodable, Equatable { init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.type = try container.decode(LayerSourceType.self, forKey: .type) - self.value = try container.decodeIfPresent(String.self, forKey: .value) + self.value = try container.decodeIfPresentSafely(String.self, forKey: .value) if !ContentBackgroundLayerSourceValidator().isValid(item: self) { throw DecodingError.dataCorruptedError( diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift index 7bf5943a..36f0defc 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift @@ -29,8 +29,8 @@ struct ContentElement: Decodable, Equatable { let container: KeyedDecodingContainer = try decoder.container(keyedBy: ContentElement.CodingKeys.self) self.type = try container.decode(ContentElementType.self, forKey: ContentElement.CodingKeys.type) - self.color = try container.decodeIfPresent(String.self, forKey: ContentElement.CodingKeys.color) - self.lineWidth = try container.decodeIfPresent(Int.self, forKey: ContentElement.CodingKeys.lineWidth) + self.color = try container.decodeIfPresentSafely(String.self, forKey: .color) + self.lineWidth = try container.decodeIfPresentSafely(Int.self, forKey: .lineWidth) self.size = try container.decodeIfPresent(ContentElementSize.self, forKey: .size) self.position = try container.decodeIfPresent(ContentElementPosition.self, forKey: .position) self.gravity = try container.decodeIfPresent(ContentElementGravity.self, forKey: .gravity) diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMargin.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMargin.swift index b33742a7..09a75943 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMargin.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMargin.swift @@ -27,10 +27,10 @@ struct ContentElementPositionMargin: Decodable, Equatable { let container: KeyedDecodingContainer = try decoder.container(keyedBy: ContentElementPositionMargin.CodingKeys.self) self.kind = try container.decode(PositionMarginKind.self, forKey: ContentElementPositionMargin.CodingKeys.kind) - self.top = try container.decodeIfPresent(Double.self, forKey: ContentElementPositionMargin.CodingKeys.top) - self.right = try container.decodeIfPresent(Double.self, forKey: ContentElementPositionMargin.CodingKeys.right) - self.left = try container.decodeIfPresent(Double.self, forKey: ContentElementPositionMargin.CodingKeys.left) - self.bottom = try container.decodeIfPresent(Double.self, forKey: ContentElementPositionMargin.CodingKeys.bottom) + self.top = try container.decodeIfPresentSafely(Double.self, forKey: ContentElementPositionMargin.CodingKeys.top) + self.right = try container.decodeIfPresentSafely(Double.self, forKey: ContentElementPositionMargin.CodingKeys.right) + self.left = try container.decodeIfPresentSafely(Double.self, forKey: ContentElementPositionMargin.CodingKeys.left) + self.bottom = try container.decodeIfPresentSafely(Double.self, forKey: ContentElementPositionMargin.CodingKeys.bottom) if !ContentElementPositionMarginValidator().isValid(item: self) { throw DecodingError.dataCorruptedError( From 1b665dd5db3bf22dd7882f7ca8db1c9c6e4718a3 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Wed, 9 Aug 2023 17:18:59 +0600 Subject: [PATCH 13/35] MBX-2706 Update version to 8 --- Mindbox/Utilities/Constants.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mindbox/Utilities/Constants.swift b/Mindbox/Utilities/Constants.swift index 6633a682..447a7c2a 100644 --- a/Mindbox/Utilities/Constants.swift +++ b/Mindbox/Utilities/Constants.swift @@ -30,6 +30,6 @@ enum Constants { } enum Versions { - static let sdkVersionNumeric = 7 + static let sdkVersionNumeric = 8 } } From bb2ab50b813f555c9d1d2102faa5f6fd47d1a051 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 9 Aug 2023 12:34:55 +0000 Subject: [PATCH 14/35] Update dependency macos to v13 --- .github/workflows/publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 46e47c18..a71487a2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,7 +10,7 @@ on: jobs: unit: - runs-on: macos-12 + runs-on: macos-13 steps: - uses: actions/checkout@v3 @@ -37,7 +37,7 @@ jobs: publish: needs: [unit] - runs-on: macos-11 + runs-on: macos-13 steps: - uses: actions/checkout@v2 - name: Release generation From 9c6e210caee0f361c29653ebc7efe257dfcf8515 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Wed, 9 Aug 2023 17:19:26 +0600 Subject: [PATCH 15/35] MBX-2706 Missed Inapp.Show added --- Mindbox.xcodeproj/project.pbxproj | 4 ++ Mindbox/DI/DependencyProvider.swift | 2 +- .../Config/InappModel/InappValidator.swift | 3 ++ .../InAppPresentationManager.swift | 49 +++++++++---------- .../PresentationDisplayUseCase.swift | 15 ++++++ 5 files changed, 47 insertions(+), 26 deletions(-) diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index eb257337..6d2514cc 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -353,6 +353,7 @@ F331DD0B2A83A56500222120 /* CrossView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCFD2A83A56500222120 /* CrossView.swift */; }; F331DD0C2A83A56500222120 /* ViewFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCFF2A83A56500222120 /* ViewFactoryProtocol.swift */; }; F331DD0D2A83A56500222120 /* ModalViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD002A83A56500222120 /* ModalViewFactory.swift */; }; + F331DD112A83CA7500222120 /* KeyedDecodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD102A83CA7400222120 /* KeyedDecodingContainer+Extensions.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -803,6 +804,7 @@ F331DCFD2A83A56500222120 /* CrossView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrossView.swift; sourceTree = ""; }; F331DCFF2A83A56500222120 /* ViewFactoryProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewFactoryProtocol.swift; sourceTree = ""; }; F331DD002A83A56500222120 /* ModalViewFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModalViewFactory.swift; sourceTree = ""; }; + F331DD102A83CA7400222120 /* KeyedDecodingContainer+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KeyedDecodingContainer+Extensions.swift"; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -2257,6 +2259,7 @@ F37613DF2A6A8CFF009F2EE4 /* Extensions */ = { isa = PBXGroup; children = ( + F331DD102A83CA7400222120 /* KeyedDecodingContainer+Extensions.swift */, F37613E02A6A8CFF009F2EE4 /* UIColor+Extensions.swift */, ); path = Extensions; @@ -2773,6 +2776,7 @@ F3482F1D2A65DC11002A41EC /* CompositeInappMessageDelegate.swift in Sources */, 33072F3C2664C713001F1AB2 /* CustomerSegmentationsResponse.swift in Sources */, 33072F3A2664C5CB001F1AB2 /* ManufacturerResponse.swift in Sources */, + F331DD112A83CA7500222120 /* KeyedDecodingContainer+Extensions.swift in Sources */, A1D017E92976CC1C00CD9F99 /* TargetingChecker.swift in Sources */, F397EEB52A44573600D48CEC /* Status.swift in Sources */, 6FDD1455266F7C9A00A50C35 /* PromotionTypeResponse.swift in Sources */, diff --git a/Mindbox/DI/DependencyProvider.swift b/Mindbox/DI/DependencyProvider.swift index d34e5c70..8fee9919 100644 --- a/Mindbox/DI/DependencyProvider.swift +++ b/Mindbox/DI/DependencyProvider.swift @@ -62,7 +62,7 @@ final class DependencyProvider: DependencyContainer { imageDownloadService = ImageDownloadService(imageDownloader: imageDownloader) abTestDeviceMixer = ABTestDeviceMixer() let tracker = InAppMessagesTracker(databaseRepository: databaseRepository) - let displayUseCase = PresentationDisplayUseCase() + let displayUseCase = PresentationDisplayUseCase(tracker: tracker) let actionUseCase = PresentationActionUseCase(tracker: tracker) let presentationManager = InAppPresentationManager(displayUseCase: displayUseCase, actionUseCase: actionUseCase) diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappValidator.swift index a54c6b81..9fea5641 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappValidator.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappValidator.swift @@ -7,6 +7,7 @@ // import Foundation +import MindboxLogger class InappValidator: Validator { typealias T = InApp @@ -19,10 +20,12 @@ class InappValidator: Validator { func isValid(item: InApp) -> Bool { if item.id.isEmpty { + Logger.common(message: "In-app id cannot be empty. In-app will be ignored.", level: .error, category: .inAppMessages) return false } if !sdkVersionValidator.isValid(item: item.sdkVersion) { + Logger.common(message: "Invalid SDK version for In-app. In-app with id \(item.id) will be ignored.", level: .error, category: .inAppMessages) return false } diff --git a/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift b/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift index 3b6ae635..d8350734 100644 --- a/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift +++ b/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift @@ -69,33 +69,32 @@ final class InAppPresentationManager: InAppPresentationManagerProtocol { } self?.displayUseCase.changeType(type: type) - self?.displayUseCase.presentInAppUIModel( - inAppUIModel: inAppFormData, - onPresented: onPresented, - onTapAction: { [weak self] action in - guard let action = action else { - return - } - - switch action.type { - case .redirectUrl: - if let value = action.value, let payload = action.intentPayload { - self?.actionUseCase.onTapAction(id: inAppFormData.inAppId, - value: value, - payload: payload, - onTap: onTapAction, - close: { - self?.displayUseCase.dismissInAppUIModel(onClose: onPresentationCompleted) - }) - } - case .unknown: - break + + self?.displayUseCase.presentInAppUIModel(inAppUIModel: inAppFormData, + onPresented: { + self?.displayUseCase.onPresented(id: inAppFormData.inAppId, onPresented) + }, onTapAction: { [weak self] action in + guard let action = action else { + return + } + + switch action.type { + case .redirectUrl: + if let value = action.value, let payload = action.intentPayload { + self?.actionUseCase.onTapAction(id: inAppFormData.inAppId, + value: value, + payload: payload, + onTap: onTapAction, + close: { + self?.displayUseCase.dismissInAppUIModel(onClose: onPresentationCompleted) + }) } - }, - onClose: { - self?.displayUseCase.dismissInAppUIModel(onClose: onPresentationCompleted) + case .unknown: + break } - ) + }, onClose: { + self?.displayUseCase.dismissInAppUIModel(onClose: onPresentationCompleted) + }) } } diff --git a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift index 5a206bbf..3b5a6d7d 100644 --- a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift +++ b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift @@ -15,6 +15,11 @@ final class PresentationDisplayUseCase { private var presentedVC: UIViewController? private var viewFactory: ViewFactoryProtocol? private var model: InAppFormData? + private var tracker: InAppMessagesTrackerProtocol + + init(tracker: InAppMessagesTrackerProtocol) { + self.tracker = tracker + } func presentInAppUIModel(inAppUIModel: InAppFormData, onPresented: @escaping () -> Void, onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, onClose: @escaping () -> Void) { guard let window = presentationStrategy?.getWindow() else { @@ -45,6 +50,16 @@ final class PresentationDisplayUseCase { self.model = nil } + func onPresented(id: String, _ completion: @escaping () -> Void) { + do { + try tracker.trackView(id: id) + Logger.common(message: "Track InApp.View. Id \(id)", level: .info, category: .notification) + } catch { + Logger.common(message: "Track InApp.View failed with error: \(error)", level: .error, category: .notification) + } + completion() + } + func changeType(type: ViewPresentationType) { switch type { case .modal: From 660f4011c20cac1ca276ffc12b0ae6628e003d9b Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Thu, 10 Aug 2023 11:22:33 +0600 Subject: [PATCH 16/35] MBX-2706 Polymorph variants --- Mindbox.xcodeproj/project.pbxproj | 38 +++++----- .../InAppConfigutationMapper.swift | 15 +++- .../InappModel/InappForm/InappForm.swift | 4 +- .../InappFormVariant/InappFormVariant.swift | 38 ---------- .../InappFormVariantType.swift | 21 ------ .../InappFormVariantValidator.swift | 25 ------- .../Variants/ModalFormVariant.swift | 12 ++++ .../InappFormVariant/iFormVariant.swift | 72 +++++++++++++++++++ .../InAppMessages/Models/InAppFormData.swift | 4 +- .../InAppPresentationManager.swift | 32 +++------ .../PresentationDisplayUseCase.swift | 37 +++++----- .../Views/ModalView/ModalViewController.swift | 29 ++++---- .../Views/ViewFactory/ModalViewFactory.swift | 25 ++++--- .../ViewFactory/ViewFactoryProtocol.swift | 6 +- Mindbox/Info.plist | 2 +- 15 files changed, 179 insertions(+), 181 deletions(-) delete mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariant.swift delete mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift delete mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantValidator.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/Variants/ModalFormVariant.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/iFormVariant.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 6d2514cc..3c631766 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -307,9 +307,6 @@ F31801FE2A386BE60021774C /* InappConfigResponseAbtestsInvalid.json in Resources */ = {isa = PBXBuildFile; fileRef = F31801FC2A386BE60021774C /* InappConfigResponseAbtestsInvalid.json */; }; F31801FF2A386BE60021774C /* InappConfigResponseMonitoringInvalid.json in Resources */ = {isa = PBXBuildFile; fileRef = F31801FD2A386BE60021774C /* InappConfigResponseMonitoringInvalid.json */; }; F331DC842A80983000222120 /* FailableDecodableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC832A80983000222120 /* FailableDecodableArray.swift */; }; - F331DCBB2A80993600222120 /* InappFormVariantValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC8F2A80993600222120 /* InappFormVariantValidator.swift */; }; - F331DCBC2A80993600222120 /* InappFormVariant.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC902A80993600222120 /* InappFormVariant.swift */; }; - F331DCBD2A80993600222120 /* InappFormVariantType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC922A80993600222120 /* InappFormVariantType.swift */; }; F331DCBE2A80993600222120 /* ContentElementType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC962A80993600222120 /* ContentElementType.swift */; }; F331DCBF2A80993600222120 /* ContentElementSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC982A80993600222120 /* ContentElementSize.swift */; }; F331DCC02A80993600222120 /* ContentElementSizeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC9A2A80993600222120 /* ContentElementSizeType.swift */; }; @@ -354,6 +351,8 @@ F331DD0C2A83A56500222120 /* ViewFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCFF2A83A56500222120 /* ViewFactoryProtocol.swift */; }; F331DD0D2A83A56500222120 /* ModalViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD002A83A56500222120 /* ModalViewFactory.swift */; }; F331DD112A83CA7500222120 /* KeyedDecodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD102A83CA7400222120 /* KeyedDecodingContainer+Extensions.swift */; }; + F331DD162A840D1700222120 /* iFormVariant.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD152A840D1700222120 /* iFormVariant.swift */; }; + F331DD192A840D2100222120 /* ModalFormVariant.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD182A840D2100222120 /* ModalFormVariant.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -758,9 +757,6 @@ F31801FC2A386BE60021774C /* InappConfigResponseAbtestsInvalid.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = InappConfigResponseAbtestsInvalid.json; sourceTree = ""; }; F31801FD2A386BE60021774C /* InappConfigResponseMonitoringInvalid.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = InappConfigResponseMonitoringInvalid.json; sourceTree = ""; }; F331DC832A80983000222120 /* FailableDecodableArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailableDecodableArray.swift; sourceTree = ""; }; - F331DC8F2A80993600222120 /* InappFormVariantValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappFormVariantValidator.swift; sourceTree = ""; }; - F331DC902A80993600222120 /* InappFormVariant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappFormVariant.swift; sourceTree = ""; }; - F331DC922A80993600222120 /* InappFormVariantType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappFormVariantType.swift; sourceTree = ""; }; F331DC962A80993600222120 /* ContentElementType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementType.swift; sourceTree = ""; }; F331DC982A80993600222120 /* ContentElementSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementSize.swift; sourceTree = ""; }; F331DC9A2A80993600222120 /* ContentElementSizeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementSizeType.swift; sourceTree = ""; }; @@ -805,6 +801,8 @@ F331DCFF2A83A56500222120 /* ViewFactoryProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewFactoryProtocol.swift; sourceTree = ""; }; F331DD002A83A56500222120 /* ModalViewFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModalViewFactory.swift; sourceTree = ""; }; F331DD102A83CA7400222120 /* KeyedDecodingContainer+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KeyedDecodingContainer+Extensions.swift"; sourceTree = ""; }; + F331DD152A840D1700222120 /* iFormVariant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = iFormVariant.swift; sourceTree = ""; }; + F331DD182A840D2100222120 /* ModalFormVariant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModalFormVariant.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -1948,8 +1946,8 @@ F331DC8D2A80993600222120 /* InappForm */ = { isa = PBXGroup; children = ( - F331DC8E2A80993600222120 /* InappFormVariant */, F331DCB82A80993600222120 /* InappForm.swift */, + F331DC8E2A80993600222120 /* InappFormVariant */, ); path = InappForm; sourceTree = ""; @@ -1957,22 +1955,13 @@ F331DC8E2A80993600222120 /* InappFormVariant */ = { isa = PBXGroup; children = ( - F331DC8F2A80993600222120 /* InappFormVariantValidator.swift */, - F331DC902A80993600222120 /* InappFormVariant.swift */, - F331DC912A80993600222120 /* InappFormVariantType */, + F331DD152A840D1700222120 /* iFormVariant.swift */, + F331DD172A840D2100222120 /* Variants */, F331DC932A80993600222120 /* InappFormVariantContent */, ); path = InappFormVariant; sourceTree = ""; }; - F331DC912A80993600222120 /* InappFormVariantType */ = { - isa = PBXGroup; - children = ( - F331DC922A80993600222120 /* InappFormVariantType.swift */, - ); - path = InappFormVariantType; - sourceTree = ""; - }; F331DC932A80993600222120 /* InappFormVariantContent */ = { isa = PBXGroup; children = ( @@ -2234,6 +2223,14 @@ path = ViewFactory; sourceTree = ""; }; + F331DD172A840D2100222120 /* Variants */ = { + isa = PBXGroup; + children = ( + F331DD182A840D2100222120 /* ModalFormVariant.swift */, + ); + path = Variants; + sourceTree = ""; + }; F3482F142A65DBA0002A41EC /* InappMessagesDelegate */ = { isa = PBXGroup; children = ( @@ -2687,7 +2684,6 @@ F331DCC12A80993600222120 /* ContentElement.swift in Sources */, 847F581725C8981F00147A9A /* HTTPURLResponseStatusCodeValidator.swift in Sources */, 33072F442664CC6C001F1AB2 /* IssuedPointOfContactResponse.swift in Sources */, - F331DCBC2A80993600222120 /* InappFormVariant.swift in Sources */, A17958762978AEC600609E91 /* OrTargetingChecker.swift in Sources */, F331DCC22A80993600222120 /* ContentElementGravity.swift in Sources */, 334F3AE1264C199900A6AC00 /* PromoCodeRequest.swift in Sources */, @@ -2699,6 +2695,7 @@ 334F3AED264C199900A6AC00 /* SubscriptionRequest.swift in Sources */, F331DCD32A80993600222120 /* InappForm.swift in Sources */, 334F3AF4264C199900A6AC00 /* ProductRequest.swift in Sources */, + F331DD162A840D1700222120 /* iFormVariant.swift in Sources */, 84C65E6825D4FE41008996FA /* BodyEncoder.swift in Sources */, 9B33F3CD28D0986900A3FFF9 /* CustomerSegmentsAPI.swift in Sources */, B3F4F982268EEAC90092EC3C /* AmountBenefitsResponse.swift in Sources */, @@ -2802,7 +2799,6 @@ A1D017E72976CBE100CD9F99 /* TargetingModel.swift in Sources */, 9B4F9DF328D088A0002C9CF0 /* InAppFormData.swift in Sources */, F3482F232A65DC37002A41EC /* InAppMessagesDelegate.swift in Sources */, - F331DCBD2A80993600222120 /* InappFormVariantType.swift in Sources */, 33072F2E2664C24F001F1AB2 /* AreaResponse.swift in Sources */, 84DEE8A925CC031200C98CC7 /* MBDatabase.xcdatamodeld in Sources */, A192786A29D38D2000CDB53D /* CheckerFactory.swift in Sources */, @@ -2895,7 +2891,6 @@ 33C81EA4264145CD00863380 /* TimerManager.swift in Sources */, B3F4F981268EEAC90092EC3C /* AmountBenefisTypeResponse.swift in Sources */, A11FBE8929DD76BF00F5FB7B /* InAppMessagesEventSender.swift in Sources */, - F331DCBB2A80993600222120 /* InappFormVariantValidator.swift in Sources */, 33072F422664CB81001F1AB2 /* PromoCodeResponse.swift in Sources */, 339ACE50262DAC74003590D2 /* CustomEvent.swift in Sources */, 314B390025AEE96F00E947B9 /* CoreController.swift in Sources */, @@ -2923,6 +2918,7 @@ 6FDD1441266F7C0600A50C35 /* AppliedPromotionTypeResponse.swift in Sources */, F3482F1F2A65DC20002A41EC /* CopyInappMessageDelegate.swift in Sources */, 33EBF0B0264E6283002A35D5 /* SessionManager.swift in Sources */, + F331DD192A840D2100222120 /* ModalFormVariant.swift in Sources */, 337C7952265646D10092B580 /* OperationBodyRequestType.swift in Sources */, 9B9C95312921116F00BB29DA /* UUIDDebugService.swift in Sources */, 6FDD1443266F7C1600A50C35 /* BalanceTypeReponse.swift in Sources */, diff --git a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift index 32e68d23..826605d2 100644 --- a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift +++ b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift @@ -266,11 +266,20 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { continue } - guard let image = inapp.content.content?.background.layers.elements.first(where: { $0.type == .image }), - let imageValue = image.source?.value else { - continue + var imageValue: String? + switch inapp.content { + case .modal(let modalModel): + guard let image = modalModel.content.background.layers.elements.first(where: { $0.type == .image }), + let modalImageValue = image.source?.value else { + continue + } + imageValue = modalImageValue + case .unknown: + continue } + guard let imageValue = imageValue else { continue } + group.enter() Logger.common(message: "Starting inapp processing. [ID]: \(inapp.inAppId)", level: .debug, category: .inAppMessages) diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappForm.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappForm.swift index 5f8e6dce..0455c31e 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappForm.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappForm.swift @@ -9,7 +9,7 @@ import Foundation struct InAppForm: Decodable, Equatable { - let variants: FailableDecodableArray + let variants: FailableDecodableArray enum CodingKeys: String, CodingKey { case variants @@ -17,7 +17,7 @@ struct InAppForm: Decodable, Equatable { init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - let variants = try container.decode(FailableDecodableArray.self, forKey: .variants) + let variants = try container.decode(FailableDecodableArray.self, forKey: .variants) if variants.elements.isEmpty { throw DecodingError.dataCorruptedError( diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariant.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariant.swift deleted file mode 100644 index a5d189f2..00000000 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariant.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// InappFormVariant.swift -// Mindbox -// -// Created by vailence on 03.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import Foundation - -struct InappFormVariant: Decodable, Equatable { - let type: InappFormVariantType - let content: InappFormVariantContent? - - enum CodingKeys: String, CodingKey { - case type = "$type" - case content - } - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - type = try container.decode(InappFormVariantType.self, forKey: .type) - content = try container.decodeIfPresent(InappFormVariantContent.self, forKey: .content) - - if !InappFormVariantValidator().isValid(item: self) { - throw DecodingError.dataCorruptedError( - forKey: .type, - in: container, - debugDescription: "Invalid Variant" - ) - } - } - - internal init(type: InappFormVariantType, content: InappFormVariantContent? = nil) { - self.type = type - self.content = content - } -} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift deleted file mode 100644 index dcbcce03..00000000 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantType/InappFormVariantType.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// InappFormVariantType.swift -// Mindbox -// -// Created by vailence on 03.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import Foundation - -enum InappFormVariantType: String, Decodable, Equatable { - case modal - case snackbar - case unknown - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = InappFormVariantType(rawValue: rawValue) ?? .unknown - } -} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantValidator.swift deleted file mode 100644 index f181f5cb..00000000 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantValidator.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// InappFormVariantValidator.swift -// Mindbox -// -// Created by vailence on 03.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import Foundation - -class InappFormVariantValidator: Validator { - typealias T = InappFormVariant - - func isValid(item: InappFormVariant) -> Bool { - if item.type == .unknown { - return false - } - - if item.content == nil && item.type == .modal { - return false - } - - return true - } -} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/Variants/ModalFormVariant.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/Variants/ModalFormVariant.swift new file mode 100644 index 00000000..a37bb2e9 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/Variants/ModalFormVariant.swift @@ -0,0 +1,12 @@ +// +// ModalFormVariant.swift +// FirebaseCore +// +// Created by vailence on 10.08.2023. +// + +import Foundation + +struct ModalFormVariant: iFormVariant, Decodable, Equatable { + let content: InappFormVariantContent +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/iFormVariant.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/iFormVariant.swift new file mode 100644 index 00000000..ea70810c --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/iFormVariant.swift @@ -0,0 +1,72 @@ +// +// iFormVariant.swift +// Mindbox +// +// Created by vailence on 03.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +protocol iFormVariant: Decodable, Equatable { } + +enum MindboxFormVariantType: String, Decodable { + case modal + case unknown + + init(from decoder: Decoder) throws { + let container: SingleValueDecodingContainer = try decoder.singleValueContainer() + let type: String = try container.decode(String.self) + self = MindboxFormVariantType(rawValue: type) ?? .unknown + } +} + +enum MindboxFormVariant: Decodable, Hashable, Equatable { + case modal(ModalFormVariant) + case unknown + + enum CodingKeys: String, CodingKey { + case type = "$type" + } + + static func == (lhs: MindboxFormVariant, rhs: MindboxFormVariant) -> Bool { + switch (lhs, rhs) { + case (.modal, .modal): return true + case (.unknown, .unknown): return true + default: return false + } + } + + func hash(into hasher: inout Hasher) { + switch self { + case .modal: hasher.combine("modal") + case .unknown: hasher.combine("unknown") + } + } + + init(from decoder: Decoder) throws { + let container: KeyedDecodingContainer = try decoder.container( + keyedBy: CodingKeys.self) + guard let type = try? container.decode(MindboxFormVariantType.self, forKey: .type) else { + throw DecodingError.dataCorruptedError( + forKey: .type, + in: container, + debugDescription: "Form variant is unknown. Ignored." + ) + } + + let variantContainer: SingleValueDecodingContainer = try decoder.singleValueContainer() + + switch type { + case .modal: + let modalVariant = try variantContainer.decode(ModalFormVariant.self) + self = .modal(modalVariant) + case .unknown: + throw DecodingError.dataCorruptedError( + forKey: .type, + in: container, + debugDescription: "Form variant is unknown. Ignored." + ) + } + } +} diff --git a/Mindbox/InAppMessages/Models/InAppFormData.swift b/Mindbox/InAppMessages/Models/InAppFormData.swift index 29b62759..fb19ebfd 100644 --- a/Mindbox/InAppMessages/Models/InAppFormData.swift +++ b/Mindbox/InAppMessages/Models/InAppFormData.swift @@ -13,10 +13,10 @@ import UIKit struct InAppFormData { let inAppId: String let image: UIImage - let content: InappFormVariant + let content: MindboxFormVariant } struct InAppTransitionData: Equatable { let inAppId: String - let content: InappFormVariant + let content: MindboxFormVariant } diff --git a/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift b/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift index d8350734..5298b5ab 100644 --- a/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift +++ b/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift @@ -33,12 +33,7 @@ protocol InAppPresentationManagerProtocol: AnyObject { enum InAppPresentationError { case failedToLoadImages case failedToLoadWindow -} - -enum ViewPresentationType { - case modal - case topSnackbar - case bottomSnackbar + case failed(String) } typealias InAppMessageTapAction = (_ tapLink: URL?, _ payload: String) -> Void @@ -64,20 +59,20 @@ final class InAppPresentationManager: InAppPresentationManagerProtocol { onError: @escaping (InAppPresentationError) -> Void ) { DispatchQueue.main.async { [weak self] in - guard let type = self?.getType(inappType: inAppFormData.content.type) else { + guard let self = self else { + onError(.failed("Self guard not passed.")) return } - self?.displayUseCase.changeType(type: type) - - self?.displayUseCase.presentInAppUIModel(inAppUIModel: inAppFormData, + self.displayUseCase.changeType(model: inAppFormData.content) + self.displayUseCase.presentInAppUIModel(model: inAppFormData, onPresented: { - self?.displayUseCase.onPresented(id: inAppFormData.inAppId, onPresented) + self.displayUseCase.onPresented(id: inAppFormData.inAppId, onPresented) }, onTapAction: { [weak self] action in guard let action = action else { return } - + switch action.type { case .redirectUrl: if let value = action.value, let payload = action.intentPayload { @@ -93,19 +88,8 @@ final class InAppPresentationManager: InAppPresentationManagerProtocol { break } }, onClose: { - self?.displayUseCase.dismissInAppUIModel(onClose: onPresentationCompleted) + self.displayUseCase.dismissInAppUIModel(onClose: onPresentationCompleted) }) } } - - func getType(inappType: InappFormVariantType) -> ViewPresentationType? { - switch inappType { - case .modal: - return .modal - case .snackbar: - return .topSnackbar - case .unknown: - return nil - } - } } diff --git a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift index 3b5a6d7d..20d47ee2 100644 --- a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift +++ b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift @@ -13,30 +13,36 @@ final class PresentationDisplayUseCase { private var presentationStrategy: PresentationStrategyProtocol? private var presentedVC: UIViewController? - private var viewFactory: ViewFactoryProtocol? private var model: InAppFormData? + private var factory: ViewFactoryProtocol? private var tracker: InAppMessagesTrackerProtocol init(tracker: InAppMessagesTrackerProtocol) { self.tracker = tracker } - func presentInAppUIModel(inAppUIModel: InAppFormData, onPresented: @escaping () -> Void, onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, onClose: @escaping () -> Void) { + func presentInAppUIModel(model: InAppFormData, onPresented: @escaping () -> Void, onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, onClose: @escaping () -> Void) { guard let window = presentationStrategy?.getWindow() else { Logger.common(message: "In-app modal window creating failed") return } - model = inAppUIModel - guard let viewController = viewFactory?.create(inAppUIModel: inAppUIModel, - onPresented: onPresented, - onTapAction: onTapAction, - onClose: onClose) else { + guard let factory = self.factory else { + Logger.common(message: "Factory does not exists.", level: .error, category: .general) + return + } + + guard let viewController = factory.create(model: model.content, + id: model.inAppId, + image: model.image, + onPresented: onPresented, + onTapAction: onTapAction, + onClose: onClose) else { return } presentedVC = viewController - presentationStrategy?.present(id: inAppUIModel.inAppId, in: window, using: viewController) + presentationStrategy?.present(id: model.inAppId, in: window, using: viewController) } func dismissInAppUIModel(onClose: @escaping () -> Void) { @@ -45,7 +51,6 @@ final class PresentationDisplayUseCase { } presentationStrategy?.dismiss(viewController: presentedVC) onClose() - self.viewFactory = nil self.presentedVC = nil self.model = nil } @@ -60,13 +65,13 @@ final class PresentationDisplayUseCase { completion() } - func changeType(type: ViewPresentationType) { - switch type { - case .modal: - self.presentationStrategy = ModalPresentationStrategy() - self.viewFactory = ModalViewFactory() - default: - return + func changeType(model: MindboxFormVariant) { + switch model { + case .modal: + self.presentationStrategy = ModalPresentationStrategy() + self.factory = ModalViewFactory() + default: + break } } } diff --git a/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift b/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift index df70d10e..721b059f 100644 --- a/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift +++ b/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift @@ -20,12 +20,16 @@ final class ModalViewController: UIViewController { ] init( - inAppUIModel: InAppFormData, + model: ModalFormVariant, + id: String, + image: UIImage, onPresented: @escaping () -> Void, onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, onClose: @escaping () -> Void ) { - self.inAppUIModel = inAppUIModel + self.model = model + self.id = id + self.image = image self.onPresented = onPresented self.onClose = onClose self.onTapAction = onTapAction @@ -36,7 +40,9 @@ final class ModalViewController: UIViewController { fatalError("init(coder:) has not been implemented") } - private let inAppUIModel: InAppFormData + private let model: ModalFormVariant + private let id: String + private let image: UIImage private let onPresented: () -> Void private let onClose: () -> Void private let onTapAction: (ContentBackgroundLayerAction?) -> Void @@ -90,17 +96,10 @@ final class ModalViewController: UIViewController { } private func setupLayers() { - guard inAppUIModel.content.type == .modal else { - return - } - - guard let layers = inAppUIModel.content.content?.background.layers.elements else { - return - } - + let layers = model.content.background.layers.elements for layer in layers { if let factory = layersFactories[layer.type] { - let layerView = factory.create(from: inAppUIModel.image, action: layer.action, in: view, with: self) + let layerView = factory.create(from: self.image, action: layer.action, in: view, with: self) if let layerView = layerView { self.layers.append(layerView) view.addSubview(layerView) @@ -111,11 +110,7 @@ final class ModalViewController: UIViewController { } private func setupElements() { - guard inAppUIModel.content.type == .modal else { - return - } - - guard let elements = inAppUIModel.content.content?.elements?.elements, + guard let elements = model.content.elements?.elements, let inappView = layers.first(where: { $0 is InAppImageOnlyView }) else { return } diff --git a/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ModalViewFactory.swift b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ModalViewFactory.swift index 5609b111..64dbc02d 100644 --- a/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ModalViewFactory.swift +++ b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ModalViewFactory.swift @@ -9,18 +9,25 @@ import UIKit class ModalViewFactory: ViewFactoryProtocol { - var myViewController: UIViewController? - func create(inAppUIModel: InAppFormData, + func create(model: MindboxFormVariant, + id: String, + image: UIImage, onPresented: @escaping () -> Void, onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, - onClose: @escaping () -> Void) -> UIViewController { - let viewController = ModalViewController(inAppUIModel: inAppUIModel, - onPresented: onPresented, - onTapAction: onTapAction, - onClose: onClose) - myViewController = viewController - return viewController + onClose: @escaping () -> Void) -> UIViewController? { + if case .modal(let modalFormVariant) = model { + let viewController = ModalViewController(model: modalFormVariant, + id: id, + image: image, + onPresented: onPresented, + onTapAction: onTapAction, + onClose: onClose) + myViewController = viewController + return viewController + } + + return nil } } diff --git a/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ViewFactoryProtocol.swift b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ViewFactoryProtocol.swift index 251b547d..af67960e 100644 --- a/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ViewFactoryProtocol.swift +++ b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ViewFactoryProtocol.swift @@ -10,8 +10,10 @@ import UIKit import Foundation protocol ViewFactoryProtocol { - func create(inAppUIModel: InAppFormData, + func create(model: MindboxFormVariant, + id: String, + image: UIImage, onPresented: @escaping () -> Void, onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, - onClose: @escaping () -> Void) -> UIViewController + onClose: @escaping () -> Void) -> UIViewController? } diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index c56f4ebc..b4ca3eb9 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4728 + 4737 From a8fe5f1ab0701e9b0173bde3cd131f9b85d88a78 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Thu, 10 Aug 2023 12:00:34 +0600 Subject: [PATCH 17/35] MBX-2706 Polymorph Layers --- Mindbox.xcodeproj/project.pbxproj | 44 ++++-------- .../InAppConfigutationMapper.swift | 11 ++- .../ContentBackgroundLayer.swift | 68 +++++++++++++------ ...ontentBackgroundLayerActionValidator.swift | 25 ------- .../LayerActionType/LayerActionType.swift | 20 ------ .../ContentBackgroundLayerType.swift | 20 ------ .../ContentBackgroundLayerValidator.swift | 29 -------- .../Layers/ImageContentBackgroundLayer.swift | 14 ++++ .../LayersFactory/LayerFactory.swift | 16 +++-- .../Views/ModalView/ModalViewController.swift | 12 +++- Mindbox/Info.plist | 2 +- 11 files changed, 104 insertions(+), 157 deletions(-) delete mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerActionValidator.swift delete mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift delete mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift delete mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerValidator.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/Layers/ImageContentBackgroundLayer.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 3c631766..dd7794cc 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -318,14 +318,10 @@ F331DCC62A80993600222120 /* ContentElementPositionMarginValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA42A80993600222120 /* ContentElementPositionMarginValidator.swift */; }; F331DCC72A80993600222120 /* ContentElementValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA52A80993600222120 /* ContentElementValidator.swift */; }; F331DCC82A80993600222120 /* ContentBackgroundLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA82A80993600222120 /* ContentBackgroundLayer.swift */; }; - F331DCC92A80993600222120 /* ContentBackgroundLayerActionValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCAA2A80993600222120 /* ContentBackgroundLayerActionValidator.swift */; }; - F331DCCA2A80993600222120 /* LayerActionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCAC2A80993600222120 /* LayerActionType.swift */; }; F331DCCB2A80993600222120 /* ContentBackgroundLayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCAD2A80993600222120 /* ContentBackgroundLayerAction.swift */; }; F331DCCC2A80993600222120 /* ContentBackgroundLayerSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCAF2A80993600222120 /* ContentBackgroundLayerSource.swift */; }; F331DCCD2A80993600222120 /* LayerSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB12A80993600222120 /* LayerSourceType.swift */; }; F331DCCE2A80993600222120 /* ContentBackgroundLayerSourceValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB22A80993600222120 /* ContentBackgroundLayerSourceValidator.swift */; }; - F331DCCF2A80993600222120 /* ContentBackgroundLayerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB42A80993600222120 /* ContentBackgroundLayerType.swift */; }; - F331DCD02A80993600222120 /* ContentBackgroundLayerValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB52A80993600222120 /* ContentBackgroundLayerValidator.swift */; }; F331DCD12A80993600222120 /* ContentBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB62A80993600222120 /* ContentBackground.swift */; }; F331DCD22A80993600222120 /* InappFormVariantContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB72A80993600222120 /* InappFormVariantContent.swift */; }; F331DCD32A80993600222120 /* InappForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB82A80993600222120 /* InappForm.swift */; }; @@ -353,6 +349,7 @@ F331DD112A83CA7500222120 /* KeyedDecodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD102A83CA7400222120 /* KeyedDecodingContainer+Extensions.swift */; }; F331DD162A840D1700222120 /* iFormVariant.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD152A840D1700222120 /* iFormVariant.swift */; }; F331DD192A840D2100222120 /* ModalFormVariant.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD182A840D2100222120 /* ModalFormVariant.swift */; }; + F331DD1C2A84B5EF00222120 /* ImageContentBackgroundLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD1B2A84B5EF00222120 /* ImageContentBackgroundLayer.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -768,14 +765,10 @@ F331DCA42A80993600222120 /* ContentElementPositionMarginValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementPositionMarginValidator.swift; sourceTree = ""; }; F331DCA52A80993600222120 /* ContentElementValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementValidator.swift; sourceTree = ""; }; F331DCA82A80993600222120 /* ContentBackgroundLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayer.swift; sourceTree = ""; }; - F331DCAA2A80993600222120 /* ContentBackgroundLayerActionValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerActionValidator.swift; sourceTree = ""; }; - F331DCAC2A80993600222120 /* LayerActionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayerActionType.swift; sourceTree = ""; }; F331DCAD2A80993600222120 /* ContentBackgroundLayerAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerAction.swift; sourceTree = ""; }; F331DCAF2A80993600222120 /* ContentBackgroundLayerSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerSource.swift; sourceTree = ""; }; F331DCB12A80993600222120 /* LayerSourceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayerSourceType.swift; sourceTree = ""; }; F331DCB22A80993600222120 /* ContentBackgroundLayerSourceValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerSourceValidator.swift; sourceTree = ""; }; - F331DCB42A80993600222120 /* ContentBackgroundLayerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerType.swift; sourceTree = ""; }; - F331DCB52A80993600222120 /* ContentBackgroundLayerValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerValidator.swift; sourceTree = ""; }; F331DCB62A80993600222120 /* ContentBackground.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackground.swift; sourceTree = ""; }; F331DCB72A80993600222120 /* InappFormVariantContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappFormVariantContent.swift; sourceTree = ""; }; F331DCB82A80993600222120 /* InappForm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappForm.swift; sourceTree = ""; }; @@ -803,6 +796,7 @@ F331DD102A83CA7400222120 /* KeyedDecodingContainer+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KeyedDecodingContainer+Extensions.swift"; sourceTree = ""; }; F331DD152A840D1700222120 /* iFormVariant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = iFormVariant.swift; sourceTree = ""; }; F331DD182A840D2100222120 /* ModalFormVariant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModalFormVariant.swift; sourceTree = ""; }; + F331DD1B2A84B5EF00222120 /* ImageContentBackgroundLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContentBackgroundLayer.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -2058,10 +2052,9 @@ isa = PBXGroup; children = ( F331DCA82A80993600222120 /* ContentBackgroundLayer.swift */, + F331DD1A2A84B5D200222120 /* Layers */, F331DCA92A80993600222120 /* ContentBackgroundLayerAction */, F331DCAE2A80993600222120 /* ContentBackgroundSource */, - F331DCB32A80993600222120 /* ContentBackgroundLayerType */, - F331DCB52A80993600222120 /* ContentBackgroundLayerValidator.swift */, ); path = ContentBackgroundLayer; sourceTree = ""; @@ -2069,21 +2062,11 @@ F331DCA92A80993600222120 /* ContentBackgroundLayerAction */ = { isa = PBXGroup; children = ( - F331DCAA2A80993600222120 /* ContentBackgroundLayerActionValidator.swift */, - F331DCAB2A80993600222120 /* LayerActionType */, F331DCAD2A80993600222120 /* ContentBackgroundLayerAction.swift */, ); path = ContentBackgroundLayerAction; sourceTree = ""; }; - F331DCAB2A80993600222120 /* LayerActionType */ = { - isa = PBXGroup; - children = ( - F331DCAC2A80993600222120 /* LayerActionType.swift */, - ); - path = LayerActionType; - sourceTree = ""; - }; F331DCAE2A80993600222120 /* ContentBackgroundSource */ = { isa = PBXGroup; children = ( @@ -2102,14 +2085,6 @@ path = LayerSourceType; sourceTree = ""; }; - F331DCB32A80993600222120 /* ContentBackgroundLayerType */ = { - isa = PBXGroup; - children = ( - F331DCB42A80993600222120 /* ContentBackgroundLayerType.swift */, - ); - path = ContentBackgroundLayerType; - sourceTree = ""; - }; F331DCDA2A80AC3300222120 /* ModelValidatorTests */ = { isa = PBXGroup; children = ( @@ -2231,6 +2206,14 @@ path = Variants; sourceTree = ""; }; + F331DD1A2A84B5D200222120 /* Layers */ = { + isa = PBXGroup; + children = ( + F331DD1B2A84B5EF00222120 /* ImageContentBackgroundLayer.swift */, + ); + path = Layers; + sourceTree = ""; + }; F3482F142A65DBA0002A41EC /* InappMessagesDelegate */ = { isa = PBXGroup; children = ( @@ -2680,7 +2663,6 @@ buildActionMask = 2147483647; files = ( F331DD062A83A56500222120 /* ModalViewController.swift in Sources */, - F331DCC92A80993600222120 /* ContentBackgroundLayerActionValidator.swift in Sources */, F331DCC12A80993600222120 /* ContentElement.swift in Sources */, 847F581725C8981F00147A9A /* HTTPURLResponseStatusCodeValidator.swift in Sources */, 33072F442664CC6C001F1AB2 /* IssuedPointOfContactResponse.swift in Sources */, @@ -2709,6 +2691,7 @@ F3A8B99A2A3A471800E9C055 /* ABTestVariantsValidator.swift in Sources */, 33E42E5C268323E60046CBCB /* CashdeskRequest.swift in Sources */, 84A312B725DA64C60096A017 /* BGTaskManager.swift in Sources */, + F331DD1C2A84B5EF00222120 /* ImageContentBackgroundLayer.swift in Sources */, F3A8B9A52A3A6F2800E9C055 /* MonitoringModel.swift in Sources */, 334F3AE8264C199900A6AC00 /* ItemRequest.swift in Sources */, 84CC799325CACF0C00C062BD /* EventRepository.swift in Sources */, @@ -2820,7 +2803,6 @@ F331DCD22A80993600222120 /* InappFormVariantContent.swift in Sources */, 337A47362654F995000DC613 /* OperationBodyRequest.swift in Sources */, 9BC24E6F28F694EA00C2619C /* SDKVersionProvider.swift in Sources */, - F331DCCF2A80993600222120 /* ContentBackgroundLayerType.swift in Sources */, 6FDD144D266F7C6600A50C35 /* AmountTypeResponse.swift in Sources */, 6FDD1461266F7CE300A50C35 /* DiscountAmountTypeResponse.swift in Sources */, 6FDD144F266F7C7000A50C35 /* UsedResponse.swift in Sources */, @@ -2839,7 +2821,6 @@ A192787229D442D900CDB53D /* ProductSegmentChecker.swift in Sources */, A153E03D29BAFEC1003C34D4 /* CustomOperationChecker.swift in Sources */, 33072F402664CB08001F1AB2 /* ProductGroupResponse.swift in Sources */, - F331DCCA2A80993600222120 /* LayerActionType.swift in Sources */, A1D017F52976FC2B00CD9F99 /* InternalTargetingChecker.swift in Sources */, 6F1EAA16266A670E007A335B /* ProductListItemsResponse.swift in Sources */, 6FDD143F266F7BF900A50C35 /* AppliedPromotionResponse.swift in Sources */, @@ -2856,7 +2837,6 @@ A1D017F02976CC8600CD9F99 /* OrTargeting.swift in Sources */, 334F3AEA264C199900A6AC00 /* CustomerRequest.swift in Sources */, A15D701629AF810E007131E7 /* SDKLogsRequest.swift in Sources */, - F331DCD02A80993600222120 /* ContentBackgroundLayerValidator.swift in Sources */, 33072F362664C4D7001F1AB2 /* RecommendationRequest.swift in Sources */, 84DC49CC25D1832300D5D758 /* EventWrapper.swift in Sources */, 317054C425AEF88E00AE624C /* DependencyProvider.swift in Sources */, diff --git a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift index 826605d2..39e867e5 100644 --- a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift +++ b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift @@ -269,11 +269,16 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { var imageValue: String? switch inapp.content { case .modal(let modalModel): - guard let image = modalModel.content.background.layers.elements.first(where: { $0.type == .image }), - let modalImageValue = image.source?.value else { + guard modalModel.content.background.layers.elements.first(where: { + switch $0 { + case .image(let imageModel): + imageValue = imageModel.source.value + return true + case .unknown: return false + } + }) != nil else { continue } - imageValue = modalImageValue case .unknown: continue } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift index 5fa266bc..17858596 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift @@ -8,35 +8,65 @@ import Foundation -struct ContentBackgroundLayer: Decodable, Equatable { - let type: ContentBackgroundLayerType - let action: ContentBackgroundLayerAction? - let source: ContentBackgroundLayerSource? +protocol ContentBackgroundLayerProtocol: Decodable, Equatable { } +enum ContentBackgroundLayerType: String, Decodable { + case image + case unknown + + init(from decoder: Decoder) throws { + let container: SingleValueDecodingContainer = try decoder.singleValueContainer() + let type: String = try container.decode(String.self) + self = ContentBackgroundLayerType(rawValue: type) ?? .unknown + } +} + +enum ContentBackgroundLayer: Decodable, Hashable, Equatable { + case image(ImageContentBackgroundLayer) + case unknown + enum CodingKeys: String, CodingKey { case type = "$type" - case action - case source + } + + static func == (lhs: ContentBackgroundLayer, rhs: ContentBackgroundLayer) -> Bool { + switch (lhs, rhs) { + case (.image, .image): return true + case (.unknown, .unknown): return true + default: return false + } + } + + func hash(into hasher: inout Hasher) { + switch self { + case .image: hasher.combine("image") + case .unknown: hasher.combine("unknown") + } } init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - type = try container.decode(ContentBackgroundLayerType.self, forKey: .type) - action = try container.decodeIfPresent(ContentBackgroundLayerAction.self, forKey: .action) - source = try container.decodeIfPresent(ContentBackgroundLayerSource.self, forKey: .source) - - if !ContentBackgroundLayerValidator().isValid(item: self) { + let container: KeyedDecodingContainer = try decoder.container( + keyedBy: CodingKeys.self) + guard let type = try? container.decode(ContentBackgroundLayerType.self, forKey: .type) else { throw DecodingError.dataCorruptedError( forKey: .type, in: container, - debugDescription: "Invalid Layer." + debugDescription: "Form variant is unknown. Ignored." ) } - } - - init(type: ContentBackgroundLayerType, action: ContentBackgroundLayerAction? = nil, source: ContentBackgroundLayerSource? = nil) { - self.type = type - self.action = action - self.source = source + + let layerContainer: SingleValueDecodingContainer = try decoder.singleValueContainer() + + switch type { + case .image: + let imageLayer = try layerContainer.decode(ImageContentBackgroundLayer.self) + self = .image(imageLayer) + case .unknown: + throw DecodingError.dataCorruptedError( + forKey: .type, + in: container, + debugDescription: "Form variant is unknown. Ignored." + ) + } } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerActionValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerActionValidator.swift deleted file mode 100644 index 4d9bc985..00000000 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerActionValidator.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// ContentBackgroundLayerActionValidator.swift -// Mindbox -// -// Created by vailence on 03.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import Foundation - -class ContentBackgroundLayerActionValidator: Validator { - typealias T = ContentBackgroundLayerAction - - func isValid(item: ContentBackgroundLayerAction) -> Bool { - if item.type == .unknown { - return false - } - - if item.type == .redirectUrl && (item.intentPayload == nil || item.value == nil) { - return false - } - - return true - } -} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift deleted file mode 100644 index 8929903a..00000000 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/LayerActionType/LayerActionType.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// LayerActionType.swift -// Mindbox -// -// Created by vailence on 03.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import Foundation - -enum LayerActionType: String, Decodable, Equatable { - case redirectUrl - case unknown - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = LayerActionType(rawValue: rawValue) ?? .unknown - } -} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift deleted file mode 100644 index 50b84144..00000000 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerType/ContentBackgroundLayerType.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// ContentBackgroundLayerType.swift -// Mindbox -// -// Created by vailence on 03.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import Foundation - -enum ContentBackgroundLayerType: String, Decodable, Equatable { - case image - case unknown - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = ContentBackgroundLayerType(rawValue: rawValue) ?? .unknown - } -} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerValidator.swift deleted file mode 100644 index a167b778..00000000 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerValidator.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// ContentBackgroundLayerValidator.swift -// Mindbox -// -// Created by vailence on 03.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import Foundation - -class ContentBackgroundLayerValidator: Validator { - typealias T = ContentBackgroundLayer - - func isValid(item: ContentBackgroundLayer) -> Bool { - if item.type == .unknown { - return false - } - - if item.action == nil && item.type == .image { - return false - } - - if item.source == nil && item.type == .image { - return false - } - - return true - } -} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/Layers/ImageContentBackgroundLayer.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/Layers/ImageContentBackgroundLayer.swift new file mode 100644 index 00000000..e55edf39 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/Layers/ImageContentBackgroundLayer.swift @@ -0,0 +1,14 @@ +// +// ImageContentBackgroundLayer.swift +// Mindbox +// +// Created by vailence on 10.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct ImageContentBackgroundLayer: ContentBackgroundLayerProtocol { + let action: ContentBackgroundLayerAction + let source: ContentBackgroundLayerSource +} diff --git a/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/LayerFactory.swift b/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/LayerFactory.swift index 9c3251d4..ebb149a0 100644 --- a/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/LayerFactory.swift +++ b/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/LayerFactory.swift @@ -9,16 +9,20 @@ import UIKit protocol LayerFactory { - func create(from image: UIImage, action: ContentBackgroundLayerAction?, in view: UIView, with controller: ModalViewController) -> UIView? + func create(from image: UIImage, layer: ContentBackgroundLayer, in view: UIView, with controller: ModalViewController) -> UIView? func setupConstraints(for view: UIView, in parentView: UIView) } class ImageLayerFactory: LayerFactory { - func create(from image: UIImage, action: ContentBackgroundLayerAction?, in view: UIView, with controller: ModalViewController) -> UIView? { - let inAppView = InAppImageOnlyView(image: image, action: action) - let imageTapGestureRecognizer = UITapGestureRecognizer(target: controller, action: #selector(controller.imageTapped(_:))) - inAppView.addGestureRecognizer(imageTapGestureRecognizer) - return inAppView + func create(from image: UIImage, layer: ContentBackgroundLayer, in view: UIView, with controller: ModalViewController) -> UIView? { + if case .image(let imageContentBackgroundLayer) = layer { + let inAppView = InAppImageOnlyView(image: image, action: imageContentBackgroundLayer.action) + let imageTapGestureRecognizer = UITapGestureRecognizer(target: controller, action: #selector(controller.imageTapped(_:))) + inAppView.addGestureRecognizer(imageTapGestureRecognizer) + return inAppView + } + + return nil } func setupConstraints(for view: UIView, in parentView: UIView) { diff --git a/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift b/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift index 721b059f..ff47b925 100644 --- a/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift +++ b/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift @@ -98,8 +98,16 @@ final class ModalViewController: UIViewController { private func setupLayers() { let layers = model.content.background.layers.elements for layer in layers { - if let factory = layersFactories[layer.type] { - let layerView = factory.create(from: self.image, action: layer.action, in: view, with: self) + let type: ContentBackgroundLayerType + switch layer { + case .image: + type = .image + case .unknown: + type = .unknown + } + + if let factory = layersFactories[type] { + let layerView = factory.create(from: self.image, layer: layer, in: view, with: self) if let layerView = layerView { self.layers.append(layerView) view.addSubview(layerView) diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index b4ca3eb9..d2b8853d 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4737 + 4738 From ed5f578d8fc9ea9ac41967f3981f92a79a1ae2b7 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Thu, 10 Aug 2023 13:18:01 +0600 Subject: [PATCH 18/35] MBX-2706 Polymorph Actions --- Mindbox.xcodeproj/project.pbxproj | 28 ++++++++ Mindbox/DI/DependencyProvider.swift | 5 +- .../ContentBackgroundLayer.swift | 23 ++++--- .../Actions/RedirectUrlLayerAction.swift | 15 +++++ .../ContentBackgroundLayerAction.swift | 66 ++++++++++++------- .../ContentElement/ContentElement.swift | 6 +- .../InappFormVariant/iFormVariant.swift | 12 +--- .../ActionHandler/InappActionHandler.swift | 42 ++++++++++++ .../InAppPresentationManager.swift | 42 +++++------- .../Views/ModalView/ModalViewController.swift | 10 +-- Mindbox/Info.plist | 2 +- .../Model/Common/CustomDecodingError.swift | 14 ++++ 12 files changed, 179 insertions(+), 86 deletions(-) create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/Actions/RedirectUrlLayerAction.swift create mode 100644 Mindbox/InAppMessages/Presentation/ActionHandler/InappActionHandler.swift create mode 100644 Mindbox/Model/Common/CustomDecodingError.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index dd7794cc..09fbb515 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -350,6 +350,9 @@ F331DD162A840D1700222120 /* iFormVariant.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD152A840D1700222120 /* iFormVariant.swift */; }; F331DD192A840D2100222120 /* ModalFormVariant.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD182A840D2100222120 /* ModalFormVariant.swift */; }; F331DD1C2A84B5EF00222120 /* ImageContentBackgroundLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD1B2A84B5EF00222120 /* ImageContentBackgroundLayer.swift */; }; + F331DD1F2A84B62200222120 /* RedirectUrlLayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD1E2A84B62200222120 /* RedirectUrlLayerAction.swift */; }; + F331DD232A84BFDE00222120 /* CustomDecodingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD222A84BFDE00222120 /* CustomDecodingError.swift */; }; + F331DD292A84C4BC00222120 /* InappActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD282A84C4BC00222120 /* InappActionHandler.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -797,6 +800,9 @@ F331DD152A840D1700222120 /* iFormVariant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = iFormVariant.swift; sourceTree = ""; }; F331DD182A840D2100222120 /* ModalFormVariant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModalFormVariant.swift; sourceTree = ""; }; F331DD1B2A84B5EF00222120 /* ImageContentBackgroundLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContentBackgroundLayer.swift; sourceTree = ""; }; + F331DD1E2A84B62200222120 /* RedirectUrlLayerAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedirectUrlLayerAction.swift; sourceTree = ""; }; + F331DD222A84BFDE00222120 /* CustomDecodingError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDecodingError.swift; sourceTree = ""; }; + F331DD282A84C4BC00222120 /* InappActionHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappActionHandler.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -1222,6 +1228,7 @@ 337A473B265510AA000DC613 /* IDS.swift */, 337A473D265528B5000DC613 /* MBDate.swift */, 337A473F26553938000DC613 /* Sex.swift */, + F331DD222A84BFDE00222120 /* CustomDecodingError.swift */, ); path = Common; sourceTree = ""; @@ -2063,6 +2070,7 @@ isa = PBXGroup; children = ( F331DCAD2A80993600222120 /* ContentBackgroundLayerAction.swift */, + F331DD1D2A84B61800222120 /* Actions */, ); path = ContentBackgroundLayerAction; sourceTree = ""; @@ -2102,6 +2110,7 @@ isa = PBXGroup; children = ( F331DCEA2A83A56500222120 /* InAppPresentationManager.swift */, + F331DD272A84C4BC00222120 /* ActionHandler */, F331DCEB2A83A56500222120 /* Manager */, F331DCF32A83A56500222120 /* Views */, ); @@ -2214,6 +2223,22 @@ path = Layers; sourceTree = ""; }; + F331DD1D2A84B61800222120 /* Actions */ = { + isa = PBXGroup; + children = ( + F331DD1E2A84B62200222120 /* RedirectUrlLayerAction.swift */, + ); + path = Actions; + sourceTree = ""; + }; + F331DD272A84C4BC00222120 /* ActionHandler */ = { + isa = PBXGroup; + children = ( + F331DD282A84C4BC00222120 /* InappActionHandler.swift */, + ); + path = ActionHandler; + sourceTree = ""; + }; F3482F142A65DBA0002A41EC /* InappMessagesDelegate */ = { isa = PBXGroup; children = ( @@ -2683,6 +2708,7 @@ B3F4F982268EEAC90092EC3C /* AmountBenefitsResponse.swift in Sources */, 31A20D4C25B6E0D700AAA0A3 /* MindboxErrors.swift in Sources */, 334F3ADF264C199900A6AC00 /* SubViewProductRequest.swift in Sources */, + F331DD232A84BFDE00222120 /* CustomDecodingError.swift in Sources */, 6FDD1463266F7CED00A50C35 /* ProductElementReponse.swift in Sources */, A192786C29D3A24000CDB53D /* ProductIDChecker.swift in Sources */, 337A473A26550FDA000DC613 /* CustomFields.swift in Sources */, @@ -2812,6 +2838,7 @@ F3A8B9AB2A3A719C00E9C055 /* ABTestModel.swift in Sources */, 33072F342664C3E1001F1AB2 /* ProductResponse.swift in Sources */, 33072F3E2664C7B5001F1AB2 /* DiscountCardResponse.swift in Sources */, + F331DD292A84C4BC00222120 /* InappActionHandler.swift in Sources */, F331DD0A2A83A56500222120 /* InAppImageOnlyView.swift in Sources */, 8410681325ECDC73004701C2 /* DatabaseLoader.swift in Sources */, A1D017F22976CC9400CD9F99 /* SegmentTargeting.swift in Sources */, @@ -2851,6 +2878,7 @@ 33072F322664C357001F1AB2 /* ProductListResponse.swift in Sources */, 9B24FAB128C74BD200F10B5D /* InAppConfigurationManager.swift in Sources */, 334F3AF0264C199900A6AC00 /* DiscountRequest.swift in Sources */, + F331DD1F2A84B62200222120 /* RedirectUrlLayerAction.swift in Sources */, 847F57FE25C88BB700147A9A /* HTTPMethod.swift in Sources */, 334F3AE9264C199900A6AC00 /* OrderRequest.swift in Sources */, 9B24FAAC28C74B8300F10B5D /* InAppConfigurationRepository.swift in Sources */, diff --git a/Mindbox/DI/DependencyProvider.swift b/Mindbox/DI/DependencyProvider.swift index 8fee9919..aaf698d7 100644 --- a/Mindbox/DI/DependencyProvider.swift +++ b/Mindbox/DI/DependencyProvider.swift @@ -64,8 +64,9 @@ final class DependencyProvider: DependencyContainer { let tracker = InAppMessagesTracker(databaseRepository: databaseRepository) let displayUseCase = PresentationDisplayUseCase(tracker: tracker) let actionUseCase = PresentationActionUseCase(tracker: tracker) - let presentationManager = InAppPresentationManager(displayUseCase: displayUseCase, - actionUseCase: actionUseCase) + let actionHandler = InAppActionHandler(actionUseCase: actionUseCase) + let presentationManager = InAppPresentationManager(actionHandler: actionHandler, + displayUseCase: displayUseCase) inAppMessagesManager = InAppCoreManager( configManager: InAppConfigurationManager( inAppConfigAPI: InAppConfigurationAPI(persistenceStorage: persistenceStorage), diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift index 17858596..6e285c81 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayer.swift @@ -48,11 +48,7 @@ enum ContentBackgroundLayer: Decodable, Hashable, Equatable { let container: KeyedDecodingContainer = try decoder.container( keyedBy: CodingKeys.self) guard let type = try? container.decode(ContentBackgroundLayerType.self, forKey: .type) else { - throw DecodingError.dataCorruptedError( - forKey: .type, - in: container, - debugDescription: "Form variant is unknown. Ignored." - ) + throw CustomDecodingError.decodingError("The layer type could not be decoded. The layer will be ignored.") } let layerContainer: SingleValueDecodingContainer = try decoder.singleValueContainer() @@ -62,11 +58,18 @@ enum ContentBackgroundLayer: Decodable, Hashable, Equatable { let imageLayer = try layerContainer.decode(ImageContentBackgroundLayer.self) self = .image(imageLayer) case .unknown: - throw DecodingError.dataCorruptedError( - forKey: .type, - in: container, - debugDescription: "Form variant is unknown. Ignored." - ) + throw CustomDecodingError.unknownType("The layer type is unrecognized. The action will be ignored.") + } + } +} + +extension ContentBackgroundLayer { + var layerType: ContentBackgroundLayerType { + switch self { + case .image: + return .image + case .unknown: + return .unknown } } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/Actions/RedirectUrlLayerAction.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/Actions/RedirectUrlLayerAction.swift new file mode 100644 index 00000000..2ad5caac --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/Actions/RedirectUrlLayerAction.swift @@ -0,0 +1,15 @@ +// +// RedirectUrlLayerAction.swift +// Mindbox +// +// Created by vailence on 10.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct RedirectUrlLayerAction: ContentBackgroundLayerActionProtocol { + let intentPayload: String + let value: String +} + diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift index 339d4bae..01dbd972 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift @@ -8,35 +8,57 @@ import Foundation -struct ContentBackgroundLayerAction: Decodable, Equatable { - let type: LayerActionType - let intentPayload: String? - let value: String? +protocol ContentBackgroundLayerActionProtocol: Decodable, Equatable { } +enum ContentBackgroundLayerActionType: String, Decodable { + case redirectUrl + case unknown + + init(from decoder: Decoder) throws { + let container: SingleValueDecodingContainer = try decoder.singleValueContainer() + let type: String = try container.decode(String.self) + self = ContentBackgroundLayerActionType(rawValue: type) ?? .unknown + } +} + +enum ContentBackgroundLayerAction: Decodable, Hashable, Equatable { + case redirectUrl(RedirectUrlLayerAction) + case unknown + enum CodingKeys: String, CodingKey { case type = "$type" - case intentPayload - case value } - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.type = try container.decode(LayerActionType.self, forKey: .type) - self.intentPayload = try container.decodeIfPresentSafely(String.self, forKey: .intentPayload) - self.value = try container.decodeIfPresentSafely(String.self, forKey: .value) - - if !ContentBackgroundLayerActionValidator().isValid(item: self) { - throw DecodingError.dataCorruptedError( - forKey: .type, - in: container, - debugDescription: "Invalid layer action." - ) + static func == (lhs: ContentBackgroundLayerAction, rhs: ContentBackgroundLayerAction) -> Bool { + switch (lhs, rhs) { + case (.redirectUrl, .redirectUrl): return true + case (.unknown, .unknown): return true + default: return false } } - init(type: LayerActionType, intentPayload: String? = nil, value: String? = nil) { - self.type = type - self.intentPayload = intentPayload - self.value = value + func hash(into hasher: inout Hasher) { + switch self { + case .redirectUrl: hasher.combine("redirectUrl") + case .unknown: hasher.combine("unknown") + } + } + + init(from decoder: Decoder) throws { + let container: KeyedDecodingContainer = try decoder.container( + keyedBy: CodingKeys.self) + guard let type = try? container.decode(ContentBackgroundLayerActionType.self, forKey: .type) else { + throw CustomDecodingError.decodingError("The action type could not be decoded. The action will be ignored.") + } + + let actionContainer: SingleValueDecodingContainer = try decoder.singleValueContainer() + + switch type { + case .redirectUrl: + let redirectUrlAction = try actionContainer.decode(RedirectUrlLayerAction.self) + self = .redirectUrl(redirectUrlAction) + case .unknown: + throw CustomDecodingError.unknownType("The action type could not be decoded. The action will be ignored.") + } } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift index 36f0defc..7dd35d07 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift @@ -36,11 +36,7 @@ struct ContentElement: Decodable, Equatable { self.gravity = try container.decodeIfPresent(ContentElementGravity.self, forKey: .gravity) if !ContentElementValidator().isValid(item: self) { - throw DecodingError.dataCorruptedError( - forKey: .type, - in: container, - debugDescription: "Layers cannot be empty." - ) + throw CustomDecodingError.decodingError("The element not passed validation. The element will be ignored.") } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/iFormVariant.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/iFormVariant.swift index ea70810c..5dbe995f 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/iFormVariant.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/iFormVariant.swift @@ -48,11 +48,7 @@ enum MindboxFormVariant: Decodable, Hashable, Equatable { let container: KeyedDecodingContainer = try decoder.container( keyedBy: CodingKeys.self) guard let type = try? container.decode(MindboxFormVariantType.self, forKey: .type) else { - throw DecodingError.dataCorruptedError( - forKey: .type, - in: container, - debugDescription: "Form variant is unknown. Ignored." - ) + throw CustomDecodingError.decodingError("The variant type could not be decoded. The variant will be ignored.") } let variantContainer: SingleValueDecodingContainer = try decoder.singleValueContainer() @@ -62,11 +58,7 @@ enum MindboxFormVariant: Decodable, Hashable, Equatable { let modalVariant = try variantContainer.decode(ModalFormVariant.self) self = .modal(modalVariant) case .unknown: - throw DecodingError.dataCorruptedError( - forKey: .type, - in: container, - debugDescription: "Form variant is unknown. Ignored." - ) + throw CustomDecodingError.unknownType("The variant type could not be decoded. The variant will be ignored.") } } } diff --git a/Mindbox/InAppMessages/Presentation/ActionHandler/InappActionHandler.swift b/Mindbox/InAppMessages/Presentation/ActionHandler/InappActionHandler.swift new file mode 100644 index 00000000..a2d63b96 --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/ActionHandler/InappActionHandler.swift @@ -0,0 +1,42 @@ +// +// InappActionHandler.swift +// FirebaseCore +// +// Created by vailence on 10.08.2023. +// + +import Foundation + +protocol InAppActionHandlerProtocol { + func handleAction( + _ action: ContentBackgroundLayerAction, + for id: String, + onTap: @escaping InAppMessageTapAction, + close: @escaping () -> Void + ) -> Void +} + +final class InAppActionHandler: InAppActionHandlerProtocol { + + private let actionUseCase: PresentationActionUseCase + + init(actionUseCase: PresentationActionUseCase) { + self.actionUseCase = actionUseCase + } + + func handleAction(_ action: ContentBackgroundLayerAction, + for id: String, + onTap: @escaping InAppMessageTapAction, + close: @escaping () -> Void) { + switch action { + case .redirectUrl(let redirectModel): + actionUseCase.onTapAction(id: id, + value: redirectModel.value, + payload: redirectModel.intentPayload, + onTap: onTap, + close: close) + case .unknown: + return + } + } +} diff --git a/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift b/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift index 5298b5ab..d7f42dbf 100644 --- a/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift +++ b/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift @@ -39,18 +39,16 @@ enum InAppPresentationError { typealias InAppMessageTapAction = (_ tapLink: URL?, _ payload: String) -> Void final class InAppPresentationManager: InAppPresentationManagerProtocol { - - init( - displayUseCase: PresentationDisplayUseCase, - actionUseCase: PresentationActionUseCase - ) { + + private let actionHandler: InAppActionHandlerProtocol + private let displayUseCase: PresentationDisplayUseCase + + init(actionHandler: InAppActionHandlerProtocol, + displayUseCase: PresentationDisplayUseCase) { + self.actionHandler = actionHandler self.displayUseCase = displayUseCase - self.actionUseCase = actionUseCase } - - private let displayUseCase: PresentationDisplayUseCase - private let actionUseCase: PresentationActionUseCase - + func present( inAppFormData: InAppFormData, onPresented: @escaping () -> Void, @@ -66,27 +64,17 @@ final class InAppPresentationManager: InAppPresentationManagerProtocol { self.displayUseCase.changeType(model: inAppFormData.content) self.displayUseCase.presentInAppUIModel(model: inAppFormData, - onPresented: { + onPresented: { self.displayUseCase.onPresented(id: inAppFormData.inAppId, onPresented) }, onTapAction: { [weak self] action in - guard let action = action else { + guard let self = self, + let action = action else { return } - - switch action.type { - case .redirectUrl: - if let value = action.value, let payload = action.intentPayload { - self?.actionUseCase.onTapAction(id: inAppFormData.inAppId, - value: value, - payload: payload, - onTap: onTapAction, - close: { - self?.displayUseCase.dismissInAppUIModel(onClose: onPresentationCompleted) - }) - } - case .unknown: - break - } + + self.actionHandler.handleAction(action, for: inAppFormData.inAppId, onTap: onTapAction, close: { + self.displayUseCase.dismissInAppUIModel(onClose: onPresentationCompleted) + }) }, onClose: { self.displayUseCase.dismissInAppUIModel(onClose: onPresentationCompleted) }) diff --git a/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift b/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift index ff47b925..8b246c58 100644 --- a/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift +++ b/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift @@ -98,15 +98,7 @@ final class ModalViewController: UIViewController { private func setupLayers() { let layers = model.content.background.layers.elements for layer in layers { - let type: ContentBackgroundLayerType - switch layer { - case .image: - type = .image - case .unknown: - type = .unknown - } - - if let factory = layersFactories[type] { + if let factory = layersFactories[layer.layerType] { let layerView = factory.create(from: self.image, layer: layer, in: view, with: self) if let layerView = layerView { self.layers.append(layerView) diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index d2b8853d..7f772439 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4738 + 4749 diff --git a/Mindbox/Model/Common/CustomDecodingError.swift b/Mindbox/Model/Common/CustomDecodingError.swift new file mode 100644 index 00000000..29aa1ba4 --- /dev/null +++ b/Mindbox/Model/Common/CustomDecodingError.swift @@ -0,0 +1,14 @@ +// +// CustomDecodingError.swift +// Mindbox +// +// Created by vailence on 10.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +enum CustomDecodingError: Error { + case decodingError(String) + case unknownType(String) +} From c28e0514332690d0f3e333a3ab2b79c7b3b1326c Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Thu, 10 Aug 2023 13:43:12 +0600 Subject: [PATCH 19/35] MBX-2706 Polymorph Sources --- Mindbox.xcodeproj/project.pbxproj | 30 ++++---- .../InAppConfigutationMapper.swift | 11 ++- .../ContentBackgroundLayerAction.swift | 11 +++ .../ContentBackgroundLayerSource.swift | 74 ++++++++++++++----- ...ontentBackgroundLayerSourceValidator.swift | 24 ------ .../LayerSourceType/LayerSourceType.swift | 19 ----- .../Sources/UrlLayerSource.swift | 13 ++++ Mindbox/Info.plist | 2 +- 8 files changed, 103 insertions(+), 81 deletions(-) delete mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSourceValidator.swift delete mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/Sources/UrlLayerSource.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 09fbb515..32d3cde8 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -320,8 +320,6 @@ F331DCC82A80993600222120 /* ContentBackgroundLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA82A80993600222120 /* ContentBackgroundLayer.swift */; }; F331DCCB2A80993600222120 /* ContentBackgroundLayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCAD2A80993600222120 /* ContentBackgroundLayerAction.swift */; }; F331DCCC2A80993600222120 /* ContentBackgroundLayerSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCAF2A80993600222120 /* ContentBackgroundLayerSource.swift */; }; - F331DCCD2A80993600222120 /* LayerSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB12A80993600222120 /* LayerSourceType.swift */; }; - F331DCCE2A80993600222120 /* ContentBackgroundLayerSourceValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB22A80993600222120 /* ContentBackgroundLayerSourceValidator.swift */; }; F331DCD12A80993600222120 /* ContentBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB62A80993600222120 /* ContentBackground.swift */; }; F331DCD22A80993600222120 /* InappFormVariantContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB72A80993600222120 /* InappFormVariantContent.swift */; }; F331DCD32A80993600222120 /* InappForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB82A80993600222120 /* InappForm.swift */; }; @@ -353,6 +351,7 @@ F331DD1F2A84B62200222120 /* RedirectUrlLayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD1E2A84B62200222120 /* RedirectUrlLayerAction.swift */; }; F331DD232A84BFDE00222120 /* CustomDecodingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD222A84BFDE00222120 /* CustomDecodingError.swift */; }; F331DD292A84C4BC00222120 /* InappActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD282A84C4BC00222120 /* InappActionHandler.swift */; }; + F331DD2E2A84CA8900222120 /* UrlLayerSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD2D2A84CA8900222120 /* UrlLayerSource.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -770,8 +769,6 @@ F331DCA82A80993600222120 /* ContentBackgroundLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayer.swift; sourceTree = ""; }; F331DCAD2A80993600222120 /* ContentBackgroundLayerAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerAction.swift; sourceTree = ""; }; F331DCAF2A80993600222120 /* ContentBackgroundLayerSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerSource.swift; sourceTree = ""; }; - F331DCB12A80993600222120 /* LayerSourceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayerSourceType.swift; sourceTree = ""; }; - F331DCB22A80993600222120 /* ContentBackgroundLayerSourceValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerSourceValidator.swift; sourceTree = ""; }; F331DCB62A80993600222120 /* ContentBackground.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackground.swift; sourceTree = ""; }; F331DCB72A80993600222120 /* InappFormVariantContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappFormVariantContent.swift; sourceTree = ""; }; F331DCB82A80993600222120 /* InappForm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappForm.swift; sourceTree = ""; }; @@ -803,6 +800,7 @@ F331DD1E2A84B62200222120 /* RedirectUrlLayerAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedirectUrlLayerAction.swift; sourceTree = ""; }; F331DD222A84BFDE00222120 /* CustomDecodingError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDecodingError.swift; sourceTree = ""; }; F331DD282A84C4BC00222120 /* InappActionHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappActionHandler.swift; sourceTree = ""; }; + F331DD2D2A84CA8900222120 /* UrlLayerSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlLayerSource.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -2049,8 +2047,8 @@ F331DCA62A80993600222120 /* ContentBackground */ = { isa = PBXGroup; children = ( - F331DCA72A80993600222120 /* ContentBackgroundLayer */, F331DCB62A80993600222120 /* ContentBackground.swift */, + F331DCA72A80993600222120 /* ContentBackgroundLayer */, ); path = ContentBackground; sourceTree = ""; @@ -2079,20 +2077,11 @@ isa = PBXGroup; children = ( F331DCAF2A80993600222120 /* ContentBackgroundLayerSource.swift */, - F331DCB02A80993600222120 /* LayerSourceType */, - F331DCB22A80993600222120 /* ContentBackgroundLayerSourceValidator.swift */, + F331DD2C2A84CA7A00222120 /* Sources */, ); path = ContentBackgroundSource; sourceTree = ""; }; - F331DCB02A80993600222120 /* LayerSourceType */ = { - isa = PBXGroup; - children = ( - F331DCB12A80993600222120 /* LayerSourceType.swift */, - ); - path = LayerSourceType; - sourceTree = ""; - }; F331DCDA2A80AC3300222120 /* ModelValidatorTests */ = { isa = PBXGroup; children = ( @@ -2239,6 +2228,14 @@ path = ActionHandler; sourceTree = ""; }; + F331DD2C2A84CA7A00222120 /* Sources */ = { + isa = PBXGroup; + children = ( + F331DD2D2A84CA8900222120 /* UrlLayerSource.swift */, + ); + path = Sources; + sourceTree = ""; + }; F3482F142A65DBA0002A41EC /* InappMessagesDelegate */ = { isa = PBXGroup; children = ( @@ -2741,7 +2738,6 @@ 84F7053B26120199009C7A07 /* Constants.swift in Sources */, A17958782978AEC600609E91 /* SegmentTargetingChecker.swift in Sources */, 847F580B25C88CAF00147A9A /* Route.swift in Sources */, - F331DCCD2A80993600222120 /* LayerSourceType.swift in Sources */, 6FDD145D266F7CCE00A50C35 /* PossibleDiscountsResponse.swift in Sources */, 9B9C95322921116F00BB29DA /* PasteboardUUIDDebugService.swift in Sources */, F331DCC42A80993600222120 /* ContentElementPositionMargin.swift in Sources */, @@ -2927,11 +2923,11 @@ F3482F1F2A65DC20002A41EC /* CopyInappMessageDelegate.swift in Sources */, 33EBF0B0264E6283002A35D5 /* SessionManager.swift in Sources */, F331DD192A840D2100222120 /* ModalFormVariant.swift in Sources */, + F331DD2E2A84CA8900222120 /* UrlLayerSource.swift in Sources */, 337C7952265646D10092B580 /* OperationBodyRequestType.swift in Sources */, 9B9C95312921116F00BB29DA /* UUIDDebugService.swift in Sources */, 6FDD1443266F7C1600A50C35 /* BalanceTypeReponse.swift in Sources */, 337C7954265652A80092B580 /* ProductCategoryRequest.swift in Sources */, - F331DCCE2A80993600222120 /* ContentBackgroundLayerSourceValidator.swift in Sources */, F331DCBF2A80993600222120 /* ContentElementSize.swift in Sources */, 334F3AF1264C199900A6AC00 /* PoolRequest.swift in Sources */, 3191C2A625ADFD8000E7D6B9 /* Mindbox.swift in Sources */, diff --git a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift index 39e867e5..9cfd98e4 100644 --- a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift +++ b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift @@ -272,9 +272,16 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { guard modalModel.content.background.layers.elements.first(where: { switch $0 { case .image(let imageModel): - imageValue = imageModel.source.value + switch imageModel.source { + case .url(let urlModel): + imageValue = urlModel.value + case .unknown: + return false + } + return true - case .unknown: return false + case .unknown: + return false } }) != nil else { continue diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift index 01dbd972..e1341d29 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundLayerAction/ContentBackgroundLayerAction.swift @@ -62,3 +62,14 @@ enum ContentBackgroundLayerAction: Decodable, Hashable, Equatable { } } } + +extension ContentBackgroundLayerAction { + var actionType: ContentBackgroundLayerActionType { + switch self { + case .redirectUrl: + return .redirectUrl + case .unknown: + return .unknown + } + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift index 576b3b34..5050aea2 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSource.swift @@ -8,31 +8,69 @@ import Foundation -struct ContentBackgroundLayerSource: Decodable, Equatable { - let type: LayerSourceType - let value: String? + +protocol ContentBackgroundLayerSourceProtocol: Decodable, Equatable { } + +enum ContentBackgroundLayerSourceType: String, Decodable { + case url + case unknown + + init(from decoder: Decoder) throws { + let container: SingleValueDecodingContainer = try decoder.singleValueContainer() + let type: String = try container.decode(String.self) + self = ContentBackgroundLayerSourceType(rawValue: type) ?? .unknown + } +} + +enum ContentBackgroundLayerSource: Decodable, Hashable, Equatable { + case url(UrlLayerSource) + case unknown enum CodingKeys: String, CodingKey { case type = "$type" - case value } - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.type = try container.decode(LayerSourceType.self, forKey: .type) - self.value = try container.decodeIfPresentSafely(String.self, forKey: .value) - - if !ContentBackgroundLayerSourceValidator().isValid(item: self) { - throw DecodingError.dataCorruptedError( - forKey: .type, - in: container, - debugDescription: "Invalid layer source." - ) + static func == (lhs: ContentBackgroundLayerSource, rhs: ContentBackgroundLayerSource) -> Bool { + switch (lhs, rhs) { + case (.url, .url): return true + case (.unknown, .unknown): return true + default: return false } } - init(type: LayerSourceType, value: String? = nil) { - self.type = type - self.value = value + func hash(into hasher: inout Hasher) { + switch self { + case .url: hasher.combine("url") + case .unknown: hasher.combine("unknown") + } + } + + init(from decoder: Decoder) throws { + let container: KeyedDecodingContainer = try decoder.container( + keyedBy: CodingKeys.self) + guard let type = try? container.decode(ContentBackgroundLayerSourceType.self, forKey: .type) else { + throw CustomDecodingError.decodingError("The source type could not be decoded. The source will be ignored.") + } + + let sourceContainer: SingleValueDecodingContainer = try decoder.singleValueContainer() + + switch type { + case .url: + let urlSource = try sourceContainer.decode(UrlLayerSource.self) + self = .url(urlSource) + case .unknown: + throw CustomDecodingError.unknownType("The source type could not be decoded. The source will be ignored.") + } + } +} + +extension ContentBackgroundLayerSource { + var sourceType: ContentBackgroundLayerSourceType { + switch self { + case .url: + return .url + case .unknown: + return .unknown + } } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSourceValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSourceValidator.swift deleted file mode 100644 index 3729b1a0..00000000 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/ContentBackgroundLayerSourceValidator.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// ContentBackgroundLayerSourceValidator.swift -// Mindbox -// -// Created by vailence on 03.08.2023. -// - -import Foundation - -class ContentBackgroundLayerSourceValidator: Validator { - typealias T = ContentBackgroundLayerSource - - func isValid(item: ContentBackgroundLayerSource) -> Bool { - if item.type == .unknown { - return false - } - - if item.type == .url && item.value == nil { - return false - } - - return true - } -} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift deleted file mode 100644 index e658e582..00000000 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/LayerSourceType/LayerSourceType.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// LayerSourceType.swift -// FirebaseCore -// -// Created by vailence on 03.08.2023. -// - -import Foundation - -enum LayerSourceType: String, Decodable, Equatable { - case url - case unknown - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = LayerSourceType(rawValue: rawValue) ?? .unknown - } -} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/Sources/UrlLayerSource.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/Sources/UrlLayerSource.swift new file mode 100644 index 00000000..db3c920f --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentBackground/ContentBackgroundLayer/ContentBackgroundSource/Sources/UrlLayerSource.swift @@ -0,0 +1,13 @@ +// +// UrlLayerSource.swift +// Mindbox +// +// Created by vailence on 10.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct UrlLayerSource: ContentBackgroundLayerSourceProtocol { + let value: String +} diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index 7f772439..de12fc5d 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4749 + 4753 From f4132352fb6c62818af9b56c190c22010b7a0dc4 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Thu, 10 Aug 2023 13:56:03 +0600 Subject: [PATCH 20/35] MBX-2706 Polymorph Elements --- Mindbox.xcodeproj/project.pbxproj | 34 +++----- .../ContentElement/ContentElement.swift | 82 ++++++++++++------- .../ContentElementType.swift | 20 ----- .../ContentElementValidator.swift | 21 ----- .../Elements/CloseButtonElement.swift | 16 ++++ .../CloseButtonElementFactory.swift | 54 ++++++------ .../Views/ModalView/ModalViewController.swift | 2 +- Mindbox/Info.plist | 2 +- .../ContentElementValidatorTests.swift | 30 ------- 9 files changed, 112 insertions(+), 149 deletions(-) delete mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift delete mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementValidator.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/Elements/CloseButtonElement.swift delete mode 100644 MindboxTests/InApp/Tests/ModelValidatorTests/ContentElementValidatorTests.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 32d3cde8..016ffda9 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -307,7 +307,6 @@ F31801FE2A386BE60021774C /* InappConfigResponseAbtestsInvalid.json in Resources */ = {isa = PBXBuildFile; fileRef = F31801FC2A386BE60021774C /* InappConfigResponseAbtestsInvalid.json */; }; F31801FF2A386BE60021774C /* InappConfigResponseMonitoringInvalid.json in Resources */ = {isa = PBXBuildFile; fileRef = F31801FD2A386BE60021774C /* InappConfigResponseMonitoringInvalid.json */; }; F331DC842A80983000222120 /* FailableDecodableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC832A80983000222120 /* FailableDecodableArray.swift */; }; - F331DCBE2A80993600222120 /* ContentElementType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC962A80993600222120 /* ContentElementType.swift */; }; F331DCBF2A80993600222120 /* ContentElementSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC982A80993600222120 /* ContentElementSize.swift */; }; F331DCC02A80993600222120 /* ContentElementSizeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC9A2A80993600222120 /* ContentElementSizeType.swift */; }; F331DCC12A80993600222120 /* ContentElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC9B2A80993600222120 /* ContentElement.swift */; }; @@ -316,7 +315,6 @@ F331DCC42A80993600222120 /* ContentElementPositionMargin.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA12A80993600222120 /* ContentElementPositionMargin.swift */; }; F331DCC52A80993600222120 /* PositionMarginKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA32A80993600222120 /* PositionMarginKind.swift */; }; F331DCC62A80993600222120 /* ContentElementPositionMarginValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA42A80993600222120 /* ContentElementPositionMarginValidator.swift */; }; - F331DCC72A80993600222120 /* ContentElementValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA52A80993600222120 /* ContentElementValidator.swift */; }; F331DCC82A80993600222120 /* ContentBackgroundLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA82A80993600222120 /* ContentBackgroundLayer.swift */; }; F331DCCB2A80993600222120 /* ContentBackgroundLayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCAD2A80993600222120 /* ContentBackgroundLayerAction.swift */; }; F331DCCC2A80993600222120 /* ContentBackgroundLayerSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCAF2A80993600222120 /* ContentBackgroundLayerSource.swift */; }; @@ -329,7 +327,6 @@ F331DCDE2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCDD2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift */; }; F331DCE02A80B37000222120 /* ContentBackgroundLayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCDF2A80B37000222120 /* ContentBackgroundLayerTests.swift */; }; F331DCE22A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCE12A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift */; }; - F331DCE42A80B54A00222120 /* ContentElementValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCE32A80B54A00222120 /* ContentElementValidatorTests.swift */; }; F331DCE62A80B5AB00222120 /* InappFormVariantValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCE52A80B5AB00222120 /* InappFormVariantValidatorTests.swift */; }; F331DD012A83A56500222120 /* InAppPresentationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCEA2A83A56500222120 /* InAppPresentationManager.swift */; }; F331DD022A83A56500222120 /* PresentationDisplayUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCEE2A83A56500222120 /* PresentationDisplayUseCase.swift */; }; @@ -352,6 +349,7 @@ F331DD232A84BFDE00222120 /* CustomDecodingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD222A84BFDE00222120 /* CustomDecodingError.swift */; }; F331DD292A84C4BC00222120 /* InappActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD282A84C4BC00222120 /* InappActionHandler.swift */; }; F331DD2E2A84CA8900222120 /* UrlLayerSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD2D2A84CA8900222120 /* UrlLayerSource.swift */; }; + F331DD312A84CCA800222120 /* CloseButtonElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD302A84CCA800222120 /* CloseButtonElement.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -756,7 +754,6 @@ F31801FC2A386BE60021774C /* InappConfigResponseAbtestsInvalid.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = InappConfigResponseAbtestsInvalid.json; sourceTree = ""; }; F31801FD2A386BE60021774C /* InappConfigResponseMonitoringInvalid.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = InappConfigResponseMonitoringInvalid.json; sourceTree = ""; }; F331DC832A80983000222120 /* FailableDecodableArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailableDecodableArray.swift; sourceTree = ""; }; - F331DC962A80993600222120 /* ContentElementType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementType.swift; sourceTree = ""; }; F331DC982A80993600222120 /* ContentElementSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementSize.swift; sourceTree = ""; }; F331DC9A2A80993600222120 /* ContentElementSizeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementSizeType.swift; sourceTree = ""; }; F331DC9B2A80993600222120 /* ContentElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElement.swift; sourceTree = ""; }; @@ -765,7 +762,6 @@ F331DCA12A80993600222120 /* ContentElementPositionMargin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementPositionMargin.swift; sourceTree = ""; }; F331DCA32A80993600222120 /* PositionMarginKind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PositionMarginKind.swift; sourceTree = ""; }; F331DCA42A80993600222120 /* ContentElementPositionMarginValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementPositionMarginValidator.swift; sourceTree = ""; }; - F331DCA52A80993600222120 /* ContentElementValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementValidator.swift; sourceTree = ""; }; F331DCA82A80993600222120 /* ContentBackgroundLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayer.swift; sourceTree = ""; }; F331DCAD2A80993600222120 /* ContentBackgroundLayerAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerAction.swift; sourceTree = ""; }; F331DCAF2A80993600222120 /* ContentBackgroundLayerSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerSource.swift; sourceTree = ""; }; @@ -778,7 +774,6 @@ F331DCDD2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerActionValidatorTests.swift; sourceTree = ""; }; F331DCDF2A80B37000222120 /* ContentBackgroundLayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerTests.swift; sourceTree = ""; }; F331DCE12A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentElementPositionMarginValidatorTests.swift; sourceTree = ""; }; - F331DCE32A80B54A00222120 /* ContentElementValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentElementValidatorTests.swift; sourceTree = ""; }; F331DCE52A80B5AB00222120 /* InappFormVariantValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InappFormVariantValidatorTests.swift; sourceTree = ""; }; F331DCEA2A83A56500222120 /* InAppPresentationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InAppPresentationManager.swift; sourceTree = ""; }; F331DCEE2A83A56500222120 /* PresentationDisplayUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationDisplayUseCase.swift; sourceTree = ""; }; @@ -801,6 +796,7 @@ F331DD222A84BFDE00222120 /* CustomDecodingError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDecodingError.swift; sourceTree = ""; }; F331DD282A84C4BC00222120 /* InappActionHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappActionHandler.swift; sourceTree = ""; }; F331DD2D2A84CA8900222120 /* UrlLayerSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlLayerSource.swift; sourceTree = ""; }; + F331DD302A84CCA800222120 /* CloseButtonElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseButtonElement.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -1974,24 +1970,15 @@ F331DC942A80993600222120 /* ContentElement */ = { isa = PBXGroup; children = ( - F331DC952A80993600222120 /* ContentElementType */, - F331DC972A80993600222120 /* ContentElementSize */, F331DC9B2A80993600222120 /* ContentElement.swift */, + F331DD2F2A84CC9C00222120 /* Elements */, + F331DC972A80993600222120 /* ContentElementSize */, F331DC9C2A80993600222120 /* ContentElementGravity */, F331DC9E2A80993600222120 /* ContentElementPosition */, - F331DCA52A80993600222120 /* ContentElementValidator.swift */, ); path = ContentElement; sourceTree = ""; }; - F331DC952A80993600222120 /* ContentElementType */ = { - isa = PBXGroup; - children = ( - F331DC962A80993600222120 /* ContentElementType.swift */, - ); - path = ContentElementType; - sourceTree = ""; - }; F331DC972A80993600222120 /* ContentElementSize */ = { isa = PBXGroup; children = ( @@ -2089,7 +2076,6 @@ F331DCDD2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift */, F331DCDF2A80B37000222120 /* ContentBackgroundLayerTests.swift */, F331DCE12A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift */, - F331DCE32A80B54A00222120 /* ContentElementValidatorTests.swift */, F331DCE52A80B5AB00222120 /* InappFormVariantValidatorTests.swift */, ); path = ModelValidatorTests; @@ -2236,6 +2222,14 @@ path = Sources; sourceTree = ""; }; + F331DD2F2A84CC9C00222120 /* Elements */ = { + isa = PBXGroup; + children = ( + F331DD302A84CCA800222120 /* CloseButtonElement.swift */, + ); + path = Elements; + sourceTree = ""; + }; F3482F142A65DBA0002A41EC /* InappMessagesDelegate */ = { isa = PBXGroup; children = ( @@ -2779,6 +2773,7 @@ 33072F3C2664C713001F1AB2 /* CustomerSegmentationsResponse.swift in Sources */, 33072F3A2664C5CB001F1AB2 /* ManufacturerResponse.swift in Sources */, F331DD112A83CA7500222120 /* KeyedDecodingContainer+Extensions.swift in Sources */, + F331DD312A84CCA800222120 /* CloseButtonElement.swift in Sources */, A1D017E92976CC1C00CD9F99 /* TargetingChecker.swift in Sources */, F397EEB52A44573600D48CEC /* Status.swift in Sources */, 6FDD1455266F7C9A00A50C35 /* PromotionTypeResponse.swift in Sources */, @@ -2807,9 +2802,7 @@ 33072F2E2664C24F001F1AB2 /* AreaResponse.swift in Sources */, 84DEE8A925CC031200C98CC7 /* MBDatabase.xcdatamodeld in Sources */, A192786A29D38D2000CDB53D /* CheckerFactory.swift in Sources */, - F331DCC72A80993600222120 /* ContentElementValidator.swift in Sources */, F331DD082A83A56500222120 /* ElementFactory.swift in Sources */, - F331DCBE2A80993600222120 /* ContentElementType.swift in Sources */, 840C38A625D1191000D50183 /* GuaranteedDeliveryManager.swift in Sources */, A1D017EC2976CC2E00CD9F99 /* TrueTargeting.swift in Sources */, 33072F302664C2E4001F1AB2 /* SubscriptionResponse.swift in Sources */, @@ -2956,7 +2949,6 @@ files = ( 840C38B325D133B000D50183 /* GuaranteedDeliveryTestCase.swift in Sources */, 84FCD3B925CA109E00D1E574 /* MockNetworkFetcher.swift in Sources */, - F331DCE42A80B54A00222120 /* ContentElementValidatorTests.swift in Sources */, 9B9C9538292111A700BB29DA /* MockUUIDDebugService.swift in Sources */, 84B625F025C98B1200AB6228 /* ValidatorsTestCase.swift in Sources */, 3132DFF625C2A811007FE358 /* TestDependencyProvider.swift in Sources */, diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift index 7dd35d07..1a5eac1b 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElement.swift @@ -8,44 +8,68 @@ import Foundation -struct ContentElement: Decodable, Equatable { - let type: ContentElementType - let color: String? - let lineWidth: Int? - let size: ContentElementSize? - let position: ContentElementPosition? - let gravity: ContentElementGravity? +protocol ContentElementProtocol: Decodable, Equatable { } + +enum ContentElementType: String, Decodable { + case closeButton + case unknown + + init(from decoder: Decoder) throws { + let container: SingleValueDecodingContainer = try decoder.singleValueContainer() + let type: String = try container.decode(String.self) + self = ContentElementType(rawValue: type) ?? .unknown + } +} + +enum ContentElement: Decodable, Hashable, Equatable { + case closeButton(CloseButtonElement) + case unknown enum CodingKeys: String, CodingKey { case type = "$type" - case color - case lineWidth - case size - case position - case gravity + } + + static func == (lhs: ContentElement, rhs: ContentElement) -> Bool { + switch (lhs, rhs) { + case (.closeButton, .closeButton): return true + case (.unknown, .unknown): return true + default: return false + } + } + + func hash(into hasher: inout Hasher) { + switch self { + case .closeButton: hasher.combine("closeButton") + case .unknown: hasher.combine("unknown") + } } init(from decoder: Decoder) throws { - let container: KeyedDecodingContainer = try decoder.container(keyedBy: ContentElement.CodingKeys.self) + let container: KeyedDecodingContainer = try decoder.container( + keyedBy: CodingKeys.self) + guard let type = try? container.decode(ContentElementType.self, forKey: .type) else { + throw CustomDecodingError.decodingError("The variant type could not be decoded. The variant will be ignored.") + } - self.type = try container.decode(ContentElementType.self, forKey: ContentElement.CodingKeys.type) - self.color = try container.decodeIfPresentSafely(String.self, forKey: .color) - self.lineWidth = try container.decodeIfPresentSafely(Int.self, forKey: .lineWidth) - self.size = try container.decodeIfPresent(ContentElementSize.self, forKey: .size) - self.position = try container.decodeIfPresent(ContentElementPosition.self, forKey: .position) - self.gravity = try container.decodeIfPresent(ContentElementGravity.self, forKey: .gravity) + let elementContainer: SingleValueDecodingContainer = try decoder.singleValueContainer() - if !ContentElementValidator().isValid(item: self) { - throw CustomDecodingError.decodingError("The element not passed validation. The element will be ignored.") + switch type { + case .closeButton: + let closeButtonElement = try elementContainer.decode(CloseButtonElement.self) + self = .closeButton(closeButtonElement) + case .unknown: + throw CustomDecodingError.unknownType("The variant type could not be decoded. The variant will be ignored.") } } - - internal init(type: ContentElementType, color: String? = nil, lineWidth: Int? = nil, size: ContentElementSize? = nil, position: ContentElementPosition? = nil, gravity: ContentElementGravity? = nil) { - self.type = type - self.color = color - self.lineWidth = lineWidth - self.size = size - self.position = position - self.gravity = gravity +} + +extension ContentElement { + var elementType: ContentElementType { + switch self { + case .closeButton: + return .closeButton + case .unknown: + return .unknown + } } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift deleted file mode 100644 index 0f82fde2..00000000 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementType/ContentElementType.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// ContentElementType.swift -// Mindbox -// -// Created by vailence on 04.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import Foundation - -enum ContentElementType: String, Decodable, Equatable { - case closeButton - case unknown - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = ContentElementType(rawValue: rawValue) ?? .unknown - } -} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementValidator.swift deleted file mode 100644 index 9fb67ed9..00000000 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementValidator.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// ContentElementValidator.swift -// Mindbox -// -// Created by vailence on 04.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import Foundation - -class ContentElementValidator: Validator { - typealias T = ContentElement - - func isValid(item: ContentElement) -> Bool { - if item.type == .unknown { - return false - } - - return true - } -} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/Elements/CloseButtonElement.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/Elements/CloseButtonElement.swift new file mode 100644 index 00000000..49e14cb9 --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/Elements/CloseButtonElement.swift @@ -0,0 +1,16 @@ +// +// CloseButtonElement.swift +// Mindbox +// +// Created by vailence on 10.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct CloseButtonElement: ContentElementProtocol { + let color: String? + let lineWidth: Int? + let size: ContentElementSize? + let position: ContentElementPosition? +} diff --git a/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift b/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift index 0b53d8d6..ffc59731 100644 --- a/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift +++ b/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift @@ -14,35 +14,37 @@ class CloseButtonElementFactory: ElementFactory { return nil } - let color = element.color?.isHexValid() ?? false ? element.color : nil - let closeButton = CrossView(lineColorHex: color, lineWidth: element.lineWidth) - closeButton.isUserInteractionEnabled = true - let closeRecognizer = UILongPressGestureRecognizer(target: controller, action: #selector(controller.onCloseButton)) - closeRecognizer.minimumPressDuration = 0 - closeButton.addGestureRecognizer(closeRecognizer) - return closeButton + if case .closeButton(let closeButtonElement) = element { + let color = closeButtonElement.color?.isHexValid() ?? false ? closeButtonElement.color : nil + let closeButton = CrossView(lineColorHex: color, lineWidth: closeButtonElement.lineWidth) + closeButton.isUserInteractionEnabled = true + let closeRecognizer = UILongPressGestureRecognizer(target: controller, action: #selector(controller.onCloseButton)) + closeRecognizer.minimumPressDuration = 0 + closeButton.addGestureRecognizer(closeRecognizer) + return closeButton + } + + return nil } func setupConstraints(for view: UIView, from element: ContentElement, in parentView: UIView) { - if element.size?.kind == .unknown { - return - } - - let size = element.size ?? ContentElementSize(kind: .dp, width: 24, height: 24) - let top = element.position?.margin?.top ?? 0.02 - let right = element.position?.margin?.right ?? 0.02 - - let horizontalOffset = (parentView.frame.width - CGFloat(size.width)) * right - let verticalOffset = (parentView.frame.height - CGFloat(size.height)) * top - - if size.kind == .dp { - view.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor, constant: -horizontalOffset), - view.topAnchor.constraint(equalTo: parentView.topAnchor, constant: verticalOffset), - view.widthAnchor.constraint(equalToConstant: CGFloat(size.width)), - view.heightAnchor.constraint(equalToConstant: CGFloat(size.height)) - ]) + if case .closeButton(let closeButtonElement) = element { + let size = closeButtonElement.size ?? ContentElementSize(kind: .dp, width: 24, height: 24) + let top = closeButtonElement.position?.margin?.top ?? 0.02 + let right = closeButtonElement.position?.margin?.right ?? 0.02 + + let horizontalOffset = (parentView.frame.width - CGFloat(size.width)) * right + let verticalOffset = (parentView.frame.height - CGFloat(size.height)) * top + + if size.kind == .dp { + view.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor, constant: -horizontalOffset), + view.topAnchor.constraint(equalTo: parentView.topAnchor, constant: verticalOffset), + view.widthAnchor.constraint(equalToConstant: CGFloat(size.width)), + view.heightAnchor.constraint(equalToConstant: CGFloat(size.height)) + ]) + } } } } diff --git a/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift b/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift index 8b246c58..ee1491e7 100644 --- a/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift +++ b/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift @@ -116,7 +116,7 @@ final class ModalViewController: UIViewController { } for element in elements { - if let factory = elementFactories[element.type] { + if let factory = elementFactories[element.elementType] { let elementView = factory.create(from: element, in: inappView, with: self) if let elementView = elementView { self.elements.append(elementView) diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index de12fc5d..16265d5e 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4753 + 4755 diff --git a/MindboxTests/InApp/Tests/ModelValidatorTests/ContentElementValidatorTests.swift b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentElementValidatorTests.swift deleted file mode 100644 index c5d63ccf..00000000 --- a/MindboxTests/InApp/Tests/ModelValidatorTests/ContentElementValidatorTests.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// ContentElementValidatorTests.swift -// MindboxTests -// -// Created by vailence on 07.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import XCTest -@testable import Mindbox - -final class ContentElementValidatorTests: XCTestCase { - - var validator: ContentElementValidator! - var model: ContentElement! - - override func setUpWithError() throws { - validator = ContentElementValidator() - } - - override func tearDownWithError() throws { - validator = nil - model = nil - } - - func test_unknownCase_returnFalse() throws { - let model = ContentElement(type: .unknown) - XCTAssertFalse(validator.isValid(item: model)) - } -} From c7a775623e6f53e5650f598ebafee4cc364801dc Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Thu, 10 Aug 2023 16:05:57 +0600 Subject: [PATCH 21/35] MBX-2706 URL Extractor Service --- Mindbox.xcodeproj/project.pbxproj | 36 +++---------- Mindbox/DI/DependencyProvider.swift | 2 + .../InAppConfigutationMapper.swift | 29 ++-------- .../VariantImageUrlExtractorService.swift | 36 +++++++++++++ .../Models/Config/InappModel/InAppModel.swift | 6 +-- .../InappModel/InappForm/InappForm.swift | 6 +-- .../ContentElementGravity.swift | 14 ----- .../ContentElementPositionMargin.swift | 6 +-- Mindbox/Info.plist | 2 +- ...tBackgroundLayerActionValidatorTests.swift | 54 ------------------- ...tBackgroundLayerSourceValidatorTests.swift | 49 ----------------- .../ContentBackgroundLayerTests.swift | 49 ----------------- .../InappFormVariantValidatorTests.swift | 40 -------------- 13 files changed, 52 insertions(+), 277 deletions(-) create mode 100644 Mindbox/InAppMessages/InAppConfigurationMapper/Services/VariantImageUrlExtractorService.swift delete mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementGravity/ContentElementGravity.swift delete mode 100644 MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerActionValidatorTests.swift delete mode 100644 MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerSourceValidatorTests.swift delete mode 100644 MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerTests.swift delete mode 100644 MindboxTests/InApp/Tests/ModelValidatorTests/InappFormVariantValidatorTests.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 016ffda9..6784085f 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -310,7 +310,6 @@ F331DCBF2A80993600222120 /* ContentElementSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC982A80993600222120 /* ContentElementSize.swift */; }; F331DCC02A80993600222120 /* ContentElementSizeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC9A2A80993600222120 /* ContentElementSizeType.swift */; }; F331DCC12A80993600222120 /* ContentElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC9B2A80993600222120 /* ContentElement.swift */; }; - F331DCC22A80993600222120 /* ContentElementGravity.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC9D2A80993600222120 /* ContentElementGravity.swift */; }; F331DCC32A80993600222120 /* ContentElementPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DC9F2A80993600222120 /* ContentElementPosition.swift */; }; F331DCC42A80993600222120 /* ContentElementPositionMargin.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA12A80993600222120 /* ContentElementPositionMargin.swift */; }; F331DCC52A80993600222120 /* PositionMarginKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCA32A80993600222120 /* PositionMarginKind.swift */; }; @@ -323,11 +322,7 @@ F331DCD32A80993600222120 /* InappForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB82A80993600222120 /* InappForm.swift */; }; F331DCD42A80993600222120 /* InappValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCB92A80993600222120 /* InappValidator.swift */; }; F331DCD52A80993600222120 /* InAppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCBA2A80993600222120 /* InAppModel.swift */; }; - F331DCDC2A80AC4D00222120 /* ContentBackgroundLayerSourceValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCDB2A80AC4D00222120 /* ContentBackgroundLayerSourceValidatorTests.swift */; }; - F331DCDE2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCDD2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift */; }; - F331DCE02A80B37000222120 /* ContentBackgroundLayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCDF2A80B37000222120 /* ContentBackgroundLayerTests.swift */; }; F331DCE22A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCE12A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift */; }; - F331DCE62A80B5AB00222120 /* InappFormVariantValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCE52A80B5AB00222120 /* InappFormVariantValidatorTests.swift */; }; F331DD012A83A56500222120 /* InAppPresentationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCEA2A83A56500222120 /* InAppPresentationManager.swift */; }; F331DD022A83A56500222120 /* PresentationDisplayUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCEE2A83A56500222120 /* PresentationDisplayUseCase.swift */; }; F331DD032A83A56500222120 /* ModalPresentationStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DCF02A83A56500222120 /* ModalPresentationStrategy.swift */; }; @@ -350,6 +345,7 @@ F331DD292A84C4BC00222120 /* InappActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD282A84C4BC00222120 /* InappActionHandler.swift */; }; F331DD2E2A84CA8900222120 /* UrlLayerSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD2D2A84CA8900222120 /* UrlLayerSource.swift */; }; F331DD312A84CCA800222120 /* CloseButtonElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD302A84CCA800222120 /* CloseButtonElement.swift */; }; + F331DD332A84D57800222120 /* VariantImageUrlExtractorService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD322A84D57800222120 /* VariantImageUrlExtractorService.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -757,7 +753,6 @@ F331DC982A80993600222120 /* ContentElementSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementSize.swift; sourceTree = ""; }; F331DC9A2A80993600222120 /* ContentElementSizeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementSizeType.swift; sourceTree = ""; }; F331DC9B2A80993600222120 /* ContentElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElement.swift; sourceTree = ""; }; - F331DC9D2A80993600222120 /* ContentElementGravity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementGravity.swift; sourceTree = ""; }; F331DC9F2A80993600222120 /* ContentElementPosition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementPosition.swift; sourceTree = ""; }; F331DCA12A80993600222120 /* ContentElementPositionMargin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentElementPositionMargin.swift; sourceTree = ""; }; F331DCA32A80993600222120 /* PositionMarginKind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PositionMarginKind.swift; sourceTree = ""; }; @@ -770,11 +765,7 @@ F331DCB82A80993600222120 /* InappForm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappForm.swift; sourceTree = ""; }; F331DCB92A80993600222120 /* InappValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappValidator.swift; sourceTree = ""; }; F331DCBA2A80993600222120 /* InAppModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InAppModel.swift; sourceTree = ""; }; - F331DCDB2A80AC4D00222120 /* ContentBackgroundLayerSourceValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerSourceValidatorTests.swift; sourceTree = ""; }; - F331DCDD2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerActionValidatorTests.swift; sourceTree = ""; }; - F331DCDF2A80B37000222120 /* ContentBackgroundLayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentBackgroundLayerTests.swift; sourceTree = ""; }; F331DCE12A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentElementPositionMarginValidatorTests.swift; sourceTree = ""; }; - F331DCE52A80B5AB00222120 /* InappFormVariantValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InappFormVariantValidatorTests.swift; sourceTree = ""; }; F331DCEA2A83A56500222120 /* InAppPresentationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InAppPresentationManager.swift; sourceTree = ""; }; F331DCEE2A83A56500222120 /* PresentationDisplayUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationDisplayUseCase.swift; sourceTree = ""; }; F331DCF02A83A56500222120 /* ModalPresentationStrategy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModalPresentationStrategy.swift; sourceTree = ""; }; @@ -797,6 +788,7 @@ F331DD282A84C4BC00222120 /* InappActionHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InappActionHandler.swift; sourceTree = ""; }; F331DD2D2A84CA8900222120 /* UrlLayerSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlLayerSource.swift; sourceTree = ""; }; F331DD302A84CCA800222120 /* CloseButtonElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseButtonElement.swift; sourceTree = ""; }; + F331DD322A84D57800222120 /* VariantImageUrlExtractorService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariantImageUrlExtractorService.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -1931,8 +1923,8 @@ F331DC8C2A80993600222120 /* InappModel */ = { isa = PBXGroup; children = ( - F331DCB92A80993600222120 /* InappValidator.swift */, F331DCBA2A80993600222120 /* InAppModel.swift */, + F331DCB92A80993600222120 /* InappValidator.swift */, F331DC8D2A80993600222120 /* InappForm */, ); path = InappModel; @@ -1960,9 +1952,9 @@ F331DC932A80993600222120 /* InappFormVariantContent */ = { isa = PBXGroup; children = ( + F331DCB72A80993600222120 /* InappFormVariantContent.swift */, F331DC942A80993600222120 /* ContentElement */, F331DCA62A80993600222120 /* ContentBackground */, - F331DCB72A80993600222120 /* InappFormVariantContent.swift */, ); path = InappFormVariantContent; sourceTree = ""; @@ -1973,7 +1965,6 @@ F331DC9B2A80993600222120 /* ContentElement.swift */, F331DD2F2A84CC9C00222120 /* Elements */, F331DC972A80993600222120 /* ContentElementSize */, - F331DC9C2A80993600222120 /* ContentElementGravity */, F331DC9E2A80993600222120 /* ContentElementPosition */, ); path = ContentElement; @@ -1996,14 +1987,6 @@ path = Kind; sourceTree = ""; }; - F331DC9C2A80993600222120 /* ContentElementGravity */ = { - isa = PBXGroup; - children = ( - F331DC9D2A80993600222120 /* ContentElementGravity.swift */, - ); - path = ContentElementGravity; - sourceTree = ""; - }; F331DC9E2A80993600222120 /* ContentElementPosition */ = { isa = PBXGroup; children = ( @@ -2072,11 +2055,7 @@ F331DCDA2A80AC3300222120 /* ModelValidatorTests */ = { isa = PBXGroup; children = ( - F331DCDB2A80AC4D00222120 /* ContentBackgroundLayerSourceValidatorTests.swift */, - F331DCDD2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift */, - F331DCDF2A80B37000222120 /* ContentBackgroundLayerTests.swift */, F331DCE12A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift */, - F331DCE52A80B5AB00222120 /* InappFormVariantValidatorTests.swift */, ); path = ModelValidatorTests; sourceTree = ""; @@ -2333,6 +2312,7 @@ F3A8B9572A389D9D00E9C055 /* GeoService.swift */, F39B67A12A3C6AF4005C0CCA /* SegmentationService.swift */, F39B67A82A3C72E5005C0CCA /* ImageDownloadService.swift */, + F331DD322A84D57800222120 /* VariantImageUrlExtractorService.swift */, ); path = Services; sourceTree = ""; @@ -2683,7 +2663,6 @@ 847F581725C8981F00147A9A /* HTTPURLResponseStatusCodeValidator.swift in Sources */, 33072F442664CC6C001F1AB2 /* IssuedPointOfContactResponse.swift in Sources */, A17958762978AEC600609E91 /* OrTargetingChecker.swift in Sources */, - F331DCC22A80993600222120 /* ContentElementGravity.swift in Sources */, 334F3AE1264C199900A6AC00 /* PromoCodeRequest.swift in Sources */, F331DD032A83A56500222120 /* ModalPresentationStrategy.swift in Sources */, A17958792978AEC600609E91 /* GeoTargetingChecker.swift in Sources */, @@ -2809,6 +2788,7 @@ F331DD052A83A56500222120 /* PresentationActionUseCase.swift in Sources */, A153E03B29BAFE01003C34D4 /* CustomOperationTargeting.swift in Sources */, 9BC24E7528F6953D00C2619C /* ConfigResponse.swift in Sources */, + F331DD332A84D57800222120 /* VariantImageUrlExtractorService.swift in Sources */, 33072F482664CCBA001F1AB2 /* PoolResponse.swift in Sources */, 337C79562656533F0092B580 /* ViewProductRequest.swift in Sources */, 6FDD145F266F7CD900A50C35 /* DiscountResponse.swift in Sources */, @@ -2957,7 +2937,6 @@ F3A8B95C2A389EAD00E9C055 /* GeoServiceTests.swift in Sources */, A17958992978E04600609E91 /* InAppStub.swift in Sources */, F39B67A72A3C6C6A005C0CCA /* SegmentationServiceTests.swift in Sources */, - F331DCE02A80B37000222120 /* ContentBackgroundLayerTests.swift in Sources */, F3D925AB2A120C0F00135C87 /* InAppImageDownloaderMock.swift in Sources */, F331DCE22A80B4C800222120 /* ContentElementPositionMarginValidatorTests.swift in Sources */, A154E32E299E0D8900F8F074 /* SDKLogManagerTests.swift in Sources */, @@ -2968,7 +2947,6 @@ 84ECB42E25D27EF100DA8AC9 /* MockUNAuthorizationStatusProvider.swift in Sources */, F3A8B9982A3A421C00E9C055 /* SDKVersionValidatorTests.swift in Sources */, A17958812978B3FA00609E91 /* InAppResponseModelTests.swift in Sources */, - F331DCDC2A80AC4D00222120 /* ContentBackgroundLayerSourceValidatorTests.swift in Sources */, 9B4F9E0528D0945F002C9CF0 /* InAppCoreManagerMock.swift in Sources */, 840C387F25CD15C200D50183 /* MockDataBaseLoader.swift in Sources */, A1F3EE4E298BEF0B005FA828 /* OSLogWritterTests.swift in Sources */, @@ -2977,7 +2955,6 @@ 313B233F25ADEA0F00A1CB72 /* MindboxTests.swift in Sources */, A154E334299E110E00F8F074 /* EventRepositoryMock.swift in Sources */, 31ED2DEC25C444C400301FAD /* MBConfigurationTestCase.swift in Sources */, - F331DCDE2A80B26C00222120 /* ContentBackgroundLayerActionValidatorTests.swift in Sources */, F3D925AD2A1236F400135C87 /* URLSessionImageDownloaderTests.swift in Sources */, F3A8B9A02A3A52F400E9C055 /* ABTestValidatorTests.swift in Sources */, 9B9C953B292111A700BB29DA /* MockDateProvider.swift in Sources */, @@ -2987,7 +2964,6 @@ 84E1D6A9261D8D54002BF03A /* DatabaseLoaderTest.swift in Sources */, 840C38B825D13A7D00D50183 /* EventGenerator.swift in Sources */, 84CC79A525CAE14200C062BD /* EventRepositoryTestCase.swift in Sources */, - F331DCE62A80B5AB00222120 /* InappFormVariantValidatorTests.swift in Sources */, A154E304299C189300F8F074 /* MBLoggerCoreDataManagerTests.swift in Sources */, 840C388525CD169400D50183 /* DatabaseRepositoryTestCase.swift in Sources */, 84FCD3B525CA0FD300D1E574 /* MockPersistenceStorage.swift in Sources */, diff --git a/Mindbox/DI/DependencyProvider.swift b/Mindbox/DI/DependencyProvider.swift index aaf698d7..4a2f04b8 100644 --- a/Mindbox/DI/DependencyProvider.swift +++ b/Mindbox/DI/DependencyProvider.swift @@ -67,6 +67,7 @@ final class DependencyProvider: DependencyContainer { let actionHandler = InAppActionHandler(actionUseCase: actionUseCase) let presentationManager = InAppPresentationManager(actionHandler: actionHandler, displayUseCase: displayUseCase) + let urlExtractorService = VariantImageUrlExtractorService() inAppMessagesManager = InAppCoreManager( configManager: InAppConfigurationManager( inAppConfigAPI: InAppConfigurationAPI(persistenceStorage: persistenceStorage), @@ -79,6 +80,7 @@ final class DependencyProvider: DependencyContainer { persistenceStorage: persistenceStorage, sdkVersionValidator: sdkVersionValidator, imageDownloadService: imageDownloadService, + urlExtractorService: urlExtractorService, abTestDeviceMixer: abTestDeviceMixer), logsManager: logsManager, sessionStorage: sessionTemporaryStorage), presentationManager: presentationManager, diff --git a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift index 9cfd98e4..fc3e668c 100644 --- a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift +++ b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift @@ -25,6 +25,7 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { var filteredInAppsByEvent: [InAppMessageTriggerEvent: [InAppTransitionData]] = [:] private let sdkVersionValidator: SDKVersionValidator private let imageDownloadService: ImageDownloadServiceProtocol + private let urlExtractorService: VariantImageUrlExtractorService private let abTestDeviceMixer: ABTestDeviceMixer private let dispatchGroup = DispatchGroup() @@ -37,6 +38,7 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { persistenceStorage: PersistenceStorage, sdkVersionValidator: SDKVersionValidator, imageDownloadService: ImageDownloadServiceProtocol, + urlExtractorService: VariantImageUrlExtractorService, abTestDeviceMixer: ABTestDeviceMixer) { self.geoService = geoService self.segmentationService = segmentationService @@ -46,6 +48,7 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { self.persistenceStorage = persistenceStorage self.sdkVersionValidator = sdkVersionValidator self.imageDownloadService = imageDownloadService + self.urlExtractorService = urlExtractorService self.abTestDeviceMixer = abTestDeviceMixer } @@ -266,31 +269,7 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { continue } - var imageValue: String? - switch inapp.content { - case .modal(let modalModel): - guard modalModel.content.background.layers.elements.first(where: { - switch $0 { - case .image(let imageModel): - switch imageModel.source { - case .url(let urlModel): - imageValue = urlModel.value - case .unknown: - return false - } - - return true - case .unknown: - return false - } - }) != nil else { - continue - } - case .unknown: - continue - } - - guard let imageValue = imageValue else { continue } + guard let imageValue = self.urlExtractorService.extractImageURL(from: inapp.content) else { continue } group.enter() Logger.common(message: "Starting inapp processing. [ID]: \(inapp.inAppId)", level: .debug, category: .inAppMessages) diff --git a/Mindbox/InAppMessages/InAppConfigurationMapper/Services/VariantImageUrlExtractorService.swift b/Mindbox/InAppMessages/InAppConfigurationMapper/Services/VariantImageUrlExtractorService.swift new file mode 100644 index 00000000..c98e088c --- /dev/null +++ b/Mindbox/InAppMessages/InAppConfigurationMapper/Services/VariantImageUrlExtractorService.swift @@ -0,0 +1,36 @@ +// +// VariantImageUrlExtractorService.swift +// Mindbox +// +// Created by vailence on 10.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +class VariantImageUrlExtractorService { + func extractImageURL(from variant: MindboxFormVariant) -> String? { + var urlString: String? + switch variant { + case .modal(let modalModel): + let modalModel = modalModel.content.background.layers.elements.first(where: { + switch $0 { + case .image(let imageModel): + switch imageModel.source { + case .url(let urlModel): + urlString = urlModel.value + return true + case .unknown: + return false + } + case .unknown: + return false + } + }) + case .unknown: + return nil + } + + return urlString + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InAppModel.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InAppModel.swift index ff1acdc7..90dfc1ac 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InAppModel.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InAppModel.swift @@ -29,11 +29,7 @@ struct InApp: Decodable, Equatable { form = try container.decode(InAppForm.self, forKey: .form) if !InappValidator().isValid(item: self) { - throw DecodingError.dataCorruptedError( - forKey: .id, - in: container, - debugDescription: "Invalid InApp" - ) + throw CustomDecodingError.decodingError("The inapp not passed validation. The inapp will be ignored.") } } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappForm.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappForm.swift index 0455c31e..af730dc1 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappForm.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappForm.swift @@ -20,11 +20,7 @@ struct InAppForm: Decodable, Equatable { let variants = try container.decode(FailableDecodableArray.self, forKey: .variants) if variants.elements.isEmpty { - throw DecodingError.dataCorruptedError( - forKey: .variants, - in: container, - debugDescription: "Variants cannot be empty" - ) + throw CustomDecodingError.decodingError("Variants array cannot be empty.") } self.variants = variants diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementGravity/ContentElementGravity.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementGravity/ContentElementGravity.swift deleted file mode 100644 index 227bd395..00000000 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementGravity/ContentElementGravity.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// ContentElementGravity.swift -// Mindbox -// -// Created by vailence on 04.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import Foundation - -struct ContentElementGravity: Decodable, Equatable { - let horizontal: String - let vertical: String -} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMargin.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMargin.swift index 09a75943..50addb0d 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMargin.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMargin.swift @@ -33,11 +33,7 @@ struct ContentElementPositionMargin: Decodable, Equatable { self.bottom = try container.decodeIfPresentSafely(Double.self, forKey: ContentElementPositionMargin.CodingKeys.bottom) if !ContentElementPositionMarginValidator().isValid(item: self) { - throw DecodingError.dataCorruptedError( - forKey: .kind, - in: container, - debugDescription: "Position margin corrupted." - ) + throw CustomDecodingError.decodingError("ContentElementPositionMargin not passed validation. It will be ignored.") } } diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index 16265d5e..f9c60578 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4755 + 4764 diff --git a/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerActionValidatorTests.swift b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerActionValidatorTests.swift deleted file mode 100644 index e5cf23d5..00000000 --- a/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerActionValidatorTests.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// ContentBackgroundLayerActionValidatorTests.swift -// MindboxTests -// -// Created by vailence on 07.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import XCTest -@testable import Mindbox - -final class ContentBackgroundLayerActionValidatorTests: XCTestCase { - - var validator: ContentBackgroundLayerActionValidator! - var model: ContentBackgroundLayerAction! - - override func setUpWithError() throws { - validator = ContentBackgroundLayerActionValidator() - } - - override func tearDownWithError() throws { - validator = nil - model = nil - } - - func test_valid_returnTrue() throws { - let model = ContentBackgroundLayerAction(type: .redirectUrl, intentPayload: "payload", value: "value") - XCTAssertTrue(validator.isValid(item: model)) - } - - func test_url_noValue_returnFalse() throws { - let model = ContentBackgroundLayerAction(type: .redirectUrl, intentPayload: "payload") - XCTAssertFalse(validator.isValid(item: model)) - } - - func test_url_noPayload_returnFalse() throws { - let model = ContentBackgroundLayerAction(type: .redirectUrl, value: "value") - XCTAssertFalse(validator.isValid(item: model)) - } - - func test_unknownCase_returnFalse() throws { - let model = ContentBackgroundLayerAction(type: .unknown, intentPayload: "payload", value: "value") - XCTAssertFalse(validator.isValid(item: model)) - } - - func testPerformanceExample() throws { - let model = ContentBackgroundLayerAction(type: .redirectUrl, intentPayload: "payload", value: "value") - self.measure { - for _ in 0..<10000 { - _ = validator.isValid(item: model) - } - } - } -} diff --git a/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerSourceValidatorTests.swift b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerSourceValidatorTests.swift deleted file mode 100644 index 6ef7410d..00000000 --- a/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerSourceValidatorTests.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// ContentBackgroundLayerSourceValidatorTests.swift -// MindboxTests -// -// Created by vailence on 07.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import XCTest -@testable import Mindbox - -final class ContentBackgroundLayerSourceValidatorTests: XCTestCase { - - var validator: ContentBackgroundLayerSourceValidator! - var model: ContentBackgroundLayerSource! - - override func setUpWithError() throws { - validator = ContentBackgroundLayerSourceValidator() - } - - override func tearDownWithError() throws { - validator = nil - model = nil - } - - func test_valid_returnTrue() throws { - let model = ContentBackgroundLayerSource(type: .url, value: "someValue") - XCTAssertTrue(validator.isValid(item: model)) - } - - func test_url_noValue_returnFalse() throws { - let model = ContentBackgroundLayerSource(type: .url) - XCTAssertFalse(validator.isValid(item: model)) - } - - func test_unknownCase_returnFalse() throws { - let model = ContentBackgroundLayerSource(type: .unknown, value: "someValue") - XCTAssertFalse(validator.isValid(item: model)) - } - - func testPerformanceExample() throws { - let model = ContentBackgroundLayerSource(type: .url, value: "someValue") - self.measure { - for _ in 0..<10000 { - _ = validator.isValid(item: model) - } - } - } -} diff --git a/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerTests.swift b/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerTests.swift deleted file mode 100644 index f0009eab..00000000 --- a/MindboxTests/InApp/Tests/ModelValidatorTests/ContentBackgroundLayerTests.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// ContentBackgroundLayerTests.swift -// MindboxTests -// -// Created by vailence on 07.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import XCTest -@testable import Mindbox - -final class ContentBackgroundLayerTests: XCTestCase { - - var validator: ContentBackgroundLayerValidator! - var model: ContentBackgroundLayer! - - override func setUpWithError() throws { - validator = ContentBackgroundLayerValidator() - } - - override func tearDownWithError() throws { - validator = nil - model = nil - } - - func test_valid_returnTrue() throws { - let actionModel = ContentBackgroundLayerAction(type: .redirectUrl, intentPayload: "payload", value: "value") - let sourceModel = ContentBackgroundLayerSource(type: .url, value: "someValue") - let model = ContentBackgroundLayer(type: .image, action: actionModel, source: sourceModel) - XCTAssertTrue(validator.isValid(item: model)) - } - - func test_image_noAction_returnFalse() throws { - let sourceModel = ContentBackgroundLayerSource(type: .url, value: "someValue") - let model = ContentBackgroundLayer(type: .image, source: sourceModel) - XCTAssertFalse(validator.isValid(item: model)) - } - - func test_image_noSource_returnFalse() throws { - let actionModel = ContentBackgroundLayerAction(type: .redirectUrl, intentPayload: "payload", value: "value") - let model = ContentBackgroundLayer(type: .image, action: actionModel) - XCTAssertFalse(validator.isValid(item: model)) - } - - func test_unknownCase_returnFalse() throws { - let model = ContentBackgroundLayer(type: .unknown) - XCTAssertFalse(validator.isValid(item: model)) - } -} diff --git a/MindboxTests/InApp/Tests/ModelValidatorTests/InappFormVariantValidatorTests.swift b/MindboxTests/InApp/Tests/ModelValidatorTests/InappFormVariantValidatorTests.swift deleted file mode 100644 index 46dd3582..00000000 --- a/MindboxTests/InApp/Tests/ModelValidatorTests/InappFormVariantValidatorTests.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// InappFormVariantValidatorTests.swift -// MindboxTests -// -// Created by vailence on 07.08.2023. -// Copyright © 2023 Mindbox. All rights reserved. -// - -import XCTest -@testable import Mindbox - -final class InappFormVariantValidatorTests: XCTestCase { - - var validator: InappFormVariantValidator! - var model: InappFormVariant! - - override func setUpWithError() throws { - validator = InappFormVariantValidator() - } - - override func tearDownWithError() throws { - validator = nil - model = nil - } - - func test_unknownCase_returnFalse() throws { - let model = InappFormVariant(type: .unknown) - XCTAssertFalse(validator.isValid(item: model)) - } - - func test_modal_noContent_returnFalse() throws { - let model = InappFormVariant(type: .modal) - XCTAssertFalse(validator.isValid(item: model)) - } - - func test_snackbar_noContent_returnTrue() throws { - let model = InappFormVariant(type: .snackbar) - XCTAssertTrue(validator.isValid(item: model)) - } -} From 77466dbbbe82ed127fdd37f0d5f2d7619fbc5b10 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Fri, 11 Aug 2023 15:00:56 +0600 Subject: [PATCH 22/35] MBX-2706 Fix tests --- Mindbox/DI/DependencyContainer.swift | 1 + Mindbox/DI/DependencyProvider.swift | 3 ++- Mindbox/Info.plist | 2 +- MindboxTests/DI/TestDependencyProvider.swift | 2 ++ MindboxTests/InApp/Tests/ABTesting/ABTests.swift | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Mindbox/DI/DependencyContainer.swift b/Mindbox/DI/DependencyContainer.swift index 4b3a4d42..eb3d00a4 100644 --- a/Mindbox/DI/DependencyContainer.swift +++ b/Mindbox/DI/DependencyContainer.swift @@ -27,6 +27,7 @@ protocol DependencyContainer { var segmentationSevice: SegmentationServiceProtocol { get } var imageDownloadService: ImageDownloadServiceProtocol { get } var abTestDeviceMixer: ABTestDeviceMixer { get } + var urlExtractorService: VariantImageUrlExtractorService { get } } protocol InstanceFactory { diff --git a/Mindbox/DI/DependencyProvider.swift b/Mindbox/DI/DependencyProvider.swift index 4a2f04b8..137c5f00 100644 --- a/Mindbox/DI/DependencyProvider.swift +++ b/Mindbox/DI/DependencyProvider.swift @@ -29,6 +29,7 @@ final class DependencyProvider: DependencyContainer { let segmentationSevice: SegmentationServiceProtocol var imageDownloadService: ImageDownloadServiceProtocol var abTestDeviceMixer: ABTestDeviceMixer + var urlExtractorService: VariantImageUrlExtractorService init() throws { utilitiesFetcher = MBUtilitiesFetcher() @@ -67,7 +68,7 @@ final class DependencyProvider: DependencyContainer { let actionHandler = InAppActionHandler(actionUseCase: actionUseCase) let presentationManager = InAppPresentationManager(actionHandler: actionHandler, displayUseCase: displayUseCase) - let urlExtractorService = VariantImageUrlExtractorService() + urlExtractorService = VariantImageUrlExtractorService() inAppMessagesManager = InAppCoreManager( configManager: InAppConfigurationManager( inAppConfigAPI: InAppConfigurationAPI(persistenceStorage: persistenceStorage), diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index f9c60578..3ed3d4ec 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4764 + 4766 diff --git a/MindboxTests/DI/TestDependencyProvider.swift b/MindboxTests/DI/TestDependencyProvider.swift index 6679b6b9..53723f51 100644 --- a/MindboxTests/DI/TestDependencyProvider.swift +++ b/MindboxTests/DI/TestDependencyProvider.swift @@ -29,6 +29,7 @@ final class TestDependencyProvider: DependencyContainer { var segmentationSevice: SegmentationServiceProtocol var imageDownloadService: ImageDownloadServiceProtocol var abTestDeviceMixer: ABTestDeviceMixer + var urlExtractorService: VariantImageUrlExtractorService init() throws { sessionTemporaryStorage = SessionTemporaryStorage() @@ -64,6 +65,7 @@ final class TestDependencyProvider: DependencyContainer { targetingChecker: inAppTargetingChecker) imageDownloadService = MockImageDownloadService() abTestDeviceMixer = ABTestDeviceMixer() + urlExtractorService = VariantImageUrlExtractorService() } } diff --git a/MindboxTests/InApp/Tests/ABTesting/ABTests.swift b/MindboxTests/InApp/Tests/ABTesting/ABTests.swift index 3a4d354a..ebfe99e2 100644 --- a/MindboxTests/InApp/Tests/ABTesting/ABTests.swift +++ b/MindboxTests/InApp/Tests/ABTesting/ABTests.swift @@ -44,6 +44,7 @@ class ABTests: XCTestCase { persistenceStorage: persistenceStorage, sdkVersionValidator: sdkVersionValidator, imageDownloadService: container.imageDownloadService, + urlExtractorService: container.urlExtractorService, abTestDeviceMixer: container.abTestDeviceMixer) shownInAppsIds = Set(persistenceStorage.shownInAppsIds ?? []) } From 9eafa07b77fc4992016ec33f17ca362239db0600 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Fri, 11 Aug 2023 17:03:19 +0600 Subject: [PATCH 23/35] MBX-2706 Fix QA requests --- Mindbox.xcodeproj/project.pbxproj | 4 +++ ...ontentElementPositionMarginValidator.swift | 10 ++++++- .../ContentElementSize.swift | 23 +++++++++++++++ .../ContentElementSizeValidator.swift | 28 +++++++++++++++++++ Mindbox/Info.plist | 2 +- 5 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/ContentElementSizeValidator.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 6784085f..ce8acca3 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -346,6 +346,7 @@ F331DD2E2A84CA8900222120 /* UrlLayerSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD2D2A84CA8900222120 /* UrlLayerSource.swift */; }; F331DD312A84CCA800222120 /* CloseButtonElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD302A84CCA800222120 /* CloseButtonElement.swift */; }; F331DD332A84D57800222120 /* VariantImageUrlExtractorService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD322A84D57800222120 /* VariantImageUrlExtractorService.swift */; }; + F331DD582A8648DB00222120 /* ContentElementSizeValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD572A8648DB00222120 /* ContentElementSizeValidator.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -789,6 +790,7 @@ F331DD2D2A84CA8900222120 /* UrlLayerSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlLayerSource.swift; sourceTree = ""; }; F331DD302A84CCA800222120 /* CloseButtonElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseButtonElement.swift; sourceTree = ""; }; F331DD322A84D57800222120 /* VariantImageUrlExtractorService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariantImageUrlExtractorService.swift; sourceTree = ""; }; + F331DD572A8648DB00222120 /* ContentElementSizeValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentElementSizeValidator.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -1974,6 +1976,7 @@ isa = PBXGroup; children = ( F331DC982A80993600222120 /* ContentElementSize.swift */, + F331DD572A8648DB00222120 /* ContentElementSizeValidator.swift */, F331DC992A80993600222120 /* Kind */, ); path = ContentElementSize; @@ -2759,6 +2762,7 @@ 334F3AEB264C199900A6AC00 /* DiscountCardRequest.swift in Sources */, 31A20D4E25B6EFB600AAA0A3 /* MindboxDelegate.swift in Sources */, A179587A2978AEC600609E91 /* AndTargetingChecker.swift in Sources */, + F331DD582A8648DB00222120 /* ContentElementSizeValidator.swift in Sources */, F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */, F331DD012A83A56500222120 /* InAppPresentationManager.swift in Sources */, 3337E6A3265FAB39006949EB /* BaseResponse.swift in Sources */, diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMarginValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMarginValidator.swift index 1134641f..851a1610 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMarginValidator.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPositionMargin/ContentElementPositionMarginValidator.swift @@ -16,6 +16,14 @@ class ContentElementPositionMarginValidator: Validator { return false } - return true + return isValidRange(value: item.top) && isValidRange(value: item.right) + } + + func isValidRange(value: Double?) -> Bool { + guard let value = value else { + return false + } + + return value >= 0 && value <= 1 } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/ContentElementSize.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/ContentElementSize.swift index 72045553..189d4468 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/ContentElementSize.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/ContentElementSize.swift @@ -12,4 +12,27 @@ struct ContentElementSize: Decodable, Equatable { let kind: ContentElementSizeKind let width: Double let height: Double + + enum CodingKeys: String, CodingKey { + case kind + case width + case height + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + kind = try container.decode(ContentElementSizeKind.self, forKey: .kind) + width = try container.decode(Double.self, forKey: .width) + height = try container.decode(Double.self, forKey: .height) + + if !ContentElementSizeValidator().isValid(item: self) { + throw CustomDecodingError.decodingError("Content element size validation not passed.") + } + } + + init(kind: ContentElementSizeKind, width: Double, height: Double) { + self.kind = kind + self.width = width + self.height = height + } } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/ContentElementSizeValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/ContentElementSizeValidator.swift new file mode 100644 index 00000000..ad3c9cee --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementSize/ContentElementSizeValidator.swift @@ -0,0 +1,28 @@ +// +// ContentElementSizeValidator.swift +// Mindbox +// +// Created by vailence on 11.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation +import MindboxLogger + +class ContentElementSizeValidator: Validator { + typealias T = ContentElementSize + + func isValid(item: ContentElementSize) -> Bool { + guard item.width > 0 else { + Logger.common(message: "Content element size width is negative. Width: [\(item.width)", level: .error, category: .inAppMessages) + return false + + } + guard item.height > 0 else { + Logger.common(message: "Content element size height is negative. Height: [\(item.height)", level: .error, category: .inAppMessages) + return false + } + + return true + } +} diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index 3ed3d4ec..71c08250 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4766 + 4768 From 4a959e610c4c7c32ac1acbd8da96cda813d5ca85 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Mon, 14 Aug 2023 17:38:38 +0600 Subject: [PATCH 24/35] MBX-2706 Fix wrong decoding --- .../ContentElementPosition/ContentElementPosition.swift | 2 +- .../ContentElement/Elements/CloseButtonElement.swift | 2 +- .../ElementFactory/CloseButtonElementFactory.swift | 6 +++--- Mindbox/Info.plist | 2 +- Mindbox/Model/Common/FailableDecodableArray.swift | 9 +++++++++ 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPosition.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPosition.swift index 7d1dccf2..1358a12b 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPosition.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/ContentElementPosition/ContentElementPosition.swift @@ -9,5 +9,5 @@ import Foundation struct ContentElementPosition: Decodable, Equatable { - let margin: ContentElementPositionMargin? + let margin: FailableDecodable? } diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/Elements/CloseButtonElement.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/Elements/CloseButtonElement.swift index 49e14cb9..43ca3b35 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/Elements/CloseButtonElement.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentElement/Elements/CloseButtonElement.swift @@ -11,6 +11,6 @@ import Foundation struct CloseButtonElement: ContentElementProtocol { let color: String? let lineWidth: Int? - let size: ContentElementSize? + let size: FailableDecodable? let position: ContentElementPosition? } diff --git a/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift b/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift index ffc59731..953c66d7 100644 --- a/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift +++ b/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift @@ -29,9 +29,9 @@ class CloseButtonElementFactory: ElementFactory { func setupConstraints(for view: UIView, from element: ContentElement, in parentView: UIView) { if case .closeButton(let closeButtonElement) = element { - let size = closeButtonElement.size ?? ContentElementSize(kind: .dp, width: 24, height: 24) - let top = closeButtonElement.position?.margin?.top ?? 0.02 - let right = closeButtonElement.position?.margin?.right ?? 0.02 + let size = closeButtonElement.size?.element ?? ContentElementSize(kind: .dp, width: 24, height: 24) + let top = closeButtonElement.position?.margin?.element?.top ?? 0.02 + let right = closeButtonElement.position?.margin?.element?.right ?? 0.02 let horizontalOffset = (parentView.frame.width - CGFloat(size.width)) * right let verticalOffset = (parentView.frame.height - CGFloat(size.height)) * top diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index 71c08250..8d012c43 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4768 + 4769 diff --git a/Mindbox/Model/Common/FailableDecodableArray.swift b/Mindbox/Model/Common/FailableDecodableArray.swift index 04d15ca3..5b434c42 100644 --- a/Mindbox/Model/Common/FailableDecodableArray.swift +++ b/Mindbox/Model/Common/FailableDecodableArray.swift @@ -33,3 +33,12 @@ struct FailableDecodableArray: Decodable, Equata private struct DummyCodable: Decodable { } } + +struct FailableDecodable: Decodable, Equatable { + let element: Element? + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + element = try? container.decode(Element.self) + } +} From 968e3fb27364bfe98c04c942eb159df53e0fe354 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Tue, 15 Aug 2023 17:45:19 +0600 Subject: [PATCH 25/35] MBX-2707 Parsing --- Mindbox.xcodeproj/project.pbxproj | 48 ++++++++++++++++ .../VariantImageUrlExtractorService.swift | 45 ++++++++++----- .../ContentPosition/ContentPosition.swift | 24 ++++++++ .../ContentPositionGravity.swift | 56 +++++++++++++++++++ .../ContentPositionMargin.swift | 47 ++++++++++++++++ .../ContentPositionMarginValidator.swift | 22 ++++++++ .../SnackbarFormVariantContent.swift | 15 +++++ .../Variants/SnackbarFormVariant.swift | 13 +++++ .../InappFormVariant/iFormVariant.swift | 7 +++ Mindbox/Info.plist | 2 +- 10 files changed, 263 insertions(+), 16 deletions(-) create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPosition.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPositionGravity/ContentPositionGravity.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPositionMargin/ContentPositionMargin.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPositionMargin/ContentPositionMarginValidator.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/SnackbarFormVariantContent.swift create mode 100644 Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/Variants/SnackbarFormVariant.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index ce8acca3..16048189 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -347,6 +347,12 @@ F331DD312A84CCA800222120 /* CloseButtonElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD302A84CCA800222120 /* CloseButtonElement.swift */; }; F331DD332A84D57800222120 /* VariantImageUrlExtractorService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD322A84D57800222120 /* VariantImageUrlExtractorService.swift */; }; F331DD582A8648DB00222120 /* ContentElementSizeValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331DD572A8648DB00222120 /* ContentElementSizeValidator.swift */; }; + F336080D2A8A142800C7C9B7 /* SnackbarFormVariant.swift in Sources */ = {isa = PBXBuildFile; fileRef = F336080C2A8A142800C7C9B7 /* SnackbarFormVariant.swift */; }; + F336080F2A8A14D800C7C9B7 /* SnackbarFormVariantContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F336080E2A8A14D800C7C9B7 /* SnackbarFormVariantContent.swift */; }; + F33608122A8A151C00C7C9B7 /* ContentPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608112A8A151C00C7C9B7 /* ContentPosition.swift */; }; + F33608152A8A156900C7C9B7 /* ContentPositionGravity.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608142A8A156900C7C9B7 /* ContentPositionGravity.swift */; }; + F33608182A8A161200C7C9B7 /* ContentPositionMargin.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608172A8A161200C7C9B7 /* ContentPositionMargin.swift */; }; + F336081A2A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608192A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -791,6 +797,12 @@ F331DD302A84CCA800222120 /* CloseButtonElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseButtonElement.swift; sourceTree = ""; }; F331DD322A84D57800222120 /* VariantImageUrlExtractorService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariantImageUrlExtractorService.swift; sourceTree = ""; }; F331DD572A8648DB00222120 /* ContentElementSizeValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentElementSizeValidator.swift; sourceTree = ""; }; + F336080C2A8A142800C7C9B7 /* SnackbarFormVariant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnackbarFormVariant.swift; sourceTree = ""; }; + F336080E2A8A14D800C7C9B7 /* SnackbarFormVariantContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnackbarFormVariantContent.swift; sourceTree = ""; }; + F33608112A8A151C00C7C9B7 /* ContentPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPosition.swift; sourceTree = ""; }; + F33608142A8A156900C7C9B7 /* ContentPositionGravity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPositionGravity.swift; sourceTree = ""; }; + F33608172A8A161200C7C9B7 /* ContentPositionMargin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPositionMargin.swift; sourceTree = ""; }; + F33608192A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPositionMarginValidator.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -1955,6 +1967,8 @@ isa = PBXGroup; children = ( F331DCB72A80993600222120 /* InappFormVariantContent.swift */, + F336080E2A8A14D800C7C9B7 /* SnackbarFormVariantContent.swift */, + F33608102A8A150E00C7C9B7 /* ContentPosition */, F331DC942A80993600222120 /* ContentElement */, F331DCA62A80993600222120 /* ContentBackground */, ); @@ -2168,6 +2182,7 @@ isa = PBXGroup; children = ( F331DD182A840D2100222120 /* ModalFormVariant.swift */, + F336080C2A8A142800C7C9B7 /* SnackbarFormVariant.swift */, ); path = Variants; sourceTree = ""; @@ -2212,6 +2227,33 @@ path = Elements; sourceTree = ""; }; + F33608102A8A150E00C7C9B7 /* ContentPosition */ = { + isa = PBXGroup; + children = ( + F33608112A8A151C00C7C9B7 /* ContentPosition.swift */, + F33608132A8A155A00C7C9B7 /* ContentPositionGravity */, + F33608162A8A160100C7C9B7 /* ContentPositionMargin */, + ); + path = ContentPosition; + sourceTree = ""; + }; + F33608132A8A155A00C7C9B7 /* ContentPositionGravity */ = { + isa = PBXGroup; + children = ( + F33608142A8A156900C7C9B7 /* ContentPositionGravity.swift */, + ); + path = ContentPositionGravity; + sourceTree = ""; + }; + F33608162A8A160100C7C9B7 /* ContentPositionMargin */ = { + isa = PBXGroup; + children = ( + F33608172A8A161200C7C9B7 /* ContentPositionMargin.swift */, + F33608192A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift */, + ); + path = ContentPositionMargin; + sourceTree = ""; + }; F3482F142A65DBA0002A41EC /* InappMessagesDelegate */ = { isa = PBXGroup; children = ( @@ -2821,6 +2863,7 @@ A192787229D442D900CDB53D /* ProductSegmentChecker.swift in Sources */, A153E03D29BAFEC1003C34D4 /* CustomOperationChecker.swift in Sources */, 33072F402664CB08001F1AB2 /* ProductGroupResponse.swift in Sources */, + F33608152A8A156900C7C9B7 /* ContentPositionGravity.swift in Sources */, A1D017F52976FC2B00CD9F99 /* InternalTargetingChecker.swift in Sources */, 6F1EAA16266A670E007A335B /* ProductListItemsResponse.swift in Sources */, 6FDD143F266F7BF900A50C35 /* AppliedPromotionResponse.swift in Sources */, @@ -2855,6 +2898,7 @@ 847F57FE25C88BB700147A9A /* HTTPMethod.swift in Sources */, 334F3AE9264C199900A6AC00 /* OrderRequest.swift in Sources */, 9B24FAAC28C74B8300F10B5D /* InAppConfigurationRepository.swift in Sources */, + F33608122A8A151C00C7C9B7 /* ContentPosition.swift in Sources */, 337A474026553938000DC613 /* Sex.swift in Sources */, F3A8B9922A3A408C00E9C055 /* SDKVersionValidator.swift in Sources */, 9B4F9DEF28D08897002C9CF0 /* SegmentationCheckResponse.swift in Sources */, @@ -2891,6 +2935,7 @@ 847F580725C88C7A00147A9A /* NetworkFetcher.swift in Sources */, 84D350D225C99F320044E4E6 /* IDFVFetcher.swift in Sources */, 84B625E925C989C100AB6228 /* UDIDValidator.swift in Sources */, + F336080F2A8A14D800C7C9B7 /* SnackbarFormVariantContent.swift in Sources */, 9B2F5F752902975B00F28A96 /* InAppMessagesTracker.swift in Sources */, 84DFB33B25FA23F500A0FCE1 /* GuaranteedDeliveryManager+State.swift in Sources */, 6FDD143B266F7BD900A50C35 /* ProcessingStatusResponse.swift in Sources */, @@ -2901,6 +2946,7 @@ 33EBF0B0264E6283002A35D5 /* SessionManager.swift in Sources */, F331DD192A840D2100222120 /* ModalFormVariant.swift in Sources */, F331DD2E2A84CA8900222120 /* UrlLayerSource.swift in Sources */, + F336081A2A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift in Sources */, 337C7952265646D10092B580 /* OperationBodyRequestType.swift in Sources */, 9B9C95312921116F00BB29DA /* UUIDDebugService.swift in Sources */, 6FDD1443266F7C1600A50C35 /* BalanceTypeReponse.swift in Sources */, @@ -2914,11 +2960,13 @@ F39B67A92A3C72E5005C0CCA /* ImageDownloadService.swift in Sources */, 84CC799725CACF5500C062BD /* MBEventRepository.swift in Sources */, F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */, + F33608182A8A161200C7C9B7 /* ContentPositionMargin.swift in Sources */, 847F581F25C8A3F500147A9A /* HTTPTypealiases.swift in Sources */, 33BEE8172681EC5F00993720 /* EventRoute.swift in Sources */, F331DCD42A80993600222120 /* InappValidator.swift in Sources */, 317054CB25AF189800AE624C /* PersistenceStorage.swift in Sources */, F331DD092A83A56500222120 /* LayerFactory.swift in Sources */, + F336080D2A8A142800C7C9B7 /* SnackbarFormVariant.swift in Sources */, 840C38AC25D11BB200D50183 /* DeliveryOperation.swift in Sources */, 334F3AEE264C199900A6AC00 /* SegmentationRequest.swift in Sources */, B3A625502689F8B600B6A3B7 /* BenefitResponse.swift in Sources */, diff --git a/Mindbox/InAppMessages/InAppConfigurationMapper/Services/VariantImageUrlExtractorService.swift b/Mindbox/InAppMessages/InAppConfigurationMapper/Services/VariantImageUrlExtractorService.swift index c98e088c..bcbc3412 100644 --- a/Mindbox/InAppMessages/InAppConfigurationMapper/Services/VariantImageUrlExtractorService.swift +++ b/Mindbox/InAppMessages/InAppConfigurationMapper/Services/VariantImageUrlExtractorService.swift @@ -12,23 +12,38 @@ class VariantImageUrlExtractorService { func extractImageURL(from variant: MindboxFormVariant) -> String? { var urlString: String? switch variant { - case .modal(let modalModel): - let modalModel = modalModel.content.background.layers.elements.first(where: { - switch $0 { - case .image(let imageModel): - switch imageModel.source { - case .url(let urlModel): - urlString = urlModel.value - return true - case .unknown: - return false + case .modal(let modalModel): + let modalModel = modalModel.content.background.layers.elements.first(where: { + switch $0 { + case .image(let imageModel): + switch imageModel.source { + case .url(let urlModel): + urlString = urlModel.value + return true + case .unknown: + return false + } + case .unknown: + return false + } + }) + case .snackbar(let snackbarModel): + let snackbarModel = snackbarModel.content.background.layers.elements.first { + switch $0 { + case .image(let imageModel): + switch imageModel.source { + case .url(let urlModel): + urlString = urlModel.value + return true + case .unknown: + return false + } + case .unknown: + return false } - case .unknown: - return false } - }) - case .unknown: - return nil + case .unknown: + return nil } return urlString diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPosition.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPosition.swift new file mode 100644 index 00000000..2bc48a9b --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPosition.swift @@ -0,0 +1,24 @@ +// +// ContentPosition.swift +// Mindbox +// +// Created by vailence on 14.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct ContentPosition: Decodable, Equatable { + let gravity: FailableDecodable? + let margin: FailableDecodable? + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + gravity = try container.decodeIfPresent(FailableDecodable.self, forKey: .gravity) + margin = try container.decodeIfPresent(FailableDecodable.self, forKey: .margin) + } + + enum CodingKeys: String, CodingKey { + case gravity, margin + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPositionGravity/ContentPositionGravity.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPositionGravity/ContentPositionGravity.swift new file mode 100644 index 00000000..0297f09a --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPositionGravity/ContentPositionGravity.swift @@ -0,0 +1,56 @@ +// +// ContentPositionGravity.swift +// Mindbox +// +// Created by vailence on 14.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct ContentPositionGravity: Decodable, Equatable { + let vertical: VerticalType? + let horizontal: HorizontalType? + + enum HorizontalType: String, Decodable { + case left + case right + case center + case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(RawValue.self) + self = HorizontalType(rawValue: rawValue) ?? .unknown + } + } + + enum VerticalType: String, Decodable { + case top + case bottom + case center + case unknown + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(RawValue.self) + self = VerticalType(rawValue: rawValue) ?? .unknown + } + } + + enum CodingKeys: CodingKey { + case vertical + case horizontal + } + + init(from decoder: Decoder) throws { + let container: KeyedDecodingContainer = try decoder.container(keyedBy: ContentPositionGravity.CodingKeys.self) + + self.vertical = try container.decodeIfPresent(ContentPositionGravity.VerticalType.self, forKey: .vertical) + self.horizontal = try container.decodeIfPresent(ContentPositionGravity.HorizontalType.self, forKey: .horizontal) + + if vertical == .unknown || horizontal == .unknown { + throw CustomDecodingError.unknownType("") + } + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPositionMargin/ContentPositionMargin.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPositionMargin/ContentPositionMargin.swift new file mode 100644 index 00000000..8781bf6b --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPositionMargin/ContentPositionMargin.swift @@ -0,0 +1,47 @@ +// +// ContentPositionMargin.swift +// Mindbox +// +// Created by vailence on 14.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct ContentPositionMargin: Decodable, Equatable { + let kind: PositionMarginKind + let top: Double? + let right: Double? + let left: Double? + let bottom: Double? + + enum CodingKeys: CodingKey { + case kind + case top + case right + case left + case bottom + } + + init(from decoder: Decoder, gravity: ContentPositionGravity) throws { + let container: KeyedDecodingContainer = try decoder.container(keyedBy: ContentPositionMargin.CodingKeys.self) + + self.kind = try container.decode(PositionMarginKind.self, forKey: .kind) + self.top = try container.decodeIfPresentSafely(Double.self, forKey: .top) + self.right = try container.decodeIfPresentSafely(Double.self, forKey: .right) + self.left = try container.decodeIfPresentSafely(Double.self, forKey: .left) + self.bottom = try container.decodeIfPresentSafely(Double.self, forKey: .bottom) + + if !ContentPositionMarginValidator().isValid(item: self) { + throw CustomDecodingError.decodingError("ContentPositionMargin not passed validation. It will be ignored.") + } + } + + init(kind: PositionMarginKind, top: Double? = nil, right: Double? = nil, left: Double? = nil, bottom: Double? = nil) { + self.kind = kind + self.top = top + self.right = right + self.left = left + self.bottom = bottom + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPositionMargin/ContentPositionMarginValidator.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPositionMargin/ContentPositionMarginValidator.swift new file mode 100644 index 00000000..3bb5923b --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/ContentPosition/ContentPositionMargin/ContentPositionMarginValidator.swift @@ -0,0 +1,22 @@ +// +// ContentPositionMarginValidator.swift +// Mindbox +// +// Created by vailence on 14.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +class ContentPositionMarginValidator: Validator { + + typealias T = ContentPositionMargin + + func isValid(item: ContentPositionMargin) -> Bool { + if item.kind == .unknown { + return false + } + + return true + } +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/SnackbarFormVariantContent.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/SnackbarFormVariantContent.swift new file mode 100644 index 00000000..3c1dfadc --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/InappFormVariantContent/SnackbarFormVariantContent.swift @@ -0,0 +1,15 @@ +// +// SnackbarFormVariantContent.swift +// Mindbox +// +// Created by vailence on 14.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct SnackbarFormVariantContent: Decodable, Equatable { + let background: ContentBackground + let position: ContentPosition? + let elements: FailableDecodableArray? +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/Variants/SnackbarFormVariant.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/Variants/SnackbarFormVariant.swift new file mode 100644 index 00000000..f27c61eb --- /dev/null +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/Variants/SnackbarFormVariant.swift @@ -0,0 +1,13 @@ +// +// SnackbarFormVariant.swift +// Mindbox +// +// Created by vailence on 14.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import Foundation + +struct SnackbarFormVariant: iFormVariant, Decodable, Equatable { + let content: SnackbarFormVariantContent +} diff --git a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/iFormVariant.swift b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/iFormVariant.swift index 5dbe995f..368f0a3f 100644 --- a/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/iFormVariant.swift +++ b/Mindbox/InAppMessages/Models/Config/InappModel/InappForm/InappFormVariant/iFormVariant.swift @@ -12,6 +12,7 @@ protocol iFormVariant: Decodable, Equatable { } enum MindboxFormVariantType: String, Decodable { case modal + case snackbar case unknown init(from decoder: Decoder) throws { @@ -23,6 +24,7 @@ enum MindboxFormVariantType: String, Decodable { enum MindboxFormVariant: Decodable, Hashable, Equatable { case modal(ModalFormVariant) + case snackbar(SnackbarFormVariant) case unknown enum CodingKeys: String, CodingKey { @@ -32,6 +34,7 @@ enum MindboxFormVariant: Decodable, Hashable, Equatable { static func == (lhs: MindboxFormVariant, rhs: MindboxFormVariant) -> Bool { switch (lhs, rhs) { case (.modal, .modal): return true + case (.snackbar, .snackbar): return true case (.unknown, .unknown): return true default: return false } @@ -40,6 +43,7 @@ enum MindboxFormVariant: Decodable, Hashable, Equatable { func hash(into hasher: inout Hasher) { switch self { case .modal: hasher.combine("modal") + case .snackbar: hasher.combine("snackbar") case .unknown: hasher.combine("unknown") } } @@ -57,6 +61,9 @@ enum MindboxFormVariant: Decodable, Hashable, Equatable { case .modal: let modalVariant = try variantContainer.decode(ModalFormVariant.self) self = .modal(modalVariant) + case .snackbar: + let snackbarVariant = try variantContainer.decode(SnackbarFormVariant.self) + self = .snackbar(snackbarVariant) case .unknown: throw CustomDecodingError.unknownType("The variant type could not be decoded. The variant will be ignored.") } diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index 8d012c43..2cdb4f20 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4769 + 4772 From 27334c1581ccf368282c36fa1791b5e31b99b57b Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Tue, 15 Aug 2023 17:54:06 +0600 Subject: [PATCH 26/35] MBX-2707 UI --- Mindbox.xcodeproj/project.pbxproj | 4 +++ .../ViewFactory/SnackbarViewFactory.swift | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 Mindbox/InAppMessages/Presentation/Views/ViewFactory/SnackbarViewFactory.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 3008c4ab..fdc31fb1 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -353,6 +353,7 @@ F33608152A8A156900C7C9B7 /* ContentPositionGravity.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608142A8A156900C7C9B7 /* ContentPositionGravity.swift */; }; F33608182A8A161200C7C9B7 /* ContentPositionMargin.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608172A8A161200C7C9B7 /* ContentPositionMargin.swift */; }; F336081A2A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608192A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift */; }; + F336081C2A8B9EA800C7C9B7 /* SnackbarViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F336081B2A8B9EA800C7C9B7 /* SnackbarViewFactory.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -803,6 +804,7 @@ F33608142A8A156900C7C9B7 /* ContentPositionGravity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPositionGravity.swift; sourceTree = ""; }; F33608172A8A161200C7C9B7 /* ContentPositionMargin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPositionMargin.swift; sourceTree = ""; }; F33608192A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPositionMarginValidator.swift; sourceTree = ""; }; + F336081B2A8B9EA800C7C9B7 /* SnackbarViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnackbarViewFactory.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -2174,6 +2176,7 @@ children = ( F331DCFF2A83A56500222120 /* ViewFactoryProtocol.swift */, F331DD002A83A56500222120 /* ModalViewFactory.swift */, + F336081B2A8B9EA800C7C9B7 /* SnackbarViewFactory.swift */, ); path = ViewFactory; sourceTree = ""; @@ -2749,6 +2752,7 @@ 334F3AE0264C199900A6AC00 /* ViewProductCategoryRequest.swift in Sources */, F331DD022A83A56500222120 /* PresentationDisplayUseCase.swift in Sources */, A1D23AF029DE082E00A75179 /* InAppProductSegmentResponse.swift in Sources */, + F336081C2A8B9EA800C7C9B7 /* SnackbarViewFactory.swift in Sources */, F331DCD52A80993600222120 /* InAppModel.swift in Sources */, A1D017EE2976CC4000CD9F99 /* AndTargeting.swift in Sources */, F331DCC32A80993600222120 /* ContentElementPosition.swift in Sources */, diff --git a/Mindbox/InAppMessages/Presentation/Views/ViewFactory/SnackbarViewFactory.swift b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/SnackbarViewFactory.swift new file mode 100644 index 00000000..c5ac0f9c --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/SnackbarViewFactory.swift @@ -0,0 +1,33 @@ +// +// SnackbarViewFactory.swift +// Mindbox +// +// Created by vailence on 15.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit + +//class SnackbarViewFactory: ViewFactoryProtocol { +// var myViewController: UIViewController? +// +// func create(model: MindboxFormVariant, +// id: String, +// image: UIImage, +// onPresented: @escaping () -> Void, +// onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, +// onClose: @escaping () -> Void) -> UIViewController? { +// if case .snackbar(let snackbarFormVariant) = model { +// let viewController = ModalViewController(model: snackbarFormVariant, +// id: id, +// image: image, +// onPresented: onPresented, +// onTapAction: onTapAction, +// onClose: onClose) +// myViewController = viewController +// return viewController +// } +// +// return nil +// } +//} From f737b67f10962e26410ba231d3adbeb5c6b57e20 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Wed, 16 Aug 2023 15:44:42 +0600 Subject: [PATCH 27/35] MBX-2707 Add SnackbarPresentationStrategy --- Mindbox.xcodeproj/project.pbxproj | 6 +++- .../SnackbarPresentationStrategy.swift | 28 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/Strategy/SnackbarPresentationStrategy.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index fdc31fb1..f596e0b7 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -354,6 +354,7 @@ F33608182A8A161200C7C9B7 /* ContentPositionMargin.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608172A8A161200C7C9B7 /* ContentPositionMargin.swift */; }; F336081A2A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608192A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift */; }; F336081C2A8B9EA800C7C9B7 /* SnackbarViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F336081B2A8B9EA800C7C9B7 /* SnackbarViewFactory.swift */; }; + F33608282A8CD17E00C7C9B7 /* SnackbarPresentationStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608272A8CD17E00C7C9B7 /* SnackbarPresentationStrategy.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -805,6 +806,7 @@ F33608172A8A161200C7C9B7 /* ContentPositionMargin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPositionMargin.swift; sourceTree = ""; }; F33608192A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPositionMarginValidator.swift; sourceTree = ""; }; F336081B2A8B9EA800C7C9B7 /* SnackbarViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnackbarViewFactory.swift; sourceTree = ""; }; + F33608272A8CD17E00C7C9B7 /* SnackbarPresentationStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnackbarPresentationStrategy.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -2119,8 +2121,9 @@ F331DCEF2A83A56500222120 /* Strategy */ = { isa = PBXGroup; children = ( - F331DCF02A83A56500222120 /* ModalPresentationStrategy.swift */, F331DCF12A83A56500222120 /* PresentationStrategyProtocol.swift */, + F331DCF02A83A56500222120 /* ModalPresentationStrategy.swift */, + F33608272A8CD17E00C7C9B7 /* SnackbarPresentationStrategy.swift */, ); path = Strategy; sourceTree = ""; @@ -2928,6 +2931,7 @@ 843DAA5C26087F3D00CAC489 /* DependencyContainer.swift in Sources */, F39B67A52A3C6BE3005C0CCA /* SegmentationCheckRequest.swift in Sources */, A184654329C3102A00E64780 /* CategoryIDTargeting.swift in Sources */, + F33608282A8CD17E00C7C9B7 /* SnackbarPresentationStrategy.swift in Sources */, 330D8CCB26579521005106D5 /* DiscountTypeRequest.swift in Sources */, A184654729C32FEF00E64780 /* CategoryIDChecker.swift in Sources */, A192786729D389EE00CDB53D /* ProductIDTargeting.swift in Sources */, diff --git a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/Strategy/SnackbarPresentationStrategy.swift b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/Strategy/SnackbarPresentationStrategy.swift new file mode 100644 index 00000000..2360e009 --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/Strategy/SnackbarPresentationStrategy.swift @@ -0,0 +1,28 @@ +// +// SnackbarPresentationStrategy.swift +// Mindbox +// +// Created by vailence on 16.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit +import MindboxLogger + +final class SnackbarPresentationStrategy: PresentationStrategyProtocol { + func getWindow() -> UIWindow? { + return UIApplication.shared.windows.first(where: { $0.isKeyWindow }) + } + + func present(id: String, in window: UIWindow, using viewController: UIViewController) { + window.rootViewController?.addChild(viewController) + window.rootViewController?.view.addSubview(viewController.view) + Logger.common(message: "In-app with id \(id) presented", level: .info, category: .inAppMessages) + } + + func dismiss(viewController: UIViewController) { + viewController.view.removeFromSuperview() + viewController.removeFromParent() + Logger.common(message: "InApp presentation dismissed", level: .debug, category: .inAppMessages) + } +} From 15bd739318f8126713250dd76a0de2eab5bc8023 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Thu, 17 Aug 2023 16:02:48 +0600 Subject: [PATCH 28/35] MBX-2707 Temp commit. Change name later --- Mindbox.xcodeproj/project.pbxproj | 40 ++- .../PresentationDisplayUseCase.swift | 3 + .../CloseButtonElementFactory.swift | 7 +- .../ElementFactory/ElementFactory.swift | 2 +- .../ImageLayer/ImageLayerFactory.swift | 43 +++ .../LayersFactory/LayerFactory.swift | 26 +- .../Views/ModalView/ModalViewController.swift | 18 +- .../Views/SnackbarView/SnackbarView.swift | 77 ++++++ .../SnackbarView/SnackbarViewController.swift | 256 ++++++++++++++++++ .../ViewFactory/SnackbarViewFactory.swift | 49 ++-- Mindbox/Info.plist | 2 +- 11 files changed, 464 insertions(+), 59 deletions(-) rename Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/{ => CloseButtonFactory}/CloseButtonElementFactory.swift (93%) create mode 100644 Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/ImageLayer/ImageLayerFactory.swift create mode 100644 Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarView.swift create mode 100644 Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index f596e0b7..5191695b 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -354,7 +354,10 @@ F33608182A8A161200C7C9B7 /* ContentPositionMargin.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608172A8A161200C7C9B7 /* ContentPositionMargin.swift */; }; F336081A2A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608192A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift */; }; F336081C2A8B9EA800C7C9B7 /* SnackbarViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F336081B2A8B9EA800C7C9B7 /* SnackbarViewFactory.swift */; }; + F33608262A8CC94A00C7C9B7 /* SnackbarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608252A8CC94A00C7C9B7 /* SnackbarViewController.swift */; }; F33608282A8CD17E00C7C9B7 /* SnackbarPresentationStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33608272A8CD17E00C7C9B7 /* SnackbarPresentationStrategy.swift */; }; + F336082D2A8E15D500C7C9B7 /* ImageLayerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F336082C2A8E15D500C7C9B7 /* ImageLayerFactory.swift */; }; + F33608302A8E167100C7C9B7 /* SnackbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F336082F2A8E167100C7C9B7 /* SnackbarView.swift */; }; F3482F172A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */; }; F3482F192A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */; }; F3482F1B2A65DC01002A41EC /* DefaultInappMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */; }; @@ -806,7 +809,10 @@ F33608172A8A161200C7C9B7 /* ContentPositionMargin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPositionMargin.swift; sourceTree = ""; }; F33608192A8A163D00C7C9B7 /* ContentPositionMarginValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPositionMarginValidator.swift; sourceTree = ""; }; F336081B2A8B9EA800C7C9B7 /* SnackbarViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnackbarViewFactory.swift; sourceTree = ""; }; + F33608252A8CC94A00C7C9B7 /* SnackbarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnackbarViewController.swift; sourceTree = ""; }; F33608272A8CD17E00C7C9B7 /* SnackbarPresentationStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnackbarPresentationStrategy.swift; sourceTree = ""; }; + F336082C2A8E15D500C7C9B7 /* ImageLayerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageLayerFactory.swift; sourceTree = ""; }; + F336082F2A8E167100C7C9B7 /* SnackbarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnackbarView.swift; sourceTree = ""; }; F3482F162A65DBB7002A41EC /* MindboxURLHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxURLHandlerDelegate.swift; sourceTree = ""; }; F3482F182A65DBBD002A41EC /* MindboxPayloadCopierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxPayloadCopierDelegate.swift; sourceTree = ""; }; F3482F1A2A65DC01002A41EC /* DefaultInappMessageDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInappMessageDelegate.swift; sourceTree = ""; }; @@ -1551,8 +1557,8 @@ 9B24FAAD28C74BA500F10B5D /* InAppCoreManager.swift */, 9B2F5F742902975B00F28A96 /* InAppMessagesTracker.swift */, A11FBE8829DD76BF00F5FB7B /* InAppMessagesEventSender.swift */, - A1D017F32976FC1900CD9F99 /* InAppTargetingChecker */, 9B24FAB928C756EB00F10B5D /* PresentChecker */, + A1D017F32976FC1900CD9F99 /* InAppTargetingChecker */, 9B24FAB628C7554C00F10B5D /* Models */, 9B24FAAF28C74BB700F10B5D /* Configuration */, 9B24FAB328C751CB00F10B5D /* Images */, @@ -2131,6 +2137,7 @@ F331DCF32A83A56500222120 /* Views */ = { isa = PBXGroup; children = ( + F336081D2A8BA02C00C7C9B7 /* SnackbarView */, F331DCF42A83A56500222120 /* ModalView */, F331DCF62A83A56500222120 /* CommonViews */, F331DCFE2A83A56500222120 /* ViewFactory */, @@ -2160,8 +2167,8 @@ F331DCF72A83A56500222120 /* ElementFactory */ = { isa = PBXGroup; children = ( - F331DCF82A83A56500222120 /* CloseButtonElementFactory.swift */, F331DCF92A83A56500222120 /* ElementFactory.swift */, + F336082E2A8E160900C7C9B7 /* CloseButtonFactory */, ); path = ElementFactory; sourceTree = ""; @@ -2170,6 +2177,7 @@ isa = PBXGroup; children = ( F331DCFB2A83A56500222120 /* LayerFactory.swift */, + F336082B2A8E15CA00C7C9B7 /* ImageLayer */, ); path = LayersFactory; sourceTree = ""; @@ -2260,6 +2268,31 @@ path = ContentPositionMargin; sourceTree = ""; }; + F336081D2A8BA02C00C7C9B7 /* SnackbarView */ = { + isa = PBXGroup; + children = ( + F336082F2A8E167100C7C9B7 /* SnackbarView.swift */, + F33608252A8CC94A00C7C9B7 /* SnackbarViewController.swift */, + ); + path = SnackbarView; + sourceTree = ""; + }; + F336082B2A8E15CA00C7C9B7 /* ImageLayer */ = { + isa = PBXGroup; + children = ( + F336082C2A8E15D500C7C9B7 /* ImageLayerFactory.swift */, + ); + path = ImageLayer; + sourceTree = ""; + }; + F336082E2A8E160900C7C9B7 /* CloseButtonFactory */ = { + isa = PBXGroup; + children = ( + F331DCF82A83A56500222120 /* CloseButtonElementFactory.swift */, + ); + path = CloseButtonFactory; + sourceTree = ""; + }; F3482F142A65DBA0002A41EC /* InappMessagesDelegate */ = { isa = PBXGroup; children = ( @@ -2734,6 +2767,7 @@ A192786C29D3A24000CDB53D /* ProductIDChecker.swift in Sources */, 337A473A26550FDA000DC613 /* CustomFields.swift in Sources */, B3A6255C268A025600B6A3B7 /* NearestExpirationResponse.swift in Sources */, + F33608302A8E167100C7C9B7 /* SnackbarView.swift in Sources */, 84A312BD25DA651C0096A017 /* UIBackgroundTaskManager.swift in Sources */, F3A8B99A2A3A471800E9C055 /* ABTestVariantsValidator.swift in Sources */, 33E42E5C268323E60046CBCB /* CashdeskRequest.swift in Sources */, @@ -2860,6 +2894,7 @@ F3A8B9AB2A3A719C00E9C055 /* ABTestModel.swift in Sources */, 33072F342664C3E1001F1AB2 /* ProductResponse.swift in Sources */, 33072F3E2664C7B5001F1AB2 /* DiscountCardResponse.swift in Sources */, + F336082D2A8E15D500C7C9B7 /* ImageLayerFactory.swift in Sources */, F331DD292A84C4BC00222120 /* InappActionHandler.swift in Sources */, F331DD0A2A83A56500222120 /* InAppImageOnlyView.swift in Sources */, 8410681325ECDC73004701C2 /* DatabaseLoader.swift in Sources */, @@ -2897,6 +2932,7 @@ 84DC4A0725D27D6000D5D758 /* UNAuthorizationStatusProvider.swift in Sources */, F331DCC82A80993600222120 /* ContentBackgroundLayer.swift in Sources */, F3D818B02A3885AD0002957C /* ABTestDeviceMixer.swift in Sources */, + F33608262A8CC94A00C7C9B7 /* SnackbarViewController.swift in Sources */, 3328FE4226303F2F000A30D0 /* String+Regex.swift in Sources */, 33072F322664C357001F1AB2 /* ProductListResponse.swift in Sources */, 9B24FAB128C74BD200F10B5D /* InAppConfigurationManager.swift in Sources */, diff --git a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift index 20d47ee2..5415774a 100644 --- a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift +++ b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift @@ -70,6 +70,9 @@ final class PresentationDisplayUseCase { case .modal: self.presentationStrategy = ModalPresentationStrategy() self.factory = ModalViewFactory() + case .snackbar: + self.presentationStrategy = SnackbarPresentationStrategy() + self.factory = SnackbarViewFactory() default: break } diff --git a/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift b/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonFactory/CloseButtonElementFactory.swift similarity index 93% rename from Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift rename to Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonFactory/CloseButtonElementFactory.swift index 953c66d7..34023bf3 100644 --- a/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonElementFactory.swift +++ b/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/CloseButtonFactory/CloseButtonElementFactory.swift @@ -9,11 +9,7 @@ import UIKit class CloseButtonElementFactory: ElementFactory { - func create(from element: ContentElement, in view: UIView, with controller: UIViewController) -> UIView? { - guard let controller = controller as? ModalViewController else { - return nil - } - + func create(from element: ContentElement, in view: UIView, with controller: GestureHandler) -> UIView? { if case .closeButton(let closeButtonElement) = element { let color = closeButtonElement.color?.isHexValid() ?? false ? closeButtonElement.color : nil let closeButton = CrossView(lineColorHex: color, lineWidth: closeButtonElement.lineWidth) @@ -21,6 +17,7 @@ class CloseButtonElementFactory: ElementFactory { let closeRecognizer = UILongPressGestureRecognizer(target: controller, action: #selector(controller.onCloseButton)) closeRecognizer.minimumPressDuration = 0 closeButton.addGestureRecognizer(closeRecognizer) + return closeButton } diff --git a/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/ElementFactory.swift b/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/ElementFactory.swift index 4ef6dfb5..aee49a5c 100644 --- a/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/ElementFactory.swift +++ b/Mindbox/InAppMessages/Presentation/Views/CommonViews/ElementFactory/ElementFactory.swift @@ -9,6 +9,6 @@ import UIKit protocol ElementFactory { - func create(from element: ContentElement, in view: UIView, with controller: UIViewController) -> UIView? + func create(from element: ContentElement, in view: UIView, with controller: GestureHandler) -> UIView? func setupConstraints(for view: UIView, from element: ContentElement, in parentView: UIView) } diff --git a/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/ImageLayer/ImageLayerFactory.swift b/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/ImageLayer/ImageLayerFactory.swift new file mode 100644 index 00000000..e185019a --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/ImageLayer/ImageLayerFactory.swift @@ -0,0 +1,43 @@ +// +// ImageLayerFactory.swift +// Mindbox +// +// Created by vailence on 17.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit + +class ImageLayerFactory: LayerFactory { + func create(from image: UIImage, layer: ContentBackgroundLayer, in view: UIView, with controller: GestureHandler) -> UIView? { + if case .image(let imageContentBackgroundLayer) = layer { + let inAppView = InAppImageOnlyView(image: image, action: imageContentBackgroundLayer.action) + let imageTapGestureRecognizer = UITapGestureRecognizer(target: controller, action: #selector(controller.imageTapped(_:))) + inAppView.addGestureRecognizer(imageTapGestureRecognizer) + + return inAppView + } + + return nil + } + + func setupConstraints(for view: UIView, in parentView: UIView) { + view.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + view.leadingAnchor.constraint(equalTo: parentView.leadingAnchor, constant: 20), + view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor, constant: -20), + view.centerYAnchor.constraint(equalTo: parentView.centerYAnchor), + view.widthAnchor.constraint(equalTo: view.heightAnchor, multiplier: 3 / 4) + ]) + } + + func setupConstraintsSnackbar(for view: UIView, in parentView: UIView) { + view.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + view.topAnchor.constraint(equalTo: parentView.topAnchor), + view.bottomAnchor.constraint(equalTo: parentView.bottomAnchor), + view.leadingAnchor.constraint(equalTo: parentView.leadingAnchor), + view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor) + ]) + } +} diff --git a/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/LayerFactory.swift b/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/LayerFactory.swift index ebb149a0..429d6af8 100644 --- a/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/LayerFactory.swift +++ b/Mindbox/InAppMessages/Presentation/Views/CommonViews/LayersFactory/LayerFactory.swift @@ -9,29 +9,7 @@ import UIKit protocol LayerFactory { - func create(from image: UIImage, layer: ContentBackgroundLayer, in view: UIView, with controller: ModalViewController) -> UIView? + func create(from image: UIImage, layer: ContentBackgroundLayer, in view: UIView, with controller: GestureHandler) -> UIView? func setupConstraints(for view: UIView, in parentView: UIView) -} - -class ImageLayerFactory: LayerFactory { - func create(from image: UIImage, layer: ContentBackgroundLayer, in view: UIView, with controller: ModalViewController) -> UIView? { - if case .image(let imageContentBackgroundLayer) = layer { - let inAppView = InAppImageOnlyView(image: image, action: imageContentBackgroundLayer.action) - let imageTapGestureRecognizer = UITapGestureRecognizer(target: controller, action: #selector(controller.imageTapped(_:))) - inAppView.addGestureRecognizer(imageTapGestureRecognizer) - return inAppView - } - - return nil - } - - func setupConstraints(for view: UIView, in parentView: UIView) { - view.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - view.leadingAnchor.constraint(equalTo: parentView.leadingAnchor, constant: 20), - view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor, constant: -20), - view.centerYAnchor.constraint(equalTo: parentView.centerYAnchor), - view.widthAnchor.constraint(equalTo: view.heightAnchor, multiplier: 3 / 4) - ]) - } + func setupConstraintsSnackbar(for view: UIView, in parentView: UIView) } diff --git a/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift b/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift index ee1491e7..dbd3b145 100644 --- a/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift +++ b/Mindbox/InAppMessages/Presentation/Views/ModalView/ModalViewController.swift @@ -7,15 +7,27 @@ import UIKit -final class ModalViewController: UIViewController { +protocol InappViewControllerProtocol { + var layers: [UIView] { get set } + var elements: [UIView] { get set } + var elementFactories: [ContentElementType: ElementFactory] { get } + var layersFactories: [ContentBackgroundLayerType: LayerFactory] { get } +} + +@objc protocol GestureHandler { + @objc func imageTapped(_ sender: UITapGestureRecognizer) + @objc func onCloseButton(_ gesture: UILongPressGestureRecognizer) +} + +final class ModalViewController: UIViewController, InappViewControllerProtocol, GestureHandler { var layers = [UIView]() var elements = [UIView]() - private let elementFactories: [ContentElementType: ElementFactory] = [ + let elementFactories: [ContentElementType: ElementFactory] = [ .closeButton: CloseButtonElementFactory() ] - private let layersFactories: [ContentBackgroundLayerType: LayerFactory] = [ + let layersFactories: [ContentBackgroundLayerType: LayerFactory] = [ .image: ImageLayerFactory() ] diff --git a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarView.swift b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarView.swift new file mode 100644 index 00000000..85c80d38 --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarView.swift @@ -0,0 +1,77 @@ +// SnackbarView.swift +// Mindbox +// +// Created by vailence on 15.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit + +class SnackbarView: UIView { + + public var swipeDirection: UISwipeGestureRecognizer.Direction = .down + + private let onClose: () -> Void + private let animationTime: TimeInterval + + enum Constants { + static let defaultAnimationTime: TimeInterval = 0.3 + static let swipeThresholdFraction: CGFloat = 0.5 + } + + init(onClose: @escaping () -> Void, + animationTime: TimeInterval = Constants.defaultAnimationTime) { + self.onClose = onClose + self.animationTime = animationTime + super.init(frame: .zero) + + setupPanGesture() + } + + private func setupPanGesture() { + let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture)) + self.addGestureRecognizer(panGesture) + } + + @objc private func handlePanGesture(_ gesture: UIPanGestureRecognizer) { + let translation = gesture.translation(in: self) + switch gesture.state { + case .changed: + handleSwipeGesture(translation: translation) + case .ended, .cancelled: + finalizeGesture(translation: translation) + default: + break + } + } + + private func handleSwipeGesture(translation: CGPoint) { + if (swipeDirection == .up && translation.y < 0) || (swipeDirection == .down && translation.y > 0) { + self.transform = CGAffineTransform(translationX: 0, y: translation.y) + self.alpha = max(0, 1 - abs(translation.y) / self.frame.height) + } + } + + private func finalizeGesture(translation: CGPoint) { + if ((swipeDirection == .up && translation.y < 0) || (swipeDirection == .down && translation.y > 0)) && + abs(translation.y) > self.frame.height * Constants.swipeThresholdFraction { + UIView.animate(withDuration: animationTime, animations: { + self.alpha = 0 + self.transform = CGAffineTransform(translationX: 0, y: self.swipeDirection == .up ? -self.frame.height : self.frame.height) + }) { [weak self] _ in + self?.onClose() + } + } else { + UIView.animate(withDuration: animationTime) { + self.alpha = 1 + self.transform = .identity + } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + + diff --git a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift new file mode 100644 index 00000000..53bcf115 --- /dev/null +++ b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift @@ -0,0 +1,256 @@ +// +// SnackbarViewController.swift +// Mindbox +// +// Created by vailence on 16.08.2023. +// Copyright © 2023 Mindbox. All rights reserved. +// + +import UIKit + +class SnackbarViewController: UIViewController { + + var snackbarView: SnackbarView? + var edgeConstraint: NSLayoutConstraint? + + var layers = [UIView]() + var elements = [UIView]() + private let elementFactories: [ContentElementType: ElementFactory] = [ + .closeButton: CloseButtonElementFactory() + ] + + private let layersFactories: [ContentBackgroundLayerType: LayerFactory] = [ + .image: ImageLayerFactory() + ] + + private let model: SnackbarFormVariant + private let id: String + public let image: UIImage + private let onPresented: () -> Void + private let onClose: () -> Void + private let onTapAction: (ContentBackgroundLayerAction?) -> Void + + private var hasSetupLayers = false + private var hasSetupElements = false + + enum Constants { + static let animationDuration: TimeInterval = 0.5 + static let screenPart: CGFloat = 3.0 + static let oneThirdScreenHeight: CGFloat = UIScreen.main.bounds.height / Constants.screenPart + } + + init( + model: SnackbarFormVariant, + id: String, + image: UIImage, + onPresented: @escaping () -> Void, + onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, + onClose: @escaping () -> Void + ) { + self.model = model + self.id = id + self.image = image + self.onPresented = onPresented + self.onClose = onClose + self.onTapAction = onTapAction + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + view.isUserInteractionEnabled = true + snackbarView = SnackbarView(onClose: onClose) + if let snackbarView = snackbarView { + snackbarView.translatesAutoresizingMaskIntoConstraints = false + snackbarView.isUserInteractionEnabled = true + view.addSubview(snackbarView) + } + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + if !hasSetupLayers { + hasSetupLayers = true + setupConstraints() + setupLayers() + + if snackbarView?.bounds.size != .zero { + setupElements() + hasSetupElements = true + } + } else if !hasSetupElements && snackbarView?.bounds.size != .zero { + setupElements() + hasSetupElements = true + } + } + + private func setupLayers() { + let layers = model.content.background.layers.elements + guard let snackbarView = snackbarView else { + return + } + + for layer in layers { + if let factory = layersFactories[layer.layerType] { + let layerView = factory.create(from: self.image, layer: layer, in: snackbarView, with: self) + if let layerView = layerView { + self.layers.append(layerView) + snackbarView.addSubview(layerView) + factory.setupConstraintsSnackbar(for: layerView, in: snackbarView) + } + } + } + } + + private func setupElements() { + guard let elements = model.content.elements?.elements, + let snackbarView = snackbarView else { + return + } + + for element in elements { + if let factory = elementFactories[element.elementType] { + let elementView = factory.create(from: element, in: snackbarView, with: self) + if let elementView = elementView { + self.elements.append(elementView) + snackbarView.addSubview(elementView) + factory.setupConstraints(for: elementView, from: element, in: snackbarView) + } + } + } + } + + var swipeDirection: UISwipeGestureRecognizer.Direction { + fatalError("This method must be overridden") + } + + func setupConstraints() { + fatalError("This method must be overridden") + } + + private func animateConstraints(withDuration duration: TimeInterval) { + view.layoutIfNeeded() + + UIView.animate(withDuration: duration) { + self.edgeConstraint?.constant = 0 + self.view.layoutIfNeeded() + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + animateConstraints(withDuration: Constants.animationDuration) + onPresented() + } +} + +extension SnackbarViewController: GestureHandler { + @objc func imageTapped(_ sender: UITapGestureRecognizer) { + guard let imageView = sender.view as? InAppImageOnlyView else { + return + } + + let action = imageView.action + onTapAction(action) + } + + @objc func onCloseButton(_ gesture: UILongPressGestureRecognizer) { + guard let crossView = gesture.view else { + return + } + + let location = gesture.location(in: crossView) + let isInsideCrossView = crossView.bounds.contains(location) + if gesture.state == .ended && isInsideCrossView { + onClose() + } + } +} + +class TopSnackbarViewController: SnackbarViewController { + override var swipeDirection: UISwipeGestureRecognizer.Direction { + return .up + } + + override func setupConstraints() { + let imageHeight = self.image.size.height + var safeAreaBottomOffset: CGFloat = 0 + if #available(iOS 11.0, *) { + safeAreaBottomOffset = view.safeAreaInsets.top + } + + let finalHeight = (imageHeight < Constants.oneThirdScreenHeight) ? imageHeight : Constants.oneThirdScreenHeight + + self.view.frame = CGRect(x: 0, y: 0, + width: UIScreen.main.bounds.width, + height: finalHeight + safeAreaBottomOffset) + + guard let snackbarView = snackbarView else { + return + } + + snackbarView.swipeDirection = .up + if #available(iOS 11.0, *) { + NSLayoutConstraint.activate([ + snackbarView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + snackbarView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), + snackbarView.heightAnchor.constraint(equalToConstant: finalHeight), + ]) + } + + setupBottomConstraint(with: finalHeight) + } + + private func setupBottomConstraint(with height: CGFloat) { + if #available(iOS 11.0, *) { + edgeConstraint = snackbarView?.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: -height) + edgeConstraint?.isActive = true + } + } +} + +class BottomSnackbarViewController: SnackbarViewController { + + override var swipeDirection: UISwipeGestureRecognizer.Direction { + return .down + } + + override func setupConstraints() { + let imageHeight = self.image.size.height + let screenHeight = UIScreen.main.bounds.height + var safeAreaBottomOffset: CGFloat = 0 + if #available(iOS 11.0, *) { + safeAreaBottomOffset = view.safeAreaInsets.bottom + } + let finalHeight = (imageHeight < Constants.oneThirdScreenHeight) ? imageHeight : Constants.oneThirdScreenHeight + + self.view.frame = CGRect(x: 0, y: screenHeight - finalHeight - safeAreaBottomOffset, + width: UIScreen.main.bounds.width, + height: finalHeight + safeAreaBottomOffset) + guard let snackbarView = snackbarView else { + return + } + snackbarView.swipeDirection = .down + + if #available(iOS 11.0, *) { + NSLayoutConstraint.activate([ + snackbarView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + snackbarView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), + snackbarView.heightAnchor.constraint(equalToConstant: finalHeight), + ]) + } + + setupBottomConstraint(with: finalHeight) + } + + private func setupBottomConstraint(with height: CGFloat) { + if #available(iOS 11.0, *) { + edgeConstraint = snackbarView?.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: height) + edgeConstraint?.isActive = true + } + } +} diff --git a/Mindbox/InAppMessages/Presentation/Views/ViewFactory/SnackbarViewFactory.swift b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/SnackbarViewFactory.swift index c5ac0f9c..62e00f9e 100644 --- a/Mindbox/InAppMessages/Presentation/Views/ViewFactory/SnackbarViewFactory.swift +++ b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/SnackbarViewFactory.swift @@ -6,28 +6,31 @@ // Copyright © 2023 Mindbox. All rights reserved. // +import Foundation import UIKit -//class SnackbarViewFactory: ViewFactoryProtocol { -// var myViewController: UIViewController? -// -// func create(model: MindboxFormVariant, -// id: String, -// image: UIImage, -// onPresented: @escaping () -> Void, -// onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, -// onClose: @escaping () -> Void) -> UIViewController? { -// if case .snackbar(let snackbarFormVariant) = model { -// let viewController = ModalViewController(model: snackbarFormVariant, -// id: id, -// image: image, -// onPresented: onPresented, -// onTapAction: onTapAction, -// onClose: onClose) -// myViewController = viewController -// return viewController -// } -// -// return nil -// } -//} +class SnackbarViewFactory: ViewFactoryProtocol { + + var viewController: UIViewController? + + func create(model: MindboxFormVariant, id: String, image: UIImage, onPresented: @escaping () -> Void, onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, onClose: @escaping () -> Void) -> UIViewController? { + if case .snackbar(let snackbarFormVariant) = model { + if let gravity = snackbarFormVariant.content.position?.gravity?.element?.vertical { + var snackbarViewController: UIViewController? + switch gravity { + case .top: + snackbarViewController = TopSnackbarViewController(model: snackbarFormVariant, id: id, image: image, onPresented: onPresented, onTapAction: onTapAction, onClose: onClose) + case .bottom: + snackbarViewController = BottomSnackbarViewController(model: snackbarFormVariant, id: id, image: image, onPresented: onPresented, onTapAction: onTapAction, onClose: onClose) + default: + return nil + } + + self.viewController = snackbarViewController + return viewController + } + } + + return nil + } +} diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index 2cdb4f20..eacca1e5 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4772 + 4778 From a87ce7717c81bbab857719e80791326dbb7fd3cc Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Fri, 18 Aug 2023 15:09:26 +0600 Subject: [PATCH 29/35] MBX-2707 Fix some leaks --- .../Presentation/InAppPresentationManager.swift | 1 - .../PresentationDisplayUseCase.swift | 7 ++++++- .../Views/SnackbarView/SnackbarViewController.swift | 8 ++++++-- .../Presentation/Views/ViewFactory/ModalViewFactory.swift | 2 +- .../Views/ViewFactory/SnackbarViewFactory.swift | 4 ++-- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift b/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift index d7f42dbf..222ad8ef 100644 --- a/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift +++ b/Mindbox/InAppMessages/Presentation/InAppPresentationManager.swift @@ -62,7 +62,6 @@ final class InAppPresentationManager: InAppPresentationManagerProtocol { return } - self.displayUseCase.changeType(model: inAppFormData.content) self.displayUseCase.presentInAppUIModel(model: inAppFormData, onPresented: { self.displayUseCase.onPresented(id: inAppFormData.inAppId, onPresented) diff --git a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift index 5415774a..a8cf485f 100644 --- a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift +++ b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationDisplayUseCase/PresentationDisplayUseCase.swift @@ -22,6 +22,9 @@ final class PresentationDisplayUseCase { } func presentInAppUIModel(model: InAppFormData, onPresented: @escaping () -> Void, onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, onClose: @escaping () -> Void) { + + changeType(model: model.content) + guard let window = presentationStrategy?.getWindow() else { Logger.common(message: "In-app modal window creating failed") return @@ -53,6 +56,8 @@ final class PresentationDisplayUseCase { onClose() self.presentedVC = nil self.model = nil + self.presentationStrategy = nil + self.factory = nil } func onPresented(id: String, _ completion: @escaping () -> Void) { @@ -65,7 +70,7 @@ final class PresentationDisplayUseCase { completion() } - func changeType(model: MindboxFormVariant) { + private func changeType(model: MindboxFormVariant) { switch model { case .modal: self.presentationStrategy = ModalPresentationStrategy() diff --git a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift index 53bcf115..0272afb2 100644 --- a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift +++ b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift @@ -83,8 +83,11 @@ class SnackbarViewController: UIViewController { hasSetupElements = true } } else if !hasSetupElements && snackbarView?.bounds.size != .zero { - setupElements() - hasSetupElements = true + UIView.performWithoutAnimation { + setupElements() + hasSetupElements = true + view.layoutIfNeeded() + } } } @@ -234,6 +237,7 @@ class BottomSnackbarViewController: SnackbarViewController { guard let snackbarView = snackbarView else { return } + snackbarView.swipeDirection = .down if #available(iOS 11.0, *) { diff --git a/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ModalViewFactory.swift b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ModalViewFactory.swift index 64dbc02d..e1225317 100644 --- a/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ModalViewFactory.swift +++ b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/ModalViewFactory.swift @@ -9,7 +9,7 @@ import UIKit class ModalViewFactory: ViewFactoryProtocol { - var myViewController: UIViewController? + weak var myViewController: UIViewController? func create(model: MindboxFormVariant, id: String, diff --git a/Mindbox/InAppMessages/Presentation/Views/ViewFactory/SnackbarViewFactory.swift b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/SnackbarViewFactory.swift index 62e00f9e..3f46ebd1 100644 --- a/Mindbox/InAppMessages/Presentation/Views/ViewFactory/SnackbarViewFactory.swift +++ b/Mindbox/InAppMessages/Presentation/Views/ViewFactory/SnackbarViewFactory.swift @@ -10,8 +10,8 @@ import Foundation import UIKit class SnackbarViewFactory: ViewFactoryProtocol { - - var viewController: UIViewController? + + weak var viewController: UIViewController? func create(model: MindboxFormVariant, id: String, image: UIImage, onPresented: @escaping () -> Void, onTapAction: @escaping (ContentBackgroundLayerAction?) -> Void, onClose: @escaping () -> Void) -> UIViewController? { if case .snackbar(let snackbarFormVariant) = model { From 88e694938f7de2b1af47357357150ff47985312b Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Wed, 23 Aug 2023 13:55:43 +0600 Subject: [PATCH 30/35] MBX-2707 Some fixes --- .../Views/SnackbarView/SnackbarView.swift | 43 ++++-- .../SnackbarView/SnackbarViewController.swift | 137 ++++++++++-------- Mindbox/Info.plist | 2 +- 3 files changed, 107 insertions(+), 75 deletions(-) diff --git a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarView.swift b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarView.swift index 85c80d38..e01d46c5 100644 --- a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarView.swift +++ b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarView.swift @@ -8,12 +8,12 @@ import UIKit class SnackbarView: UIView { - + public var swipeDirection: UISwipeGestureRecognizer.Direction = .down private let onClose: () -> Void private let animationTime: TimeInterval - + enum Constants { static let defaultAnimationTime: TimeInterval = 0.3 static let swipeThresholdFraction: CGFloat = 0.5 @@ -27,7 +27,7 @@ class SnackbarView: UIView { setupPanGesture() } - + private func setupPanGesture() { let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture)) self.addGestureRecognizer(panGesture) @@ -48,30 +48,45 @@ class SnackbarView: UIView { private func handleSwipeGesture(translation: CGPoint) { if (swipeDirection == .up && translation.y < 0) || (swipeDirection == .down && translation.y > 0) { self.transform = CGAffineTransform(translationX: 0, y: translation.y) - self.alpha = max(0, 1 - abs(translation.y) / self.frame.height) } } private func finalizeGesture(translation: CGPoint) { if ((swipeDirection == .up && translation.y < 0) || (swipeDirection == .down && translation.y > 0)) && - abs(translation.y) > self.frame.height * Constants.swipeThresholdFraction { - UIView.animate(withDuration: animationTime, animations: { - self.alpha = 0 - self.transform = CGAffineTransform(translationX: 0, y: self.swipeDirection == .up ? -self.frame.height : self.frame.height) - }) { [weak self] _ in - self?.onClose() - } + abs(translation.y) > frame.height * Constants.swipeThresholdFraction { + animateHide(completion: onClose, animated: true) } else { UIView.animate(withDuration: animationTime) { - self.alpha = 1 self.transform = .identity } } } + private func animateHide(completion: @escaping () -> Void, animated: Bool) { + if animated { + UIView.animate(withDuration: animationTime, animations: { + self.setHiddenTransform() + }) { _ in + completion() + } + } else { + setHiddenTransform() + completion() + } + } + + private func setHiddenTransform() { + self.transform = CGAffineTransform(translationX: 0, y: swipeDirection == .up ? -frame.height : frame.height) + } + + public func hide(animated: Bool = true, completion: (() -> Void)? = nil) { + animateHide(completion: { + self.onClose() + completion?() + }, animated: animated) + } + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } - - diff --git a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift index 0272afb2..2389858c 100644 --- a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift +++ b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift @@ -7,23 +7,25 @@ // import UIKit +import MindboxLogger -class SnackbarViewController: UIViewController { +class SnackbarViewController: UIViewController, InappViewControllerProtocol { var snackbarView: SnackbarView? var edgeConstraint: NSLayoutConstraint? + let model: SnackbarFormVariant var layers = [UIView]() var elements = [UIView]() - private let elementFactories: [ContentElementType: ElementFactory] = [ + + let elementFactories: [ContentElementType: ElementFactory] = [ .closeButton: CloseButtonElementFactory() ] - private let layersFactories: [ContentBackgroundLayerType: LayerFactory] = [ + let layersFactories: [ContentBackgroundLayerType: LayerFactory] = [ .image: ImageLayerFactory() ] - private let model: SnackbarFormVariant private let id: String public let image: UIImage private let onPresented: () -> Void @@ -91,6 +93,12 @@ class SnackbarViewController: UIViewController { } } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + animateConstraints(withDuration: Constants.animationDuration) + onPresented() + } + private func setupLayers() { let layers = model.content.background.layers.elements guard let snackbarView = snackbarView else { @@ -128,11 +136,7 @@ class SnackbarViewController: UIViewController { } var swipeDirection: UISwipeGestureRecognizer.Direction { - fatalError("This method must be overridden") - } - - func setupConstraints() { - fatalError("This method must be overridden") + return .down } private func animateConstraints(withDuration duration: TimeInterval) { @@ -143,11 +147,49 @@ class SnackbarViewController: UIViewController { self.view.layoutIfNeeded() } } + + func setupConstraints() { + let imageHeight = self.image.size.height + let finalHeight = (imageHeight < Constants.oneThirdScreenHeight) ? imageHeight : Constants.oneThirdScreenHeight + + setViewFrame(with: finalHeight) + guard let snackbarView = snackbarView else { + return + } + + snackbarView.swipeDirection = swipeDirection + snackbarView.translatesAutoresizingMaskIntoConstraints = false + + setupLayoutConstraints(with: finalHeight) + setupEdgeConstraint(with: finalHeight) + } - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - animateConstraints(withDuration: Constants.animationDuration) - onPresented() + func setViewFrame(with height: CGFloat) { + + } + + func setupLayoutConstraints(with height: CGFloat) { + guard let snackbarView = snackbarView else { + return + } + + if #available(iOS 11.0, *) { + NSLayoutConstraint.activate([ + snackbarView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + snackbarView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), + snackbarView.heightAnchor.constraint(equalToConstant: height), + ]) + } else { + NSLayoutConstraint.activate([ + snackbarView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + snackbarView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + snackbarView.heightAnchor.constraint(equalToConstant: height), + ]) + } + } + + func setupEdgeConstraint(with height: CGFloat) { + Logger.common(message: "Method setupEdgeConstraint must be overriden in subclass.") } } @@ -169,7 +211,7 @@ extension SnackbarViewController: GestureHandler { let location = gesture.location(in: crossView) let isInsideCrossView = crossView.bounds.contains(location) if gesture.state == .ended && isInsideCrossView { - onClose() + snackbarView?.hide() } } } @@ -178,41 +220,28 @@ class TopSnackbarViewController: SnackbarViewController { override var swipeDirection: UISwipeGestureRecognizer.Direction { return .up } - - override func setupConstraints() { - let imageHeight = self.image.size.height - var safeAreaBottomOffset: CGFloat = 0 + + override func setViewFrame(with height: CGFloat) { + var safeAreaTopOffset: CGFloat = 0 if #available(iOS 11.0, *) { - safeAreaBottomOffset = view.safeAreaInsets.top + safeAreaTopOffset = view.safeAreaInsets.top } - let finalHeight = (imageHeight < Constants.oneThirdScreenHeight) ? imageHeight : Constants.oneThirdScreenHeight + let finalHeight = height + safeAreaTopOffset self.view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, - height: finalHeight + safeAreaBottomOffset) - - guard let snackbarView = snackbarView else { - return - } - - snackbarView.swipeDirection = .up - if #available(iOS 11.0, *) { - NSLayoutConstraint.activate([ - snackbarView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), - snackbarView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), - snackbarView.heightAnchor.constraint(equalToConstant: finalHeight), - ]) - } - - setupBottomConstraint(with: finalHeight) + height: finalHeight) } - private func setupBottomConstraint(with height: CGFloat) { + override func setupEdgeConstraint(with height: CGFloat) { if #available(iOS 11.0, *) { edgeConstraint = snackbarView?.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: -height) - edgeConstraint?.isActive = true + } else { + edgeConstraint = snackbarView?.topAnchor.constraint(equalTo: view.topAnchor, constant: -height) } + + edgeConstraint?.isActive = true } } @@ -222,39 +251,27 @@ class BottomSnackbarViewController: SnackbarViewController { return .down } - override func setupConstraints() { - let imageHeight = self.image.size.height + override func setViewFrame(with height: CGFloat) { let screenHeight = UIScreen.main.bounds.height var safeAreaBottomOffset: CGFloat = 0 if #available(iOS 11.0, *) { safeAreaBottomOffset = view.safeAreaInsets.bottom } - let finalHeight = (imageHeight < Constants.oneThirdScreenHeight) ? imageHeight : Constants.oneThirdScreenHeight + + let finalHeight = height + safeAreaBottomOffset - self.view.frame = CGRect(x: 0, y: screenHeight - finalHeight - safeAreaBottomOffset, + self.view.frame = CGRect(x: 0, y: screenHeight - finalHeight, width: UIScreen.main.bounds.width, - height: finalHeight + safeAreaBottomOffset) - guard let snackbarView = snackbarView else { - return - } - - snackbarView.swipeDirection = .down - - if #available(iOS 11.0, *) { - NSLayoutConstraint.activate([ - snackbarView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), - snackbarView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), - snackbarView.heightAnchor.constraint(equalToConstant: finalHeight), - ]) - } - - setupBottomConstraint(with: finalHeight) + height: finalHeight) } - private func setupBottomConstraint(with height: CGFloat) { + override func setupEdgeConstraint(with height: CGFloat) { if #available(iOS 11.0, *) { edgeConstraint = snackbarView?.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: height) - edgeConstraint?.isActive = true + } else { + edgeConstraint = snackbarView?.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: height) } + + edgeConstraint?.isActive = true } } diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index eacca1e5..ea2f1d8a 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 4778 + 4781 From 287a5bb6d75e61416b01cb537892efb8d347341b Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Thu, 24 Aug 2023 12:49:39 +0600 Subject: [PATCH 31/35] MBX-2707 Fix animation and time --- .../Views/SnackbarView/SnackbarView.swift | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarView.swift b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarView.swift index e01d46c5..486f7b32 100644 --- a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarView.swift +++ b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarView.swift @@ -13,6 +13,21 @@ class SnackbarView: UIView { private let onClose: () -> Void private let animationTime: TimeInterval + + private var safeAreaBottomInset: CGFloat { + if #available(iOS 11.0, *) { + return window?.safeAreaInsets.bottom ?? 0 + } else { + return 0 + } + } + private var safeAreaTopInset: CGFloat { + if #available(iOS 11.0, *) { + return window?.safeAreaInsets.top ?? 0 + } else { + return 0 + } + } enum Constants { static let defaultAnimationTime: TimeInterval = 0.3 @@ -52,8 +67,10 @@ class SnackbarView: UIView { } private func finalizeGesture(translation: CGPoint) { + let threshold = frame.height * Constants.swipeThresholdFraction + + (swipeDirection == .up ? safeAreaTopInset : safeAreaBottomInset) if ((swipeDirection == .up && translation.y < 0) || (swipeDirection == .down && translation.y > 0)) && - abs(translation.y) > frame.height * Constants.swipeThresholdFraction { + abs(translation.y) > threshold { animateHide(completion: onClose, animated: true) } else { UIView.animate(withDuration: animationTime) { @@ -76,7 +93,16 @@ class SnackbarView: UIView { } private func setHiddenTransform() { - self.transform = CGAffineTransform(translationX: 0, y: swipeDirection == .up ? -frame.height : frame.height) + let yOffset: CGFloat + switch swipeDirection { + case .up: + yOffset = -(frame.height + safeAreaTopInset) + case .down: + yOffset = frame.height + safeAreaBottomInset + default: + yOffset = 0 + } + self.transform = CGAffineTransform(translationX: 0, y: yOffset) } public func hide(animated: Bool = true, completion: (() -> Void)? = nil) { From d187208240c9f6ce512f01191c0f95b1c13f4471 Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Thu, 24 Aug 2023 14:42:29 +0600 Subject: [PATCH 32/35] MBX-2707 Fix offsets left/right --- .../SnackbarView/SnackbarViewController.swift | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift index 2389858c..ee54a7f3 100644 --- a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift +++ b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift @@ -149,7 +149,11 @@ class SnackbarViewController: UIViewController, InappViewControllerProtocol { } func setupConstraints() { - let imageHeight = self.image.size.height + let left = model.content.position?.margin?.element?.left ?? 0 + let right = model.content.position?.margin?.element?.right ?? 0 + let width = view.layer.frame.width - left - right + let heightMultiplier = width / image.size.width + let imageHeight = image.size.height * heightMultiplier let finalHeight = (imageHeight < Constants.oneThirdScreenHeight) ? imageHeight : Constants.oneThirdScreenHeight setViewFrame(with: finalHeight) @@ -169,20 +173,23 @@ class SnackbarViewController: UIViewController, InappViewControllerProtocol { } func setupLayoutConstraints(with height: CGFloat) { + let left = model.content.position?.margin?.element?.left ?? 0 + let right = model.content.position?.margin?.element?.right ?? 0 + guard let snackbarView = snackbarView else { return } if #available(iOS 11.0, *) { NSLayoutConstraint.activate([ - snackbarView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), - snackbarView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), + snackbarView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: left), + snackbarView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -right), snackbarView.heightAnchor.constraint(equalToConstant: height), ]) } else { NSLayoutConstraint.activate([ - snackbarView.leadingAnchor.constraint(equalTo: view.leadingAnchor), - snackbarView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + snackbarView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: left), + snackbarView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -right), snackbarView.heightAnchor.constraint(equalToConstant: height), ]) } From 6154b2ec2f380d81272ac084b62900cb508722ec Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Fri, 25 Aug 2023 13:51:34 +0600 Subject: [PATCH 33/35] MBX-2707 Added interaction with background. --- .../SnackbarView/SnackbarViewController.swift | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift index ee54a7f3..52cb1eac 100644 --- a/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift +++ b/Mindbox/InAppMessages/Presentation/Views/SnackbarView/SnackbarViewController.swift @@ -25,6 +25,14 @@ class SnackbarViewController: UIViewController, InappViewControllerProtocol { let layersFactories: [ContentBackgroundLayerType: LayerFactory] = [ .image: ImageLayerFactory() ] + + var leftOffset: CGFloat { + return model.content.position?.margin?.element?.left ?? 0 + } + + var rightOffset: CGFloat { + return model.content.position?.margin?.element?.right ?? 0 + } private let id: String public let image: UIImage @@ -149,9 +157,7 @@ class SnackbarViewController: UIViewController, InappViewControllerProtocol { } func setupConstraints() { - let left = model.content.position?.margin?.element?.left ?? 0 - let right = model.content.position?.margin?.element?.right ?? 0 - let width = view.layer.frame.width - left - right + let width = view.layer.frame.width - leftOffset - rightOffset let heightMultiplier = width / image.size.width let imageHeight = image.size.height * heightMultiplier let finalHeight = (imageHeight < Constants.oneThirdScreenHeight) ? imageHeight : Constants.oneThirdScreenHeight @@ -169,27 +175,24 @@ class SnackbarViewController: UIViewController, InappViewControllerProtocol { } func setViewFrame(with height: CGFloat) { - + // Need override in sub-class. } func setupLayoutConstraints(with height: CGFloat) { - let left = model.content.position?.margin?.element?.left ?? 0 - let right = model.content.position?.margin?.element?.right ?? 0 - guard let snackbarView = snackbarView else { return } if #available(iOS 11.0, *) { NSLayoutConstraint.activate([ - snackbarView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: left), - snackbarView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -right), + snackbarView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + snackbarView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), snackbarView.heightAnchor.constraint(equalToConstant: height), ]) } else { NSLayoutConstraint.activate([ - snackbarView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: left), - snackbarView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -right), + snackbarView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + snackbarView.trailingAnchor.constraint(equalTo: view.trailingAnchor), snackbarView.heightAnchor.constraint(equalToConstant: height), ]) } @@ -236,8 +239,8 @@ class TopSnackbarViewController: SnackbarViewController { let finalHeight = height + safeAreaTopOffset - self.view.frame = CGRect(x: 0, y: 0, - width: UIScreen.main.bounds.width, + self.view.frame = CGRect(x: leftOffset, y: 0, + width: UIScreen.main.bounds.width - leftOffset - rightOffset, height: finalHeight) } @@ -267,8 +270,8 @@ class BottomSnackbarViewController: SnackbarViewController { let finalHeight = height + safeAreaBottomOffset - self.view.frame = CGRect(x: 0, y: screenHeight - finalHeight, - width: UIScreen.main.bounds.width, + self.view.frame = CGRect(x: leftOffset, y: screenHeight - finalHeight, + width: UIScreen.main.bounds.width - leftOffset - rightOffset, height: finalHeight) } From d725fb5a00f65e2c616bc284b3946d088868b38c Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Mon, 28 Aug 2023 18:07:19 +0600 Subject: [PATCH 34/35] Version Up 2.8.0-rc --- Mindbox.podspec | 2 +- MindboxNotifications.podspec | 2 +- SDKVersionProvider/SDKVersionProvider.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Mindbox.podspec b/Mindbox.podspec index 8b354eb3..d95d9ab5 100644 --- a/Mindbox.podspec +++ b/Mindbox.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "Mindbox" - spec.version = "2.7.0" + spec.version = "2.8.0-rc" spec.summary = "SDK for integration with Mindbox" spec.description = "This library allows you to integrate data transfer to Mindbox Marketing Cloud" spec.homepage = "https://github.com/mindbox-cloud/ios-sdk" diff --git a/MindboxNotifications.podspec b/MindboxNotifications.podspec index 2063d2a4..7565b752 100644 --- a/MindboxNotifications.podspec +++ b/MindboxNotifications.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "MindboxNotifications" - spec.version = "2.7.0" + spec.version = "2.8.0-rc" spec.summary = "SDK for integration notifications with Mindbox" spec.description = "This library allows you to integrate notifications and transfer them to Mindbox Marketing Cloud" spec.homepage = "https://github.com/mindbox-cloud/ios-sdk" diff --git a/SDKVersionProvider/SDKVersionProvider.swift b/SDKVersionProvider/SDKVersionProvider.swift index 3473fea4..1e6baf12 100644 --- a/SDKVersionProvider/SDKVersionProvider.swift +++ b/SDKVersionProvider/SDKVersionProvider.swift @@ -8,5 +8,5 @@ import Foundation public class SDKVersionProvider { - public static let sdkVersion = "2.7.0" + public static let sdkVersion = "2.8.0-rc" } From ba7b1d3a31afb95c31204006e113cdbfd814d5cf Mon Sep 17 00:00:00 2001 From: Akylbek Utekeshev Date: Tue, 29 Aug 2023 13:56:02 +0600 Subject: [PATCH 35/35] MBX-2707 Skip image if image corrupted --- .../Services/ImageDownloadService.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mindbox/InAppMessages/InAppConfigurationMapper/Services/ImageDownloadService.swift b/Mindbox/InAppMessages/InAppConfigurationMapper/Services/ImageDownloadService.swift index 981108ed..7731baad 100644 --- a/Mindbox/InAppMessages/InAppConfigurationMapper/Services/ImageDownloadService.swift +++ b/Mindbox/InAppMessages/InAppConfigurationMapper/Services/ImageDownloadService.swift @@ -27,6 +27,8 @@ class ImageDownloadService: ImageDownloadServiceProtocol { Logger.common(message: "Failed to download image for url: \(url). \nError: \(error.localizedDescription)", level: .debug, category: .inAppMessages) if error.code == NSURLErrorTimedOut { completion(.failure(error)) + } else { + completion(.failure(error)) } } else if let response = response, response.statusCode != 200 { Logger.common(message: "Image download failed with status code \(response.statusCode). [URL]: \(url)", level: .debug, category: .inAppMessages)