Skip to content

feat: add typing indicator support via IMCore private framework#41

Merged
steipete merged 3 commits intosteipete:mainfrom
kohoj:feat/typing-indicators
Feb 16, 2026
Merged

feat: add typing indicator support via IMCore private framework#41
steipete merged 3 commits intosteipete:mainfrom
kohoj:feat/typing-indicators

Conversation

@kohoj
Copy link
Contributor

@kohoj kohoj commented Feb 6, 2026

Summary

Implements typing indicators for outgoing messages (closes #22) using runtime dynamic loading of Apple's IMCore private framework. This is the only way to programmatically send typing indicators — AppleScript has no equivalent capability.

What's included

1. IMsgCoreTypingIndicator struct

  • startTyping(chatIdentifier:) — show the "..." typing bubble on the recipient's device
  • stopTyping(chatIdentifier:) — dismiss the typing bubble
  • typeForDuration(chatIdentifier:duration:) — show for N seconds, then auto-stop
  • Uses dlopen to load /System/Library/PrivateFrameworks/IMCore.framework at runtime
  • Connects to IMDaemonController, resolves chat via IMChatRegistry, then calls [IMChat setLocalUserIsTyping:]

2. CLI — imsg typing command

# Start typing indicator (stays until timeout or --stop)
imsg typing --to +14155551212

# Show for a specific duration
imsg typing --to +14155551212 --duration 5s

# Stop typing indicator
imsg typing --to +14155551212 --stop true

# Target by chat identifier directly
imsg typing --chat-identifier "iMessage;-;+14155551212"

# JSON output
imsg typing --to +14155551212 --json

Supports all the same chat targeting as send: --to, --chat-id, --chat-identifier, --chat-guid.

3. RPC — typing.start / typing.stop methods

{"jsonrpc":"2.0","id":1,"method":"typing.start","params":{"to":"+14155551212"}}
{"jsonrpc":"2.0","id":2,"method":"typing.stop","params":{"to":"+14155551212"}}

Also supports chat_identifier, chat_guid, chat_id, and service params.

How it works

The implementation uses the same approach as BlueBubbles — loading IMCore at runtime via dlopen/objc_getClass/sel_registerName. The core call is:

[chat setLocalUserIsTyping:YES];  // start
[chat setLocalUserIsTyping:NO];   // stop

No new dependencies are required. The private framework is available on all macOS versions that support Messages.app.

Files changed

File Change
Sources/IMsgCore/TypingIndicator.swift New — core typing indicator logic
Sources/IMsgCore/Errors.swift Add typingIndicatorFailed error case
Sources/imsg/Commands/TypingCommand.swift New — CLI command
Sources/imsg/CommandRouter.swift Register typing command
Sources/imsg/RPCServer.swift Add typing.start/typing.stop RPC methods

Caveats

  • Private API: Uses IMCore private framework. Not suitable for Mac App Store distribution, but fine for CLI tools and side-loaded apps.
  • Requires active conversation: The chat must already exist in Messages.app (same as send).
  • Daemon connection: First call has ~500ms latency for IMDaemonController connection setup; subsequent calls are fast.

Testing

  • swift build passes with zero errors and zero warnings
  • Manual testing: imsg typing --to <number> shows the typing bubble on the recipient's iPhone
  • Existing tests are unaffected (pre-existing Testing framework issue on non-Xcode-16 environments)

@aryan5276
Copy link

aryan5276 commented Feb 15, 2026

lgtm, @steipete good to merge?

@steipete
Copy link
Owner

Does that work without disabling SIP?

kohoj and others added 3 commits February 16, 2026 06:27
Implements typing indicators for outgoing messages using runtime
dynamic loading of Apple's IMCore private framework. This is the
only way to programmatically send typing indicators — AppleScript
has no equivalent capability.

Closes steipete#22

Changes:
- IMsgCore: Add TypingIndicator struct with start/stop/duration APIs
- CLI: Add 'imsg typing' command with --to, --duration, --stop flags
- RPC: Add typing.start and typing.stop methods
- Errors: Add typingIndicatorFailed error case

Usage:
  imsg typing --to +14155551212
  imsg typing --to +14155551212 --duration 5s
  imsg typing --to +14155551212 --stop true
  imsg typing --chat-identifier "iMessage;-;+14155551212"
@steipete steipete force-pushed the feat/typing-indicators branch from 6488f85 to 6cd7238 Compare February 16, 2026 05:30
@steipete steipete merged commit a2c0865 into steipete:main Feb 16, 2026
1 check passed
@steipete
Copy link
Owner

Landed via temp rebase onto main.

  • Gate: make lint && make test
  • Land commit: 6cd7238
  • Merge commit: a2c0865

Thanks @kohoj!

@steipete
Copy link
Owner

Correction after GitHub rebase-merge rewrite: final landed SHAs on main are\n- 69499d8 (feat)\n- 7fa01f1 (fix)\n- a2c0865 (changelog)

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.

Feature: typing indicators for outgoing messages

3 participants