$30 off During Our Annual Pro Sale. View Details »
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
SUGARのアーキテクチャー / SUGAR ARCHITECTURE 20190425
Search
sugitani
April 25, 2019
Technology
2
2.1k
SUGARのアーキテクチャー / SUGAR ARCHITECTURE 20190425
2019/4/25に行われた「突撃!!隣のアーキテクチャ」
https://totsugeki-architecture.connpass.com/event/125845/
での発表資料です
sugitani
April 25, 2019
Tweet
Share
Other Decks in Technology
See All in Technology
NIKKEI Tech Talk #41: セキュア・バイ・デザインからクラウド管理を考える
sekido
PRO
0
210
ESXi のAIOps だ!2025冬
unnowataru
0
370
業務の煩悩を祓うAI活用術108選 / AI 108 Usages
smartbank
9
11k
日本の AI 開発と世界の潮流 / GenAI Development in Japan
hariby
1
470
モダンデータスタックの理想と現実の間で~1.3億人Vポイントデータ基盤の現在地とこれから~
taromatsui_cccmkhd
2
270
TED_modeki_共創ラボ_20251203.pdf
iotcomjpadmin
0
150
「もしもデータ基盤開発で『強くてニューゲーム』ができたなら今の僕はどんなデータ基盤を作っただろう」
aeonpeople
0
250
LayerX QA Night#1
koyaman2
0
260
Identity Management for Agentic AI 解説
fujie
0
470
オープンソースKeycloakのMCP認可サーバの仕様の対応状況 / 20251219 OpenID BizDay #18 LT Keycloak
oidfj
0
180
Bedrock AgentCore Memoryの新機能 (Episode) を試してみた / try Bedrock AgentCore Memory Episodic functionarity
hoshi7_n
2
1.9k
20251219 OpenIDファウンデーション・ジャパン紹介 / OpenID Foundation Japan Intro
oidfj
0
500
Featured
See All Featured
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
49
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
190
Designing for humans not robots
tammielis
254
26k
Designing Experiences People Love
moore
143
24k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
5
680
GitHub's CSS Performance
jonrohan
1032
470k
Information Architects: The Missing Link in Design Systems
soysaucechin
0
720
A Tale of Four Properties
chriscoyier
162
23k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
2
2.8k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
How GitHub (no longer) Works
holman
316
140k
Transcript
46("3ͷΞʔΩςΫνϟ ಥܸʂʂྡͷΞʔΩςΫνϟ !TVHJUBOJ " 3 $ ) * 5 &
$ 5 6 3 &
લઆ
ࣗݾհ • ਿ୩ͱਃ͠·͢ • ܦྺ • υϫϯΰ χίχίੜ์ૹ • ηϓςʔχɾΦϦδφϧ
$50 ("/." • 46("3$50 46("3 !TVHJUBOJ
46("3ͱʁ • ஶ໊ਓʹಛԽͨ͠ϥΠϒ৴Ξ ϓϦ • ొͯ͠ΞʔςΟετΛϑΥϩʔ͠ ͓ͯ͘ͱి $BMM,JU ͕དྷΔ •
Ұఆ͕ϐοΫΞοϓ͞ΕબΤϦ ΞʹೖΔʢ௨শ͓ण࢘Ϩʔϯʣ • ΞʔςΟετ͕௨ରΛબͿͱ௨ ͕Ͱ͖Δ ଞͷਓͦͷ༷ࢠΛ ோΊΔ • ৴ऀ͕εΰ͘ͳ͚ΕΓཱ ͨͳ͍ • ৴ऀ͕Ϡό͍
ΞʔΩςΫνϟհ͡Ί·͢ ˞͝ҙ˞ %%%ͳͲ͍ΖΜͳ୯ޠ͕Ͱ͖ͯ·͕͢ ຊϓϨθϯςʔγϣϯฐࣾͷݱঢ়ͷհʹա͗ͣ Կ͔͠Βͷํ๏ͷਖ਼͍࣮͠ફํ๏Λࣔ͢ͷͰ͋Γ·ͤΜ
࠾༻ΞʔΩςΫνϟͱհൣғ 4DBMBαʔό 4XJGUJ04 ,PUMJO"OESPJE ͥΜͿΔ γεςϜΞʔΩςΫνϟ %*1Λ׆༻͢ΔҰʁ γεςϜΞʔΩςΫνϟ %*1Λ׆༻͢ΔҰʁ γεςϜΞʔΩςΫνϟ
%*1Λ׆༻͢ΔҰʁ (6*ΞʔΩςΫνϟ ແ͠ +40/ग़͚ͩ͢ (6*ΞʔΩςΫνϟ .7$͔ͳ͊ʜʁ (6*ΞʔΩςΫνϟ .77.ͩͱࢥͬͯΔ
ͲΕجຊతʹߏҰॹ TSD EPNBJO BQQMJDBUJPO BEBQUFS WJFX *OGSBTUSVDUVSF
Πϝʔδ͓ͳ͡Έͷԁܗ %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF
%PNBJO %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF %%%Ͱੜ͞ΕΔ֓೦ͷॅ·͏ॴ &OUJUZ7BMVF0CKFDU4FSWJDF 3FQPTJUPSZͷఆٛ
USBJUJOUFSGBDFQSPUPDPM Λஔ͘
"QQMJDBUJPO %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF υϝΠϯͷొਓΛ ҙຯͷ͋Δॲཧʹ·ͱΊΔΫϥεͷॅ·͏ॴ *OKFDU͞Εͨ%PNBJOΠϯελϯεΛͬͯͻͱ·ͱ·Γ
ͷॲཧʢྫσʔλΛ3FQPTJUPSZ͔ΒݺΜͰ4FSWJDFʹ௨ ͢ͳͲՃ͔ͯ͠Βอଘ͢Δ Λߦ͏࣮͔ఆٛΛஔ͘
"EBQUFS %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF υϝΠϯͷ࣮ͷॅ·͏ॴ υϝΠϯͷఆٛΛܧঝ࣮ͨ͠ʢ%#ʹ ΞΫηε͢Δ3FQPTJUPSZʣΛஔ͘
7JFX ͦͷଞʣ %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF 6*ͱίϯτϩʔϥ 6*7JFX$POUSPMMFS6*7JFX"DUJWJUZ'SB
HNFOU QMBZͷ $POUSPMMFSͳͲݟͨ ͱૢ࡞ͷ࢝·Γ͔ΒऴΘΓ·Ͱͷ੍ޚʹ ؔΘΔ࣮Λஔ͖·͢ɻ
*OGSBTUSVDUVSF %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF ൚༻తͳͷஔ͖ จࣈྻ࣌ࠁૢ࡞ϢʔςΟϦςΟɺ"84 ͷԿ͔͠ΒΛ͍͘͢ϥοϓͨ͠ͷ
ͷͲ͜Ͱ͑ͦ͏ͳ࣮Λஔ͘ɻ
ଆ͚ͩར༻Ͱ͖Δ %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF ґଘੑٯసͷݪଇΛ׆༻ %PNBJOr 7JFXɺ"EBQUFSɺ"QQMJDBUJPOΛར༻
JNQPSU Ͱ͖ͳ͍ "QQMJDBUJPOr %PNBJOΛར༻Ͱ͖Δɻ 7JFXɺ"EBQUFSΛར༻Ͱ͖ͳ͍ "EBQUFSr"QQMJDBUJPOɺ%PNBJOΛར༻Ͱ͖Δɻ7JFXΛར༻Ͱ͖ͳ͍ 7JFXr "EBQUFSɺ"QQMJDBUJPOɺ%PNBJOΛར༻Ͱ͖Δ *OGSBTUSVDUVSFr ଞΛར༻Ͱ͖ͳ͍ɻଞ͔Βར༻͞ΕΔ
%PNBJOͷਂ۷Γ
%PNBJOͷํ • υϝΠϯۦಈઃܭͰϞσϦϯάΛߦ͏ • &SJDຊʹͰ͖Δ͚ͩ४ڌͯ͠ݕ౼͢Δ • ʹग़ͯٞ͢͠Δ • ݪଇఆٛ USBJUJOUFSGBDFQSPUPDPM
Λஔ͘ • γϯϓϧͳ7BMVF0CKFDUʹྫ֎͋Δ • طͷΫϥε63-ΫϥεΛ͔ͭͬͨΓ • UZQFBMJBT'PP4USJOH TXJGULPUMJO "OZ7BM TDBMB Ͱදݱͨ͠Γ • &OVNͬͨΓ • ಉ͡ଘࡏʹରͯ͠αʔόͱΫϥΠΞϯτͰಉ͡ݴ༿ݟ͑ํʹ ͳΔͱݶΒͳ͍͜ͱʹؾΛ͚ͭΔ
ʮΞΧϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 4DBMB trait EntityId { /* … */ }
trait Entity[T <: EntityId] { val id: T } trait UserId extends EntityId trait ImageResource { /* … */ } case class UserName(value: String) extends AnyVal trait RegisteredUser extends Entity[UserId]{ val name: UserName val icon: ImageResource val registeredDate: LocalDateTime }
ʮΞΧϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 4DBMB trait EntityId { /* … */ }
trait Entity[T <: EntityId] { val id: T } trait UserId extends EntityId trait ImageResource { /* … */ } case class UserName(value: String) extends AnyVal trait RegisteredUser extends Entity[UserId]{ val name: UserName val icon: ImageResource val registeredDate: LocalDateTime } ొࡁΈͰͳ͍Ϣʔβʔ͍ͩͬͯΔͷҙ
trait Entity { type ID <: EntityId; val id: ID
} case class User(id: UserId, …) extends Entity { type ID = UserId } 5JQTɿ&OUJUZͱ*% ར༻ྫ ͕ͧ͘͞Μᐌ͘ ҙຯ ৽نͰ࡞Δ߹ͪ͜Βʹ͓͖ͯ͠·͠ΐ͏ʂ https://twitter.com/gakuzzzz/status/1103240324120403968
ʮΞΧϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 4XJGU protocol EntityId { /* … */ }
protocol Entity { var entityId: EntityId { get } } protocol UserId: EntityId { } typealias UserName = String protocol Profile: Entity { var userId: UserId { get } var name: UserName { get } var icon: URL? { get } } extension Profile { var entityId: EntityId { return userId } }
ʮΞΧϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 4XJGU protocol EntityId { /* … */ }
protocol Entity { var entityId: EntityId { get } } protocol UserId: EntityId { } typealias UserName = String protocol Profile: Entity { var userId: UserId { get } var name: UserName { get } var icon: URL? { get } } extension Profile { var entityId: EntityId { return userId } } ΫϥΠΞϯτ͔Β͢Εਓ͡Όͳͯ͘ ϓϩϑΟʔϧ͔͠ڵຯͳ͍ΑͶɺ ͷҙΛࠐΊ໋໊ͨ
ʮΞΧϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 4XJGU protocol EntityId { /* … */ }
protocol Entity { var entityId: EntityId { get } } protocol UserId: EntityId { } typealias UserName = String protocol Profile: Entity { var userId: UserId { get } var name: UserName { get } var icon: URL? { get } } extension Profile { var entityId: EntityId { return userId } } (FOFSJD1SPUPDPM໘͕ଟ͍ͷͰ খࡉͰར༻Λආ͚Δ
ʮΞΧϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ ,PUMJO interface EntityId { /* ... */ }
interface Entity<ID : EntityId> { val id: ID } interface UserId: EntityId typealias UserName = String interface Profile: Entity<UserId> { val name:UserName val icon: URI? }
%%%తʹ͍͋͠ΫϥεୡډΔ • ΫϥΠΞϯτଆʹ͍Δ • 8FC3UD˞ • 8FC3UD%FWJDF8FC3UD4JHOBMJOH8FC3UD4FUUJOHʜ • )UUQ-JWF4USFBNJOH˞ •
"VEJP4PVSDF • 7JEFP4PVSDF • $BNFSB7JEFP4PVSDF4UJMM*NBHF7*EFP4PVSDF • %%%ͬΆ͘ͳ͍ʜ • ͔ͱ͍ͬͯݴ༿ͱͯ͠ઈର֎ͤͳ͍ʜ • Ͱศར ˞͜ΕΒࣗୡͷఆٛͨ͠ͷͰ ϥΠϒϥϦͷੜ0CKFDUΛࢦ͢ͷͰ͋Γ·ͤΜ
উखʹ૿ͨ͠ఆٛୡ ΫϥΠΞϯτଆͰͷΈ%PNBJOʹஔ͚ΔఆٛΛ૿͍ͯ͠Δ • -PDBM3FQPTJUPSZ • ϩʔΧϧͰ͔͠อଘ͠ͳ͍͔ΒͶʁ͍͍ͶʁΛڧௐͨ͠3FQPTJUPSZ • ओʹ$BDIF༻్Ͱ͏ͷͰ$MFBS$MFBS"MMΛ͍࣋ͬͯΔ • %BUB4PVSDF
• 3FQPTJUPSZͷѥछɻϩʔΧϧݶఆͷҰ࣌σʔλΛऔΓѻ͏ɻ • ߋ৽ݕग़ͷ EFMFHBUF FWFOU-JTUFS ΛηοτͰ͖Δ • 7JFXʹηοτ͢Δ༻ • %BUB4UPSF • %BUB4PVSDFͷ্Ґ൛ɻ • σʔλͷՃߋ৽͕Ͱ͖Δɻ
"QQMJDBUJPOͷਂ۷Γ
"QQMJDBUJPOͷΠϝʔδ 'PP3FQPTJUPSZ ࠩ͠ࠐΈޱ #BS3FQPTJUPSZ ࠩ͠ࠐΈޱ #B['BDUPSZ ࠩ͠ࠐΈޱ "4QFDJBM"QQMJDBUJPO 'PP3FQPTJUPSZ *NQMFNFOU
#BS3FQPTJUPSZ *NQMFNFOU #B['BDUPSZ *NQMFNFOU ͜ΕΛ ϝιου" ϝιου# ʜ
"QQMJDBUJPOͷΠϝʔδ 'PP3FQPTJUPSZ ࠩ͠ࠐΈޱ #BB3FQPTJUPSZ ࠩ͠ࠐΈޱ #B['BDUPSZ ࠩ͠ࠐΈޱ "4QFDJBM"QQMJDBUJPO 'PP3FQPTJUPSZ *NQMFNFOU
#BS3FQPTJUPSZ *NQMFNFOU #B['BDUPSZ *NQMFNFOU ͜͏͢Δͱ ϝιου" ϝιου# ʜ
"QQMJDBUJPOͷΠϝʔδ 'PP3FQPTJUPSZ ࠩ͠ࠐΈޱ #BB3FQPTJUPSZ ࠩ͠ࠐΈޱ #B['BDUPSZ ࠩ͠ࠐΈޱ "4QFDJBM"QQMJDBUJPO 'PP3FQPTJUPSZ *NQMFNFOU
#BS3FQPTJUPSZ *NQMFNFOU #B['BDUPSZ *NQMFNFOU ར༻Մೳʂ ར༻Մೳʂ ͜͏ͳΔʂ ϝιου" ϝιου# ʜ
"QQMJDBUJPOͷํ • %*͞Εͨ%PNBJOͷ࣮ΛΈ߹ΘͤͯԿ͔͠Βͷҙຯͷ͋ Δॲཧʹ͢Δ࣮Λஔ͘ • ଞͷ"QQMJDBUJPOΛड͚औΔࣄ͋ΔͷͰ࣮ଟ • ෭࡞༻ͷ͋ΔॲཧΛߦ͏͜ͱͳ͘ɺ%*͞ΕͨΠϯελϯ εͷࢦࣔΛ௨ͯ͠ߦ͏ •
λΠϚʔॲཧΛߦͬͨΓ"DUPSͰಈ͍ͯͨΓ͢Δ͜ͱ͋Γ݁ ߏࣗ༝
۩ମྫ r ܝࣔ൘ॻ͖ࠐΈΞϓϦ class CommentPoster(commentsRepository: CommentsRepository, commentFactory: CommentFactory){ def post(userId:UserId,
body:String, timestamp:LocalDateTime): Future[Done] ={ val comment = commentFactory.build(userId, body, timestamp) commentsRepository.store(comment) } } ˞ͿͬͪΌ͚͜Ε͘Β͍ͷॲཧͳΒ"QQMJDBUJPOΛ࡞Βͣ $POUSPMMFSʹॻ͍ͯ͠·͏͜ͱ͕ଟ͍
۩ମྫ r $BDIF͖3FQPTJUPSZ protocol FooRepository { func get(id: FooId) ->
Promise<Foo> func store(foo: Foo) -> Promise<Void> } protocol FooLocalRepository { func get(id: FooId) -> Promise<Foo> func store(foo: Foo) -> Promise<Void> func clear(id: FooId) func clearAll() } class CachedFooRepository: FooRepository { ʜ init(fooRepository: FooRepository, fooLocalRepository: FooLocalRepository) { ʜ } func get(id: FooId) -> Promise<Foo> { MPDBM3FQPTJUPSZ͔Βऔಘˠͳ͚ΕSFQPTJUPSZ͔Βऔಘ } func store(foo: Foo) -> Promise<Void> { SFQPTJUPSZʹอଘ͔ͯ͠Β MPDBM3FQPTJUPSZDMFBS } } ˞1SPNJTFϥΠϒϥϦ )ZESB ΛΜͰ͍ͬͯ·͢
۩ମྫ r $BDIF͖3FQPTJUPSZ interface FooRepository { suspend fun get(id: FooId):
Foo suspend fun store(foo: Foo) } interface FooLocalRepository { suspend fun get(id: FooId): Foo suspend fun store(foo: Foo) fun clear(id:FooId) fun clearAll() } class CachedFooRepository( private val fooRepository: FooRepository, private val fooLocalRepository: FooLocalRepository ) : FooRepository { override suspend fun get(id: FooId): Foo { /* localRepositoryから取得→なければrepositoryから取得 */ } override suspend fun store(foo: Foo) { /* repositoryに保存してから localRepository.clear() */ } }
۩ମྫ r $BDIF$MFBOFS class CacheCleaner( private val fooLocalRepository: FooLocalRepository, private
val barLocalRepository: BarLocalRepository, private val bazLocalRepository: BazLocalRepository ) { fun execute() { fooLocalRepository.clearAll() barLocalRepository.clearAll() bazLocalRepository.clearAll() } }
۩ମྫ r ϥΠϒ1MBZFSͷίΞ 1MBZFS6* EFMFHBUF ࠩ͠ࠐΈޱ Session ParticipantList DataStore ChatList
DataStore ͍ͬͯΔۀ • ঢ়گʹԠͯ͡)4-8FC35$ͷଓΛ͍͚ΔΑ͏$POOFDUJPOʹࢦࣔΛग़͢ • 8FC4PDLFU&WFOU4PVSDF͔ΒνϟοτࢀՃऀใΛड৴ͯ͠$IBU-JTU1BSUJDJQBOU-JTUʹ อଘ͢Δ • ϝιουΛ௨ͯ͠֎ଆʹ·ͱΊͨใΛฦͨ͠ΓɺࢦࣔΛड͚ͨΓ Connection WebRtc Signaling Device WebSocket CommandFactory HLS EventSource (server sent event) QMBZFS4UBUVT DIBUૹ৴ DMPTF ʜ
"EBQUFSͷਂ۷Γ
"EBQUFSͷํ • %PNBJOͷఆٛ USBJUJOUFSGBDFQSPUPDPM Λܧঝ࣮ͯ͠͠ ͨΫϥεͷஔ͖ • &OUJUZ7BMVF 0CKFDUͰ͋Ε DBTFDMBTTEBUBDMBTT
TUSVDUͰ࡞ͬͨΓ • +40/ͱͷ૬ޓม͜͜Ͱॻ͍ͯ͠·͏ • 3FQPTJUPSZͰ͋Ε%#"1*ͱ௨৴ͨ͠Γςετ༻ʹΦϯϝ ϞϦͷ࣮Λ࡞ͬͨΓɻ ͱ͘ʹͳ͍ͷͰ۩ମྫলུ͠·͢
7JFXͷਂ۷Γ
7JFX NJTD ͷํ • "QQMJDBUJPOΛΠϯελϯεԽͯ͠ը໘Λग़ྗߏங͢Δ • αʔόଆ+40/ు͚ͩ͘ͳͷͰγϯϓϧ • ΫϥΠΞϯτଆը໘ͷෳࡶ͞ʹԠͯ͡࿈ܞํ๏Λม͍͑ͯΔ •
ओͳॅਓ • 6*7JFX$POUSPMMFS6*7JFX"DUJWJUZ'SBHNFOU QMBZͷ $POUSPMMFS ͳͲͷίϯτϩʔϥʔܥ • 4UPSZCPBSE9JCMBZPVUͷ9.- σΟϨΫτϦతʹผʣ "1*ͷҾ ฦͷ+40/ૢ࡞ • %*ίϯςφ͜ͷ
αʔόଆͷ7JFX࣮ • جຊ3&45 "1*ܗࣜͰ+40/ฦ͚ͩ͢ • &OUJUZ7BMVF0CKFDUͱ+40/ͷ૬ޓม"EBQUFSͰ࣮͍ͯ͠Δ͜ͱ͋Δ͕ɺ ग़ྗͷ߹ʹҾͬுΒΕͨ͘ͳ͍ͷͰɺ͜ΕΒΛૉͷ··ฦ͢͜ͱগͳ͍ • جຊ"1*ຖʹฦܗࣜΛܾΊͯͦͪΒʹ٧Ίସ͑Δ import
play.api.libs.json.Json case class FooGetApiResponse(id: FooId, description: String) object FooGetApiResponse { implicit val jsonWrites = Json.writes[FooResponse] }
J04ͷ7JFX࣮ γϯϓϧͳը໘ • ؆୯ͳը໘Ͱ͋ΔͳΒͦͷ··7JFX$POUSPMMFSͱ4UPSZCPBSE͚ͬͭ͘Δ • 4UPSZCPBSEجຊը໘ຖʹ࡞ • Ұ࿈ͷը໘ʢΞϓϦىಈޙͷ͍Ζ͍ΖհϑϩʔͳͲʣෳը໘Λ֨ೲ 閉じるボタン
class FooViewController: UIViewController { @IBAction func onTouchUpInsideCloseBotton(_: Any) { navigationController?.popViewController(animated: true) } } 4UPSZCPBSE
J04ͷ7JFX࣮ ෳࡶͳը໘ • ଟগෳࡶͳΒ7JFXૢ࡞ΧελϜ7JFXʹԡ͠ࠐΊͯ͠·͏ • 7JFX$POUSPMMFSΧελϜ7JFXͷσʔληοτ SFQPTJUPSZΛݺͼग़͢ ͱɺ 7JFX͔Βͷૢ࡞ͷରԠ͕ओͳࣄɻ
Submit TextText TextTextTextTextText TextTextText TextText TextTextText 9JC ੜૢ࡞ !*#"DUJPO protocol FooViewDelegate: class { func onSubmit(value: String) } class FooView: UIView { /* ... */ weak var delegate: FooViewDelegate? var foo: Foo? { didSet { setNeedsLayout() } } override func layoutSubviews() { super.layoutSubviews() /* fooを描画 */ } } 'PP7JFX class FooViewController: UIViewController { @IBOutlet weak var fooView: FooView? { didSet { fooView?.delegate = self } } /* ... */ } extension FooViewController: FooViewDelegate { func onSubmit(value: String) { /* … */ } } 'PP7JFX$POUSPMMFS ᖤա͞Εͨ ૢ࡞ EFMFHBUF σʔλ ࢦࣔ QSPQFSUZTFU NFUIPEDBMM ඳը QSPQFSUZTFU
J04ͷ7JFX࣮ ෳࡶͳը໘ • 7JFX$POUSPMMFSͷࣄ͕ଟ͍ͳΒ"QQMJDBUJPOͷΫϥεʹ ͤͯ͠·͏ Submit TextText TextTextTextTextText TextTextText
TextText TextTextText 9JC ඳը QSPQFSUZTFU ੜૢ࡞ !*#"DUJPO σʔλ ࢦࣔ QSPQFSUZTFU NFUIPEDBMM ᖤա͞Εͨ ૢ࡞ EFMFHBUF class FooView: UIView { /* ... */ } 'PP7JFX class FooViewController : UIViewController, PlayerUI { /* ... */ } 'PP7JFX$POUSPMMFS 1MBZFS6* ͬͱ ᖤա͞Εͨ ૢ࡞ NFUIPEDBMM Πϕϯτ EFMFHBUF 1MBZFS$PSF
"OESPJEͷ7JFX࣮ γϯϓϧͳը໘ • ؆୯ͳը໘Ͱ͋ΔͳΒ,PUMJO"OESPJE&YUFOTJPOTͰ"DUJWJUZ ͔Β6*ύʔπΛͦͷ··ૢ࡞ YNM 閉じるボタン import kotlinx.android.synthetic.main.fooview.*
class FooActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.fooview) buttonView.setOnClickListener { /* … */ } } }
"OESPJEͷ7JFX࣮ ෳࡶͳը໘ • ෳࡶͳը໘ͳΒͰ"OESPJE"SDIJUFDUVSF$PNQPOFOUTͷ 7JFX.PEFM-JWF%BUBͱ%BUB#JOEJOHΛ׆༻ • J04ͱൺֱ͢Δͱɺߏಉ͡ͰΧελϜ7JFXΛ࡞Βͳͯ͘ྑ͍໘͕ ຆͲʹͳΔΠϝʔδ YNM
Submit TextText TextTextTextTextText TextTextText TextText TextTextText class FooViewModel : ViewModel() { interface Listener { fun onSubmit() } var listener: Listener? by weakVar() val foo = MutableLiveData<Foo>() fun onClickSubmitButton() { listener?.onSubmit() } } ํ #JOEJOH class FooActivity : FragmentActivity() { override fun onCreate( savedInstanceState: Bundle?) { /* BindingとViewModelの⽤意 */ model.listener = this } } } σʔλ QSPQFSUZTFU ᖤա͞Εͨ ૢ࡞ EFMFHBUF
"OESPJEͷ7JFX࣮ ෳࡶͳը໘ • "DUJWJUZ'SBHNFOUͷࣄ͕ଟ͍ͳΒ"QQMJDBUJPOͷΫϥεʹ ͤͯ͠·͏ Submit TextText TextTextTextTextText TextTextText
TextText TextTextText 9JC σʔλ ࢦࣔ QSPQFSUZTFU NFUIPEDBMM ᖤա͞Εͨ ૢ࡞ EFMFHBUF class FooViewModel: ViewModel() { /* ... */ } 'PP7JFX.PEFM 'PP"DUJWJUZ 1MBZFS6* ͬͱ ᖤա͞Εͨ ૢ࡞ NFUIPEDBMM Πϕϯτ EFMFHBUF 1MBZFS$PSF class FooActivity : FragmentActivity(), PlayerUI { override fun onCreate() { /* … */ } } ํ #JOEJOH
հҎ্Ͱ͢ ԿͰͨ͠Ͱ͠ΐ͏͔ʁ
8FBSFIJSJOH IUUQTTVHBSDPSQKQ
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ