iOS Hot-Reloading with Inject
I have integrated krzysztofzablocki/Inject into my new SwiftUI Project and it was astonishing.
I don’t know how exactly working inside the box, but Inject drove me a feeling like developing a React application(or any other hot-reloadable framework?).
I wrote this post for sharing my script to inject the code snippets for each swift file and View
required by Inject.
Happy Coding! XD
#!/usr/bin/env zx
/* eslint-disable max-len */
// region ZX Util
import { glob, path, fs } from 'zx';
const filename = path.basename(__filename);
const _printTag = '' || filename;
function printSuccess (...args) {
echo(chalk.bold.bgBlue(`[${_printTag}]`, ...args));
}
// endregion
async function main () {
const projectRoot = './pill_mate';
const swiftFiles = await glob(`${projectRoot}/**/*.swift`);
for (const filepath of swiftFiles) {
const filename = path.basename(filepath);
const content = await fs.readFile(filepath, 'utf8');
// Skip if not a SwiftUI View
if (!content.includes(': View {') || content.includes('no-inject')) {
// print(`Skipping: ${filename} (No ': View {' found)`);
continue;
}
let modified = content;
let changed = false;
// 2. Add `@ObserveInjection var inject` if missing
{
const lines = modified.split('\n');
let changedThisFile = false;
for (let i = 0; i < lines.length; i++) {
if (/struct\s+\w+.*?:\s*View\s*{/s.test(lines[i])) {
let braceCount = 0;
let blockStart = i;
let blockEnd = -1;
for (let j = i; j < lines.length; j++) {
const open = (lines[j].match(/{/g) || []).length;
const close = (lines[j].match(/}/g) || []).length;
braceCount += open - close;
if (braceCount === 0) {
blockEnd = j;
break;
}
}
if (blockEnd !== -1) {
const blockLines = lines.slice(blockStart, blockEnd + 1);
const hasInject = blockLines.some(line => line.includes('@ObserveInjection var inject'));
if (!hasInject) {
lines.splice(blockStart + 1, 0, ' @ObserveInjection var inject');
changedThisFile = true;
i = blockEnd + 1;
}
}
}
}
if (changedThisFile) {
modified = lines.join('\n');
changed = true;
}
}
// 3. Add `.enableInjection()` before closing brace of `var body`
{
const lines = modified.split('\n');
let changedThisFile = false;
for (let i = 0; i < lines.length; i++) {
if (
/var\s+[_\w\d]+\s*:\s*some\s+View\s*{/.test(lines[i]) ||
/->\s*some\s+View\s*{/.test(lines[i])
) {
let braceCount = 0;
let bodyEndIdx = -1;
for (let j = i; j < lines.length; j++) {
const open = (lines[j].match(/{/g) || []).length;
const close = (lines[j].match(/}/g) || []).length;
braceCount += open - close;
if (braceCount === 0) {
bodyEndIdx = j;
break;
}
}
if (bodyEndIdx !== -1) {
const blockLines = lines.slice(i, bodyEndIdx + 1);
if (!blockLines.some(line => line.includes('.enableInjection()'))) {
lines.splice(bodyEndIdx, 0, ' .enableInjection()');
changedThisFile = true;
i = bodyEndIdx + 1;
}
}
}
}
if (changedThisFile) {
modified = lines.join('\n');
changed = true;
}
}
// Overwrite file if modified
if (changed) {
await fs.writeFile(filepath, modified);
printSuccess(`Modified: ${filename}`);
} else {
// console.log(`No changes for: ${filename}`);
}
}
printSuccess('Inject modification script completed.');
};
main();
Comments