Skip to content

Resolver Specific Parameters (ID5 Mobile In-App)#58

Open
OlenaPostindustria wants to merge 2 commits into
masterfrom
feature/resolver-specific-params-id5-mobile-inapp
Open

Resolver Specific Parameters (ID5 Mobile In-App)#58
OlenaPostindustria wants to merge 2 commits into
masterfrom
feature/resolver-specific-params-id5-mobile-inapp

Conversation

@OlenaPostindustria

Copy link
Copy Markdown
Collaborator

Closes #57

@eugenedorfman-optable eugenedorfman-optable left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of the ID5 Mobile In-App changes. Overall a clean, well-tested feature, and the hid= wire encoding is correct: each hint is emitted as its own repeated hid= query item (never comma-joined), which is what the edge expects since it does not split hid on commas.

Inline comments below, tagged major / minor / nit. Two cross-cutting points that are not line-specific:

  • README not updated. There is no targeting code sample in the README, so the new hids parameter and the whole ID5 Mobile In-App feature are undocumented for integrators. A short README/demo snippet showing how to pass hints would help.
  • ObjC + Swift docs. See the inline note on the doc comment: it should actually explain what a hint ID is and which identifier types are valid.

Most important items to resolve before merge are the three major comments (ObjC API break, IDFA casing, and the implicit opt-in for all existing callers).

*/
@objc
func targeting(_ ids: [OptableSDKIdentifier]) throws {
func targeting(_ ids: [OptableSDKIdentifier], _ hids: [OptableSDKIdentifier]) throws {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Major - breaking change for Objective-C integrators. Changing @objc func targeting(_ ids:) to targeting(_:_:) changes the exported selector from targeting:error: to targeting::error:, so every existing ObjC call site breaks at compile time (ObjC has no default args, which is what the :@[] edits in the demo apps are working around), and the bare :: selector is unidiomatic.

Suggest keeping a single-array @objc func targeting(_ ids:) that delegates with hids: [], and adding a separately-named overload, e.g. @objc(targetingWithIds:hids:error:).

Comment thread Source/Core/EdgeAPI.swift

let hids = hids.hids
.compactMap({ $0.extendedIdentifier })
.compactMap({ URLQueryItem(name: "hid", value: $0) })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Major - IDFA is lowercased before it goes out as a hint. enrichIfNeeded injects the system IDFA as an uppercase UUID string, but OptableIdentifierEncoder.idfa(...) lowercases it, so the edge receives hid=a:<lowercase>. Combined with the edge's known MAID-lowercasing before the ID5 call, ID5 ends up with a lowercase IDFA, while the IAB/OpenRTB convention is IDFA uppercase - risking a 204/no-match for the exact in-app case this PR targets.

Worth settling the canonical casing with the edge/ID5 team (preserve IDFA case in the a: encoding, or uppercase MAIDs server-side) before merge.

Comment thread Source/OptableSDK.swift
var hids = hids ?? []

enrichIfNeeded(ids: &ids)
enrichIfNeeded(ids: &hids)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Major - all existing callers are silently opted into hint/ID5 resolution. enrichIfNeeded(ids: &hids) runs even when the caller passed hids: nil, and in EdgeAPI.targeting the bundle/ver/ua/id5_signature params are appended unconditionally. So every current targeting() call will start sending hid=a:<idfa> plus the resolver params, with no opt-out other than skipAdvertisingIdDetection (which also disables the id= enrichment).

If that implicit opt-in is intended edge behavior, fine - otherwise gate hid emission (and arguably the resolver params) on the caller actually supplying hids.

Comment thread Source/Core/EdgeAPI.swift
queryItems.append(URLQueryItem(name: "ua", value: userAgent))
}

if let targeting = storage.getTargeting(), let id5Signature = targeting.targetingData["id5_signature"] as? String {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor - the id5_signature cache can be clobbered. It is read from the cached targeting response, but _profile (OptableSDK.swift:400) also calls setTargeting, overwriting the cache with a profile response that will not carry id5_signature. So a profile() call between two targeting() calls silently drops the signature and breaks the ID5 refresh chain. Consider persisting the signature under its own storage key.

Also worth confirming the edge returns id5_signature at the top level of the mobile in-app response (vs nested).

let urlRequest = try sdk.api.targeting(ids: [.emailAddress("12345"), .phoneNumber("54321")])

// `id5_signature` is a resolver-specific parameter sourced from the stored targeting data.
sdk.api.storage.setTargeting(OptableTargeting(optableTargeting: ["id5_signature": "id5-sig-abc123"]))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor - test writes to shared storage without cleanup. This persists a fake id5_signature into UserDefaults.standard under the same tenant/slug key the integration tests use, and it is never cleared. That can leak the fake signature into a real edge call during integration runs and makes unit tests order-dependent. Add a clearTargeting() in tearDown (or use an isolated config/suite).

var hids: [OptableIdentifier] {
filter {
switch $0 {
case .ipv6Address(_), .emailAddress(_), .phoneNumber(_),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor - every .custom case is treated as a hint. puid/idfv prefixes are node-configured, so only specific cN indexes are valid ID5 hints; passing all custom IDs sends publisher PPIDs down the resolver path where unconfigured ones are dropped server-side. Defensible since the SDK cannot know the node config, but worth a doc note on hids that unconfigured custom prefixes are ignored. The rest of the set (e/p/i6/a/g) correctly matches the ID5-in-app supported hints.

Comment thread Source/OptableSDK.swift
*/
func targeting(_ ids: [OptableIdentifier]? = nil, completion: @escaping (Result<OptableTargeting, Error>) -> Void) throws {
try _targeting(ids: ids, completion: completion)
func targeting(_ ids: [OptableIdentifier]? = nil, _ hids: [OptableIdentifier]? = nil, completion: @escaping (Result<OptableTargeting, Error>) -> Void) throws {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor - unlabeled _ hids invites arg transposition. Both params are [OptableIdentifier]?, so targeting(a, b) gives no signal which is which; a swap silently sends hints as id= and vice versa. Labeling it (hids:) stays source-compatible for existing single-arg Swift callers. Same applies to the async overload at line 152.

Comment thread Source/OptableSDK.swift
public extension OptableSDK {
/**
targeting(ids?, completion) calls the Optable Sandbox Targeting API and returns key-value targeting data
targeting(ids?, hids?, completion) calls the Optable Sandbox Targeting API and returns key-value targeting data

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor - doc comment does not explain hids. The summary line was updated, but the body still just says "You may optionally supply identifiers to enrich the request." It does not say what a hint ID is, how hids differs from ids, which identifier types are valid hints (email/phone/ipv6/IDFA/GAID/custom), or that they drive ID5 resolution. Worth expanding here (and on the async and ObjC overloads).


// MARK: - Equatable
extension OptableIdentifier: Equatable {
public static func == (lhs: OptableIdentifier, rhs: OptableIdentifier) -> Bool {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor - new Equatable changes public == semantics and is unused in this PR. Hashable already provided a synthesized case-based ==; the eid-based == makes e.g. .emailAddress("A@B.com") == .emailAddress("a@b.com") true, a behavior change for any integrator comparing identifiers. It does make == consistent with the eid-based hash(into:), so if that is the intent it is reasonable - but nothing here uses it. Keep only if intentional.

Comment thread Source/Core/EdgeAPI.swift
.compactMap({ $0.extendedIdentifier })
.compactMap({ URLQueryItem(name: "id", value: $0) })

let hids = hids.hids

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit - shadowing. The parameter, this local, and the .hids filter property are all named hids. Rename the local (e.g. hidQueryItems) for readability.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Resolver Specific Parameters (ID5 Mobile In-App)

2 participants