Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Compose Multiplatform で Bluesky のクライアント作ってみた / ...
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Yuki Anzai
April 30, 2023
Technology
780
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Compose Multiplatform で Bluesky のクライアント作ってみた / Bluesky client with Compose Multiplatform
Bluesky/ATProtocol 勉強会#1
https://428lab.connpass.com/event/280610/
Yuki Anzai
April 30, 2023
More Decks by Yuki Anzai
See All by Yuki Anzai
StackChan 買いました / I bought my StackChan
yanzm
0
22
Android の公式 Skill / Android skills
yanzm
0
170
Android Studio Otter の最新 Gemini 機能 / Latest Gemini features in Android Studio Otter
yanzm
0
1.1k
Devin で iOS の PR から Android のコードを生成する / Generate Android code from iOS PR using Devin
yanzm
0
260
AI ツールを活用したコードリーディング - Android の公式サンプル Now in Android のソースコードを読んでみよう - / Code reading with AI tools
yanzm
1
640
EncryptedSharedPreferences が deprecated になっちゃった!どうしよう! / Oh no! EncryptedSharedPreferences has been deprecated! What should I do?
yanzm
0
3.1k
Devinを使ったモバイルアプリ開発 / Mobile app development with Devin
yanzm
0
450
Android Studio の 新しいAI機能を試してみよう / Try out the new AI features in Android Studio
yanzm
0
470
What’s new in Android development tools
yanzm
0
1k
Other Decks in Technology
See All in Technology
When Platform Engineering Meets GenAI
sucitw
0
170
Flow 不死:AI 時代 DevOps 的不變本質
cheng_wei_chen
2
500
From Prompt Engineering to Loop Engineering
shibuiwilliam
1
190
Kiro Ambassador を目指す話
k_adachi_01
0
130
Oracle Cloud Infrastructure:2026年6月度サービス・アップデート
oracle4engineer
PRO
0
290
コミュニティの有益性 ~JAWS Days 2026 での体験を通して~ / The Benefits of a Community ~Through My Experience at JAWS Days 2026~
seike460
PRO
0
270
脱SaaS!FDEを支えるプロビジョニングと分離設計
knih
0
300
レガシーな広告配信システムでのAI駆動開発/運用の挑戦
i16fujimoto
0
120
秘密度ラベル初心者が第1歩でつまづかないための「設計・運用」ポイント
seafay
PRO
1
480
AIのReact習熟度を測る
uhyo
2
680
AI-DLCを “そのまま導入しなかった”話 ~組織に合わせてアジャストした 私たちの実践共有~
hiroramos4
PRO
1
420
不要なレビューをAIにまかせて AIコーディングの環境改善を加速した
shoota
1
260
Featured
See All Featured
Typedesign – Prime Four
hannesfritz
42
3.1k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
300
How to Think Like a Performance Engineer
csswizardry
28
2.7k
[SF Ruby Conf 2025] Rails X
palkan
2
1.1k
How to Talk to Developers About Accessibility
jct
2
250
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
620
Navigating Team Friction
lara
192
16k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.7k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
3.5k
YesSQL, Process and Tooling at Scale
rocio
174
15k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
150
Joys of Absence: A Defence of Solitary Play
codingconduct
1
400
Transcript
$PNQPTF.VMUJQMBUGPSNͰ #MVFTLZͷΫϥΠΞϯτ࡞ͬͯΈͨ yanzm
ZBO[NʢΜ͟Ήʣ w !ZBO[NCTLZTPDJBM w ݹʢ͍ʹ͑͠ʣͷ"OESPJEFS w ݄ʹ,PKJSB͞ΜʹটίʔυΒͬͯCMVFTLZ͡Ί·ͨ͠ɻ৽ถͰ ͢ɻ
,PUMJO w ʢͱͱʣ+7.ݴޠ w CFUUFS+BWBతͳ w ͔Β"OESPJEͷެࣜαϙʔτݴޠͷҰͭ
,PUMJO.VMUJQMBUGPSN ,.1 w ,PUMJOͷίʔυΛXFC +T ͱ͔OBUJWF EFTLUPQ ͱ͔ͷόΠφϦʹϏϧυ w ಉ͡,PUMJOͷίʔυͰෳͷ1MBUGPSNʹରԠ
w J04ରԠ w ͨͩ͠ɺ6*ͦΕͧΕͷ1MBUGPSN/BUJWFͳ෦Ͱ࣮
+FUQBDL$PNQPTF w "OESPJEͷωΠςΟϒ6*Λߏங͢ΔͨΊͷ࠷৽ͷπʔϧΩοτ w એݴత6* Row { Icon( imageVector =
Icons.Default.Star, contentDescription = null ) Column { Text( text = "username" ) Text( text = "description" ) } Button( onClick = onClickFollow ) { Text("follow") } }
$PNQPTFGPS%FTLUPQ w ݄ w +FUQBDL$PNQPTFͰ,PUMJO.VMUJQMBUGPSNͷEFTLUPQͷ6*͕࡞ΕΔ
$PNQPTF.VUMJQMBUGPSN6*'SBNFXPSL w IUUQTXXXKFUCSBJOTDPNMQDPNQPTFNVMUJQMBUGPSN w ݄ͷ,PUMJO$POGͰൃද w EFTLUPQ͚ͩ͡Όͳ͘J04 "MQIB ,PUMJO/BUJWF 8FC
&YQFSJNFOUBM ,PUMJO8BTN αϙʔτ
#MVFTLZͷΫϥΠΞϯτΞϓϦΛ $PNQPTF.VMUJQMBUGPSNͰ࡞Δ https://github.com/yanzm/Rayleigh
4UFQϕʔεͷϓϩδΣΫτΛ࡞Δ w +FU#SBJOTͷHJUIVCʹDPNQPTFNVMUJQMBUGPSNͷςϯϓϨʔτ͕͋Δ w IUUQTHJUIVCDPN+FU#SBJOTDPNQPTFNVMUJQMBUGPSNJPTBOESPJE UFNQMBUF
4UFQ௨৴Ͱ͖ΔΑ͏ʹ͢Δ w 4FJVO͞Μ3FUSP fi UΛ͍͚ͬͯͨͲ,.1Ͱ͑ͳ͍ w ,UPS w IUUQTLUPSJP w
,PUMJO$POG`ͷηογϣϯͰग़͖ͯͨ$PNQPTF.VMUJQMBUGPSNͰνϟο τ͢ΔΞϓϦ͕ࢀߟʹͳͬͨ w IUUQTHJUIVCDPNTWUL,$$IBU"QQ w ,UPS ,PUMJOY4FSJBMJ[BUJPOͱ͔
4UFQϩάΠϯը໘ͷ6*Λ࡞Δ
4UFQϩάΠϯը໘ͷ6*Λ࡞Δ w SEQBSUZDMJFOU"QQ1BTTXPSEͰϩάΠϯ͢ΔΑ͏ʹͳ͔ͬͨΒɺΞΧ ϯτͷ࡞ϩάΠϯ"QQ1BTTXPSEͷ࡞ϒϥβΛ։͍ͯIUUQT TUBHJOHCTLZBQQΛද͍ࣔͨ͠ w "OESPJE J04ͦΕͧΕͷQMBUGPSNͷॲཧ͕ඞཁ
@Composable fun App( onOpenBrowser: (String) -> Unit = {} )
{ RayleighTheme { … LoginScreen( viewModel = viewModel, onOpenBrowser = onOpenBrowser, ) } } shared/commonMain
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContent { MainView( onOpenBrowser = { startActivity( Intent(Intent.ACTION_VIEW, Uri.parse(it)) ) } ) } } @Composable fun MainView( onOpenBrowser: (String) -> Unit ) { App( onOpenBrowser = onOpenBrowser ) } androidApp/ shared/androidMain Android
struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController {
Main_iosKt.MainViewController( onOpenBrowser: { let url = URL(string:$0)! UIApplication.shared.open(url, options: [:], completionHandler: nil) } ) } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} } fun MainViewController( onOpenBrowser: (String) -> Unit ) = ComposeUIViewController { App( onOpenBrowser = onOpenBrowser ) } iosApp/iosApp/ContentView.swift shared/iosMain iOS
4UFQϩάΠϯ͢Δ w "1*Λୟ͍ͯฦ͖ͬͯͨUPLFOΛอଘ͠ͳ͍ͱ͍͚ͳ͍ w NVMUJQMBUGPSNTFUUJOHTΛ͏͜ͱʹ͢Δ w IUUQTHJUIVCDPNSVTTIXPMGNVMUJQMBUGPSNTFUUJOHT w ϓϥοτϑΥʔϜ͝ͱʹ4FUUJOHTͷੜΠϯελϯε͕ҟͳΔ w
%*͕ͳ͍ͱͭΒ͍
4UFQ%FQFOEFODZ*OKFDUJPO w "OESPJEͰEBHHFSIJMU͕σϑΝΫτ͕ͩ,.1Ͱ͑ͳ͍ w ,PJO w LPJODPSF,.1ʹରԠ͍ͯ͠Δ͕ w LPJODPNQPTF$PNQPTF.VMUJQMBUGPSNʹ·ͩରԠ͍ͯ͠ͳ͍ w
l$PVMEOPU fi OEJPJOTFSULPJOLPJODPNQPTFJPTBSNzͱౖΒΕ Δ w IUUQTJOTFSULPJOJPEPDTSFGFSFODFLPJODPNQPTFNVMUJQMBUGPSN 8*1
class AppComponent : KoinComponent { val authRepository: AuthRepository by inject()
} shared/commonMain @Composable fun App( appComponent: AppComponent, onOpenBrowser: (String) -> Unit = {} ) { RayleighTheme { val appViewModel = remember { AppViewModel(appComponent.authRepository) } … } }
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContent { MainView( appComponent = AppComponent(), onOpenBrowser = { startActivity( Intent(Intent.ACTION_VIEW, Uri.parse(it)) ) @Composable fun MainView( appComponent: AppComponent, onOpenBrowser: (String) -> Unit ) { App( appComponent = appComponent, onOpenBrowser = onOpenBrowser ) } androidApp/ shared/androidMain Android
struct ComposeView: UIViewControllerRepresentable { let appComponent = AppComponent() func makeUIViewController(context:
Context) -> UIViewController { Main_iosKt.MainViewController( appComponent: appComponent, onOpenBrowser: { let url = URL(string:$0)! UIApplication.shared.open(url, options: [:], completionHandler: nil) } ) } fun MainViewController( appComponent: AppComponent, onOpenBrowser: (String) -> Unit ) = ComposeUIViewController { App( appComponent = appComponent, onOpenBrowser = onOpenBrowser ) } iosApp/iosApp/ContentView.swift shared/iosMain iOS
4UFQλΠϜϥΠϯΛऔಘ͢Δ w ϩάΠϯ࣌ʹऔಘͨ͠5PLFOΛCFBSFSBVUIFOUJDBUJPOʹηοτ͢Δ w ,UPSͷ"VUIQMVHJO͏͚ͩ w IUUQTLUPSJPEPDTCFBSFSDMJFOUIUNM w UZQFͰΦϒδΣΫτ͕มΘΔ3FDPSEͱ͔$MBTTEJTDSJNJOBUPSGPS QPMZNPSQIJTN͏
w IUUQTHJUIVCDPN,PUMJOLPUMJOYTFSJBMJ[BUJPOCMPCNBTUFSEPDT KTPONEDMBTTEJTDSJNJOBUPSGPSQPMZNPSQIJTN
None
·ͱΊࠓޙ w /BWJHBUJPOͲ͏ͨ͠Β͍͍ͷͩʜ w %FDPNQPTF IUUQTHJUIVCDPNBSLJWBOPW%FDPNQPTF w %*͕ͭΒ͍ʜ
w LPJODPNQPTF͕ରԠͨ͠ΒָʹͳΔ͔ʁ w 5FYU'JFMEͷڍಈ͕J044JNVMBUPSͰ͓͔͍͠ʜ w $PNQPTFͰී௨ʹJ04ͷ6*͕Ͱ͖͍ͯ͢͝ͷͰɺ͍Ζ͍ΖΓ͍ͨ