quartz/content/01-Sessions/Session 4 - IPC Communication.md
Mohammad Jarrar dec3e85fe2 ll
2026-03-15 07:36:20 +03:00

101 lines
2.9 KiB
Markdown

---
tags: [electron, ipc, preload, contextBridge, security]
session: 4
duration: 2 hours
status: ready
---
# Session 4 — IPC: Making the Two Processes Talk
## 🎯 Objectives
- Understand why IPC exists and why it's necessary
- Send messages from Renderer → Main and back
- Use `preload.js` and `contextBridge` safely
- Wire a full two-way communication flow
---
## 🧠 Concepts Covered
### Why IPC?
The Renderer runs in a sandboxed browser context.
It cannot directly call `fs.readFile()` or access Node.js.
IPC is the secure message-passing bridge.
```
Renderer → ipcRenderer.send('open-file') →
Main → ipcMain.on('open-file', handler) →
Main → win.webContents.send('file-content', data) →
Renderer → ipcRenderer.on('file-content', handler)
```
### The Secure Way: preload.js + contextBridge
```js
// preload.js — runs in a special context with BOTH Node and DOM access
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
openFile: () => ipcRenderer.invoke('open-file'),
saveFile: (content) => ipcRenderer.invoke('save-file', content),
onFileLoaded: (callback) => ipcRenderer.on('file-loaded', callback)
})
```
```js
// main.js — handles the requests
const { ipcMain, dialog } = require('electron')
const fs = require('fs')
ipcMain.handle('open-file', async () => {
const result = await dialog.showOpenDialog({ ... })
if (!result.canceled) {
return fs.readFileSync(result.filePaths[0], 'utf-8')
}
})
ipcMain.handle('save-file', async (event, content) => {
const result = await dialog.showSaveDialog({})
if (!result.canceled) {
fs.writeFileSync(result.filePath, content)
return true
}
})
```
```js
// renderer (index.html script) — uses the exposed API
document.getElementById('open-btn').addEventListener('click', async () => {
const content = await window.electronAPI.openFile()
document.getElementById('editor').value = content
})
```
### invoke vs send
| `ipcRenderer.send` | `ipcRenderer.invoke` |
|--------------------|----------------------|
| Fire and forget | Returns a Promise |
| One-way | Two-way (request/response) |
| Use for events | Use for data fetching |
---
## 🛠 Hands-on Exercise — Wire the Notepad (60 min)
Complete the Notepad from Session 3 using proper IPC:
1. Set up `preload.js` with `contextBridge`
2. Add `webPreferences: { preload: ... }` to BrowserWindow
3. Expose `openFile` and `saveFile` via `electronAPI`
4. Wire up Open and Save buttons in the renderer
5. **Bonus:** Add a word count display that updates as you type
---
## ⚠️ Common Mistakes
- Forgetting to set `preload` in `webPreferences`
- Using `nodeIntegration: true` (insecure, avoid it)
- Trying to `require('fs')` in renderer directly
---
## 📦 Resources
- [IPC Docs](https://www.electronjs.org/docs/latest/tutorial/ipc)
- [contextBridge Docs](https://www.electronjs.org/docs/latest/api/context-bridge)
- [[Session 5 - App Design & WebSockets Intro]] ← next