@@ -67,6 +67,26 @@ let client = try SlackWebhookClient.create(
6767try await client.send (Message (text : " Hello, Slack!" ))
6868```
6969
70+ ## Convenience Builder API
71+
72+ SlackKit includes a builder API for cleaner, more readable message construction:
73+
74+ ``` swift
75+ // Clean, declarative syntax
76+ let message = Message {
77+ Header (" Deployment Complete!" )
78+ Section (" Build *#123* was deployed to *production*" )
79+ Divider ()
80+ SectionBlock (
81+ fields : [
82+ .markdown (" *Environment:*\n Production" ),
83+ .markdown (" *Version:*\n v2.4.1" )
84+ ]
85+ )
86+ }
87+ try await client.send (message)
88+ ```
89+
7090## Usage
7191
7292### Simple Text Message
@@ -79,52 +99,45 @@ try await client.send(message)
7999### Message with Blocks
80100
81101``` swift
82- let message = Message (
83- username : " DeployBot" ,
84- iconEmoji : " :rocket:" ,
85- blocks : [
86- HeaderBlock (text : " Deployment Complete!" ),
87-
88- SectionBlock (
89- text : .markdown (" Build *#123* was deployed to *production*" )
90- ),
102+ let message = Message {
103+ Header (" Deployment Complete!" )
104+ Section (markdown : " Build *#123* was deployed to *production*" )
105+ Divider ()
106+
107+ Section {
108+ Field.markdown (" *Environment:*\n Production" )
109+ Field.markdown (" *Version:*\n v2.4.1" )
110+ Field.markdown (" *Duration:*\n 5m 32s" )
111+ Field.markdown (" *Status:*\n :white_check_mark: Success" )
112+ }
113+ }
114+ try await client.send (message)
115+ ```
91116
92- DividerBlock (),
117+ ** With custom username and icon: **
93118
94- SectionBlock (
95- fields : [
96- .markdown (" *Environment:*\n Production" ),
97- .markdown (" *Version:*\n v2.4.1" ),
98- .markdown (" *Duration:*\n 5m 32s" ),
99- .markdown (" *Status:*\n :white_check_mark: Success" )
100- ]
101- )
102- ]
103- )
119+ ``` swift
120+ let message = Message (
121+ username : " DeployBot" ,
122+ iconEmoji : " :rocket:"
123+ ) {
124+ Header (" Deployment Complete!" )
125+ Section (" Build *#123* was deployed to *production*" )
126+ Divider ()
127+ }
104128try await client.send (message)
105129```
106130
107131### Message with Actions
108132
109133``` swift
110- let message = Message (
111- text : " Approval required for production deployment" ,
112- blocks : [
113- SectionBlock (text : .plainText (" Deploy to production?" )),
114- ActionsBlock (elements : [
115- ButtonElement (
116- text : .plainText (" Approve" ),
117- style : .primary ,
118- value : " approve"
119- ),
120- ButtonElement (
121- text : .plainText (" Reject" ),
122- style : .danger ,
123- value : " reject"
124- )
125- ])
126- ]
127- )
134+ let message = Message (text : " Approval required for production deployment" ) {
135+ Section (" Deploy to production?" )
136+ Actions {
137+ ButtonElement (text : .plainText (" Approve" ), style : .primary , value : " approve" )
138+ ButtonElement (text : .plainText (" Reject" ), style : .danger , value : " reject" )
139+ }
140+ }
128141try await client.send (message)
129142```
130143
@@ -165,85 +178,93 @@ try await client.send(message)
165178Text sections with optional fields:
166179
167180``` swift
168- SectionBlock (
169- text : .markdown (" Some *formatted* text" ),
170- fields : [
171- .markdown (" *Field 1*\n Value 1" ),
172- .markdown (" *Field 2*\n Value 2" )
173- ]
174- )
181+ Section (" Some *formatted* text" )
182+ // Or with markdown
183+ Section (markdown : " Some *formatted* text" )
184+ ```
185+
186+ With fields using the result builder:
187+
188+ ``` swift
189+ Section {
190+ Field.markdown (" *Field 1*\n Value 1" )
191+ Field.plainText (" Field 2" )
192+ }
175193```
176194
177195### Header Block
178196
179197Large header text:
180198
181199``` swift
182- HeaderBlock ( text : " Important Announcement" )
200+ Header ( " Important Announcement" )
183201```
184202
185203### Divider Block
186204
187205Horizontal line divider:
188206
189207``` swift
190- DividerBlock ()
208+ Divider ()
191209```
192210
193211### Image Block
194212
195213Display an image:
196214
197215``` swift
198- ImageBlock (
199- imageURL : URL (string : " https://example.com/image.png" )! ,
200- altText : " An example image" ,
201- title : .plainText (" Image Title" )
202- )
216+ Image (url : " https://example.com/image.png" , altText : " An example image" )
203217```
204218
205219### Actions Block
206220
207221Interactive buttons:
208222
209223``` swift
210- ActionsBlock (elements : [
211- ButtonElement (
212- text : .plainText (" Click Me" ),
213- actionID : " button_1" ,
214- value : " button_value" ,
215- style : .primary
216- )
217- ])
224+ Actions {
225+ ButtonElement (text : .plainText (" Click Me" ), actionID : " button_1" , value : " button_value" , style : .primary )
226+ }
227+ ```
228+
229+ The builder also supports conditionals and loops:
230+
231+ ``` swift
232+ Actions {
233+ ButtonElement (text : .plainText (" Approve" ), actionID : " approve" , value : " yes" )
234+
235+ if needsReview {
236+ ButtonElement (text : .plainText (" Request Review" ), actionID : " review" , value : " review" )
237+ }
238+
239+ for option in options {
240+ ButtonElement (text : .plainText (option), actionID : " opt_\( option ) " , value : option)
241+ }
242+ }
218243```
219244
220245### Context Block
221246
222- Contextual information with images and text :
247+ Contextual information with text and images :
223248
224249``` swift
225- ContextBlock (elements : [
226- TextContextElement (text : " Created by @john" ),
227- ImageContextElement (
228- imageURL : " https://example.com/avatar.png" ,
229- altText : " Avatar"
230- )
231- ])
250+ // Simple text context
251+ Context (" Created by @john" , " 2 minutes ago" )
252+
253+ // Or with elements using the builder
254+ Context {
255+ TextContextElement (text : " Created by @john" )
256+ ImageContextElement (imageURL : " https://example.com/avatar.png" , altText : " Avatar" )
257+ }
232258```
233259
234260### Input Block (Modals)
235261
236262Input blocks for collecting user input in modals:
237263
238264``` swift
239- InputBlock (
240- label : .plainText (" Task description" ),
241- element : PlainTextInputElement (
242- placeholder : " Enter task details..." ,
243- multiline : true
244- ),
245- hint : .plainText (" Be specific about the requirements" ),
246- optional : false
265+ Input (
266+ label : " Task description" ,
267+ element : PlainTextInputElement (placeholder : " Enter task details..." , multiline : true )
247268)
248269```
249270
@@ -263,26 +284,19 @@ ButtonElement(
263284### Select Menu
264285
265286``` swift
266- StaticSelectElement (
267- placeholder : .plainText (" Choose an option" ),
268- options : [
269- Option (text : .plainText (" Option 1" ), value : " opt1" ),
270- Option (text : .plainText (" Option 2" ), value : " opt2" )
271- ]
272- )
287+ StaticSelectElement (placeholder : .plainText (" Choose an option" )) {
288+ Option (text : .plainText (" Option 1" ), value : " opt1" )
289+ Option (text : .plainText (" Option 2" ), value : " opt2" )
290+ }
273291```
274292
275293### Multi-Select Menu
276294
277295``` swift
278- MultiStaticSelectElement (
279- placeholder : .plainText (" Select options" ),
280- options : [
281- Option (text : .plainText (" Option 1" ), value : " opt1" ),
282- Option (text : .plainText (" Option 2" ), value : " opt2" )
283- ],
284- maxSelectedItems : 3
285- )
296+ MultiStaticSelectElement (placeholder : .plainText (" Select options" ), maxSelectedItems : 3 ) {
297+ Option (text : .plainText (" Option 1" ), value : " opt1" )
298+ Option (text : .plainText (" Option 2" ), value : " opt2" )
299+ }
286300```
287301
288302### Date Picker
0 commit comments