diff --git a/PrebidMobile.xcodeproj/project.pbxproj b/PrebidMobile.xcodeproj/project.pbxproj index 7e5fcc3e5..18d67a937 100644 --- a/PrebidMobile.xcodeproj/project.pbxproj +++ b/PrebidMobile.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 2A9DDDCC2C5BE190000EA4A0 /* PrebidEventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9DDDCB2C5BE190000EA4A0 /* PrebidEventDelegate.swift */; }; + 2A9DDDD42C63665A000EA4A0 /* PBMEventDelegateHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9DDDD32C63665A000EA4A0 /* PBMEventDelegateHelper.swift */; }; + 2A9DDDD82C63A88C000EA4A0 /* PrebidEventDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9DDDD72C63A88C000EA4A0 /* PrebidEventDelegateTests.swift */; }; 34C9CD5F2850CE6300FB5451 /* OMSDKVersionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 34C9CD5D2850CE6300FB5451 /* OMSDKVersionProvider.h */; settings = {ATTRIBUTES = (Private, ); }; }; 34C9CD602850CE6300FB5451 /* OMSDKVersionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 34C9CD5E2850CE6300FB5451 /* OMSDKVersionProvider.m */; }; 34F51A562850F50E0063763D /* OMSDK-Static_Prebidorg.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34F51A552850F50E0063763D /* OMSDK-Static_Prebidorg.xcframework */; }; @@ -866,6 +869,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 2A9DDDCB2C5BE190000EA4A0 /* PrebidEventDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidEventDelegate.swift; sourceTree = ""; }; + 2A9DDDD32C63665A000EA4A0 /* PBMEventDelegateHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBMEventDelegateHelper.swift; sourceTree = ""; }; + 2A9DDDD72C63A88C000EA4A0 /* PrebidEventDelegateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidEventDelegateTests.swift; sourceTree = ""; }; 34C9CD5D2850CE6300FB5451 /* OMSDKVersionProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OMSDKVersionProvider.h; sourceTree = ""; }; 34C9CD5E2850CE6300FB5451 /* OMSDKVersionProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OMSDKVersionProvider.m; sourceTree = ""; }; 34F51A552850F50E0063763D /* OMSDK-Static_Prebidorg.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = "OMSDK-Static_Prebidorg.xcframework"; path = "Frameworks/OMSDK-Static_Prebidorg.xcframework"; sourceTree = ""; }; @@ -1959,6 +1965,7 @@ 53C8FE1629C0851300ED9230 /* ClickbrowserType.swift */, 53138B292A71152200B18B5C /* PrebidSDKInitializer.swift */, 53138B2B2A7132CD00B18B5C /* PrebidGAMVersionChecker.swift */, + 2A9DDDCB2C5BE190000EA4A0 /* PrebidEventDelegate.swift */, ); path = ConfigurationAndTargeting; sourceTree = ""; @@ -2423,6 +2430,7 @@ 5BC377C6271F1CFF00444D5E /* PBMWinNotifierBlock.h */, 5BC3780D271F1CFF00444D5E /* PBMWinNotifierFactoryBlock.h */, 5BC37831271F1CFF00444D5E /* TransactionFactory */, + 2A9DDDD32C63665A000EA4A0 /* PBMEventDelegateHelper.swift */, ); path = PBMCore; sourceTree = ""; @@ -3330,6 +3338,7 @@ 606FAC5122022932008EAE5E /* AdUnitSwizzleHelper.swift */, 38F03B262576421400E026A2 /* CacheManagerTests.swift */, 38F03B322576624C00E026A2 /* TrackerManagerTests.swift */, + 2A9DDDD72C63A88C000EA4A0 /* PrebidEventDelegateTests.swift */, 47D9A35B40BBC16996BC05A9 /* RenderingTests */, ); path = PrebidMobileTests; @@ -3814,6 +3823,7 @@ 92C85D9627A9DC9D0080BAC5 /* NativeAdTests.swift in Sources */, 922AFCE42735573900732C53 /* ServerConnectionTest.swift in Sources */, 925D5D9F2737C33800A8A2B5 /* NetworkParameterBuilderTest.swift in Sources */, + 2A9DDDD82C63A88C000EA4A0 /* PrebidEventDelegateTests.swift in Sources */, 922AFD4B27372A0500732C53 /* PBMHTMLCreativeTest_NoMRAID.swift in Sources */, 9743CB86235F264B002E2CAA /* NativeEventTrackerTests.swift in Sources */, 922AFCE12735424F00732C53 /* PBMAssert.swift in Sources */, @@ -4025,6 +4035,7 @@ 5BC378D7271F1CFF00444D5E /* PBMORTBUser.m in Sources */, 5BC3798D271F1D0000444D5E /* PBMAdRefreshOptions.m in Sources */, 5BC378CC271F1CFF00444D5E /* PBMWeakTimerTargetBox.m in Sources */, + 2A9DDDD42C63665A000EA4A0 /* PBMEventDelegateHelper.swift in Sources */, 5BC379AC271F1D0000444D5E /* PBMModalManager.m in Sources */, FAEE4D1C262DC2B200AD9966 /* InstreamVideoAdUnit.swift in Sources */, 53B221D12A0E3D3D00C91CCB /* PrebidJSLibraryManager.swift in Sources */, @@ -4105,6 +4116,7 @@ 929865392806CCC4007A2F34 /* UIView+Extensions.swift in Sources */, 5BC37997271F1D0000444D5E /* PBMAbstractCreative.m in Sources */, 92EE5A0D27F9D292003D7691 /* Position.swift in Sources */, + 2A9DDDCC2C5BE190000EA4A0 /* PrebidEventDelegate.swift in Sources */, 92C3A83627F989E200DC05E9 /* AutoRefreshCountConfig.swift in Sources */, 5BC37A72271F1D0000444D5E /* BidResponse.swift in Sources */, 5BC379C8271F1D0000444D5E /* PBMAutoRefreshManager.m in Sources */, diff --git a/PrebidMobile/ConfigurationAndTargeting/Prebid.swift b/PrebidMobile/ConfigurationAndTargeting/Prebid.swift index db6438197..9576bc750 100644 --- a/PrebidMobile/ConfigurationAndTargeting/Prebid.swift +++ b/PrebidMobile/ConfigurationAndTargeting/Prebid.swift @@ -54,6 +54,9 @@ public class Prebid: NSObject { /// Stored bid responses identified by bidder names. public var storedBidResponses: [String: String] = [:] + /// Optional Delegate which returns Request and Response Data for further processing + public weak var eventDelegate: PrebidEventDelegate? + /// This property is set by the developer when he is willing to assign the assetID for Native ad. public var shouldAssignNativeAssetID : Bool = false diff --git a/PrebidMobile/ConfigurationAndTargeting/PrebidEventDelegate.swift b/PrebidMobile/ConfigurationAndTargeting/PrebidEventDelegate.swift new file mode 100644 index 000000000..0f0ce3e00 --- /dev/null +++ b/PrebidMobile/ConfigurationAndTargeting/PrebidEventDelegate.swift @@ -0,0 +1,21 @@ +/*   Copyright 2018-2021 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +@objc +public protocol PrebidEventDelegate { + func prebidBidRequestDidFinish(requestData: Data?, responseData: Data?) +} diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMBidRequester.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMBidRequester.m index dc4437dde..428e4ae0b 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMBidRequester.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMBidRequester.m @@ -94,10 +94,12 @@ - (void)makeRequestWithCompletion:(void (^)(BidResponse *, NSError *))completion const NSTimeInterval postTimeout = (dynamicTimeout_onRead ? dynamicTimeout_onRead.doubleValue : (rawTimeoutMS_onRead / 1000.0)); + NSData *rtbRequestData = [requestString dataUsingEncoding:NSUTF8StringEncoding]; + @weakify(self); NSDate * const requestDate = [NSDate date]; [self.connection post:requestServerURL - data:[requestString dataUsingEncoding:NSUTF8StringEncoding] + data:rtbRequestData timeout:postTimeout callback:^(PrebidServerResponse * _Nonnull serverResponse) { @strongify(self); @@ -157,6 +159,8 @@ - (void)makeRequestWithCompletion:(void (^)(BidResponse *, NSError *))completion } completion(bidResponse, trasformationError); + [Prebid.shared callEventDelegate_prebidBidRequestDidFinishWithRequestData:rtbRequestData + responseData:serverResponse.rawData]; }]; } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMEventDelegateHelper.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMEventDelegateHelper.swift new file mode 100644 index 000000000..988549dad --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMEventDelegateHelper.swift @@ -0,0 +1,24 @@ +/*   Copyright 2018-2021 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +@objc public extension Prebid { + func callEventDelegate_prebidBidRequestDidFinishWith(requestData: Data?, responseData: Data?) { + if let delegate = self.eventDelegate { + delegate.prebidBidRequestDidFinish(requestData: requestData, responseData: responseData) + } + } +} diff --git a/PrebidMobileTests/PrebidEventDelegateTests.swift b/PrebidMobileTests/PrebidEventDelegateTests.swift new file mode 100644 index 000000000..575449f9a --- /dev/null +++ b/PrebidMobileTests/PrebidEventDelegateTests.swift @@ -0,0 +1,61 @@ +/*   Copyright 2018-2021 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import XCTest +@testable import PrebidMobile + +private typealias Callback = (Data?, Data?) -> Void + +class PrebidEventDelegateTests: XCTestCase { + + let mockRequestData = "req".data(using: .utf8) + let mockResponseData = "res".data(using: .utf8) + + var delegate: PrebidEventDelegate? + + func test_eventDelegate_isCalled() { + let exp = expectation(description: "Expect PrebidEventDelegate to be called") + + delegate = PrebidEventDelegateTestsMockDelegate(onRequestDidFinish: { requestData, responseData in + XCTAssertEqual(requestData, self.mockRequestData) + XCTAssertEqual(responseData, self.mockResponseData) + exp.fulfill() + }) + + Prebid.shared.eventDelegate = delegate + + Prebid.shared.callEventDelegate_prebidBidRequestDidFinishWith(requestData: mockRequestData, responseData: mockResponseData) + waitForExpectations(timeout: 1.0) + } + + func test_callEventDelegate_doesNothing_whenDelegateIsNil() { + /// This test aims to ensure that there is no nullpointer exception if the delegate is unset + /// and a call to `callEventDelegate_prebidBidRequestDidFinishWith(_:,:_)` is made + Prebid.shared.eventDelegate = nil + Prebid.shared.callEventDelegate_prebidBidRequestDidFinishWith(requestData: mockRequestData, responseData: mockResponseData) + } +} + +private class PrebidEventDelegateTestsMockDelegate: PrebidEventDelegate { + private let onRequestDidFinish: Callback + + init(onRequestDidFinish: @escaping Callback) { + self.onRequestDidFinish = onRequestDidFinish + } + + func prebidBidRequestDidFinish(requestData: Data?, responseData: Data?) { + onRequestDidFinish(requestData, responseData) + } +}