Skip to content

Commit 81b56a5

Browse files
committed
initial swift support setup
1 parent 07db7f1 commit 81b56a5

File tree

12 files changed

+2103
-91
lines changed

12 files changed

+2103
-91
lines changed

.editorconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# EditorConfig: https://EditorConfig.org
2+
3+
root = true
4+
5+
[*]
6+
charset = utf-8
7+
end_of_line = lf
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
indent_style = space
11+
indent_size = 2

README.md

Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -95,25 +95,25 @@ Use this to understand where your events live in the code and how they're being
9595
Your LLM of choice is used for generating descriptions of events, properties, and implementations.
9696
9797
See [schema.json](schema.json) for a JSON Schema of the output.
98-
98+
9999
100100
## Supported tracking libraries & languages
101101
102-
| Library | JavaScript/TypeScript | Python | Ruby | Go |
103-
|---------|:---------------------:|:------:|:----:|:--:|
104-
| Google Analytics | ✅ | ❌ | ❌ | ❌ |
105-
| Google Tag Manager | ✅ | ❌ | ❌ | ❌ |
106-
| Segment | ✅ | ✅ | ✅ | ✅ |
107-
| Mixpanel | ✅ | ✅ | ✅ | ✅ |
108-
| Amplitude | ✅ | ✅ | ❌ | ✅ |
109-
| Rudderstack | ✅ | ✅ | ✳️ | ✳️ |
110-
| mParticle | ✅ | ❌ | ❌ | ❌ |
111-
| PostHog | ✅ | ✅ | ✅ | ✅ |
112-
| Pendo | ✅ | ❌ | ❌ | ❌ |
113-
| Heap | ✅ | ❌ | ❌ | ❌ |
114-
| Snowplow | ✅ | ✅ | ✅ | ✅ |
115-
| Datadog RUM | ✅ | ❌ | ❌ | ❌ |
116-
| Custom Function | ✅ | ✅ | ✅ | ✅ |
102+
| Library | JavaScript/TypeScript | Python | Ruby | Go | Swift |
103+
|---------|:---------------------:|:------:|:----:|:--:|:--:|
104+
| Google Analytics | ✅ | ❌ | ❌ | ❌ | ✅ |
105+
| Google Tag Manager | ✅ | ❌ | ❌ | ❌ | ✅ |
106+
| Segment | ✅ | ✅ | ✅ | ✅ | ✅ |
107+
| Mixpanel | ✅ | ✅ | ✅ | ✅ | ✅ |
108+
| Amplitude | ✅ | ✅ | ❌ | ✅ | ✅ |
109+
| Rudderstack | ✅ | ✅ | ✳️ | ✳️ | ✅ |
110+
| mParticle | ✅ | ❌ | ❌ | ❌ | ✅ |
111+
| PostHog | ✅ | ✅ | ✅ | ✅ | ✅ |
112+
| Pendo | ✅ | ❌ | ❌ | ❌ | ✅ |
113+
| Heap | ✅ | ❌ | ❌ | ❌ | ✅ |
114+
| Snowplow | ✅ | ✅ | ✅ | ✅ | ❌ |
115+
| Datadog RUM | ✅ | ❌ | ❌ | ❌ | ❌ |
116+
| Custom Function | ✅ | ✅ | ✅ | ✅ | ✅ |
117117
118118
✳️ Rudderstack's SDKs often use the same format as Segment, so Rudderstack events may be detected as Segment events.
119119
@@ -129,6 +129,13 @@ See [schema.json](schema.json) for a JSON Schema of the output.
129129
'<property_name>': '<property_value>'
130130
});
131131
```
132+
133+
**Swift**
134+
```swift
135+
Analytics.logEvent("<event_name>", parameters: [
136+
"<property_name>": "<property_value>"
137+
])
138+
```
132139
</details>
133140

134141
<details>
@@ -147,6 +154,11 @@ See [schema.json](schema.json) for a JSON Schema of the output.
147154
'<property_name>': '<property_value>'
148155
});
149156
```
157+
158+
**Swift**
159+
```swift
160+
dataLayer.push(["event": "<event_name>", "<property_name>": "<property_value>"])
161+
```
150162
</details>
151163

152164
<details>
@@ -185,6 +197,11 @@ See [schema.json](schema.json) for a JSON Schema of the output.
185197
Set("<property_name>", "<property_value>"),
186198
})
187199
```
200+
201+
**Swift**
202+
```swift
203+
analytics.track(name: "<event_name>", properties: TrackProperties("<property_name>": "<property_value>"))
204+
```
188205
</details>
189206

190207
<details>
@@ -221,6 +238,13 @@ See [schema.json](schema.json) for a JSON Schema of the output.
221238
}),
222239
})
223240
```
241+
242+
**Swift**
243+
```swift
244+
Mixpanel.mainInstance().track(event: "<event_name>", properties: [
245+
"<property_name>": "<property_value>"
246+
])
247+
```
224248
</details>
225249

226250
<details>
@@ -256,6 +280,14 @@ See [schema.json](schema.json) for a JSON Schema of the output.
256280
},
257281
})
258282
```
283+
284+
**Swift**
285+
```swift
286+
amplitude.track(
287+
eventType: "<event_name>",
288+
eventProperties: ["<property_name>": "<property_value>"]
289+
)
290+
```
259291
</details>
260292

261293
<details>
@@ -295,6 +327,13 @@ See [schema.json](schema.json) for a JSON Schema of the output.
295327
Set("<property_name>", "<property_value>"),
296328
})
297329
```
330+
331+
**Swift**
332+
```swift
333+
RSClient.sharedInstance()?.track("<event_name>", properties: [
334+
"<property_name>": "<property_value>"
335+
])
336+
```
298337
</details>
299338

300339
<details>
@@ -306,6 +345,15 @@ See [schema.json](schema.json) for a JSON Schema of the output.
306345
'<property_name>': '<property_value>'
307346
});
308347
```
348+
349+
**Swift**
350+
```swift
351+
let event = MPEvent(name: "<event_name>", type: .other)
352+
event.customAttributes = [
353+
"<property_name>": "<property_value>"
354+
]
355+
MParticle.sharedInstance().logEvent(event)
356+
```
309357
</details>
310358

311359
<details>
@@ -353,6 +401,13 @@ See [schema.json](schema.json) for a JSON Schema of the output.
353401
Set("<property_name>", "<property_value>"),
354402
})
355403
```
404+
405+
**Swift**
406+
```swift
407+
PostHogSDK.shared.capture("<event_name>", properties: [
408+
"<property_name>": "<property_value>"
409+
])
410+
```
356411
</details>
357412

358413
<details>
@@ -372,7 +427,12 @@ See [schema.json](schema.json) for a JSON Schema of the output.
372427
})
373428
```
374429

375-
430+
**Swift**
431+
```swift
432+
PendoManager.shared().track("<event_name>", properties: [
433+
"<property_name>": "<property_value>"
434+
])
435+
```
376436
</details>
377437

378438
<details>
@@ -392,7 +452,12 @@ See [schema.json](schema.json) for a JSON Schema of the output.
392452
})
393453
```
394454

395-
455+
**Swift**
456+
```swift
457+
Heap.shared.track("<event_name>", properties: [
458+
"<property_name>": "<property_value>"
459+
])
460+
```
396461
</details>
397462

398463
<details>
@@ -403,7 +468,7 @@ See [schema.json](schema.json) for a JSON Schema of the output.
403468
datadogRum.addAction('<event_name>', {
404469
'<property_name>': '<property_value>'
405470
});
406-
471+
407472
// Or via window
408473
window.DD_RUM.addAction('<event_name>', {
409474
'<property_name>': '<property_value>'

package-lock.json

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"test:python": "node --experimental-vm-modules --test tests/analyzePython.test.js",
1414
"test:ruby": "node --experimental-vm-modules --test tests/analyzeRuby.test.js",
1515
"test:go": "node --test tests/analyzeGo.test.js",
16+
"test:swift": "node --test tests/analyzeSwift.test.js",
1617
"test:cli": "node --test tests/cli.test.js",
1718
"test:schema": "node --test tests/schema.test.js",
1819
"test:generateDescriptions": "node --test tests/generateDescriptions.test.js",
@@ -34,6 +35,7 @@
3435
},
3536
"homepage": "https://github.com/fliskdata/analyze-tracking#readme",
3637
"dependencies": {
38+
"@flisk/swift-ast": "^0.1.2",
3739
"@langchain/core": "^0.3.56",
3840
"@langchain/google-vertexai": "^0.2.9",
3941
"@langchain/openai": "^0.5.10",
@@ -52,9 +54,9 @@
5254
"zod": "^3.24.4"
5355
},
5456
"devDependencies": {
57+
"@types/react": "^19.1.6",
5558
"ajv": "^8.17.1",
5659
"lodash": "^4.17.21",
57-
"react": "^19.1.0",
58-
"@types/react": "^19.1.6"
60+
"react": "^19.1.0"
5961
}
6062
}

src/analyze/index.js

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ const { analyzeTsFiles } = require('./typescript');
1313
const { analyzePythonFile } = require('./python');
1414
const { analyzeRubyFile, prebuildConstantMaps } = require('./ruby');
1515
const { analyzeGoFile } = require('./go');
16+
const { analyzeSwiftFile } = require('./swift');
1617

1718
/**
1819
* Analyzes a single file for analytics tracking calls
19-
*
20+
*
2021
* Note: typescript files are handled separately by analyzeTsFiles, which is a batch processor
21-
*
22+
*
2223
* @param {string} file - Path to the file to analyze
2324
* @param {Array<string>} customFunctionSignatures - Custom function signatures to detect
2425
* @returns {Promise<Array<Object>>} Array of events found in the file
@@ -28,19 +29,20 @@ async function analyzeFile(file, customFunctionSignatures) {
2829
if (/\.py$/.test(file)) return analyzePythonFile(file, customFunctionSignatures)
2930
if (/\.rb$/.test(file)) return analyzeRubyFile(file, customFunctionSignatures)
3031
if (/\.go$/.test(file)) return analyzeGoFile(file, customFunctionSignatures)
32+
if (/\.swift$/.test(file)) return analyzeSwiftFile(file, customFunctionSignatures)
3133
return []
3234
}
3335

3436
/**
3537
* Adds an event to the events collection, merging properties if event already exists
36-
*
38+
*
3739
* @param {Object} allEvents - Collection of all events
3840
* @param {Object} event - Event to add
3941
* @param {string} baseDir - Base directory for relative path calculation
4042
*/
4143
function addEventToCollection(allEvents, event, baseDir) {
4244
const relativeFilePath = path.relative(baseDir, event.filePath);
43-
45+
4446
const implementation = {
4547
path: relativeFilePath,
4648
line: event.line,
@@ -64,12 +66,12 @@ function addEventToCollection(allEvents, event, baseDir) {
6466

6567
/**
6668
* Processes all files that are not TypeScript files in parallel
67-
*
69+
*
6870
* Checks the system's file descriptor limit and uses 80% of it to avoid running out of file descriptors
6971
* Creates a promise pool and launches one analysis for each file in parallel
7072
* When a slot frees up, the next file is launched
7173
* Waits for the remaining work to complete
72-
*
74+
*
7375
* @param {Array<string>} files - Array of file paths
7476
* @param {Object} allEvents - Collection to add events to
7577
* @param {string} baseDir - Base directory for relative paths
@@ -121,28 +123,33 @@ async function processFiles(files, allEvents, baseDir, customFunctionSignatures)
121123

122124
/**
123125
* Analyze a directory recursively for analytics tracking calls
124-
*
126+
*
125127
* This function scans all supported files in a directory tree and identifies analytics tracking calls,
126128
* handling different file types appropriately.
127-
*
129+
*
128130
* @param {string} dirPath - Path to the directory to analyze
129131
* @param {Array<string>} [customFunctions=null] - Array of custom tracking function signatures to detect
130132
* @returns {Promise<Object>} Object mapping event names to their tracking implementations
131133
*/
132134
async function analyzeDirectory(dirPath, customFunctions) {
133135
const allEvents = {};
134136

135-
const customFunctionSignatures = (customFunctions?.length > 0)
136-
? customFunctions.map(parseCustomFunctionSignature)
137-
: null;
137+
let customFunctionSignatures = null;
138+
if (Array.isArray(customFunctions) && customFunctions.length > 0) {
139+
customFunctionSignatures = customFunctions.map(cf => {
140+
if (cf && typeof cf === 'object' && cf.functionName) return cf;
141+
if (typeof cf === 'string') return parseCustomFunctionSignature(cf);
142+
return null;
143+
}).filter(Boolean);
144+
}
138145

139146
const files = getAllFiles(dirPath);
140-
147+
141148
// Separate TypeScript files from others for optimized processing
142149
const tsFiles = [];
143150
const nonTsFiles = [];
144151
const rubyFiles = [];
145-
152+
146153
for (const file of files) {
147154
const isTsFile = /\.(tsx?)$/.test(file);
148155
if (isTsFile) {

0 commit comments

Comments
 (0)