<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Manuel González Villegas</title>
    <description>The latest articles on Forem by Manuel González Villegas (@mgonzlez).</description>
    <link>https://forem.com/mgonzlez</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F711904%2F91067d2f-fc0a-45c8-aec0-dec8f38a5c84.jpeg</url>
      <title>Forem: Manuel González Villegas</title>
      <link>https://forem.com/mgonzlez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mgonzlez"/>
    <language>en</language>
    <item>
      <title>Including Figma images in your SwiftUI/Compose Previews</title>
      <dc:creator>Manuel González Villegas</dc:creator>
      <pubDate>Fri, 06 Jun 2025 12:51:55 +0000</pubDate>
      <link>https://forem.com/playtomic/including-figma-images-in-your-swiftuicompose-previews-2kno</link>
      <guid>https://forem.com/playtomic/including-figma-images-in-your-swiftuicompose-previews-2kno</guid>
      <description>&lt;p&gt;A seamless way to integrate your figma designs into your workflow&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;During our development process here at Playtomic, we often encounter challenges that lead to frustrating back-and-forth exchanges between developers and designers. One of the main issues is ensuring that the implementation aligns perfectly with the designs, often due to small misalignments like missing or extra spaces.&lt;/p&gt;

&lt;p&gt;To address this, we looked for ways to enhance our workflow and improve collaboration. Figma is the tool our product design team uses to create stunning interfaces, so integrating those designs into our mobile projects can be cumbersome.&lt;/p&gt;

&lt;p&gt;That’s why we created FigmaPreview, a component available for both SwiftUI and Jetpack Compose, allowing us to set an image from Figma as a background directly in the Xcode or Android Studio preview while we code. This simple yet powerful addition to our workflow helps ensure pixel-perfect precision across platforms, significantly reducing the back-and-forth between design and development and allowing us to move faster while maintaining high-quality UI.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡To keep this post clear and avoid repetition, I'll focus on the SwiftUI implementation. However, both the SwiftUI and Jetpack Compose versions share the same API and behave almost identically, with just a few platform-specific differences that I'll cover at the end.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What is FigmaPreview?
&lt;/h3&gt;

&lt;p&gt;FigmaPreview is a custom SwiftUI component that lets you overlay your figma designs directly onto the SwiftUI views, and this background image can serve as a reference, allowing you to make a pixel perfect (or very closely) implementation.&lt;/p&gt;

&lt;p&gt;Before diving into the details, here’s a quick example of how FigmaPreview helps catch subtle misalignments that might go unnoticed at first glance:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbykx7230bfyu4n4szyo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbykx7230bfyu4n4szyo.gif" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to use FigmaPreview
&lt;/h3&gt;

&lt;p&gt;To use FigmaPreview you just need to follow the following simple steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Obtain a token from your account to allow to the component to reach up to your private design files. Obtaining a token is pretty simple, you just need to go to your account settings and click on &lt;code&gt;Generate new token&lt;/code&gt; button&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8xwz27befi3vacg49pis.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8xwz27befi3vacg49pis.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get a link from Figma by clicking on the view that you need to work on&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8e5ptq8akk93xkqlqh9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8e5ptq8akk93xkqlqh9.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Wrap your preview view inside of the FigmaPreview component. You only need to invoke the class init from your preview view&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ExamplePlaytomicView_Previews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PreviewProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;previews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;FigmaPreview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Your figma's url here"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;ExamplePlaytomicView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;firstParam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"something"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nv"&gt;secondParam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;
                &lt;span class="o"&gt;...&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now you can adjust the alpha of the figma image that you have in the background of your preview to compare the figma design with your implementation&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2txqrkji93yxg09vv2nf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2txqrkji93yxg09vv2nf.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How we use FigmaPreview
&lt;/h3&gt;

&lt;p&gt;Essentially, we are doing 2 main usages of &lt;code&gt;FigmaPreview&lt;/code&gt; across our development process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coding complete screens&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When building entire screens, we use &lt;code&gt;FigmaPreview&lt;/code&gt; as a background, so we can align our implementation with precision. This ensures that our screens closely matches the intended design from the very beginning, reducing misalignments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkmxb8k0akz57b39if8g.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkmxb8k0akz57b39if8g.gif" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Catalog components&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In addition to full screens, we also use &lt;code&gt;FigmaPreview&lt;/code&gt; for individual UI components. To support this, we maintain a separate app within our main project as a component catalog. Our designers can use this app to review and validate UI components, ensuring they meet pixel perfect standards with &lt;code&gt;FigmaPreview&lt;/code&gt; enabled.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi651kdpptauo94z0w69w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi651kdpptauo94z0w69w.gif" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach not only ensures design accuracy at a granular level but also streamlines our workflow, as designers can review, test and approve components directly in the catalog app. By the time the components are added to the main screens, they are already pixel perfect, reducing the need for further adjustments.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it is implemented
&lt;/h3&gt;

&lt;p&gt;The implementation of this component is not very complex, essentially, we just need to transform the url that we obtain from figma into a one that provides you only the image view.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡To obtain the original figma url you can review the second step of the section &lt;code&gt;How to use FigmaPreview&lt;/code&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To implement this, we use the Figma API, which provides the necessary functionality to access images from Figma files. Here’s a quick breakdown of the process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extracting the &lt;code&gt;fileId&lt;/code&gt; and &lt;code&gt;nodeId&lt;/code&gt;: First, we parse the URL of the Figma file to obtain two key parameters, the &lt;code&gt;fileId&lt;/code&gt; and the &lt;code&gt;nodeId&lt;/code&gt;. These identifiers specify the file and specific frame or component from which we want to obtain an image.&lt;/li&gt;
&lt;li&gt;Requesting the image from Figma’s API: Now, with the &lt;code&gt;fileId&lt;/code&gt; and &lt;code&gt;nodeId&lt;/code&gt; we can request the image with a simple &lt;code&gt;GET&lt;/code&gt; request to the following endpoint:
&lt;code&gt;https://api.figma.com/v1/images/{fileId}?ids={nodeId}&lt;/code&gt; 
In the request header, we include a custom header named &lt;code&gt;X-Figma-Token&lt;/code&gt; with our figma access token. This returns a URL of the image, which we then use as the base of this component.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
  Obtaining the image URL
  &lt;p&gt;Here is an example of how to retrieve the Figma image URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    class FigmaPreviewModel: ObservableObject {
        @State private var originalUrl = ""
        @Published var imageUrl: String = ""

        /**
         * Original URL: https://www.figma.com/file/ILeBSptVwqyqpr3gR2KZLh/Improvements-for-switching-teams?type=design&amp;amp;node-id=2012-6765&amp;amp;mode=design&amp;amp;t=60wrWFvQkrJyMHIg-1
         * API URL that needs to be tranformed: https://api.figma.com/v1/images/ILeBApGukqyqprK932KZLh?ids=2099-6965&amp;amp;format=png
         */
        func updateUrl(figmaUrl: String, scale: CGFloat) {
            guard originalUrl != figmaUrl else { return }
            guard
                let url = URL(string: figmaUrl),
                let components = NSURLComponents(url: url, resolvingAgainstBaseURL: false),
                let fileId = components.path?.split(usingRegex: "/").getOrNull(index: 2),
                let nodeId = components.queryItems?.first(where: { $0.name == "node-id" })?.value
            else {
                return
            }
            guard
                let requesrUrl =
                URL(string: "https://api.figma.com/v1/images/\(fileId)?ids=\(nodeId)&amp;amp;format=png&amp;amp;use_absolute_bounds=true&amp;amp;scale=\(scale)")
            else { return }
            var request = URLRequest(url: requesrUrl)
            request.addValue("{your_token_here}", forHTTPHeaderField: "X-Figma-Token")
            let dataTask = URLSession.shared.dataTask(with: request) { data, response, _ in
                guard
                    (response as? HTTPURLResponse)?.statusCode == 200,
                    let data
                else { return }
                DispatchQueue.main.async {
                    guard
                        let decodedPreview = try? JSONDecoder().decode(FigmaPreviewItem.self, from: data),
                        let imageURL = decodedPreview.images?[nodeId.replacingOccurrences(of: "-", with: ":")]
                    else { return }
                    self.imageUrl = imageURL
                    self.originalUrl = figmaUrl
                }
            }
            dataTask.resume()
        }
    }

    struct FigmaPreviewItem: Decodable {
        let err: String?
        let images: [String: String]?
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Android implementation:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        class FigmaPreviewModel {
            private var originalUrl = ""
            var imageUrl by mutableStateOf("")

            /**
             * Original URL: https://www.figma.com/file/ILeBSptVwqyqpr3gR2KZLh/Improvements-for-switching-teams?type=design&amp;amp;node-id=2012-6765&amp;amp;mode=design&amp;amp;t=60wrWFvQkrJyMHIg-1
             * API URL that needs to be tranformed: https://api.figma.com/v1/images/ILeBApGukqyqprK932KZLh?ids=2099-6965&amp;amp;format=png
             */
            @Throws(IOException::class)
            fun updateUrl(figmaUrl: String, density: Float) {
                if (originalUrl == figmaUrl) {
                    return
                }
                val url = URL(figmaUrl)
                val components = url.path.split("/")
                val fileId = components.getOrNull(2) ?: return
                val query = url.query ?: return
                val nodeId = query.split("&amp;amp;").firstOrNull { it.startsWith("node-id=") }?.split("=")?.getOrNull(1) ?: return
                val requestUrl = URL("https://api.figma.com/v1/images/$fileId?ids=$nodeId&amp;amp;format=png&amp;amp;use_absolute_bounds=true&amp;amp;scale=$density")
                val httpURLConnection = requestUrl.openConnection() as HttpURLConnection
                httpURLConnection.apply {
                    requestMethod = "GET"
                    addRequestProperty("X-Figma-Token", "{your_token_here}")
                }
                httpURLConnection.requestMethod = "GET"
                val responseCode = httpURLConnection.responseCode
                if (responseCode != HttpURLConnection.HTTP_OK) {
                    return
                }
                val inputReader = BufferedReader(InputStreamReader(httpURLConnection.inputStream))
                var inputLine: String?
                val response = StringBuffer()
                while (inputReader.readLine().also { inputLine = it } != null) {
                    response.append(inputLine)
                }
                inputReader.close()
                Handler(Looper.getMainLooper()).post {
                    JSONObject(response.toString()).optJSONObject("images")?.optString(nodeId.replace("-", ":"))?.let { imageUrl = it }
                    originalUrl = figmaUrl
                }
            }
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  Displaying the Figma image
  &lt;p&gt;Once we have the URL, we can create the view to display the image as a background:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public struct FigmaPreview&amp;lt;Content: View&amp;gt;: View {
        @StateObject var model = FigmaPreviewModel()
        @State private var url: String
        @State private var opacity: Double
        private let content: Content

        public init(
            url: String,
            opacity: Double = 0.4,
            @ViewBuilder content: () -&amp;gt; Content
        ) {
            _url = State(initialValue: url)
            _opacity = State(initialValue: opacity)
            self.content = content()
        }

        public var body: some View {
            @Environment(\.colorScheme) var colorScheme
            ScrollView {
                VStack {
                    HStack {
                        Text("Code")
                        Slider(value: $opacity, in: 0 ... 1)
                            .padding(.horizontal)
                        Text("Figma")
                    }.padding(.horizontal, medium)
                    ZStack(alignment: .top) {
                        content.frame(maxWidth: figmaPreviewDefaultWidth)
                        HStack {
                            let scale = UIScreen.main.scale
                                AsyncImage(url: URL(string: model.imageUrl), scale: scale)
                                .opacity(opacity)
                                .onAppear { model.updateUrl(figmaUrl: url, scale: scale) }
                                .dashedBorder(color: R.color.playtomicSwiftUI.emerald.opacity(0.5))
                        }
                        .ignoresSafeArea(.all)
                        .frame(width: figmaPreviewDefaultWidth)
                        .dashedBorder(color: R.color.playtomicSwiftUI.sky_blue.opacity(0.5))
                    }.frame(minWidth: figmaPreviewDefaultWidth)
                }
            }
        }
    }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Android implementation:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        val figmaPreviewDefaultWidth = 375.dp

        @Composable
        fun FigmaPreview(url: String, opacity: Float = 0.4f, contentScale: ContentScale = ContentScale.FillBounds, content: @Composable () -&amp;gt; Unit) {
            var opacityState by remember { mutableStateOf(opacity) }
            val model = remember { FigmaPreviewModel() }

            val density = LocalDensity.current.density
            val localContext = LocalContext.current

            LaunchedEffect(url) { Executors.newFixedThreadPool(1).execute { model.updateUrl(url, density = density) } }
            LaunchedEffect(Unit) {
                // mgonzalez: Put here the context that is needed to start any preview
                val contextProvider = object : IContextProvider {
                    override val applicationContext: android.content.Context
                        get() = localContext.applicationContext

                    override val currentActivity: Activity?
                        get() = localContext as? Activity

                    override val activityStack: List&amp;lt;Activity&amp;gt;
                        get() = listOf()
                }
                Context.contextProvider = contextProvider
                PlaytomicUI.initialize(contextProvider = contextProvider)
            }

            Column(verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier) {
                Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = Medium)) {
                    Text("Code")
                    Slider(
                        value = opacityState, onValueChange = { opacityState = it }, valueRange = 0f..1f, modifier = Modifier
                        .padding(horizontal = 16.dp)
                        .weight(1f)
                    )
                    Text("Figma")
                }

                Box(
                    contentAlignment = Alignment.TopCenter,
                    modifier = Modifier
                        .requiredWidth(figmaPreviewDefaultWidth)
                        .dashedBorder(Color.Blue)
                ) {
                    Box(modifier = Modifier.dashedBorder(Color.Green)) { content() }

                    val modifier = if (contentScale == ContentScale.FillBounds) {
                        Modifier
                    } else {
                        Modifier.matchParentSize()
                    }

                    PlaytomicImage(
                        urlString = model.imageUrl,
                        placeholder = com.playtomicui.R.drawable.ic_asset_add_picture_disable,
                        options = ImageOptions.ORIGINAL,
                        contentScale = contentScale,
                        modifier = modifier.alpha(opacityState)
                    )
                }
            }
        }
        ```


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;/p&gt;

&lt;p&gt;This approach simplifies our development process by displaying the figma design right within Xcode. You can see our implementation here: &lt;/p&gt;
&lt;h3&gt;
  
  
  Some limitations that the component have
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Each time you make a change in your code, the component needs to load again the image, what some times might be a bit annoying&lt;/li&gt;
&lt;li&gt;On android, you need to run the preview to see the figmapreview working
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwkgbul7exfk3jyvh4dm0.png" alt=" "&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>figma</category>
      <category>swiftui</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Decorator pattern vs inheriting in Kotlin</title>
      <dc:creator>Manuel González Villegas</dc:creator>
      <pubDate>Mon, 13 Dec 2021 08:15:03 +0000</pubDate>
      <link>https://forem.com/playtomic/decorator-pattern-vs-inheriting-in-kotlin-1ihh</link>
      <guid>https://forem.com/playtomic/decorator-pattern-vs-inheriting-in-kotlin-1ihh</guid>
      <description>&lt;p&gt;In playtomic, especially in the mobile team, we are fan of the decorator pattern, and in this article I will try to explain why it is a better idea to use the decorator pattern than inheritance, showing an example of how we use it.&lt;/p&gt;

&lt;p&gt;This article will be technical, so I recommend to you to read before a bit of theory about the difference between inheritance and the decorator pattern. &lt;a href="https://newbedev.com/decorator-pattern-versus-sub-classing"&gt;https://newbedev.com/decorator-pattern-versus-sub-classing&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  The inheritance
&lt;/h4&gt;

&lt;p&gt;The inheritance in Kotlin is the same as in some other languages, we use the inheritance when we know there is a relationship between a child and its parent class, for example, an iPhone is a phone, a phone is an electronic device. Each child is an specialised version of the parent in which inherits all the properties of the parent.&lt;/p&gt;

&lt;h4&gt;
  
  
  The decorator pattern
&lt;/h4&gt;

&lt;p&gt;The decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class.&lt;br&gt;
The coolest of the decorator pattern is that you can create a decorator class and use it more than once, and at any moment you can add or remove any other one (with the inheritance you can not).&lt;/p&gt;
&lt;h4&gt;
  
  
  An example in playtomic
&lt;/h4&gt;

&lt;p&gt;Let’s see an example about how Playtomic app manages the HTTP requests. As a spoiler I will tell you that with the decorator pattern we can change which http client we are using in runtime, yes, in runtime. Let 's see.&lt;/p&gt;

&lt;p&gt;To start, we have a &lt;code&gt;IHttpClient&lt;/code&gt; interface, which is a basic thing that we will need to create the decorator classes. In this case we only have one simple method to make a request with an object that contains the needed information to make that request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface IHttpClient {
    fun request(httpRequest: HttpRequest): Promise&amp;lt;HttpResponse&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our case, we also have some other implemented methods (inside the interface) to make our lives easier while we are making requests. All of them are very similar to this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    fun get(endpoint: String, params: Map&amp;lt;String, Any&amp;gt;?): Promise&amp;lt;ByteArray&amp;gt; =
            request(HttpRequest(method = HttpMethod.get, url = endpoint, queryParams = params))
                    .then(map = { it.body })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, I am not going to show very deeply the implementation of each of our http clients implementation, I will just enumerate some of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OkHttpClient =&amp;gt; This one is the main client that makes the remote http requests to our backend
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class OkHttpClient(
        private val baseUrl: String,
        timeOut: Long? = null,
        client: OkHttpClient? = null,
        private val urlEncoder: IHttpParameterEncoder = HttpUrlParameterEncoder(),
        private val bodyEncoders: List&amp;lt;IHttpParameterEncoder&amp;gt; = listOf(HttpJsonParameterEncoder(), ...))
    : IHttpClient {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the base HTTP client, so here there is nothing too much to see.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AuthHttpClient =&amp;gt; With this one, we include the authentication and if the session has expired we can re-login to the user and throw the request again (and for the user is totally transparent)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class AuthHttpClient(private val httpClient: IHttpClient,
                     private val keychain: IKeyValueStorage) : IHttpClient by httpClient {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case we are decorating the http client to add the authentication into the request, so, as you can see into the class definition (&lt;code&gt;... by httpClient&lt;/code&gt;) means that the interface that this class is implementing &lt;code&gt;IHttpClient&lt;/code&gt; interface is being decorated by the parameter &lt;code&gt;httpclient&lt;/code&gt;. So, in that case we can only implement into this class the methods that we need for the decoration (In this case, is the only one that we have). So, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    override fun request(httpRequest: HttpRequest): Promise&amp;lt;HttpResponse&amp;gt; =
            request(httpRequest, refreshTokenAllowed = true)

    fun request(httpRequest: HttpRequest, refreshTokenAllowed: Boolean): Promise&amp;lt;HttpResponse&amp;gt; {
        val headers = httpRequest.headers?.toMutableMap() ?: mutableMapOf()
        val accessToken = keychain.accessToken
        if (accessToken != null &amp;amp;&amp;amp; isAnemoneRequest(httpRequest)) {
            headers.put("Authorization", "Bearer $accessToken")
        }
        val authHttpRequest = httpRequest.copy(headers = headers)
        val pendingPromise = PendingPromise&amp;lt;HttpResponse&amp;gt;()
        httpClient.request(authHttpRequest).then(pendingPromise::fulfill).catchError { error -&amp;gt;
            if (this.isAnemoneRequest(httpRequest) &amp;amp;&amp;amp; this.isSessionError(error)) {
                if (refreshTokenAllowed) {
                    if (accessToken != this.keychain.accessToken) {
                        this.request(httpRequest = httpRequest, refreshTokenAllowed = false).then(pendingPromise::fulfill).catchError(pendingPromise::reject)
                    } else {
                        this.refreshToken(PendingRequest(request = httpRequest, promise = pendingPromise, error = error))
                    }
                } else {
                    this.logout?.invoke()
                    this.httpClient.request(httpRequest).then(pendingPromise::fulfill).catchError(pendingPromise::reject)
                }
            } else {
                pendingPromise.reject(error)
            }
        }
        return pendingPromise
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I said before, we are including into the request a header with the authentication needed information and checking if the request fails because of a session expired error to throw the request again.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AnalyticsHttpClient =&amp;gt; This one measures and track the response, so with this we can analyse the traffic, the error rate ...
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class AnalyticsHttpClient(private val httpClient: IHttpClient,
                          private val analyticsManager: IAnalyticsManager) : IHttpClient by httpClient {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This decorator is doing something very similar to the previous one to be able to track timings and errors&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LocalHttpClient =&amp;gt; This one use as response some jsons stored into the app
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class LocalHttpClient(private val context: Context) : IHttpClient {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is another base client to read local documents in JSON to be able to serve it as a response.&lt;/p&gt;

&lt;p&gt;And if you are still asking how we are changing the used http client in runtime, I will confess that it has nothing to do with the decorator pattern but this is how we are doing it. We have another implementation of the &lt;code&gt;IHttpClient&lt;/code&gt; interface that receives 2 implementations of the HTTP client and depending on the endpoint url and method verb we decide which one to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class AppHttpClient(val appVersion: String,
                    var localHttpClient: IHttpClient,
                    var remoteHttpClient: IHttpClient)
    : IHttpClient {

    private val localEndpoints = mapOf&amp;lt;String, String&amp;gt;(
            "POST /v2/auth/login" to "/v2/auth/login",
            "GET /v2/users/me" to "/v2/users/me",
            "GET /v2/tournaments/7c0b8e6f-3693-4f48-b370-b9643a8d04fe" to "/v2/tournaments/1",
            "GET /v2/status/version_control" to "/v2/status/version_control",
            "" to ""
    )

    override fun request(httpRequest: HttpRequest): Promise&amp;lt;HttpResponse&amp;gt; {
        val headers = httpRequest.headers?.toMutableMap() ?: mutableMapOf()
        headers.put("X-Requested-With", "${BuildConfig.APPLICATION_ID}.app $appVersion")
        headers.put("User-Agent", "Android ${android.os.Build.VERSION.RELEASE}")

        val appHttpRequest = httpRequest.copy(headers = headers)
        val key = "${appHttpRequest.method.description} ${appHttpRequest.url}"

        val file = localEndpoints[key]
        return if (file != null) {
            localHttpClient.request(appHttpRequest.copy(url = file))
        } else {
            remoteHttpClient.request(appHttpRequest)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To complete this post and understand the power of the composition thanks to the decorator pattern, let’s take a quick look at how easy it is to add a new decorator component, to include a new behavior or remove one of it just by adding or removing the proper line into the instantiation. Ours one looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        val baseHttpClient = OkHttpClient(baseUrl = baseUrl, timeOut = 30)
        val authHttpClient = AuthHttpClient(
                httpClient = baseHttpClient,
                keychain = keychain
        )
        val analyticsHttpClient = AnalyticsHttpClient(
                httpClient = authHttpClient,
                analyticsManager = managerProvider.analyticsManager
        )
        val appHttpClient = AppHttpClient(
                appVersion = appVersion,
                localHttpClient = LocalHttpClient(applicationContext),
                remoteHttpClient = analyticsHttpClient
        )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
  </channel>
</rss>
