Skip to content

Conversation

@jclou
Copy link
Contributor

@jclou jclou commented Apr 1, 2025

Changes:

  • Migrated most PBMORTB response model objects to Swift. The remaining model objects use objc generics which are not compatible with Swift and will need further consideration to migrate. Helper functions in JSONParsing.swift keep the model object code simple and ensure consistent and type-safe conversions to and from JSON.

Additions:

  • Added API to let publishers provide their own custom model objects and response JSON handling.

Fixes:
-Made PBMORTBAbstractResponse.ext nullable to handle the case in -[PBMORTBAbstractResponse initWithJsonDictionary:extParser] where it does not get set. This case previously existed in tests, but after migrating PBMORTBBidExt to swift, the nil objc class instance instead became an uninitialized Swift class instance and crashed.

  • Added nil check to Swift function call in -[PBMAbstractCreative handleClickthrough:sdkConfiguration:completionHandler:onExit:]

Documentation Change:
prebid/prebid.github.io#6344

Copy link
Contributor

@YuriyVelichkoPI YuriyVelichkoPI left a comment

Choose a reason for hiding this comment

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

Please, remove PBM prefix in new Swift classes.

Since we are switching converting JSON to Swift. can we use advantages of Codable?

@jclou
Copy link
Contributor Author

jclou commented Apr 28, 2025

Its possible to use codable, but in my experience Codable tends to be too strict for most uses. In our case I think we want to prioritize successful parsing of response objects over enforcing strict conformance to the response spec. If for example for some field the response contains "true" instead of the expected true, we would probably want to fallback to a default value rather of failing to decode the entire object.

You could loosen strictness in these cases, but this is not the default behavior and you would need to write your own custom decoding logic and the end result is usually harder to read and more error prone than alternatives.

@jclou
Copy link
Contributor Author

jclou commented Apr 28, 2025

@YuriyVelichkoPI Can we hold up on this PR and prioritize merging in the SPM migration PRs? Those PRs include the swift migration part of this PR, and I'd like to avoid having to do a rebase/merge. I can add back in the custom class feature afterwards.

@YuriyVelichkoPI
Copy link
Contributor

Its possible to use codable, but in my experience Codable tends to be too strict for most uses. In our case I think we want to prioritize successful parsing of response objects over enforcing strict conformance to the response spec. If for example for some field the response contains "true" instead of the expected true, we would probably want to fallback to a default value rather of failing to decode the entire object.

You could loosen strictness in these cases, but this is not the default behavior and you would need to write your own custom decoding logic and the end result is usually harder to read and more error prone than alternatives.

Thanks for the clarification. Makes sense. We can try Codable later.

@YuriyVelichkoPI
Copy link
Contributor

@YuriyVelichkoPI Can we hold up on this PR and prioritize merging in the SPM migration PRs? Those PRs include the swift migration part of this PR, and I'd like to avoid having to do a rebase/merge. I can add back in the custom class feature afterwards.

Hi @jclou ! Do you want to move forward with this PR since a couple of PRs for SPM are already merged?

@YuriyVelichkoPI
Copy link
Contributor

Hi @jclou @mdanylov-sigma ! What are the plans for this PR? I noticed that many classes were rewritten in Swift as part of the migration to SPM, but I didn't verify the alignment of functional requirements between this PR and the changes for SPM.

Are you going to reimplement this one based on the new files, or is everything already done and this PR can be closed?

@YuriyVelichkoPI
Copy link
Contributor

Hi @jclou, @mdanylov-sigma, what is the status of this PR?

@mdanylov-sigma
Copy link
Collaborator

Hi @jclou, @mdanylov-sigma, what is the status of this PR?

In our latest conversation with James, he mentioned that he wanted to update the PR, since it includes custom parsing that could be useful to have.

@jclou Are you able to wrap up the work you’ve done so we can merge it?

@jclou
Copy link
Contributor Author

jclou commented Oct 3, 2025

Sorry, I forgot about this PR. The type safety from moving to Swift is already in now SPM migration. I'll try to find some time next week to rebase this for the custom parsing feature.

@jclou jclou force-pushed the response_parsing_swift branch from db9093c to 2304666 Compare October 10, 2025 23:37
@jclou
Copy link
Contributor Author

jclou commented Oct 14, 2025

Where can I see which tests are failing? I suspect they aren't related to my changes.

@mdanylov-sigma
Copy link
Collaborator

Where can I see which tests are failing? I suspect they aren't related to my changes.

There was an internal runner error, it works after re-running.

Copy link
Contributor

@YuriyVelichkoPI YuriyVelichkoPI left a comment

Choose a reason for hiding this comment

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

Also, please, add the short description of custom types utilization into the documentation, I think we can put it on this page:

https://docs.prebid.org/prebid-mobile/pbm-api/ios/pbm-targeting-ios.html

The repository to update the doc: https://github.com/prebid/prebid.github.io

subscript<T: PBMJsonCodable>(_ key: Key) -> T? {
get {
(dict[key.rawValue] as? [String : Any]).flatMap { T.init(jsonDictionary: $0) }
(dict[key.rawValue] as? [String : Any]).flatMap { CustomModelObjects.instantiate(json: $0) }
Copy link
Contributor

Choose a reason for hiding this comment

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

How much does this instantiation affect parsing performance, given that it involves a synchronous operation under the hood?

Could you run benchmarks before and after this change and share the results here, so we can confirm that it’s optimal?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@YuriyVelichkoPI
We're using synchronization here to prevent a crash, but in practice apps will very likely just register a custom type once (if at all) before using PBM, and so lock contention for reads should be practically zero.

The fast path (i.e. no lock contention) performance of pthread_rwlock is very fast. But anyway I did a few runs of creating BidResponse objects. The bid response is nested with other types that also can have custom types and therefore go through the lock code, so each bid response performs 37 locks+unlocks. I also included in the testing os_unfair_lock which has less overhead, but can cause lock contention on simultaneous reads. This would happen if if PBM is parsing multiple responses simultaneously, which I is likely a fairly common use case.

Each run creates 1000 BidResponses. Time is in seconds. I ran this on a simulator with the normal test configuration, so we should expect faster performance on real devices and with compiler optimizations turned on.

no locking
0.08809900283813477
0.08694291114807129
0.08736598491668701
0.08878195285797119

pthread_rw_lock
0.08940601348876953
0.08895599842071533
0.08983302116394043
0.0893028974533081

os_unfair_lock
0.09004998207092285
0.08857595920562744
0.08893299102783203
0.08905899524688721

It's basically a wash. Maybe we can say there's a one or two micro second overhead for each BidResponse

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi @jclou, Thanks for the detailed information and for running the validation - it’s very helpful from both a strategic and reasoning perspective. In theory, many things look different than in practice, so when we provide a tool, we need to be sure we’ve done everything correctly from a practical point of view.

Having benchmarks here in the PR will be very useful, so we can reference them anytime we need to prove that performance is not actually impacted.

And thanks for the docs.

@jclou
Copy link
Contributor Author

jclou commented Nov 17, 2025

Documentation update PR here:
prebid/prebid.github.io#6344
@YuriyVelichkoPI

Copy link
Contributor

@YuriyVelichkoPI YuriyVelichkoPI left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Collaborator

@mdanylov-sigma mdanylov-sigma left a comment

Choose a reason for hiding this comment

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

LGTM!

@mdanylov-sigma mdanylov-sigma merged commit 4661ebe into prebid:master Nov 20, 2025
9 of 10 checks passed
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.

3 participants