-
Notifications
You must be signed in to change notification settings - Fork 4
Feature/v2 sdk updates #51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Feature/v2 sdk updates #51
Conversation
…ableSDK interface with async/await support
…API more test-friendly
| dependencies: [], | ||
| path: "Source", | ||
| exclude: ["Source/Info.plist"]), | ||
| exclude: ["Info.plist"]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we test SwiftPM integration on some minimal demo app?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This integration was manually tested using the demo-ios-swift project.
Do you want to create separate demo with the SPM integration?
| @@ -0,0 +1,187 @@ | |||
| ## Usage (Objective-C) | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we continue to maintain objective-c API this doc needs to be updated too, unless it was already updated, but the APIs seem to be a bit different from those used in Swift version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes APIs are different, for obj-c version the delegate approach was and still is used, i have not changed it.
For swift apis still use callbacks but also provides async/await syntax.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The obj-c doc was updated, and separated to this file.
| } | ||
|
|
||
| /// Builds Enriched Identifier from Google GAID | ||
| static func gaid(_ gaid: String) -> String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
funny to see on iOS, but let it be I guess
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code normalizes string according to Optable API Reference. To be consistent.
| } | ||
|
|
||
| // MARK: - HTTPHeader | ||
| enum HTTPHeader: String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this file looks like it has been copied from somewhere... is it genuinely coded by us, or was it taken from an Apache/MIT/any-other-permissive license source? can we give them credit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is written by us. Fully.
| if ([email length] > 0) { | ||
| [output appendString:[NSString stringWithFormat:@"Email: %@\n", email]]; | ||
| NSString *email = _identifyInput.text; | ||
| BOOL aaid = _identifyIDFA.isOn; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No this toggle is not used, because on simulator the idfa comes as zero 000....-0000, and for the demo purposes IDFA was hardcoded.
I will remove the toggle and add the request info logs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updt:
As this is separate demo projects that use public-only SDK API, i can't provide request info on SDK consumer side, without breaking the API, because API only provides URLHTTPResponse object.
But there is work-around for this, i will rework logging.
| appendOutput("ℹ️ Cache empty.\n") | ||
| } | ||
|
|
||
| loadBanner(keyvalues: tdata) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
targeting data is not just keyvalues. it is a rich object containing audience data and eids. specifically it is condensed under ortb2 key, f.e., here is the targeting data I see
[OptableSDK] Success on /targeting API call: {
audience = (
{
ids = (
{
id = 082793f9;
}
);
keyspace = "optable-test";
provider = "optable.co";
"rtb_segtax" = 5001;
}
);
ortb2 = {
user = {
data = (
{
id = "optable.co";
segment = (
{
id = 082793f9;
}
);
}
);
eids = (
{
inserter = "optable.co";
matcher = "optable.co";
mm = 3;
source = "optable.co";
uids = (
{
id = "c:custom.ABCDEFG";
},
{
id = "c:new-custom.ABC";
},
{
id = "e:04aff671ae26e58d662f7fa19e8400cd3943d20c1e7e4d05863e21be909fbc5b";
},
{
id = "e:129c907cd4f2b4d95d76b63ec9661c64cddad5de53415a0116173bc324072968";
},
{
id = "e:7e6ea62ad413e64265919670246b2992f4d15ca38b994c27298b63a588dd8ba8";
},
{
id = "e:8dd86dbb33654227291e5abc33dafeb7803a4086c990f54c5a3749edbd198398";
},
{
id = "e:911da1f8c9f6433b0fd4134a949c001b2d9a92228230c83de582a9fb01e31e0e";
},
{
id = "e:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
},
{
id = "v:0b5zRoKB03RnzAZJaneBN4";
},
{
id = "v:2kozF4XZ7i87FSabsr3E9a";
},
{
id = "v:3GyM7NmcaJ4cBj3eIZOrkh";
},
{
id = "v:3VAn89d4ykHHQVQsJwyE7o";
},
{
id = "v:6eLs6T7LvpKUu2SkMw0aST";
}
);
},
{
inserter = "optable.co";
matcher = "optable.co";
mm = 3;
source = "uidapi.com";
uids = (
{
atype = 3;
ext = {
optable = {
ref = 13;
};
};
id = "A4AAACTU1S_haNZJvbPH51bFaFA9dFgSZootbRgQNi_LVDTwF8F_ba9bpwzuChNYh_MqsLdLqqUrBN3TCsYkgewDVoUmmaWhXDxzZGi9ldOaYDPjygAi7oWwsNvKFY7ZZq-Wjh72_nab5s9NJzB8lKF45DM6B6KdN7uVghQ1-wfApZBb190ST9AbFD0yU2Ej64d2AM7zcvbd-vZ5NMBCF8IfkQ";
}
);
},
{
inserter = "optable.co";
matcher = "optable.co";
mm = 3;
source = "criteo-hemapi.com";
uids = (
{
atype = 3;
ext = {
stype = "cto_bundle_hem_api";
};
id = kEyE5l9BeGZxSzI0WG9FVjE4UnZFaVhmZUJ3V2lmV3lqNzBic2ttREwzcTBxaUtEYVYyWjFTeTN1MjYxJTJCN21XVW9sYnhMcGxSa2Q4UXg0YU55dVloUDFwS3NCdHVOOGwlMkJENUNwV0tJS1BtMzFwM1ElM0Q;
}
);
}
);
};
};
refs = {
13 = {
"advertising_token" = "A4AAACTU1S_haNZJvbPH51bFaFA9dFgSZootbRgQNi_LVDTwF8F_ba9bpwzuChNYh_MqsLdLqqUrBN3TCsYkgewDVoUmmaWhXDxzZGi9ldOaYDPjygAi7oWwsNvKFY7ZZq-Wjh72_nab5s9NJzB8lKF45DM6B6KdN7uVghQ1-wfApZBb190ST9AbFD0yU2Ej64d2AM7zcvbd-vZ5NMBCF8IfkQ";
"identity_expires" = 1768144718027;
"refresh_expires" = 1770477518027;
"refresh_from" = 1767889118027;
"refresh_response_key" = "Swl0DS42zZ7v2ROaY5OSSmdfkxsbb3wxGnmSTORfXSA=";
"refresh_token" = "AAAAJNUk7ydR6rTX2eh9hrT6TFXcBHZTn+ySeXzlU8hTCN7zWVg+cTATJcCrBkUam3C+7ygmfC4sY/o5qQ9ePy8JQrQZnfSHcbN9k8RctSmQlXB30uM4DOWu0CfcrGv9evJWh7FzwWLeQAuq315VEuaCdEzJva3g5emfzzF68oT1RWzlH9dFfW7A1V+4hfnay3k/+wyTm7M2xTIw1OIbT5/E4ID61A5Pet4yeo4C8n0fOWwHlH/ekCjqKn4aVu4hDNHdB3XkxeR5Gp1/WY6RrEruebN56jFpjnMNxFnhvUdTl7Kwb7wC6N4wRaK+xErwkoerzu7Lu09xEIch12cXpnukeTFln/SCqgLhbsGl7sXdvHJc1KdLQaujrDuk1U6xmPAM";
};
};
"resolved_ids" = (
"v:0b5zRoKB03RnzAZJaneBN4"
);
user = (
);
}
this entire ortb2 object it should be merged into the Prebid SDK to make it form a correct OpenRTB request before making fetchDemand call. please consult @OlenaPostindustria on how to pass this data to Prebid.
So keyvalues parameter should be renamed into targetingData rather. the ortb2 should be properly passed to Prebid SDK, also there are GAM targeting keywords in the targeting data, but these are to be set as key=audience.keyspace value= coma separated ids, in the above case the only targeting key-value pair we pass to gam would be: optable-test=082793f9, if there were more audience ids, they would be coma-separated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't touch the logic of this code, this additions are just part of project file structure updates.
Should I leave them as they were, or investigate and apply your suggestions?
|
|
||
| ### Call tryIdentifyFromURL SDK API | ||
|
|
||
| When iOS launches your app after a user taps a universal link, you receive an `NSUserActivity` object with an `activityType` value of `NSUserActivityTypeBrowsingWeb`. The activity object's `webpageURL` property contains the URL that the user is accessing. You can then pass it to the SDK's `tryIdentifyFromURL()` API which will automatically look for `oeid` in the query string of the URL and call `identify` with its value if found. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this API actually work? or did we remove it? also it is peculiar that oeid value mentioned here is already sha256-hashed - so if it is passed as an email identifier to identify API - we need to make sure it is not hashed again. We might need a special parameter whether email needs to be hashed or not on the OptableIdentifiers or other object, by default it should be true, but in this particular case it needs to use raw value not hashing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are old apis that were already in sdk.
The only thing I did was restructure repetitive docs, separating this to file (so that obj-c/swift version not duplicate each other).
| /// Builds Enriched Identifier from Postal code | ||
| static func postalCode(_ postalCode: String) -> String { | ||
| let prefix = OptableIdentifierType.postalCode.rawValue | ||
| let identifier = postalCode.components(separatedBy: CharacterSet.whitespacesAndNewlines).joined().lowercased() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are we sure we need to remove whitespace and newlines and also lowercase postal code? British postal codes always contain a space, and upper case letters - are we sure this "normalization" is needed? is it specified in the Optable docs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code normalizes string according to Optable API Reference.
Though, I will change whitespaces and new lines to trim only on both ends of the string.
| /// Builds Enriched Identifier from Apple IDFA | ||
| static func idfa(_ idfa: String) -> String { | ||
| let prefix = OptableIdentifierType.appleIDFA.rawValue | ||
| let identifier = idfa.components(separatedBy: CharacterSet.whitespacesAndNewlines).joined().lowercased() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to normalize idfa this way? I think it can be passed in a raw form. we don't know what Apple comes up with - using upper case or lower case letters - I don't think other parts of the system would do with this ID, so I would not apply this normalization here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code normalizes string according to Optable API Reference.
|
@vladislav-yermakov could you please check the comments above ^ and also write a short description for this PR, like a summary of changes to the APIs. roughly there are 4 categories of changes:
|
|
Here is how audience object is converted into GAM key-value pairs: function TargetingKeyValues(tdata: TargetingResponse | null): TargetingKeyValues {
const result: TargetingKeyValues = {};
if (!tdata) {
return result;
}
for (const identifiers of tdata.audience ?? []) {
if (identifiers.keyspace) {
if (!(identifiers.keyspace in result)) {
result[identifiers.keyspace] = [];
}
result[identifiers.keyspace].push(...identifiers.ids.map((el) => el.id));
}
}
return result;
}audience is an array of objects: "audience": [
{
"provider": "optable.co",
"ids": [
{
"id": "15f30409"
}
],
"keyspace": "optable",
"rtb_segtax": 5001
}
],each of these objects becomes a single key-value pair. |
|
Re: setting targetingData.ortb2 to Prebid - seems it is done correctly in Android: https://github.com/Optable/optable-android-sdk/pull/35/files#diff-c55c975d4a7bd768528a4687e2fbc25857690b9457d09282cb847d1532d06867R152 |
|
@justadreamer Please review recent changes. |


This PR addresses the following issues:
Add Prebid SDK and passing optable IDs into the demo app #43
Prebid test case was merged to into this PR, the old PR can be closed.
Rework Optable Real-time APIs configuration and request forming #49
SDK was updated according to real time api integrations guide
Extend Optable Targeting API request forming with additional parameters #50
SDK Targeting API was updated to accept arbitrary IDs as defined in real time api integrations guide. Privacy parameters is controlled via
OptableConfig.Breaking changes:
OptableSDK.init(host:, app:, insecure:, useragent:)was replaced withOptableSDK.init(config:)tenantandoriginSlugparameters to initialise, instead ofhostandappOptableConfigwas introduced to contain all configuration parameters for the SDKOptableConfigsupports dynamic changes of all parameters, such ashost,path,apiKey,custopUserAgentand others.Other changes:
IABConsentfor automatically retrieving user consent according to the IAB Transparency & Consent FrameworkAppTrackingTransparencyto handle App Tracking IdentifiersOptableIdentifierType,OptableIdentifiers, andOptableIdentifierEncoderto support optable identifiers and normalisationasync/awaitSDK API for modern swift