Skip to content

Commit

Permalink
Merge pull request #270 from mindbox-cloud/release/2.8.0
Browse files Browse the repository at this point in the history
Release/2.8.0
  • Loading branch information
AndreyEmtsov authored Oct 6, 2023
2 parents 4d502a5 + 139d808 commit 7569e81
Show file tree
Hide file tree
Showing 96 changed files with 7,958 additions and 1,423 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
build:
runs-on: macos-13
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
- name: Update bundler
run: gem install bundler
- name: Install bundler dependencies
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
unit:
runs-on: macos-13
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
- name: Update bundler
run: gem install bundler
- name: Install bundler dependencies
Expand All @@ -22,9 +22,9 @@ jobs:

publish:
needs: [unit]
runs-on: macos-11
runs-on: macos-13
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
- name: Release generation
run: ./git-release.sh "${{ github.event.head_commit.message }}" "${{secrets.GITHUBACCESSTOKEN}}" "${{secrets.GITHUBUSER}}"
- name: Update bundler
Expand Down
26 changes: 12 additions & 14 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,25 @@ GEM
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.4)
addressable (2.8.5)
public_suffix (>= 2.0.2, < 6.0)
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
aws-partitions (1.786.0)
aws-sdk-core (3.178.0)
aws-partitions (1.825.0)
aws-sdk-core (3.182.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.71.0)
aws-sdk-core (~> 3, >= 3.177.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.130.0)
aws-sdk-core (~> 3, >= 3.177.0)
aws-sdk-s3 (1.134.0)
aws-sdk-core (~> 3, >= 3.181.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.6)
aws-sigv4 (1.6.0)
Expand Down Expand Up @@ -86,7 +86,7 @@ GEM
escape (0.0.4)
ethon (0.16.0)
ffi (>= 1.15.0)
excon (0.100.0)
excon (0.103.0)
faraday (1.10.3)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
Expand Down Expand Up @@ -159,9 +159,9 @@ GEM
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.45.0)
google-apis-androidpublisher_v3 (0.49.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-core (0.11.0)
google-apis-core (0.11.1)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
Expand Down Expand Up @@ -190,10 +190,9 @@ GEM
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.6.0)
googleauth (1.8.0)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
Expand All @@ -206,9 +205,8 @@ GEM
jmespath (1.6.2)
json (2.6.3)
jwt (2.7.1)
memoist (0.16.2)
mini_magick (4.12.0)
mini_mime (1.1.2)
mini_mime (1.1.5)
minitest (5.18.0)
molinillo (0.8.0)
multi_json (1.15.0)
Expand All @@ -227,13 +225,13 @@ GEM
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.2.5)
rexml (3.2.6)
rouge (2.0.7)
ruby-macho (2.5.1)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
signet (0.17.0)
signet (0.18.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
Expand Down
2 changes: 1 addition & 1 deletion Mindbox.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |spec|
spec.name = "Mindbox"
spec.version = "2.8.0-rc"
spec.version = "2.8.0"
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"
Expand Down
212 changes: 180 additions & 32 deletions Mindbox.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Mindbox/DI/DependencyContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ protocol DependencyContainer {
var imageDownloadService: ImageDownloadServiceProtocol { get }
var abTestDeviceMixer: ABTestDeviceMixer { get }
var urlExtractorService: VariantImageUrlExtractorService { get }
var inappFilterService: InappFilterProtocol { get }
}

protocol InstanceFactory {
Expand Down
16 changes: 15 additions & 1 deletion Mindbox/DI/DependencyProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ final class DependencyProvider: DependencyContainer {
var imageDownloadService: ImageDownloadServiceProtocol
var abTestDeviceMixer: ABTestDeviceMixer
var urlExtractorService: VariantImageUrlExtractorService
var inappFilterService: InappFilterProtocol

init() throws {
utilitiesFetcher = MBUtilitiesFetcher()
Expand Down Expand Up @@ -69,11 +70,24 @@ final class DependencyProvider: DependencyContainer {
let presentationManager = InAppPresentationManager(actionHandler: actionHandler,
displayUseCase: displayUseCase)
urlExtractorService = VariantImageUrlExtractorService()
let actionFilter = LayerActionFilterService()
let sourceFilter = LayersSourceFilterService()
let layersFilterService = LayersFilterService(actionFilter: actionFilter, sourceFilter: sourceFilter)
let sizeFilter = ElementSizeFilterService()
let colorFilter = ElementsColorFilterService()
let positionFilter = ElementsPositionFilterService()
let elementsFilterService = ElementsFilterService(sizeFilter: sizeFilter, positionFilter: positionFilter, colorFilter: colorFilter)
let contentPositionFilterService = ContentPositionFilterService()
let variantsFilterService = VariantFilterService(layersFilter: layersFilterService,
elementsFilter: elementsFilterService,
contentPositionFilter: contentPositionFilterService)
inappFilterService = InappsFilterService(variantsFilter: variantsFilterService)
inAppMessagesManager = InAppCoreManager(
configManager: InAppConfigurationManager(
inAppConfigAPI: InAppConfigurationAPI(persistenceStorage: persistenceStorage),
inAppConfigRepository: InAppConfigurationRepository(),
inAppConfigurationMapper: InAppConfigutationMapper(geoService: geoService,
inAppConfigurationMapper: InAppConfigutationMapper(inappFilterService: inappFilterService,
geoService: geoService,
segmentationService: segmentationSevice,
customerSegmentsAPI: .live,
targetingChecker: inAppTargetingChecker,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class InAppConfigurationManager: InAppConfigurationManagerProtocol {
rawConfigurationResponse = configResponse
inAppConfigurationMapper.mapConfigResponse(event, configResponse, { inapp in
self.inapp = inapp
Logger.common(message: "In-app сonfiguration applied: \n\(String(describing: inapp))", level: .debug, category: .inAppMessages)
Logger.common(message: "In-app applied: \(String(describing: inapp?.inAppId)))", level: .debug, category: .inAppMessages)
self.delegate?.didPreparedConfiguration()
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ protocol InAppConfigurationMapperProtocol {

final class InAppConfigutationMapper: InAppConfigurationMapperProtocol {

private let inappFilterService: InappFilterProtocol
private let geoService: GeoServiceProtocol
private let segmentationService: SegmentationServiceProtocol
private let customerSegmentsAPI: CustomerSegmentsAPI
Expand All @@ -25,21 +26,23 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol {
var filteredInAppsByEvent: [InAppMessageTriggerEvent: [InAppTransitionData]] = [:]
private let sdkVersionValidator: SDKVersionValidator
private let imageDownloadService: ImageDownloadServiceProtocol
private let urlExtractorService: VariantImageUrlExtractorService
private let urlExtractorService: VariantImageUrlExtractorServiceProtocol
private let abTestDeviceMixer: ABTestDeviceMixer

private let dispatchGroup = DispatchGroup()

init(geoService: GeoServiceProtocol,
init(inappFilterService: InappFilterProtocol,
geoService: GeoServiceProtocol,
segmentationService: SegmentationServiceProtocol,
customerSegmentsAPI: CustomerSegmentsAPI,
targetingChecker: InAppTargetingCheckerProtocol,
sessionTemporaryStorage: SessionTemporaryStorage,
persistenceStorage: PersistenceStorage,
sdkVersionValidator: SDKVersionValidator,
imageDownloadService: ImageDownloadServiceProtocol,
urlExtractorService: VariantImageUrlExtractorService,
urlExtractorService: VariantImageUrlExtractorServiceProtocol,
abTestDeviceMixer: ABTestDeviceMixer) {
self.inappFilterService = inappFilterService
self.geoService = geoService
self.segmentationService = segmentationService
self.customerSegmentsAPI = customerSegmentsAPI
Expand All @@ -57,7 +60,8 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol {
_ response: ConfigResponse,
_ completion: @escaping (InAppFormData?) -> Void) {
let shownInAppsIds = Set(persistenceStorage.shownInAppsIds ?? [])
let responseInapps = filterInappsByABTests(response.abtests, responseInapps: response.inapps?.elements)
let inapps = inappFilterService.filter(inapps: response.inapps?.elements)
let responseInapps = filterInappsByABTests(response.abtests, responseInapps: inapps)
let filteredInapps = filterInappsBySDKVersion(responseInapps, shownInAppsIds: shownInAppsIds)
Logger.common(message: "Shown in-apps ids: [\(shownInAppsIds)]", level: .info, category: .inAppMessages)
if filteredInapps.isEmpty {
Expand Down Expand Up @@ -242,7 +246,7 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol {
}

var inAppsForEvent = filteredInAppsByEvent[triggerEvent] ?? [InAppTransitionData]()
if let inAppFormVariants = inapp.form.variants.elements.first {
if let inAppFormVariants = inapp.form.variants.first {
let formData = InAppTransitionData(inAppId: inapp.id,
content: inAppFormVariants)
inAppsForEvent.append(formData)
Expand All @@ -255,39 +259,53 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol {

private func buildInAppByEvent(inapps: [InAppTransitionData],
completion: @escaping (InAppFormData?) -> Void) {
var shouldDownloadImage = true
var formData: InAppFormData?
let group = DispatchGroup()
let imageDictQueue = DispatchQueue(label: "com.mindbox.imagedict.queue", attributes: .concurrent)

DispatchQueue.global().async {
for inapp in inapps {
if !shouldDownloadImage {
guard formData == nil else {
break
}

var imageDict: [String: UIImage] = [:]
var gotError = false

if let shownInapps = self.persistenceStorage.shownInAppsIds, shownInapps.contains(inapp.inAppId) {
continue
}

guard let imageValue = self.urlExtractorService.extractImageURL(from: inapp.content) else { continue }
let imageValues = self.urlExtractorService.extractImageURL(from: inapp.content)

group.enter()
Logger.common(message: "Starting inapp processing. [ID]: \(inapp.inAppId)", level: .debug, category: .inAppMessages)

self.imageDownloadService.downloadImage(withUrl: imageValue) { result in
defer { group.leave() }
switch result {
case .success(let image):
formData = InAppFormData(inAppId: inapp.inAppId,
image: image,
content: inapp.content)
shouldDownloadImage = false
case .failure:
break
Logger.common(message: "Starting in-app processing. [ID]: \(inapp.inAppId)", level: .debug, category: .inAppMessages)
for imageValue in imageValues {
group.enter()
Logger.common(message: "Initiating the process of image loading from the URL: \(imageValue)", level: .debug, category: .inAppMessages)
self.imageDownloadService.downloadImage(withUrl: imageValue) { result in
defer {
group.leave()
}

switch result {
case .success(let image):
imageDictQueue.async(flags: .barrier) {
imageDict[imageValue] = image
}
case .failure:
gotError = true
}
}
}

group.wait()

imageDictQueue.sync {
if !imageDict.isEmpty && !gotError {
let firstImageValue = imageValues.first ?? ""
formData = InAppFormData(inAppId: inapp.inAppId, imagesDict: imageDict, firstImageValue: firstImageValue, content: inapp.content)
}
}
}

group.notify(queue: .main) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ImageDownloadService: ImageDownloadServiceProtocol {
func downloadImage(withUrl url: String, completion: @escaping (Result<UIImage, Error>) -> Void) {
self.imageDownloader.downloadImage(withUrl: url) { localURL, response, error in
if let error = error as? NSError {
Logger.common(message: "Failed to download image for url: \(url). \nError: \(error.localizedDescription)", level: .debug, category: .inAppMessages)
Logger.common(message: "Failed to download image. [URL]: \(url). \nError: \(error.localizedDescription)", level: .debug, category: .inAppMessages)
if error.code == NSURLErrorTimedOut {
completion(.failure(error))
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// ContentPositionFilter.swift
// Mindbox
//
// Created by vailence on 07.09.2023.
// Copyright © 2023 Mindbox. All rights reserved.
//

import Foundation
import MindboxLogger

protocol ContentPositionFilterProtocol {
func filter(_ contentPosition: ContentPositionDTO?) throws -> ContentPosition
}

final class ContentPositionFilterService: ContentPositionFilterProtocol {

enum Constants {
static let defaultGravity = ContentPositionGravity(vertical: .bottom, horizontal: .center)
static let defaultMargin = ContentPositionMargin(kind: .dp, top: 0, right: 0, left: 0, bottom: 0)
static let defaultContentPosition = ContentPosition(gravity: defaultGravity, margin: defaultMargin)
}

func filter(_ contentPosition: ContentPositionDTO?) throws -> ContentPosition {
guard let contentPosition = contentPosition else {
Logger.common(message: "Content position is invalid or missing. Default value set: [\(Constants.defaultContentPosition)].", level: .debug, category: .inAppMessages)
return Constants.defaultContentPosition
}

var customGravity: ContentPositionGravity
if let gravity = contentPosition.gravity {
let vertical = gravity.vertical ?? .bottom
let horizontal = gravity.horizontal ?? .center
customGravity = ContentPositionGravity(vertical: vertical, horizontal: horizontal)
} else {
Logger.common(message: "Gravity is invalid or missing. Default value set: [\(Constants.defaultGravity)].", level: .debug, category: .inAppMessages)
customGravity = Constants.defaultGravity
}

var customMargin: ContentPositionMargin?
if let margin = contentPosition.margin {
switch margin.kind {
case .dp:
if let top = margin.top,
let left = margin.left,
let right = margin.right,
let bottom = margin.bottom,
top >= 0,
left >= 0,
right >= 0,
bottom >= 0 {
customMargin = ContentPositionMargin(kind: margin.kind, top: top, right: right, left: left, bottom: bottom)
}
case .unknown:
Logger.common(message: "Content position margin kind is unknown. Default value set: [\(Constants.defaultMargin)].", level: .debug, category: .inAppMessages)
customMargin = Constants.defaultMargin
}
} else {
Logger.common(message: "Content position margin is invalid or missing. Default value set: [\(Constants.defaultMargin)].", level: .debug, category: .inAppMessages)
customMargin = Constants.defaultMargin
}

guard let customMargin = customMargin else {
throw CustomDecodingError.unknownType("ContentPositionFilterService validation not passed. Inapp will be skipped.")
}

return ContentPosition(gravity: customGravity, margin: customMargin)
}
}
Loading

0 comments on commit 7569e81

Please sign in to comment.