Slide 1

Slide 1 text

͋ͳͨ͸Ͳ͏ॻ͖ ͳͥͦ͏ॻ͘ͷ͔ʁ USZ4XJGU1SF5BMLT

Slide 2

Slide 2 text

౎಺ͷࣗࣾαʔϏε։ൃձࣾͷ J04ΤϯδχΞЋ TIJ[!TU[O TIJ[ ͣ͠ !TIJ[ TU[O TIJ[ ։ൃܦݧݴޠ 4XJGU,PUMJO+BWBTDSJQU1)1$+BWB

Slide 3

Slide 3 text

Ͳ͏΍ͬͯ ίʔυͷॻ͖ํΛ ܾΊ͍ͯ·͔͢ʁ

Slide 4

Slide 4 text

໌֬ͳཧ༝ ීஈͷ׳श ܾఆ ࢼߦࡨޡ ʜ ൑அ

Slide 5

Slide 5 text

ʁ

Slide 6

Slide 6 text

ϩάΛૹΔ YYYY QSPUPDPM

Slide 7

Slide 7 text

struct AnalyticsEvent { var name: String var metadata: [String: Any] = [:] init(name: String, metadata: [String: Any] = [:]) { self.name = name self.metadata = metadata } } class AnalyticsManager { private let engine: AnalyticsEngine init(engine: AnalyticsEngine) { self.engine = engine } func log(_ event: AnalyticsEvent) { engine.send(name: event.name, metadata: event.metadata) } } protocol AnalyticsEngine: class { func send(name: String, metadata: [String: Any]) } struct Book { let name: String } final class Library { private var library: [Book] = [] func add(_ book: Book) -> Book { library.append(book) return book } }

Slide 8

Slide 8 text

final class BookDetailViewController: UIViewController { func addBook(book: Book) { library.add(book) let event = AnalyticsEvent(name: "bookOdded") analytics.log(event) } } wλΠϓϛεΛ͢ΔՄೳੑ͕͋Δ wमਖ਼͕ෳ਺ՕॴʹೖΔ final class BookListViewController: UIViewController { func addBook(book: Book) { library.add(book) let event = AnalyticsEvent(name: "bookAdded") analytics.log(event) } }

Slide 9

Slide 9 text

FOVN

Slide 10

Slide 10 text

enum AnalyticsEvent { case bookAdded(Book) var name: String { switch self { case .bookAdded: return "bookAdded" } } var metadata: [String: Any] { switch self { case .bookAdded(let book): return ["book": book.name] } } } final class BookListViewController: UIViewController { func addBook(book: Book) { library.add(book) analytics.log(.bookAdded(book)) } } final class BookDetailViewController: UIViewController { func addBook(book: Book) { library.add(book) analytics.log(.bookAdded(book)) } } ɾλΠϓϛε͕ͳ͘ͳΔ ɾίϯύΠϧ࣌ʹνΣοΫ͕Ͱ͖Δ

Slide 11

Slide 11 text

enum AnalyticsEvent { case loginScreenViewed case loginAttempted case loginFailed(reason: LoginFailureReason) case loginSucceeded case bookListViewed case bookAdded(Book) case bookSelected(Book) case bookDeleted(Book) var name: String { switch self { case .loginScreenViewed, .loginAttempted, .loginSucceeded, .bookListViewed: return String(describing: self) case .loginFailed: return "loginFailed" case .bookAdded: return "bookAdded" case .bookSelected: return "bookSelected" case .bookDeleted: return "bookDeleted" } } var metadata: [String: Any] { switch self { case .loginScreenViewed, .loginAttempted, .loginSucceeded, .bookListViewed: return [:] case .loginFailed(let reason): return ["reason" : String(describing: reason)] case .bookAdded(let book): return ["book" : book.name] case .bookSelected(let index): return ["book" : book.name] case .bookDeleted(let book): return ["book" : book.name] } } } w4XJUDIจ͕େྔʹͳΔ wએݴͱม਺ͷఆ͕ٛ෼͔ΕΔ ͲΜͲΜ૿͍͑ͯ͘ɻɻɻ

Slide 12

Slide 12 text

Ϟδϡʔϧͷ෼ׂ͕Ͱ͖ͳ͍ Πϕϯτ͕૿͑Δ enumʹcaseΛ௥Ճ enumͷextensionͷमਖ਼ YYYY QSPUPDPM

Slide 13

Slide 13 text

struct

Slide 14

Slide 14 text

struct AnalyticsEvent { let name: String let metadata: [String : Any] private init(name: String, metadata: [String: Any] = [:]) { self.name = name self.metadata = metadata } } • ҰՕॴͰએݴͰ͖Δ • ݺͼग़͠ํ͸enumͷ࣌ͱมΘΒͳ͍ final class BookListViewController: UIViewController { func addBook(book: Book) { library.add(book) analytics.log(.bookList_BookAdded(book)) } } extension AnalyticsEvent { static func bookList_BookAdded(_ book: Book) -> AnalyticsEvent { return AnalyticsEvent( name: "bookList_BookAdded", metadata: ["book" : book.name] ) } }

Slide 15

Slide 15 text

Ϟδϡʔϧͷ෼ׂ͕Ͱ͖Δ extensionʹ͢Δ͜ͱͰଞʹӨڹΛ༩͑ͳ͍ YYYY QSPUPDPM

Slide 16

Slide 16 text

w໊લͷিಥ final class BookListViewController: UIViewController { func addBook(book: Book) { library.add(book) analytics.log(.bookList_BookAdded(book)) } } extension AnalyticsEvent { static func bookList_BookAdded(_ book: Book) -> AnalyticsEvent { return AnalyticsEvent( name: "bookList_BookAdded", metadata: ["book" : book.name] ) } static func bookDetail_BookAdded(_ book: Book) -> AnalyticsEvent { return AnalyticsEvent( name: “bookDetail_BookAdded", metadata: ["book" : book.name] ) } } final class BookDetailViewController: UIViewController { func addBook(book: Book) { library.add(book) analytics.log(.bookDetail_BookAdded(book)) } }

Slide 17

Slide 17 text

ݺͼग़͠ଆͰॳظԽͷํ๏Λ TUSVDUͷ౎߹ʹ߹ΘͤΔඞཁ͕͋Δ extension AnalyticsEvent { static func bookDetail_BookRead(_ book: Book, count: Int) -> AnalyticsEvent { return AnalyticsEvent( name: “bookDetail_BookAdded", metadata: ["book": book.name, “read_count”: count] ) } }

Slide 18

Slide 18 text

init(book: Book, count: Int) { self.init( name: "bookRead", metadata: ["book" : book.name, "read_count": count] ) } init(a: String) { self.init(name: a) } init(b: String) { self.init(name: b) } init(c: String) { self.init(name: c) } init(d: String) { self.init(name: d) } … DVTUPNJOJUΛఆٛͰ͖Δ͕ʜ ͲΜͲΜ૿͍͑ͯ͘ɻɻɻ

Slide 19

Slide 19 text

protocol

Slide 20

Slide 20 text

• ৽͍͠Πϕϯτͷ࡞੒͕༰қ(case΍extensionͷ௥Ճඞཁͳ͠) • ಠࣗͷΠχγϟϥΠβΛఆٛͰ͖ݺͼग़͠ଆͷෛ୲Λ૿΍͞ͳ͍ • enum΋struct΋class΋࢖͑Δ • Xcodeͷࣗಈิ׬΋໰୊ͳ͠ protocol AnalyticsEvent { var name: String { get } var metadata: [String: Any] { get } } struct BookReadErrorAnalyticsEvent: AnalyticsEvent { var name: String { return "BookReadError" } var metadata: [String: Any] { return ["code": reason.code, "description": reason.description] struct Reason { let code: Int let description: String } private let reason: Reason init(reason: Reason) { self.reason = reason } }

Slide 21

Slide 21 text

final class ProductionAnalyticsEngine: AnalyticsEngine { func log(_ event: AnalyticsEvent) { let name = "Production-\(event.name)" AnalyticsAPI.send(name: name, metadata: event.metadata) } } final class DevAnalyticsEngine: AnalyticsEngine { func log(_ event: AnalyticsEvent) { let name = "Dev-\(event.name)" AnalyticsAPI.send(name: name, metadata: event.metadata) } } final class StagingAnalyticsEngine: AnalyticsEngine { func log(_ event: AnalyticsEvent) { let name = "Staging-\(event.name)" AnalyticsAPI.send(name: name, metadata: event.metadata) } } final class TestAnalyticsEngine: AnalyticsEngine { func log(_ event: AnalyticsEvent) { let name = "Test-\(event.name)" AnalyticsAPI.send(name: name, metadata: event.metadata) } } ಉ͡Α͏ͳܕͷఆ͕ٛ૿͍͑ͯ͘

Slide 22

Slide 22 text

• ܕΛఆٛ͢Δඞཁ͕ͳ͘ͳΔ • ΧελϚΠζ͕ࣗ༝ʹͳΔ struct AnalyticsEngine { let log: (Event) -> Void init(log: @escaping (Event) -> Void) { self.log = log } } final class BookListViewController: UIViewController { private let engine: AnalyticsEngine } let prefix = "Production-" let bookListEngine = AnalyticsEngine { event in let name = "\(prefix)\(event.name)" AnalyticsAPI.send(name: name, metadata: event.metadata) } let collection = Library() let vc = BookListViewController(library: collection, engine: bookListEngine) let bookDetailEngine = AnalyticsEngine { event in let name = "\(prefix)\(event.name)" AnalyticsAPI.send(name: name, metadata: event.metadata) }

Slide 23

Slide 23 text

struct MultipleAnalyticsEngine { let log: ([Event]) -> Void init(log: @escaping ([Event]) -> Void) { self.log = log } } let multiEngine = MultipleAnalyticsEngine { events in events.forEach { AnalyticsAPI.send(name: $0.name, metadata: $0.metadata) } } let book = Book(name: "ΧϥϚʔκϑͷܑఋ") multiEngine.log([BookListAnalyticsEvent.bookAdded(book), BookDetailAnalyticsEvent.bookAdded(book)]) struct MultipleAnalyticsEngine { let log: ([AnalyticsEvent]) -> Void init(log: @escaping ([AnalyticsEvent]) -> Void) { self.log = log } } let book = Book(name: "ΧϥϚʔκϑͷܑఋ") multiEngine.log([BookListAnalyticsEvent.bookAdded(book), BookListAnalyticsEvent.bookAdded(book)]) ຊ౰͸BookDetailAnalyticsEvent.bookAdded(book)])ΛૹΓ͔ͨͬͨɻɻɻ

Slide 24

Slide 24 text

ͦ΋ͦ΋

Slide 25

Slide 25 text

protocol AnalyticsEvent { var name: String { get } var metadata: [String: Any] { get } } "OBMZUJDTʹΑͬͯ͸ ࠶ม׵ͷඞཁ͕ग़ͯ͘Δ

Slide 26

Slide 26 text

ʁ

Slide 27

Slide 27 text

·ͱΊ

Slide 28

Slide 28 text

-FU`T USZBOEFSSPS -FU`T FOKPZUSZTXJGU

Slide 29

Slide 29 text

͋Γ͕ͱ͏ ͍͟͝·ͨ͠ ʁ

Slide 30

Slide 30 text

ࢀরϦϯΫ IUUQTXXXTXJGUCZTVOEFMMDPNQPTUTCVJMEJOHBOFOVNCBTFEBOBMZUJDT TZTUFNJOTXJGU IUUQTNBUUEJFQIPVTFDPNXIFOOPUUPVTFBOFOVN IUUQTEBWFEFMPOHDPNCMPHNJTVTJOHFOVNT IUUQTXXXZPVUVCFDPNXBUDI W1OR+J+7D1