Skip to content

Fix retain cycle in Client#550

Open
yewreeka wants to merge 4 commits into
mainfrom
jarod/client-retain-cycle
Open

Fix retain cycle in Client#550
yewreeka wants to merge 4 commits into
mainfrom
jarod/client-retain-cycle

Conversation

@yewreeka

@yewreeka yewreeka commented Aug 14, 2025

Copy link
Copy Markdown
Contributor

Introduction 📟

Client kept a strong reference to Conversations, PrivatePreferences, and XMTPDebugInformation, all which kept a strong reference to Client. I also noticed that XmtpApiClient objects hang around because of ApiClientCache, but not sure if that's really an issue worth worrying about.

Purpose ℹ️

Fix retain cycle in Client

Testing 🧪

Ran tests and checked the memory graph in the example app

@yewreeka yewreeka requested a review from a team as a code owner August 14, 2025 00:14
@macroscopeapp

macroscopeapp Bot commented Aug 14, 2025

Copy link
Copy Markdown

Fix retain cycle by making Conversations.client weak and add ClientError.clientDeallocated for deallocated Client handling across conversation APIs

Convert Conversations.client to a weak reference and route all access through Conversations.requireClient() which throws ClientError.clientDeallocated; update PrivatePreferences and XMTPDebugInformation initializers to drop Client and use ffiClient and historySyncUrl; adjust stream logic to finish with ClientError.clientDeallocated when the client is nil.

📍Where to Start

Start with Conversations.requireClient() and its use across methods in Sources/XMTPiOS/Conversations.swift.


Macroscope summarized a0ee41b.

Comment thread Sources/XMTPiOS/Client.swift
@claude

claude Bot commented Aug 18, 2025

Copy link
Copy Markdown

Claude Code is working…

I'll analyze this and get back to you.

View job run

@cameronvoell cameronvoell left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thanks for finding this @Jarodl . I added small change to remove the client reference from debuginformation. I also made the client reference in Conversations private, to minimize the chance of someone trying to acceess it when it's not available.

There is a small chance an integrator uses the SDK in an untended way holding a reference to a client.conversations() and then trying to use it when the client is not in memory. But hopefully suggested use cases in our docs will make that rare enough, so still think fixing memory leak possibilities here is the better way to go.

If you can suggest a follow up at some point to pass in some context provider instead of the client so we dont have the risk of someone trying to access the unowned client reference when its not available, that would be awesome. But this is a great improvement 👍

@yewreeka yewreeka force-pushed the jarod/client-retain-cycle branch from 581505d to 061222b Compare December 17, 2025 02:47
Comment thread Sources/XMTPiOS/PrivatePreferences.swift Outdated
@yewreeka

Copy link
Copy Markdown
Contributor Author

thanks for finding this @Jarodl . I added small change to remove the client reference from debuginformation. I also made the client reference in Conversations private, to minimize the chance of someone trying to acceess it when it's not available.

There is a small chance an integrator uses the SDK in an untended way holding a reference to a client.conversations() and then trying to use it when the client is not in memory. But hopefully suggested use cases in our docs will make that rare enough, so still think fixing memory leak possibilities here is the better way to go.

If you can suggest a follow up at some point to pass in some context provider instead of the client so we dont have the risk of someone trying to access the unowned client reference when its not available, that would be awesome. But this is a great improvement 👍

I updated this to use a weak reference to avoid crashing in case it is used improperly, and to throw an error if the client has been deallocated. Let me know what you think! In Convos we're only keeping a max number of clients active at a time now, so this retain cycle is much more obvious now. Thank you!

@yewreeka yewreeka force-pushed the jarod/client-retain-cycle branch from a1d84d2 to a0ee41b Compare January 8, 2026 19:34
@cameronvoell

Copy link
Copy Markdown
Contributor

@yewreeka I just did a dev release of these changes off latest main and stable libxmtp main using the branch cv/jarod/client-retain-cycle-dev-release

Release version will be:

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.

2 participants