---
title: 「この計測、どこで送ってる？」をなくす
tags: 
author: [小清水健人](https://image.docswell.com/user/_take_hito_)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/L71YGLL4JG.jpg?width=480
description: extension DC 2026 Day1 発表資料
published: September 18, 26
canonical: https://image.docswell.com/s/_take_hito_/5E1VJJ-2026-09-18-201236
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/L71YGLL4JG.jpg)

「この計測、どこで送ってる？」をなくす
Swift Macros による宣⾔的トラッキング
extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 2

![Page Image](https://bcdn.docswell.com/page/G7WGKVVZE2.jpg)

この計測どこで送ってる？
記述に⾃由があると、実は誤った実装をしてしまう
2
extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 3

![Page Image](https://bcdn.docswell.com/page/4JZLZ44LE3.jpg)

書ける場所が多すぎる
計測処理をどこに書いても、コンパイルはできる
func purchase() {
// ① 先頭
guard let item = selectedItem else { return }
// ② guard の後
presenter.purchase(item)
// ③ 末尾
}
3
extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 4

![Page Image](https://bcdn.docswell.com/page/YE6WZ33MEV.jpg)

誤った実装1: ⽋測する
必ず呼びたいので末尾に実装した。
guard が追加されると計測ができなくなる。
func purchase() {
guard let item = selectedItem else { return }
presenter.purchase(item)
Analytics.track(event: &quot;purchase_requested&quot;)
}
4
extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 5

![Page Image](https://bcdn.docswell.com/page/GE5MWLLQE4.jpg)

誤った実装2: 過剰に出る
処理の前処理として計測がしたいので先頭に実装した。
guard が追加されると、処理が実⾏されなくても計測されてしまう。
func purchase() {
Analytics.track(event: &quot;purchase_completed&quot;)
guard let item = selectedItem else { return }
guard item.isAvailable else { return }
presenter.purchase(item)
}
5
extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 6

![Page Image](https://bcdn.docswell.com/page/9729QLLWJR.jpg)

誤った実装3: 失敗が成功になる
エラーの場合だけ計測したい。
defer を追加すると throw で抜けても計測してしまう。
func purchase(_ item: Item) throws -&gt; Receipt {
defer { Analytics.track(event: &quot;purchase_completed&quot;) }
return try charge(item)
}
6
extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 7

![Page Image](https://bcdn.docswell.com/page/DJY4WQQ97M.jpg)

こう書きたい
送るタイミングを when で宣⾔する。
Macroが期待するタイミングで処理を展開する。
@Tracked(event: &quot;purchase_completed&quot;, when: .onSuccess)
func purchase() {
guard let item = selectedItem else { return }
guard item.isAvailable else { return }
presenter.purchase(item)
}
7
extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 8

![Page Image](https://bcdn.docswell.com/page/V7NY9KKDE8.jpg)

作ったマクロ
@attached(body)
macro Tracked(event: String, when: When)
// 関数の中⾝を書き換えるマクロ
// When は4つの case
// - .before
// - .after
// - .onSuccess
// - .onFailure
@Tracked(event: &quot;purchase_completed&quot;, when: .onSuccess)
func purchase(_ item: Item) -&gt; Receipt { ... }
8
extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 9

![Page Image](https://bcdn.docswell.com/page/YJ9P2VV873.jpg)

9
Quiz
extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 10

![Page Image](https://bcdn.docswell.com/page/GJ8D5PPZJD.jpg)

Quiz
指定⽅法で、実装がどう差し替わるか予想してみてください。
• .before: 前処理
• .after: 後処理
• .onSuccess: 処理が成功した場合
• .onFailure: 処理が失敗した場合
10 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 11

![Page Image](https://bcdn.docswell.com/page/LJLMY111ER.jpg)

Quiz 1
11 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 12

![Page Image](https://bcdn.docswell.com/page/47MYNWW57W.jpg)

.before
メソッドが呼ばれた瞬間に記録したい。
@Tracked(event: &quot;view_home&quot;, when: .before)
func showHome() {
presenter.loadHome()
}
12 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 13

![Page Image](https://bcdn.docswell.com/page/P7R9DYYZE9.jpg)

.before
先頭に計測処理を挿⼊
func showHome() {
Analytics.track(event: &quot;view_home&quot;)
presenter.loadHome()
}
13 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 14

![Page Image](https://bcdn.docswell.com/page/PJXQ1GG17X.jpg)

Quiz 2
14 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 15

![Page Image](https://bcdn.docswell.com/page/3JK9244MJD.jpg)

.after
途中で return しても、抜けるときに必ず記録したい。
@Tracked(event: &quot;purchase_requested&quot;, when: .after)
func purchase() {
guard let item = selectedItem else { return }
presenter.purchase(item)
}
15 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 16

![Page Image](https://bcdn.docswell.com/page/LE3W4LL2E5.jpg)

.after
defer で囲む。スコープを出るときに必ず⾛る。
func purchase() {
defer {
Analytics.track(event: &quot;purchase_requested&quot;)
}
guard let item = selectedItem else { return }
presenter.purchase(item)
}
16 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 17

![Page Image](https://bcdn.docswell.com/page/8EDKQNN67G.jpg)

Quiz 3
17 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 18

![Page Image](https://bcdn.docswell.com/page/V7PKL5NZJ8.jpg)

.onSuccess
最後まで到達したときだけ記録したい。
@Tracked(event: &quot;purchase_completed&quot;, when: .onSuccess)
func purchase(_ item: Item) -&gt; Receipt? {
guard item.isAvailable else { return nil }
let receipt = charge(item)
return receipt
}
18 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 19

![Page Image](https://bcdn.docswell.com/page/2JVVQPKMJQ.jpg)

.onSuccess
フラグを⽴てて、defer の中で判定する。
func purchase(_ item: Item) -&gt; Receipt? {
var didComplete = false
defer {
if didComplete {
Analytics.track(event: &quot;purchase_completed&quot;)
}
}
guard item.isAvailable else { return nil }
let receipt = charge(item)
didComplete = true
return receipt
}
19 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 20

![Page Image](https://bcdn.docswell.com/page/5EGLWPQXJL.jpg)

Quiz 4
20 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 21

![Page Image](https://bcdn.docswell.com/page/4JQY32K57P.jpg)

.onFailure
throw したときだけ記録したい。
@Tracked(event: &quot;purchase_failed&quot;, when: .onFailure)
func purchase(_ item: Item) throws -&gt; Receipt {
return try charge(item)
}
21 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 22

![Page Image](https://bcdn.docswell.com/page/K74W1LPVE1.jpg)

.onFailure
do-catch で包み、記録してから再 throw する。
func purchase(_ item: Item) throws -&gt; Receipt {
do {
return try charge(item)
} catch {
Analytics.track(event: &quot;purchase_failed&quot;)
throw error
}
}
22 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 23

![Page Image](https://bcdn.docswell.com/page/LJ1YGL94EG.jpg)

運⽤して出た課題
実際に運⽤してみて困ったこと。
23 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 24

![Page Image](https://bcdn.docswell.com/page/GJWGKVQZ72.jpg)

同じメソッドに⼆度付けできない
同じメソッドにマクロを2つ付けたくなる。
ただし body マクロは1関数に1つまで（SE-0415）。
@Recorded(id: 1024, when: .after)
@Tracked(event: &quot;purchase_requested&quot;, when: .after)
func purchase() {
startPurchase()
}
24 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 25

![Page Image](https://bcdn.docswell.com/page/4EZLZ4WL73.jpg)

同じメソッドに⼆度付けできない
コンパイルエラーにはならない。後ろの1つだけが展開される。
// 展開されたのは、後ろだけ
func purchase() {
defer {
Analytics.track(event: &quot;purchase_requested&quot;)
}
startPurchase()
}
25 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 26

![Page Image](https://bcdn.docswell.com/page/Y76WZ3GM7V.jpg)

まとめ
• 宣⾔的な記述によって、誤った実装を潰すことができる
• 展開後の実装がイメージできていればマクロの実装はできる
• body マクロの⼆度付けは後ろの1つだけが展開される
26 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


# Page. 27

![Page Image](https://bcdn.docswell.com/page/G75MWLVQ74.jpg)

ありがとうございました
27 extension DC 2026 Day1 / ⼩清⽔健⼈ @_take_hito_


