{"id":1469,"date":"2024-03-18T16:19:15","date_gmt":"2024-03-18T16:19:15","guid":{"rendered":"https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/"},"modified":"2024-03-18T16:19:15","modified_gmt":"2024-03-18T16:19:15","slug":"macrame-untangling-the-knot-on-the-etsy-android-listing-screen","status":"publish","type":"post","link":"https:\/\/dev95.site\/ar\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/","title":{"rendered":"Macram\u00e9: Untangling the Knot on the Etsy Android Listing Screen"},"content":{"rendered":"<div id=\"dev95-496150450\" class=\"dev95-- dev95-entity-placement\"><script async=\"async\" data-cfasync=\"false\" src=\"https:\/\/pl27862732.profitableratecpmnetwork.com\/2ad7a50e0bbc23ac6801d7b77c501463\/invoke.js\"><\/script>\r\n<div id=\"container-2ad7a50e0bbc23ac6801d7b77c501463\"><\/div><\/div><div>\n<p>Easily the most important and complex screen in the Buy on Etsy Android app is the listing screen, where all key information about an item for sale in the Etsy marketplace is displayed to buyers. Far from just a title and description, a price and a few images, over the years the listing screen has come to aggregate ratings and reviews, seller and shipping and stock information, and gained a variety of personalization and recommendation features. As information-rich as it is, as central as it is to the buying experience, for product teams the listing screen is an irresistible place to test out new methods and approaches. In just the last three years, apps teams have run nearly 200 experiments on it, often with multiple teams building and running experiments in parallel.<\/p>\n<p>Eventually, with such a high velocity of experiment and code change, the listing screen started showing signs of stress. Its architecture was inconsistent and not meant to support a codebase expanding so much and so rapidly in size and complexity. Given the relative autonomy of Etsy app development teams, there ended up being a lot of reinventing the wheel, lots of incompatible patterns getting layered atop one another; in short the code resembled a giant plate of spaghetti. The main listing Fragment file alone had over 4000 lines of code in it!<\/p><div id=\"dev95-3675703795\" class=\"dev95- dev95-entity-placement\"><center>\r\n<script>\r\n  atOptions = {\r\n    'key' : '4ba6b6513c00e0ba76511f798ae56401',\r\n    'format' : 'iframe',\r\n    'height' : 50,\r\n    'width' : 320,\r\n    'params' : {}\r\n  };\r\n<\/script>\r\n<script src=\"https:\/\/www.highrevenueformat.com\/4ba6b6513c00e0ba76511f798ae56401\/invoke.js\"><\/script>\r\n\t<\/center><\/div>\n<p>Code that isn\u2019t built for testability doesn\u2019t test well, and test coverage for the listing screen was low. VERY low. Our legacy architecture made it hard for developers to add tests for business logic, and the tests that did get written were complex and brittle, and often caused continuous integration failures for seemingly unrelated changes. Developers would skip tests when it seemed too costly to write and maintain them, those skipped tests made the codebase harder for new developers to onboard into or work with confidently, and the result was a vicious circle that would lead to even less test coverage.<\/p>\n<h2>Introducing Macram\u00e9<\/h2>\n<p>We decided that our new architecture for the listing screen, which we\u2019ve named <a href=\"https:\/\/www.etsy.com\/search?q=macram%25C3%25A9\">Macram\u00e9<\/a>, would be based on immutable data propagated through a reactive UI. Reactive frameworks are widely deployed and well understood, and we could see a number of ways that reactivity would help us untangle the spaghetti. We chose to emulate architectures like Spotify\u2019s <a href=\"https:\/\/spotify.github.io\/mobius\/\">Mobius<\/a>, molded to fit the shape of Etsy\u2019s codebase and its business requirements.<\/p>\n<p>At the core of the architecture is an immutable State object that represents our data model. State for the listing screen is passed to the UI as a single data object via a <a href=\"https:\/\/developer.android.com\/kotlin\/flow\/stateflow-and-sharedflow\">StateFlow<\/a> instance; each time a piece of the data model changes the UI re-renders. Updates to State can be made either from a background thread or from the main UI thread, and using StateFlow ensures that all updates reach the main UI thread. When the data model for a screen is large, as it is for the listing screen, updating the UI from a single object makes things much simpler to test and reason about than if multiple separate models are making changes independently. And that simplicity lets us streamline the rest of the architecture.<\/p>\n<p>When changes are made to the State, the monolithic data model gets transformed into a list of smaller models that represent what will actually be shown to the user, in vertical order on the screen. The code below shows an example of state held in the Buy Box section of the screen, along with its smaller Title sub-component.<\/p>\n<pre><code>data class BuyBox(\n    val title: Title,\n    val price: Price,\n    val saleEndingSoonBadge: SaleEndingSoonBadge,\n    val unitPricing: UnitPricing,\n    val vatTaxDescription: VatTaxDescription,\n    val transparentPricing: TransparentPricing,\n    val firstVariation: Variation,\n    val secondVariation: Variation,\n    val klarnaInfo: KlarnaInfo,\n    val freeShipping: FreeShipping,\n    val estimatedDelivery: EstimatedDelivery,\n    val quantity: Quantity,\n    val personalization: Personalization,\n    val expressCheckout: ExpressCheckout,\n    val cartButton: CartButton,\n    val termsAndConditions: TermsAndConditions,\n    val ineligibleShipping: IneligibleShipping,\n    val lottieNudge: LottieNudge,\n    val listingSignalColumns: ListingSignalColumns,\n    val shopBanner: ShopBanner,\n)\n\ndata class Title(\n    val text: String,\n    val textInAlternateLanguage: String? = null,\n    val isExpanded: Boolean = false,\n) : ListingUiModel()<\/code><\/pre>\n<p>In our older architecture, the screen was based on a single scrollable View. All data was bound and rendered during the View&#8217;s initial layout pass, which created a noticeable pause the first time the screen was loaded. In the new screen, a RecyclerView is backed by a ListAdapter, which allows for asynchronous diffs of the data changes, avoiding the need to rebind portions of the screen that aren&#8217;t receiving updates. Each of the vertical elements on the screen (title, image gallery, price, etc.) is represented by its own ViewHolder, which binds whichever of the smaller data models the element relies on.<\/p>\n<p>In this code, the BuyBox is transformed into a vertical list of ListingUiModels to display in the RecyclerView.<\/p>\n<pre><code>fun BuyBox.toUiModels(): List&lt;ListingUiModel&gt; {\n    return listOf(\n        price,\n        title,\n        shopBanner,\n        listingSignalColumns,\n        unitPricing,\n        vatTaxDescription,\n        transparentPricing,\n        klarnaInfo,\n        estimatedDelivery,\n        firstVariation,\n        secondVariation,\n        quantity,\n        personalization,\n        ineligibleShipping,\n        cartButton,\n        expressCheckout,\n        termsAndConditions,\n        lottieNudge,\n    )\n}<\/code><\/pre>\n<p>An Event dispatching system handles user actions, which are represented by a sealed Event class. The use of <a href=\"https:\/\/www.etsy.com\/codeascraft\/sealed-classes-opened-my-mind\">sealed classes<\/a> for Events, coupled with Kotlin &#8220;when&#8221; statements mapping Events to Handlers, provides compile-time safety to ensure all of the pieces are in place to handle the Event properly. These Events are fed to a single Dispatcher queue, which is responsible for routing Events to the Handlers that are registered to receive them.<\/p>\n<p>Handlers perform a variety of tasks: starting asynchronous network calls, dispatching more Events, dispatching SideEffects, or updating State. We want to make it easy to reason about what Handlers are doing, so our architecture promotes keeping their scope of responsibility as small as possible. Simple Handlers are simple to write tests for, which leads to better test coverage and improved developer confidence.<\/p>\n<p>In the example below, a click handler on the listing title sets a State property that tells the UI to display an expanded title:<\/p>\n<pre><code>class TitleClickedHandler constructor() {\n\n    fun handle(state: ListingViewState.Listing): ListingEventResult.StateChange {\n        val buyBox = state.buyBox \n        return ListingEventResult.StateChange(\n            state = state.copy(\n                buyBox = buyBox.copy(\n                    title = title.copy(isExpanded = true)\n                )\n            )\n        )\n    }\n}<\/code><\/pre>\n<p>SideEffects are a special type of Event used to represent, typically, one-time operations that need to interact with the UI but aren\u2019t considered pure business logic: showing dialogs, logging events, performing navigation or showing Snackbar messages. SideEffects end up being routed to the Fragment to be handled.<\/p>\n<p>Take the scenario of a user clicking on a listing&#8217;s Add to Cart button. The Handler for that Event might:<\/p>\n<ul>\n<li>dispatch a SideEffect to log the button click<\/li>\n<li>start an asynchronous network call to update the user\u2019s cart<\/li>\n<li>update the State to show a loading indicator while the cart update finishes<\/li>\n<\/ul>\n<p>While the network call is running on a background thread, the Dispatcher is free to handle other Events that may be in the queue. When the network call completes in the background, a new Event will be dispatched with either a success or failure result. A different Handler is then responsible for handling both the success and failure Events.<\/p>\n<p>This diagram illustrates the flow of Events, SideEffects, and State through the architecture:<\/p>\n<figure>\n<img data-recalc-dims=\"1\" decoding=\"async\" alt=\"Macram\u00e9 Architecture\" src=\"https:\/\/i0.wp.com\/i.etsystatic.com\/inv\/044fad\/5843012537\/inv_fullxfull.5843012537_l4a37ye8.jpg?w=1280&#038;ssl=1\" title=\"Macram\u00e9 Architecture\"><figcaption>Figure 1. A flow chart illustrating system components (blue boxes) and how events and state changes (yellow boxes) flow between them.<\/figcaption><\/figure>\n<h2>Results<\/h2>\n<p>The rewrite process took five months, with as many as five Android developers working on the project at once. One challenge we faced along the way was keeping the new listing screen up to date with all of the experiments being run on the old listing screen while development was in progress. The team also had to create a suite of tests that could comprehensively cover the diversity of listings available on Etsy, to ensure that we didn\u2019t forget any features or break any.<\/p>\n<p>With the rewrite complete, the team ran an A\/B experiment against the existing listing screen to test both performance and user behavior between the two versions. Though the new listing screen felt qualitatively quicker than the old listing screen, we wanted to understand how users would react to subtle changes in the new experience.<\/p>\n<p>We instrumented both the old and the new listing screens to measure performance changes from the refactor. The new screen performed even better than expected. Time to First Content was decreased by 18%, going from 1585 ms down to 1298 ms. This speedup resulted in the average number of listings viewed by buyers increasing 2.4%, add to carts increasing 0.43%, searches increasing by 2%, and buyer review photo views increasing by 3.3%.<\/p>\n<p>On the developer side, unit test coverage increased from single digit percentages to a whopping 76% code coverage of business logic classes. This significantly validates our decision to put nearly all business logic into Handler classes, each responsible for handling just a single Event at a time. We built a robust collection of tools for generating testing States in a variety of common configurations, so writing unit tests for the Handlers is as simple as generating an input event and validating that the correct State and SideEffects are produced.<\/p>\n<p>Creating any new architecture involves making tradeoffs, and this project was no exception. Macram\u00e9 is under active development, and we have a few pieces of feedback on our agenda to be addressed:<\/p>\n<ul>\n<li>There is some amount of boilerplate still needed to correctly wire up a new Event and Handler, and we&#8217;d like to make that go away.<\/li>\n<li>The ability of Handlers to dispatch their own Events sometimes makes debugging complex Handler interactions more difficult than previous formulations of the same business logic.<\/li>\n<li>On a relatively simple screen, the architecture can feel like overkill.<\/li>\n<\/ul>\n<p>Adding new features correctly to the listing screen is now the easy thing to do. The dual benefit of increasing business metrics while also increasing developer productivity and satisfaction has resulted in the Android team expanding the usage of Macram\u00e9 to two more of the key screens in the app (Cart and Shop), both of which completely rewrote their UI using <a href=\"https:\/\/developer.android.com\/jetpack\/compose\">Jetpack Compose<\/a>: but those are topics for future Code as Craft posts.<\/p>\n<\/div>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_1469\" class=\"pvc_stats total_only\" data-element-id=\"1469\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" version=\"1.0\" viewbox=\"0 0 502 315\" preserveaspectratio=\"xMidYMid meet\"><g transform=\"translate(0,332) scale(0.1,-0.1)\" fill=\"\" stroke=\"none\"><path d=\"M2394 3279 l-29 -30 -3 -207 c-2 -182 0 -211 15 -242 39 -76 157 -76 196 0 15 31 17 60 15 243 l-3 209 -33 29 c-26 23 -41 29 -80 29 -41 0 -53 -5 -78 -31z\"\/><path d=\"M3085 3251 c-45 -19 -58 -50 -96 -229 -47 -217 -49 -260 -13 -295 52 -53 146 -42 177 20 16 31 87 366 87 410 0 70 -86 122 -155 94z\"\/><path d=\"M1751 3234 c-13 -9 -29 -31 -37 -50 -12 -29 -10 -49 21 -204 19 -94 39 -189 45 -210 14 -50 54 -80 110 -80 34 0 48 6 76 34 21 21 34 44 34 59 0 14 -18 113 -40 219 -37 178 -43 195 -70 221 -36 32 -101 37 -139 11z\"\/><path d=\"M1163 3073 c-36 -7 -73 -59 -73 -102 0 -56 133 -378 171 -413 34 -32 83 -37 129 -13 70 36 67 87 -16 290 -86 209 -89 214 -129 231 -35 14 -42 15 -82 7z\"\/><path d=\"M3689 3066 c-15 -9 -33 -30 -42 -48 -48 -103 -147 -355 -147 -375 0 -98 131 -148 192 -74 13 15 57 108 97 206 80 196 84 226 37 273 -30 30 -99 39 -137 18z\"\/><path d=\"M583 2784 c-38 -19 -67 -74 -58 -113 9 -42 211 -354 242 -373 16 -10 45 -18 66 -18 51 0 107 52 107 100 0 39 -1 41 -124 234 -80 126 -108 162 -133 173 -41 17 -61 16 -100 -3z\"\/><path d=\"M4250 2784 c-14 -9 -74 -91 -133 -183 -95 -150 -107 -173 -107 -213 0 -55 33 -94 87 -104 67 -13 90 8 211 198 130 202 137 225 78 284 -27 27 -42 34 -72 34 -22 0 -50 -8 -64 -16z\"\/><path d=\"M2275 2693 c-553 -48 -1095 -270 -1585 -649 -135 -104 -459 -423 -483 -476 -23 -49 -22 -139 2 -186 73 -142 361 -457 571 -626 285 -228 642 -407 990 -497 242 -63 336 -73 660 -74 310 0 370 5 595 52 535 111 1045 392 1455 803 122 121 250 273 275 326 19 41 19 137 0 174 -41 79 -309 363 -465 492 -447 370 -946 591 -1479 653 -113 14 -422 18 -536 8z m395 -428 c171 -34 330 -124 456 -258 112 -119 167 -219 211 -378 27 -96 24 -300 -5 -401 -72 -255 -236 -447 -474 -557 -132 -62 -201 -76 -368 -76 -167 0 -236 14 -368 76 -213 98 -373 271 -451 485 -162 444 86 934 547 1084 153 49 292 57 452 25z m909 -232 c222 -123 408 -262 593 -441 76 -74 138 -139 138 -144 0 -16 -233 -242 -330 -319 -155 -123 -309 -223 -461 -299 l-81 -41 32 46 c18 26 49 83 70 128 143 306 141 649 -6 957 -25 52 -61 116 -79 142 l-34 47 45 -20 c26 -10 76 -36 113 -56z m-2057 25 c-40 -58 -105 -190 -130 -263 -110 -324 -59 -707 132 -981 25 -35 42 -64 37 -64 -19 0 -241 119 -326 174 -188 122 -406 314 -532 468 l-58 71 108 103 c185 178 428 349 672 473 66 33 121 60 123 61 2 0 -10 -19 -26 -42z\"\/><path d=\"M2375 1950 c-198 -44 -350 -190 -395 -379 -18 -76 -8 -221 19 -290 114 -284 457 -406 731 -260 98 52 188 154 231 260 27 69 37 214 19 290 -38 163 -166 304 -326 360 -67 23 -215 33 -279 19z\"\/><\/g><\/svg><\/i> <img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/i0.wp.com\/dev95.site\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif?resize=16%2C16&#038;ssl=1\" border=\"0\" \/><\/p>\n<div class=\"pvc_clear\"><\/div>","protected":false},"excerpt":{"rendered":"<p>Easily the most important and complex screen in the Buy on Etsy Android app is the listing screen, where all key information about an item for sale in the Etsy marketplace is displayed to buyers. Far from just a title<\/p>\n<div class=\"hosteria-entry-more\"><a href=\"https:\/\/dev95.site\/ar\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/\" class=\"no-underline font-light  group-hover:text-primary-800 dark:group-hover:text-primary-300 py-1\">Read more &gt;&gt;&gt;<\/a><\/div>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_1469\" class=\"pvc_stats total_only\" data-element-id=\"1469\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" version=\"1.0\" viewbox=\"0 0 502 315\" preserveaspectratio=\"xMidYMid meet\"><g transform=\"translate(0,332) scale(0.1,-0.1)\" fill=\"\" stroke=\"none\"><path d=\"M2394 3279 l-29 -30 -3 -207 c-2 -182 0 -211 15 -242 39 -76 157 -76 196 0 15 31 17 60 15 243 l-3 209 -33 29 c-26 23 -41 29 -80 29 -41 0 -53 -5 -78 -31z\"\/><path d=\"M3085 3251 c-45 -19 -58 -50 -96 -229 -47 -217 -49 -260 -13 -295 52 -53 146 -42 177 20 16 31 87 366 87 410 0 70 -86 122 -155 94z\"\/><path d=\"M1751 3234 c-13 -9 -29 -31 -37 -50 -12 -29 -10 -49 21 -204 19 -94 39 -189 45 -210 14 -50 54 -80 110 -80 34 0 48 6 76 34 21 21 34 44 34 59 0 14 -18 113 -40 219 -37 178 -43 195 -70 221 -36 32 -101 37 -139 11z\"\/><path d=\"M1163 3073 c-36 -7 -73 -59 -73 -102 0 -56 133 -378 171 -413 34 -32 83 -37 129 -13 70 36 67 87 -16 290 -86 209 -89 214 -129 231 -35 14 -42 15 -82 7z\"\/><path d=\"M3689 3066 c-15 -9 -33 -30 -42 -48 -48 -103 -147 -355 -147 -375 0 -98 131 -148 192 -74 13 15 57 108 97 206 80 196 84 226 37 273 -30 30 -99 39 -137 18z\"\/><path d=\"M583 2784 c-38 -19 -67 -74 -58 -113 9 -42 211 -354 242 -373 16 -10 45 -18 66 -18 51 0 107 52 107 100 0 39 -1 41 -124 234 -80 126 -108 162 -133 173 -41 17 -61 16 -100 -3z\"\/><path d=\"M4250 2784 c-14 -9 -74 -91 -133 -183 -95 -150 -107 -173 -107 -213 0 -55 33 -94 87 -104 67 -13 90 8 211 198 130 202 137 225 78 284 -27 27 -42 34 -72 34 -22 0 -50 -8 -64 -16z\"\/><path d=\"M2275 2693 c-553 -48 -1095 -270 -1585 -649 -135 -104 -459 -423 -483 -476 -23 -49 -22 -139 2 -186 73 -142 361 -457 571 -626 285 -228 642 -407 990 -497 242 -63 336 -73 660 -74 310 0 370 5 595 52 535 111 1045 392 1455 803 122 121 250 273 275 326 19 41 19 137 0 174 -41 79 -309 363 -465 492 -447 370 -946 591 -1479 653 -113 14 -422 18 -536 8z m395 -428 c171 -34 330 -124 456 -258 112 -119 167 -219 211 -378 27 -96 24 -300 -5 -401 -72 -255 -236 -447 -474 -557 -132 -62 -201 -76 -368 -76 -167 0 -236 14 -368 76 -213 98 -373 271 -451 485 -162 444 86 934 547 1084 153 49 292 57 452 25z m909 -232 c222 -123 408 -262 593 -441 76 -74 138 -139 138 -144 0 -16 -233 -242 -330 -319 -155 -123 -309 -223 -461 -299 l-81 -41 32 46 c18 26 49 83 70 128 143 306 141 649 -6 957 -25 52 -61 116 -79 142 l-34 47 45 -20 c26 -10 76 -36 113 -56z m-2057 25 c-40 -58 -105 -190 -130 -263 -110 -324 -59 -707 132 -981 25 -35 42 -64 37 -64 -19 0 -241 119 -326 174 -188 122 -406 314 -532 468 l-58 71 108 103 c185 178 428 349 672 473 66 33 121 60 123 61 2 0 -10 -19 -26 -42z\"\/><path d=\"M2375 1950 c-198 -44 -350 -190 -395 -379 -18 -76 -8 -221 19 -290 114 -284 457 -406 731 -260 98 52 188 154 231 260 27 69 37 214 19 290 -38 163 -166 304 -326 360 -67 23 -215 33 -279 19z\"\/><\/g><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/dev95.site\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=\"0\" \/><\/p>\n<div class=\"pvc_clear\"><\/div>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"fp_fajr_begins":"","fp_fajr_iqamah":"","fp_dhuhr_begins":"","fp_dhuhr_iqamah":"","fp_asr_begins":"","fp_asr_iqamah":"","fp_maghrib_begins":"","fp_maghrib_iqamah":"","fp_isha_begins":"","fp_isha_iqamah":"","fp_midnight":"","fp_midnight_name":"","fp_sunrise":"","fp_single_prayer_begins_title":"","fp_single_prayer_iqamah_title":"","fp_prayer_times_for_today":"","fp_hijra_date":"","fp_fajr_name":"","fp_dhuhr_name":"","fp_asr_name":"","fp_maghrib_name":"","fp_isha_name":"","fp_sunrise_name":"","fp_currentDate":"","fp_current_time":"","fp_current_title":"","fp_current_location":"","fp_masjid_name":"","fp_prayer_title":"","fp_next_prayer_iqamah_time":"","fp_next_prayer_iqamah_title":"","fp_next_prayer_begins_time":"","fp_next_prayer_begins_title":"","fp_next_prayer_title":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[37],"tags":[],"class_list":["post-1469","post","type-post","status-publish","format-standard","hentry","category-posts"],"a3_pvc":{"activated":true,"total_views":0,"today_views":0},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Macram\u00e9: Untangling the Knot on the Etsy Android Listing Screen - Dev95<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/dev95.site\/ar\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/\" \/>\n<meta property=\"og:locale\" content=\"ar_AR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Macram\u00e9: Untangling the Knot on the Etsy Android Listing Screen - Dev95\" \/>\n<meta property=\"og:description\" content=\"Easily the most important and complex screen in the Buy on Etsy Android app is the listing screen, where all key information about an item for sale in the Etsy marketplace is displayed to buyers. Far from just a titleRead more &gt;&gt;&gt;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/dev95.site\/ar\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/\" \/>\n<meta property=\"og:site_name\" content=\"Dev95\" \/>\n<meta property=\"article:published_time\" content=\"2024-03-18T16:19:15+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/i.etsystatic.com\/inv\/044fad\/5843012537\/inv_fullxfull.5843012537_l4a37ye8.jpg?version=0\" \/>\n<meta name=\"author\" content=\"dev95\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u0643\u064f\u062a\u0628 \u0628\u0648\u0627\u0633\u0637\u0629\" \/>\n\t<meta name=\"twitter:data1\" content=\"dev95\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u0648\u0642\u062a \u0627\u0644\u0642\u0631\u0627\u0621\u0629 \u0627\u0644\u0645\u064f\u0642\u062f\u0651\u0631\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 \u062f\u0642\u0627\u0626\u0642\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/dev95.site\\\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/dev95.site\\\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\\\/\"},\"author\":{\"name\":\"dev95\",\"@id\":\"https:\\\/\\\/dev95.site\\\/#\\\/schema\\\/person\\\/b807805ffe2916206b04d0938bce0298\"},\"headline\":\"Macram\u00e9: Untangling the Knot on the Etsy Android Listing Screen\",\"datePublished\":\"2024-03-18T16:19:15+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/dev95.site\\\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\\\/\"},\"wordCount\":1528,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/dev95.site\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/dev95.site\\\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/i.etsystatic.com\\\/inv\\\/044fad\\\/5843012537\\\/inv_fullxfull.5843012537_l4a37ye8.jpg?version=0\",\"articleSection\":[\"Posts\"],\"inLanguage\":\"ar\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/dev95.site\\\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/dev95.site\\\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\\\/\",\"url\":\"https:\\\/\\\/dev95.site\\\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\\\/\",\"name\":\"Macram\u00e9: Untangling the Knot on the Etsy Android Listing Screen - Dev95\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/dev95.site\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/dev95.site\\\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/dev95.site\\\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/i.etsystatic.com\\\/inv\\\/044fad\\\/5843012537\\\/inv_fullxfull.5843012537_l4a37ye8.jpg?version=0\",\"datePublished\":\"2024-03-18T16:19:15+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/dev95.site\\\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\\\/#breadcrumb\"},\"inLanguage\":\"ar\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/dev95.site\\\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"ar\",\"@id\":\"https:\\\/\\\/dev95.site\\\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\\\/#primaryimage\",\"url\":\"https:\\\/\\\/i.etsystatic.com\\\/inv\\\/044fad\\\/5843012537\\\/inv_fullxfull.5843012537_l4a37ye8.jpg?version=0\",\"contentUrl\":\"https:\\\/\\\/i.etsystatic.com\\\/inv\\\/044fad\\\/5843012537\\\/inv_fullxfull.5843012537_l4a37ye8.jpg?version=0\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/dev95.site\\\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/dev95.site\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Macram\u00e9: Untangling the Knot on the Etsy Android Listing Screen\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/dev95.site\\\/#website\",\"url\":\"https:\\\/\\\/dev95.site\\\/\",\"name\":\"Dev95\",\"description\":\"A comprehensive platform for data and knowledge, delivering reliable content that meets the aspirations of readers and enthusiasts.\",\"publisher\":{\"@id\":\"https:\\\/\\\/dev95.site\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/dev95.site\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"ar\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/dev95.site\\\/#organization\",\"name\":\"Dev95\",\"url\":\"https:\\\/\\\/dev95.site\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ar\",\"@id\":\"https:\\\/\\\/dev95.site\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/dev95.site\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/rbrrbr-6.png?fit=512%2C512&ssl=1\",\"contentUrl\":\"https:\\\/\\\/i0.wp.com\\\/dev95.site\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/rbrrbr-6.png?fit=512%2C512&ssl=1\",\"width\":512,\"height\":512,\"caption\":\"Dev95\"},\"image\":{\"@id\":\"https:\\\/\\\/dev95.site\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/dev95.site\\\/#\\\/schema\\\/person\\\/b807805ffe2916206b04d0938bce0298\",\"name\":\"dev95\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ar\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a70a73d950838b20cd80d7ebdc955737e802e8cd896044c5473b32b946c0662a?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a70a73d950838b20cd80d7ebdc955737e802e8cd896044c5473b32b946c0662a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a70a73d950838b20cd80d7ebdc955737e802e8cd896044c5473b32b946c0662a?s=96&d=mm&r=g\",\"caption\":\"dev95\"},\"url\":\"https:\\\/\\\/dev95.site\\\/ar\\\/author\\\/mohammad\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Macram\u00e9: Untangling the Knot on the Etsy Android Listing Screen - Dev95","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/dev95.site\/ar\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/","og_locale":"ar_AR","og_type":"article","og_title":"Macram\u00e9: Untangling the Knot on the Etsy Android Listing Screen - Dev95","og_description":"Easily the most important and complex screen in the Buy on Etsy Android app is the listing screen, where all key information about an item for sale in the Etsy marketplace is displayed to buyers. Far from just a titleRead more &gt;&gt;&gt;","og_url":"https:\/\/dev95.site\/ar\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/","og_site_name":"Dev95","article_published_time":"2024-03-18T16:19:15+00:00","og_image":[{"url":"https:\/\/i.etsystatic.com\/inv\/044fad\/5843012537\/inv_fullxfull.5843012537_l4a37ye8.jpg?version=0","type":"","width":"","height":""}],"author":"dev95","twitter_card":"summary_large_image","twitter_misc":{"\u0643\u064f\u062a\u0628 \u0628\u0648\u0627\u0633\u0637\u0629":"dev95","\u0648\u0642\u062a \u0627\u0644\u0642\u0631\u0627\u0621\u0629 \u0627\u0644\u0645\u064f\u0642\u062f\u0651\u0631":"8 \u062f\u0642\u0627\u0626\u0642"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/#article","isPartOf":{"@id":"https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/"},"author":{"name":"dev95","@id":"https:\/\/dev95.site\/#\/schema\/person\/b807805ffe2916206b04d0938bce0298"},"headline":"Macram\u00e9: Untangling the Knot on the Etsy Android Listing Screen","datePublished":"2024-03-18T16:19:15+00:00","mainEntityOfPage":{"@id":"https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/"},"wordCount":1528,"commentCount":0,"publisher":{"@id":"https:\/\/dev95.site\/#organization"},"image":{"@id":"https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/#primaryimage"},"thumbnailUrl":"https:\/\/i.etsystatic.com\/inv\/044fad\/5843012537\/inv_fullxfull.5843012537_l4a37ye8.jpg?version=0","articleSection":["Posts"],"inLanguage":"ar","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/","url":"https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/","name":"Macram\u00e9: Untangling the Knot on the Etsy Android Listing Screen - Dev95","isPartOf":{"@id":"https:\/\/dev95.site\/#website"},"primaryImageOfPage":{"@id":"https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/#primaryimage"},"image":{"@id":"https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/#primaryimage"},"thumbnailUrl":"https:\/\/i.etsystatic.com\/inv\/044fad\/5843012537\/inv_fullxfull.5843012537_l4a37ye8.jpg?version=0","datePublished":"2024-03-18T16:19:15+00:00","breadcrumb":{"@id":"https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/#breadcrumb"},"inLanguage":"ar","potentialAction":[{"@type":"ReadAction","target":["https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/"]}]},{"@type":"ImageObject","inLanguage":"ar","@id":"https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/#primaryimage","url":"https:\/\/i.etsystatic.com\/inv\/044fad\/5843012537\/inv_fullxfull.5843012537_l4a37ye8.jpg?version=0","contentUrl":"https:\/\/i.etsystatic.com\/inv\/044fad\/5843012537\/inv_fullxfull.5843012537_l4a37ye8.jpg?version=0"},{"@type":"BreadcrumbList","@id":"https:\/\/dev95.site\/macrame-untangling-the-knot-on-the-etsy-android-listing-screen\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/dev95.site\/"},{"@type":"ListItem","position":2,"name":"Macram\u00e9: Untangling the Knot on the Etsy Android Listing Screen"}]},{"@type":"WebSite","@id":"https:\/\/dev95.site\/#website","url":"https:\/\/dev95.site\/","name":"Dev95","description":"A comprehensive platform for data and knowledge, delivering reliable content that meets the aspirations of readers and enthusiasts.","publisher":{"@id":"https:\/\/dev95.site\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/dev95.site\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"ar"},{"@type":"Organization","@id":"https:\/\/dev95.site\/#organization","name":"Dev95","url":"https:\/\/dev95.site\/","logo":{"@type":"ImageObject","inLanguage":"ar","@id":"https:\/\/dev95.site\/#\/schema\/logo\/image\/","url":"https:\/\/i0.wp.com\/dev95.site\/wp-content\/uploads\/2026\/07\/rbrrbr-6.png?fit=512%2C512&ssl=1","contentUrl":"https:\/\/i0.wp.com\/dev95.site\/wp-content\/uploads\/2026\/07\/rbrrbr-6.png?fit=512%2C512&ssl=1","width":512,"height":512,"caption":"Dev95"},"image":{"@id":"https:\/\/dev95.site\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/dev95.site\/#\/schema\/person\/b807805ffe2916206b04d0938bce0298","name":"dev95","image":{"@type":"ImageObject","inLanguage":"ar","@id":"https:\/\/secure.gravatar.com\/avatar\/a70a73d950838b20cd80d7ebdc955737e802e8cd896044c5473b32b946c0662a?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/a70a73d950838b20cd80d7ebdc955737e802e8cd896044c5473b32b946c0662a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a70a73d950838b20cd80d7ebdc955737e802e8cd896044c5473b32b946c0662a?s=96&d=mm&r=g","caption":"dev95"},"url":"https:\/\/dev95.site\/ar\/author\/mohammad\/"}]}},"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/dev95.site\/ar\/wp-json\/wp\/v2\/posts\/1469","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dev95.site\/ar\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev95.site\/ar\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev95.site\/ar\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dev95.site\/ar\/wp-json\/wp\/v2\/comments?post=1469"}],"version-history":[{"count":0,"href":"https:\/\/dev95.site\/ar\/wp-json\/wp\/v2\/posts\/1469\/revisions"}],"wp:attachment":[{"href":"https:\/\/dev95.site\/ar\/wp-json\/wp\/v2\/media?parent=1469"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev95.site\/ar\/wp-json\/wp\/v2\/categories?post=1469"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev95.site\/ar\/wp-json\/wp\/v2\/tags?post=1469"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}