shortcuts-playground 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/agents/shortcut-builder.md +172 -0
- package/agents/shortcut-remixer.md +239 -0
- package/commands/build-shortcut.md +32 -0
- package/commands/remix-shortcut.md +34 -0
- package/opencode.json +30 -0
- package/package.json +47 -0
- package/plugin/validator.ts +179 -0
- package/skills/shortcuts-playground/ACTIONS.md +713 -0
- package/skills/shortcuts-playground/APPINTENTS.md +2760 -0
- package/skills/shortcuts-playground/AUTOMATION_TRIGGERS.md +133 -0
- package/skills/shortcuts-playground/BEST_PRACTICES.md +357 -0
- package/skills/shortcuts-playground/CHANGELOG.md +585 -0
- package/skills/shortcuts-playground/CONTROL_FLOW.md +777 -0
- package/skills/shortcuts-playground/DATE_TIME.md +75 -0
- package/skills/shortcuts-playground/EXAMPLES.md +738 -0
- package/skills/shortcuts-playground/FILTERS.md +697 -0
- package/skills/shortcuts-playground/HEALTHKIT.md +317 -0
- package/skills/shortcuts-playground/ICONS_AND_COLORS.md +89 -0
- package/skills/shortcuts-playground/JAVASCRIPT_WEBPAGE.md +51 -0
- package/skills/shortcuts-playground/PARAMETER_TYPES.md +1117 -0
- package/skills/shortcuts-playground/PLIST_FORMAT.md +296 -0
- package/skills/shortcuts-playground/README.md +77 -0
- package/skills/shortcuts-playground/SKILL.md +459 -0
- package/skills/shortcuts-playground/THIRD_PARTY_ACTIONS.md +77 -0
- package/skills/shortcuts-playground/TOOLKIT_SNAPSHOT.md +62 -0
- package/skills/shortcuts-playground/URL_SCHEMES.md +59 -0
- package/skills/shortcuts-playground/VARIABLES.md +569 -0
- package/skills/shortcuts-playground/assets/shortcuts-small.svg +14 -0
- package/skills/shortcuts-playground/assets/shortcuts.png +0 -0
- package/skills/shortcuts-playground/data/healthkit-ios26.2-reference.json +2603 -0
- package/skills/shortcuts-playground/data/macos27-shortpy-grounding.json +4085 -0
- package/skills/shortcuts-playground/data/macos27-workflow-trigger-samples.json +1 -0
- package/skills/shortcuts-playground/data/shortcuts-glyph-synonyms.json +5102 -0
- package/skills/shortcuts-playground/data/shortcuts-icon-colors.json +107 -0
- package/skills/shortcuts-playground/data/shortcuts-official-glyph-mapping.json +509 -0
- package/skills/shortcuts-playground/data/toolkit-v63-tool-ids.json +1806 -0
- package/skills/shortcuts-playground/data/toolkit-v78-first-party-enum-cases.json +1 -0
- package/skills/shortcuts-playground/data/toolkit-v78-first-party-parameter-keys.json +1 -0
- package/skills/shortcuts-playground/data/toolkit-v78-ios27-tool-ids.json +1222 -0
- package/skills/shortcuts-playground/data/toolkit-v78-tool-ids.json +2745 -0
- package/skills/shortcuts-playground/data/toolkit-v78-trigger-parameter-keys.json +1051 -0
- package/skills/shortcuts-playground/golden-shortcuts/index.jsonl +19 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/1be4dde95b794253bf82438e201b33e7.xml +174 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/2e0fb675e45948aaacee7e534f910492.xml +1016 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/332c12a0060043b388b22b806be7ab58.xml +1924 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/3dd4ee24e43f464f92adaa70a0311eaa.xml +1177 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/4d102301c6e646faa7a8a221f4f4ec98.xml +321 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/51cc4e26d1044893a0c3f2f3630cf2d2.xml +1933 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/623e7f1ca5f948e2bd53811fec63e544.xml +136 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/6a18b76843ac45c384ac3400f3740997.xml +1791 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/71f0cacb0f604b399b76c5dcb7286e7c.xml +2228 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/807525ed9f974829bc8494defac923a8.xml +259 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/8ab0d39826ab4249be58672763caa3ba.xml +420 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/91c45fd3fd3b427897d9ba485efb1227.xml +800 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/97be626bb25c41709d175646a7f6d8f2.xml +424 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/ae59e10d409348f9bd33894f03f9beb4.xml +568 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/afa83b6be811483b9c32189c41eb9312.xml +391 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/cfdb083b857e4ac189629fa386d27cdc.xml +193 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/e01cebe192d64b2fbca80204d03d92ab.xml +111 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/ef669bcf8bad489d9ef4b88bfaf5772f.xml +780 -0
- package/skills/shortcuts-playground/golden-shortcuts/xml/f44f5caf5e3e48d4817e73af450c4404.xml +570 -0
- package/skills/shortcuts-playground/scripts/generate_healthkit_reference.py +394 -0
- package/skills/shortcuts-playground/scripts/lookup_action_grounding.py +1164 -0
- package/skills/shortcuts-playground/scripts/select_shortcut_icon_color.py +597 -0
- package/skills/shortcuts-playground/scripts/test_random_mixed_shortcuts.py +1067 -0
- package/skills/shortcuts-playground/scripts/test_wiring_regressions.py +2247 -0
- package/skills/shortcuts-playground/scripts/validate_shortcut.py +4526 -0
|
@@ -0,0 +1,777 @@
|
|
|
1
|
+
# Control Flow Patterns
|
|
2
|
+
|
|
3
|
+
How to implement loops, conditionals, and menus in Shortcuts.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Control flow actions use two key parameters:
|
|
8
|
+
- **GroupingIdentifier**: A UUID that links related actions (start, middle, end)
|
|
9
|
+
- **WFControlFlowMode**: An integer indicating the action's role
|
|
10
|
+
- `0` = Start (begin block)
|
|
11
|
+
- `1` = Middle (else, case)
|
|
12
|
+
- `2` = End (close block)
|
|
13
|
+
|
|
14
|
+
**Important**: `WFControlFlowMode` must be an `<integer>`, not a `<string>`.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Repeat Count
|
|
19
|
+
|
|
20
|
+
Repeat a block of actions a specific number of times.
|
|
21
|
+
|
|
22
|
+
### Structure
|
|
23
|
+
| Mode | Action | Description |
|
|
24
|
+
|------|--------|-------------|
|
|
25
|
+
| 0 | Start | Begin repeat, set count |
|
|
26
|
+
| 2 | End | Close repeat block |
|
|
27
|
+
|
|
28
|
+
### Template
|
|
29
|
+
|
|
30
|
+
```xml
|
|
31
|
+
<!-- Repeat Start -->
|
|
32
|
+
<dict>
|
|
33
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
34
|
+
<string>is.workflow.actions.repeat.count</string>
|
|
35
|
+
<key>WFWorkflowActionParameters</key>
|
|
36
|
+
<dict>
|
|
37
|
+
<key>GroupingIdentifier</key>
|
|
38
|
+
<string>REPEAT-GROUP-UUID</string>
|
|
39
|
+
<key>WFControlFlowMode</key>
|
|
40
|
+
<integer>0</integer>
|
|
41
|
+
<key>WFRepeatCount</key>
|
|
42
|
+
<!-- Can be integer or variable reference -->
|
|
43
|
+
<integer>5</integer>
|
|
44
|
+
</dict>
|
|
45
|
+
</dict>
|
|
46
|
+
|
|
47
|
+
<!-- Actions inside the loop go here -->
|
|
48
|
+
|
|
49
|
+
<!-- Repeat End -->
|
|
50
|
+
<dict>
|
|
51
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
52
|
+
<string>is.workflow.actions.repeat.count</string>
|
|
53
|
+
<key>WFWorkflowActionParameters</key>
|
|
54
|
+
<dict>
|
|
55
|
+
<key>UUID</key>
|
|
56
|
+
<string>END-ACTION-UUID</string>
|
|
57
|
+
<key>GroupingIdentifier</key>
|
|
58
|
+
<string>REPEAT-GROUP-UUID</string>
|
|
59
|
+
<key>WFControlFlowMode</key>
|
|
60
|
+
<integer>2</integer>
|
|
61
|
+
</dict>
|
|
62
|
+
</dict>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Accessing Repeat Index
|
|
66
|
+
|
|
67
|
+
Inside the loop, reference the current index as a **named Variable** (Type=Variable, not ActionOutput):
|
|
68
|
+
|
|
69
|
+
```xml
|
|
70
|
+
<key>attachmentsByRange</key>
|
|
71
|
+
<dict>
|
|
72
|
+
<key>{0, 1}</key>
|
|
73
|
+
<dict>
|
|
74
|
+
<key>Type</key>
|
|
75
|
+
<string>Variable</string>
|
|
76
|
+
<key>VariableName</key>
|
|
77
|
+
<string>Repeat Index</string>
|
|
78
|
+
</dict>
|
|
79
|
+
</dict>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**CRITICAL:** Repeat Index uses `Type: Variable` with `VariableName: "Repeat Index"`, NOT `Type: ActionOutput` referencing the end action's UUID. Using the wrong type causes the variable to appear as "Repeat Results" in the UI and fails at runtime.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Repeat with Each (For Each)
|
|
87
|
+
|
|
88
|
+
Iterate over each item in a list.
|
|
89
|
+
|
|
90
|
+
### Structure
|
|
91
|
+
| Mode | Action | Description |
|
|
92
|
+
|------|--------|-------------|
|
|
93
|
+
| 0 | Start | Begin loop, specify input list |
|
|
94
|
+
| 2 | End | Close loop |
|
|
95
|
+
|
|
96
|
+
### Template
|
|
97
|
+
|
|
98
|
+
```xml
|
|
99
|
+
<!-- Repeat Each Start -->
|
|
100
|
+
<dict>
|
|
101
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
102
|
+
<string>is.workflow.actions.repeat.each</string>
|
|
103
|
+
<key>WFWorkflowActionParameters</key>
|
|
104
|
+
<dict>
|
|
105
|
+
<key>GroupingIdentifier</key>
|
|
106
|
+
<string>FOREACH-GROUP-UUID</string>
|
|
107
|
+
<key>WFControlFlowMode</key>
|
|
108
|
+
<integer>0</integer>
|
|
109
|
+
<key>WFInput</key>
|
|
110
|
+
<dict>
|
|
111
|
+
<key>Value</key>
|
|
112
|
+
<dict>
|
|
113
|
+
<key>OutputUUID</key>
|
|
114
|
+
<string>LIST-SOURCE-UUID</string>
|
|
115
|
+
<key>OutputName</key>
|
|
116
|
+
<string>List</string>
|
|
117
|
+
<key>Type</key>
|
|
118
|
+
<string>ActionOutput</string>
|
|
119
|
+
</dict>
|
|
120
|
+
<key>WFSerializationType</key>
|
|
121
|
+
<string>WFTextTokenAttachment</string>
|
|
122
|
+
</dict>
|
|
123
|
+
</dict>
|
|
124
|
+
</dict>
|
|
125
|
+
|
|
126
|
+
<!-- Actions inside the loop go here -->
|
|
127
|
+
|
|
128
|
+
<!-- Repeat Each End -->
|
|
129
|
+
<dict>
|
|
130
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
131
|
+
<string>is.workflow.actions.repeat.each</string>
|
|
132
|
+
<key>WFWorkflowActionParameters</key>
|
|
133
|
+
<dict>
|
|
134
|
+
<key>UUID</key>
|
|
135
|
+
<string>END-ACTION-UUID</string>
|
|
136
|
+
<key>GroupingIdentifier</key>
|
|
137
|
+
<string>FOREACH-GROUP-UUID</string>
|
|
138
|
+
<key>WFControlFlowMode</key>
|
|
139
|
+
<integer>2</integer>
|
|
140
|
+
</dict>
|
|
141
|
+
</dict>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Accessing Current Item
|
|
145
|
+
|
|
146
|
+
Reference the current item using a **named Variable** (Type=Variable):
|
|
147
|
+
|
|
148
|
+
```xml
|
|
149
|
+
<key>attachmentsByRange</key>
|
|
150
|
+
<dict>
|
|
151
|
+
<key>{0, 1}</key>
|
|
152
|
+
<dict>
|
|
153
|
+
<key>Type</key>
|
|
154
|
+
<string>Variable</string>
|
|
155
|
+
<key>VariableName</key>
|
|
156
|
+
<string>Repeat Item</string>
|
|
157
|
+
</dict>
|
|
158
|
+
</dict>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Alternatively** (less common), use the Start action's UUID as an ActionOutput:
|
|
162
|
+
|
|
163
|
+
```xml
|
|
164
|
+
<key>attachmentsByRange</key>
|
|
165
|
+
<dict>
|
|
166
|
+
<key>{0, 1}</key>
|
|
167
|
+
<dict>
|
|
168
|
+
<key>OutputUUID</key>
|
|
169
|
+
<string>START-ACTION-UUID</string>
|
|
170
|
+
<key>OutputName</key>
|
|
171
|
+
<string>Repeat Item</string>
|
|
172
|
+
<key>Type</key>
|
|
173
|
+
<string>ActionOutput</string>
|
|
174
|
+
</dict>
|
|
175
|
+
</dict>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Preferred approach:** Use `Type: Variable` with `VariableName: "Repeat Item"` for consistency and UI stability.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Conditional (If/Otherwise/Otherwise If)
|
|
183
|
+
|
|
184
|
+
Execute different actions based on a condition.
|
|
185
|
+
|
|
186
|
+
### Structure
|
|
187
|
+
| Mode | Action | Description |
|
|
188
|
+
|------|--------|-------------|
|
|
189
|
+
| 0 | If | Start conditional, define condition |
|
|
190
|
+
| 1 | Otherwise / Otherwise If | Else branch, or a conditional middle branch when condition fields are present |
|
|
191
|
+
| 2 | End If | Close conditional |
|
|
192
|
+
|
|
193
|
+
### Template (single-condition If)
|
|
194
|
+
|
|
195
|
+
```xml
|
|
196
|
+
<!-- If -->
|
|
197
|
+
<dict>
|
|
198
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
199
|
+
<string>is.workflow.actions.conditional</string>
|
|
200
|
+
<key>WFWorkflowActionParameters</key>
|
|
201
|
+
<dict>
|
|
202
|
+
<key>GroupingIdentifier</key>
|
|
203
|
+
<string>IF-GROUP-UUID</string>
|
|
204
|
+
<key>WFControlFlowMode</key>
|
|
205
|
+
<integer>0</integer>
|
|
206
|
+
<key>WFCondition</key>
|
|
207
|
+
<integer>4</integer>
|
|
208
|
+
<key>WFConditionalActionString</key>
|
|
209
|
+
<string>Yes</string>
|
|
210
|
+
<key>WFInput</key>
|
|
211
|
+
<dict>
|
|
212
|
+
<key>Type</key>
|
|
213
|
+
<string>Variable</string>
|
|
214
|
+
<key>Variable</key>
|
|
215
|
+
<dict>
|
|
216
|
+
<key>Value</key>
|
|
217
|
+
<dict>
|
|
218
|
+
<key>OutputName</key>
|
|
219
|
+
<string>Text</string>
|
|
220
|
+
<key>OutputUUID</key>
|
|
221
|
+
<string>VALUE-TO-TEST-UUID</string>
|
|
222
|
+
<key>Type</key>
|
|
223
|
+
<string>ActionOutput</string>
|
|
224
|
+
</dict>
|
|
225
|
+
<key>WFSerializationType</key>
|
|
226
|
+
<string>WFTextTokenAttachment</string>
|
|
227
|
+
</dict>
|
|
228
|
+
</dict>
|
|
229
|
+
</dict>
|
|
230
|
+
</dict>
|
|
231
|
+
|
|
232
|
+
<!-- Actions for the "If" branch go here -->
|
|
233
|
+
|
|
234
|
+
<!-- Otherwise -->
|
|
235
|
+
<dict>
|
|
236
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
237
|
+
<string>is.workflow.actions.conditional</string>
|
|
238
|
+
<key>WFWorkflowActionParameters</key>
|
|
239
|
+
<dict>
|
|
240
|
+
<key>GroupingIdentifier</key>
|
|
241
|
+
<string>IF-GROUP-UUID</string>
|
|
242
|
+
<key>WFControlFlowMode</key>
|
|
243
|
+
<integer>1</integer>
|
|
244
|
+
</dict>
|
|
245
|
+
</dict>
|
|
246
|
+
|
|
247
|
+
<!-- Actions for the "Otherwise" branch go here -->
|
|
248
|
+
|
|
249
|
+
<!-- End If -->
|
|
250
|
+
<dict>
|
|
251
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
252
|
+
<string>is.workflow.actions.conditional</string>
|
|
253
|
+
<key>WFWorkflowActionParameters</key>
|
|
254
|
+
<dict>
|
|
255
|
+
<key>GroupingIdentifier</key>
|
|
256
|
+
<string>IF-GROUP-UUID</string>
|
|
257
|
+
<key>WFControlFlowMode</key>
|
|
258
|
+
<integer>2</integer>
|
|
259
|
+
</dict>
|
|
260
|
+
</dict>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Otherwise If (macOS 27+)
|
|
264
|
+
|
|
265
|
+
`Otherwise If` is not a new action identifier and does not use a new control-flow mode. It is still `is.workflow.actions.conditional` with `WFControlFlowMode = 1`; the difference from plain Otherwise is that it also carries the same condition fields as an If start (`WFCondition`, `WFInput`, and the required literal field for that condition code).
|
|
266
|
+
|
|
267
|
+
Plain Otherwise:
|
|
268
|
+
|
|
269
|
+
```xml
|
|
270
|
+
<key>WFControlFlowMode</key>
|
|
271
|
+
<integer>1</integer>
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Otherwise If:
|
|
275
|
+
|
|
276
|
+
```xml
|
|
277
|
+
<dict>
|
|
278
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
279
|
+
<string>is.workflow.actions.conditional</string>
|
|
280
|
+
<key>WFWorkflowActionParameters</key>
|
|
281
|
+
<dict>
|
|
282
|
+
<key>GroupingIdentifier</key>
|
|
283
|
+
<string>IF-GROUP-UUID</string>
|
|
284
|
+
<key>WFControlFlowMode</key>
|
|
285
|
+
<integer>1</integer>
|
|
286
|
+
<key>WFCondition</key>
|
|
287
|
+
<integer>99</integer>
|
|
288
|
+
<key>WFConditionalActionString</key>
|
|
289
|
+
<string>Four</string>
|
|
290
|
+
<key>WFInput</key>
|
|
291
|
+
<dict>
|
|
292
|
+
<key>Type</key>
|
|
293
|
+
<string>Variable</string>
|
|
294
|
+
<key>Variable</key>
|
|
295
|
+
<dict>
|
|
296
|
+
<key>Value</key>
|
|
297
|
+
<dict>
|
|
298
|
+
<key>Type</key>
|
|
299
|
+
<string>Variable</string>
|
|
300
|
+
<key>VariableName</key>
|
|
301
|
+
<string>List Variable</string>
|
|
302
|
+
</dict>
|
|
303
|
+
<key>WFSerializationType</key>
|
|
304
|
+
<string>WFTextTokenAttachment</string>
|
|
305
|
+
</dict>
|
|
306
|
+
</dict>
|
|
307
|
+
</dict>
|
|
308
|
+
</dict>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Place any `Otherwise If` actions after the initial mode 0 If branch and before a final plain Otherwise. Use the same `GroupingIdentifier` for every branch in the block.
|
|
312
|
+
|
|
313
|
+
### macOS 27 List Contains Import Trap
|
|
314
|
+
|
|
315
|
+
When checking whether a list contains an item, avoid this pattern:
|
|
316
|
+
|
|
317
|
+
1. Set `Fruit List` from a List action.
|
|
318
|
+
2. Add an item to `Fruit List`.
|
|
319
|
+
3. Set `Fruit List` again from the Add to List output.
|
|
320
|
+
4. Run `If Fruit List contains "Orange"`.
|
|
321
|
+
|
|
322
|
+
On macOS 27, imported shortcuts using that repeated-name list pattern can show a blank comparison chip even though the plist contains `WFConditionalActionString`. Use one of these safe shapes instead:
|
|
323
|
+
|
|
324
|
+
- Reference the final `List`/`Add to List` action output directly in the conditional `WFInput`.
|
|
325
|
+
- Or use an intermediate name while mutating (for example `Working Fruit List`) and assign the final list once to a fresh name (`Fruit List`) immediately before the If.
|
|
326
|
+
|
|
327
|
+
### Condition Codes (DEFINITIVE — verified against an Apple-built sample shortcut)
|
|
328
|
+
|
|
329
|
+
**ALWAYS use integer codes** for `WFCondition`. String names may import but degrade at runtime.
|
|
330
|
+
|
|
331
|
+
| Code | UI label | Category | Required extra fields |
|
|
332
|
+
|------|----------|----------|------------------------|
|
|
333
|
+
| `0` | is less than | numeric | `WFNumberValue` |
|
|
334
|
+
| `1` | is less than or equal to | numeric | `WFNumberValue` |
|
|
335
|
+
| `2` | is greater than | numeric | `WFNumberValue` |
|
|
336
|
+
| `3` | is greater than or equal to | numeric | `WFNumberValue` |
|
|
337
|
+
| `4` | is (string equals) | string | `WFConditionalActionString` |
|
|
338
|
+
| `5` | is not | string | `WFConditionalActionString` |
|
|
339
|
+
| `8` | begins with | string | `WFConditionalActionString` |
|
|
340
|
+
| `9` | ends with | string | `WFConditionalActionString` |
|
|
341
|
+
| `99` | contains | string | `WFConditionalActionString` |
|
|
342
|
+
| `100`| has any value | existence | (none) |
|
|
343
|
+
| `101`| does not have any value | existence | (none) |
|
|
344
|
+
| `999`| does not contain | string | `WFConditionalActionString` |
|
|
345
|
+
| `1003` | is between | numeric | `WFNumberValue` (lower) + `WFAnotherNumber` (upper) |
|
|
346
|
+
|
|
347
|
+
⚠️ **Common confusions to avoid:**
|
|
348
|
+
- Code `4` is the string `is` (exact equality). Code `99` is the string `contains` (substring). They are NOT interchangeable.
|
|
349
|
+
- Code `0` is `is less than`, NOT `equals`. There is no numeric "equals" code in the modern Shortcuts conditional action — to check equality of two numbers, use `is greater than or equal to` AND `is less than or equal to` (Any-of-two pattern below) or compare via Text equality (code `4`).
|
|
350
|
+
- Code `2` is `is greater than`, code `3` is `is greater than or equal to`. They differ by inclusivity — easy to swap by mistake.
|
|
351
|
+
|
|
352
|
+
### Input rule (uniform across all codes)
|
|
353
|
+
|
|
354
|
+
**ALL conditional codes require an explicit `WFInput` as a `Type=Variable` wrapper.** There is no "implicit input" mode. The previous documentation that told you numeric conditions (0–3) use implicit input was incorrect — verified against an Apple-built sample where every single `is.workflow.actions.conditional` action sets `WFInput` explicitly.
|
|
355
|
+
|
|
356
|
+
The wrapper structure:
|
|
357
|
+
|
|
358
|
+
```xml
|
|
359
|
+
<key>WFInput</key>
|
|
360
|
+
<dict>
|
|
361
|
+
<key>Type</key>
|
|
362
|
+
<string>Variable</string>
|
|
363
|
+
<key>Variable</key>
|
|
364
|
+
<dict>
|
|
365
|
+
<key>Value</key>
|
|
366
|
+
<dict>
|
|
367
|
+
<key>OutputName</key>
|
|
368
|
+
<string>Text</string>
|
|
369
|
+
<key>OutputUUID</key>
|
|
370
|
+
<string>UPSTREAM-ACTION-UUID</string>
|
|
371
|
+
<key>Type</key>
|
|
372
|
+
<string>ActionOutput</string>
|
|
373
|
+
</dict>
|
|
374
|
+
<key>WFSerializationType</key>
|
|
375
|
+
<string>WFTextTokenAttachment</string>
|
|
376
|
+
</dict>
|
|
377
|
+
</dict>
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
For `Type=Variable` referencing a named variable instead of an `ActionOutput`, replace the inner `Value` dict with:
|
|
381
|
+
|
|
382
|
+
```xml
|
|
383
|
+
<key>Value</key>
|
|
384
|
+
<dict>
|
|
385
|
+
<key>Type</key>
|
|
386
|
+
<string>Variable</string>
|
|
387
|
+
<key>VariableName</key>
|
|
388
|
+
<string>MyVarName</string>
|
|
389
|
+
</dict>
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Placement rules:**
|
|
393
|
+
- `WFInput` belongs on Mode 0 (If start) and on Mode 1 only when the action is an Otherwise If. Plain Otherwise (mode 1 with no condition fields) and End If (mode 2) carry only `GroupingIdentifier` + `WFControlFlowMode`.
|
|
394
|
+
- The `WFInput` field itself is a dict with `Type` = `"Variable"` and a nested `Variable` dict. Bare `WFTextTokenAttachment` (without the `Type`/`Variable` wrapper) imports as blank in the editor.
|
|
395
|
+
|
|
396
|
+
### "is between" (code 1003)
|
|
397
|
+
|
|
398
|
+
The between condition needs both a lower bound (`WFNumberValue`, a literal string) and an upper bound (`WFAnotherNumber`, a token attachment that can hold either a literal or a variable reference):
|
|
399
|
+
|
|
400
|
+
```xml
|
|
401
|
+
<dict>
|
|
402
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
403
|
+
<string>is.workflow.actions.conditional</string>
|
|
404
|
+
<key>WFWorkflowActionParameters</key>
|
|
405
|
+
<dict>
|
|
406
|
+
<key>GroupingIdentifier</key>
|
|
407
|
+
<string>IF-BETWEEN-UUID</string>
|
|
408
|
+
<key>WFControlFlowMode</key>
|
|
409
|
+
<integer>0</integer>
|
|
410
|
+
<key>WFCondition</key>
|
|
411
|
+
<integer>1003</integer>
|
|
412
|
+
<key>WFInput</key>
|
|
413
|
+
<dict>
|
|
414
|
+
<!-- Type=Variable wrapper around the value being tested -->
|
|
415
|
+
</dict>
|
|
416
|
+
<key>WFNumberValue</key>
|
|
417
|
+
<string>1</string>
|
|
418
|
+
<key>WFAnotherNumber</key>
|
|
419
|
+
<dict>
|
|
420
|
+
<key>Value</key>
|
|
421
|
+
<dict>
|
|
422
|
+
<key>OutputName</key>
|
|
423
|
+
<string>Number</string>
|
|
424
|
+
<key>OutputUUID</key>
|
|
425
|
+
<string>UPPER-BOUND-UUID</string>
|
|
426
|
+
<key>Type</key>
|
|
427
|
+
<string>ActionOutput</string>
|
|
428
|
+
</dict>
|
|
429
|
+
<key>WFSerializationType</key>
|
|
430
|
+
<string>WFTextTokenAttachment</string>
|
|
431
|
+
</dict>
|
|
432
|
+
</dict>
|
|
433
|
+
</dict>
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Multi-condition If (Any are true / All are true)
|
|
437
|
+
|
|
438
|
+
Apple's modern Shortcuts conditional supports a single If block testing **multiple conditions at once**, joined by either "Any are true" (OR) or "All are true" (AND). This is encoded as `WFConditions` with serialization type `WFContentPredicateTableTemplate`, NOT as a sequence of separate If blocks.
|
|
439
|
+
|
|
440
|
+
**When to use:** the user explicitly asks for "if A or B", "if any of these are true", "if all of these match", or similar combinatorial logic. Otherwise, chain single-condition Ifs.
|
|
441
|
+
|
|
442
|
+
**Top-level structure:** The action sets ONLY `WFConditions`, `WFControlFlowMode`, and `GroupingIdentifier`. There is **no** top-level `WFCondition`, `WFInput`, `WFConditionalActionString`, or `WFNumberValue` — those live inside each template. The validator rejects mixing the two patterns.
|
|
443
|
+
|
|
444
|
+
```xml
|
|
445
|
+
<dict>
|
|
446
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
447
|
+
<string>is.workflow.actions.conditional</string>
|
|
448
|
+
<key>WFWorkflowActionParameters</key>
|
|
449
|
+
<dict>
|
|
450
|
+
<key>GroupingIdentifier</key>
|
|
451
|
+
<string>MULTI-IF-GROUP-UUID</string>
|
|
452
|
+
<key>WFControlFlowMode</key>
|
|
453
|
+
<integer>0</integer>
|
|
454
|
+
<key>WFConditions</key>
|
|
455
|
+
<dict>
|
|
456
|
+
<key>Value</key>
|
|
457
|
+
<dict>
|
|
458
|
+
<key>WFActionParameterFilterPrefix</key>
|
|
459
|
+
<integer>0</integer>
|
|
460
|
+
<!--
|
|
461
|
+
WFActionParameterFilterPrefix:
|
|
462
|
+
0 = Any are true (OR — at least one row matches)
|
|
463
|
+
1 = All are true (AND — every row must match)
|
|
464
|
+
-->
|
|
465
|
+
<key>WFActionParameterFilterTemplates</key>
|
|
466
|
+
<array>
|
|
467
|
+
<!-- Row 1: string is "Yes" -->
|
|
468
|
+
<dict>
|
|
469
|
+
<key>WFCondition</key>
|
|
470
|
+
<integer>4</integer>
|
|
471
|
+
<key>WFConditionalActionString</key>
|
|
472
|
+
<string>Yes</string>
|
|
473
|
+
<key>WFInput</key>
|
|
474
|
+
<dict>
|
|
475
|
+
<key>Type</key>
|
|
476
|
+
<string>Variable</string>
|
|
477
|
+
<key>Variable</key>
|
|
478
|
+
<dict>
|
|
479
|
+
<key>Value</key>
|
|
480
|
+
<dict>
|
|
481
|
+
<key>OutputName</key>
|
|
482
|
+
<string>Text</string>
|
|
483
|
+
<key>OutputUUID</key>
|
|
484
|
+
<string>TEXT-VAR-UUID</string>
|
|
485
|
+
<key>Type</key>
|
|
486
|
+
<string>ActionOutput</string>
|
|
487
|
+
</dict>
|
|
488
|
+
<key>WFSerializationType</key>
|
|
489
|
+
<string>WFTextTokenAttachment</string>
|
|
490
|
+
</dict>
|
|
491
|
+
</dict>
|
|
492
|
+
</dict>
|
|
493
|
+
<!-- Row 2: string is not "No" -->
|
|
494
|
+
<dict>
|
|
495
|
+
<key>WFCondition</key>
|
|
496
|
+
<integer>5</integer>
|
|
497
|
+
<key>WFConditionalActionString</key>
|
|
498
|
+
<string>No</string>
|
|
499
|
+
<key>WFInput</key>
|
|
500
|
+
<dict>
|
|
501
|
+
<!-- Same Type=Variable wrapper -->
|
|
502
|
+
</dict>
|
|
503
|
+
</dict>
|
|
504
|
+
<!-- Row 3: number is greater than 1 -->
|
|
505
|
+
<dict>
|
|
506
|
+
<key>WFCondition</key>
|
|
507
|
+
<integer>2</integer>
|
|
508
|
+
<key>WFNumberValue</key>
|
|
509
|
+
<string>1</string>
|
|
510
|
+
<key>WFInput</key>
|
|
511
|
+
<dict>
|
|
512
|
+
<!-- Type=Variable wrapper pointing at a Number variable -->
|
|
513
|
+
</dict>
|
|
514
|
+
</dict>
|
|
515
|
+
</array>
|
|
516
|
+
<key>WFContentPredicateBoundedDate</key>
|
|
517
|
+
<false/>
|
|
518
|
+
</dict>
|
|
519
|
+
<key>WFSerializationType</key>
|
|
520
|
+
<string>WFContentPredicateTableTemplate</string>
|
|
521
|
+
</dict>
|
|
522
|
+
</dict>
|
|
523
|
+
</dict>
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
**Per-template rules** (each entry inside `WFActionParameterFilterTemplates`):
|
|
527
|
+
|
|
528
|
+
- Each template is an independent condition with its own `WFCondition`, `WFInput`, and the appropriate literal field (`WFConditionalActionString` for codes 4/5/8/9/99/999, `WFNumberValue` for codes 0/1/2/3, `WFNumberValue`+`WFAnotherNumber` for code 1003, no literal for codes 100/101).
|
|
529
|
+
- Each template's `WFInput` follows the same `Type=Variable` wrapper rule as the single-condition pattern, and can reference a different upstream variable than other templates in the same block.
|
|
530
|
+
- Templates inside the same multi-condition block don't need a `GroupingIdentifier` — the parent action holds it.
|
|
531
|
+
- Apple's table-template format may include `WFContentPredicateBoundedDate` (a boolean) for date-aware filters; emit `<false/>` unless the conditions involve dates.
|
|
532
|
+
|
|
533
|
+
Plain Otherwise / End If actions for a multi-condition block are identical to the single-condition pattern: just `GroupingIdentifier` + `WFControlFlowMode` (1 or 2). A macOS 27+ Otherwise If middle branch can carry condition fields on mode 1.
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## Choose from Menu
|
|
538
|
+
|
|
539
|
+
Present a menu of options and execute different actions based on the user's choice.
|
|
540
|
+
|
|
541
|
+
### Structure
|
|
542
|
+
| Mode | Action | Description |
|
|
543
|
+
|------|--------|-------------|
|
|
544
|
+
| 0 | Menu | Define menu with items |
|
|
545
|
+
| 1 | Case | One case per menu item |
|
|
546
|
+
| 2 | End Menu | Close menu |
|
|
547
|
+
|
|
548
|
+
### Template
|
|
549
|
+
|
|
550
|
+
```xml
|
|
551
|
+
<!-- Menu Definition -->
|
|
552
|
+
<dict>
|
|
553
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
554
|
+
<string>is.workflow.actions.choosefrommenu</string>
|
|
555
|
+
<key>WFWorkflowActionParameters</key>
|
|
556
|
+
<dict>
|
|
557
|
+
<key>GroupingIdentifier</key>
|
|
558
|
+
<string>MENU-GROUP-UUID</string>
|
|
559
|
+
<key>WFControlFlowMode</key>
|
|
560
|
+
<integer>0</integer>
|
|
561
|
+
<key>WFMenuPrompt</key>
|
|
562
|
+
<string>Choose an option:</string>
|
|
563
|
+
<key>WFMenuItems</key>
|
|
564
|
+
<array>
|
|
565
|
+
<string>Option 1</string>
|
|
566
|
+
<string>Option 2</string>
|
|
567
|
+
<string>Option 3</string>
|
|
568
|
+
</array>
|
|
569
|
+
</dict>
|
|
570
|
+
</dict>
|
|
571
|
+
|
|
572
|
+
<!-- Case 1: Option 1 -->
|
|
573
|
+
<dict>
|
|
574
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
575
|
+
<string>is.workflow.actions.choosefrommenu</string>
|
|
576
|
+
<key>WFWorkflowActionParameters</key>
|
|
577
|
+
<dict>
|
|
578
|
+
<key>GroupingIdentifier</key>
|
|
579
|
+
<string>MENU-GROUP-UUID</string>
|
|
580
|
+
<key>WFControlFlowMode</key>
|
|
581
|
+
<integer>1</integer>
|
|
582
|
+
<key>WFMenuItemTitle</key>
|
|
583
|
+
<string>Option 1</string>
|
|
584
|
+
</dict>
|
|
585
|
+
</dict>
|
|
586
|
+
|
|
587
|
+
<!-- Actions for Option 1 go here -->
|
|
588
|
+
|
|
589
|
+
<!-- Case 2: Option 2 -->
|
|
590
|
+
<dict>
|
|
591
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
592
|
+
<string>is.workflow.actions.choosefrommenu</string>
|
|
593
|
+
<key>WFWorkflowActionParameters</key>
|
|
594
|
+
<dict>
|
|
595
|
+
<key>GroupingIdentifier</key>
|
|
596
|
+
<string>MENU-GROUP-UUID</string>
|
|
597
|
+
<key>WFControlFlowMode</key>
|
|
598
|
+
<integer>1</integer>
|
|
599
|
+
<key>WFMenuItemTitle</key>
|
|
600
|
+
<string>Option 2</string>
|
|
601
|
+
</dict>
|
|
602
|
+
</dict>
|
|
603
|
+
|
|
604
|
+
<!-- Actions for Option 2 go here -->
|
|
605
|
+
|
|
606
|
+
<!-- Case 3: Option 3 -->
|
|
607
|
+
<dict>
|
|
608
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
609
|
+
<string>is.workflow.actions.choosefrommenu</string>
|
|
610
|
+
<key>WFWorkflowActionParameters</key>
|
|
611
|
+
<dict>
|
|
612
|
+
<key>GroupingIdentifier</key>
|
|
613
|
+
<string>MENU-GROUP-UUID</string>
|
|
614
|
+
<key>WFControlFlowMode</key>
|
|
615
|
+
<integer>1</integer>
|
|
616
|
+
<key>WFMenuItemTitle</key>
|
|
617
|
+
<string>Option 3</string>
|
|
618
|
+
</dict>
|
|
619
|
+
</dict>
|
|
620
|
+
|
|
621
|
+
<!-- Actions for Option 3 go here -->
|
|
622
|
+
|
|
623
|
+
<!-- End Menu -->
|
|
624
|
+
<dict>
|
|
625
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
626
|
+
<string>is.workflow.actions.choosefrommenu</string>
|
|
627
|
+
<key>WFWorkflowActionParameters</key>
|
|
628
|
+
<dict>
|
|
629
|
+
<key>GroupingIdentifier</key>
|
|
630
|
+
<string>MENU-GROUP-UUID</string>
|
|
631
|
+
<key>WFControlFlowMode</key>
|
|
632
|
+
<integer>2</integer>
|
|
633
|
+
</dict>
|
|
634
|
+
</dict>
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### Important Notes (from 127 real shortcuts analysis)
|
|
638
|
+
|
|
639
|
+
1. **Menu definition mode 0**: Uses `WFMenuItems` array containing all option strings
|
|
640
|
+
2. **Case mode 1 for each option**: One mode 1 action per menu item with exact `WFMenuItemTitle` match
|
|
641
|
+
3. **WFMenuItemTitle must match exactly** - Each case title must match the corresponding item in WFMenuItems exactly (case-sensitive)
|
|
642
|
+
4. **Order must match** - Case (mode 1) actions must appear in the same order as items in the WFMenuItems array
|
|
643
|
+
5. **One case per item** - You need exactly one mode 1 action for each menu item
|
|
644
|
+
6. **Close with mode 2** - End the menu structure with a mode 2 action (uses same GroupingIdentifier, no additional parameters)
|
|
645
|
+
|
|
646
|
+
---
|
|
647
|
+
|
|
648
|
+
## Nesting Control Flow
|
|
649
|
+
|
|
650
|
+
Control flow blocks can be nested to arbitrary depth. Analysis of 127 real shortcuts confirms nesting up to **depth 7** in production shortcuts (AppRedirect.xml).
|
|
651
|
+
|
|
652
|
+
**Key rule:** Each nested block needs its own unique GroupingIdentifier:
|
|
653
|
+
|
|
654
|
+
```xml
|
|
655
|
+
<!-- Outer Repeat -->
|
|
656
|
+
<dict>
|
|
657
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
658
|
+
<string>is.workflow.actions.repeat.count</string>
|
|
659
|
+
<key>WFWorkflowActionParameters</key>
|
|
660
|
+
<dict>
|
|
661
|
+
<key>GroupingIdentifier</key>
|
|
662
|
+
<string>OUTER-LOOP-UUID</string>
|
|
663
|
+
<key>WFControlFlowMode</key>
|
|
664
|
+
<integer>0</integer>
|
|
665
|
+
<key>WFRepeatCount</key>
|
|
666
|
+
<integer>3</integer>
|
|
667
|
+
</dict>
|
|
668
|
+
</dict>
|
|
669
|
+
|
|
670
|
+
<!-- Inner Conditional -->
|
|
671
|
+
<dict>
|
|
672
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
673
|
+
<string>is.workflow.actions.conditional</string>
|
|
674
|
+
<key>WFWorkflowActionParameters</key>
|
|
675
|
+
<dict>
|
|
676
|
+
<key>GroupingIdentifier</key>
|
|
677
|
+
<string>INNER-IF-UUID</string>
|
|
678
|
+
<key>WFControlFlowMode</key>
|
|
679
|
+
<integer>0</integer>
|
|
680
|
+
<!-- condition params -->
|
|
681
|
+
</dict>
|
|
682
|
+
</dict>
|
|
683
|
+
|
|
684
|
+
<!-- Inner Otherwise -->
|
|
685
|
+
<dict>
|
|
686
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
687
|
+
<string>is.workflow.actions.conditional</string>
|
|
688
|
+
<key>WFWorkflowActionParameters</key>
|
|
689
|
+
<dict>
|
|
690
|
+
<key>GroupingIdentifier</key>
|
|
691
|
+
<string>INNER-IF-UUID</string>
|
|
692
|
+
<key>WFControlFlowMode</key>
|
|
693
|
+
<integer>1</integer>
|
|
694
|
+
</dict>
|
|
695
|
+
</dict>
|
|
696
|
+
|
|
697
|
+
<!-- Inner End If -->
|
|
698
|
+
<dict>
|
|
699
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
700
|
+
<string>is.workflow.actions.conditional</string>
|
|
701
|
+
<key>WFWorkflowActionParameters</key>
|
|
702
|
+
<dict>
|
|
703
|
+
<key>GroupingIdentifier</key>
|
|
704
|
+
<string>INNER-IF-UUID</string>
|
|
705
|
+
<key>WFControlFlowMode</key>
|
|
706
|
+
<integer>2</integer>
|
|
707
|
+
</dict>
|
|
708
|
+
</dict>
|
|
709
|
+
|
|
710
|
+
<!-- Outer End Repeat -->
|
|
711
|
+
<dict>
|
|
712
|
+
<key>WFWorkflowActionIdentifier</key>
|
|
713
|
+
<string>is.workflow.actions.repeat.count</string>
|
|
714
|
+
<key>WFWorkflowActionParameters</key>
|
|
715
|
+
<dict>
|
|
716
|
+
<key>GroupingIdentifier</key>
|
|
717
|
+
<string>OUTER-LOOP-UUID</string>
|
|
718
|
+
<key>WFControlFlowMode</key>
|
|
719
|
+
<integer>2</integer>
|
|
720
|
+
</dict>
|
|
721
|
+
</dict>
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
---
|
|
725
|
+
|
|
726
|
+
## Nothing and Exit Actions
|
|
727
|
+
|
|
728
|
+
Two special control flow actions have no parameters:
|
|
729
|
+
|
|
730
|
+
- **Nothing** (`is.workflow.actions.nothing`): Produces no output. Use as a placeholder inside branches that should intentionally return no result.
|
|
731
|
+
- **Exit Shortcut** (`is.workflow.actions.exit`): Immediately stops the shortcut. Empty parameters dict.
|
|
732
|
+
|
|
733
|
+
Both need only `WFWorkflowActionIdentifier` and an empty `WFWorkflowActionParameters` dict (no UUID needed).
|
|
734
|
+
|
|
735
|
+
---
|
|
736
|
+
|
|
737
|
+
## Common Mistakes
|
|
738
|
+
|
|
739
|
+
1. **Using string instead of integer for WFControlFlowMode**
|
|
740
|
+
- Wrong: `<string>0</string>`
|
|
741
|
+
- Right: `<integer>0</integer>`
|
|
742
|
+
|
|
743
|
+
2. **Mismatched GroupingIdentifier**
|
|
744
|
+
- All parts of a control flow block must share the same GroupingIdentifier
|
|
745
|
+
- Nested blocks MUST have distinct GroupingIdentifiers per level
|
|
746
|
+
- Confirmed working up to depth 7 (from AppRedirect.xml in 127-shortcut analysis)
|
|
747
|
+
|
|
748
|
+
3. **Missing End action**
|
|
749
|
+
- Every start (mode 0) must have a corresponding end (mode 2)
|
|
750
|
+
|
|
751
|
+
4. **Wrong order in menu cases**
|
|
752
|
+
- Cases must appear in the same order as WFMenuItems
|
|
753
|
+
|
|
754
|
+
5. **Referencing wrong UUID for loop items**
|
|
755
|
+
- Repeat Item uses the **start** action's UUID (OR `Type=Variable` with `VariableName="Repeat Item"` — preferred)
|
|
756
|
+
- Repeat Index uses `Type=Variable` with `VariableName="Repeat Index"` — NOT ActionOutput
|
|
757
|
+
|
|
758
|
+
6. **Using WFTextTokenAttachment for display parameters in control flow branches**
|
|
759
|
+
- Show Alert message, Notification body, and Show Result text MUST use `WFTextTokenString` (with `` placeholder + `attachmentsByRange`)
|
|
760
|
+
- Using `WFTextTokenAttachment` for these causes default/empty text at runtime
|
|
761
|
+
|
|
762
|
+
7. **Omitting `WFInput` on conditional codes 0–3**
|
|
763
|
+
- All conditional codes — numeric, string, and existence — require an explicit `WFInput` as a `Type=Variable` wrapper. The earlier "implicit input for numeric conditions" rule was wrong; an Apple-built sample shortcut has explicit `WFInput` on every conditional, including codes 0, 1, 2, 3, and 1003.
|
|
764
|
+
|
|
765
|
+
8. **Confusing condition code semantics**
|
|
766
|
+
- Code `0` = `is less than` (NOT equals). Code `1` = `is less than or equal to`. Code `2` = `is greater than`. Code `3` = `is greater than or equal to`.
|
|
767
|
+
- Code `4` = string `is` (exact equality). Code `99` = string `contains` (substring). Using code 4 when you mean "contains" will only match the exact string.
|
|
768
|
+
|
|
769
|
+
9. **Mixing single-condition and multi-condition fields on one If**
|
|
770
|
+
- If you set `WFConditions` (multi-condition `WFContentPredicateTableTemplate`), do NOT also set top-level `WFCondition`, `WFInput`, or any literal field. The validator rejects this.
|
|
771
|
+
- Conversely, if you set top-level `WFCondition` + `WFInput`, do NOT set `WFConditions`. Pick one pattern per If.
|
|
772
|
+
|
|
773
|
+
10. **Forgetting `WFAnotherNumber` on `is between` (code 1003)**
|
|
774
|
+
- Code 1003 needs both `WFNumberValue` (lower bound, literal string) and `WFAnotherNumber` (upper bound, token attachment). Missing `WFAnotherNumber` imports as an empty upper-bound field.
|
|
775
|
+
|
|
776
|
+
11. **Treating Otherwise If as a separate action**
|
|
777
|
+
- macOS 27 serializes Otherwise If as `is.workflow.actions.conditional` with `WFControlFlowMode = 1` plus condition fields. Plain Otherwise is the same mode with no condition fields.
|