Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Brick by Brick: Building Open Source libraries

Brick by Brick: Building Open Source libraries

So you have an idea, and you want to publish a library for it? But where do you start? Doing Open Source is a fine art which requires skill you don’t easily learn on schoolbooks. Creating a new library is like building a new house that people want to live in: you need to start with a great foundation, build your inner walls and then add all the niceties that make your house the best place to live in.

Join us as we share our journey building Open Source Android libraries: we’ll start from tools to help you organize your code, we’ll learn how to publish your libraries publicly and how to effectively maintain them. Throughout this journey, we’ll share our experience maintaining popular Android libraries such as Detekt, Chucker and AppIntro.

A house won’t be a home without someone living inside it, though. As your library won’t be a popular library without a strong community around it. So we’ll have the opportunity to share our insights on building strong communities around Open Source, getting developers together, finding new contributors and dealing with maintainer burnout.

Curious to know how to build a shiny new library brick by brick? Then make sure to don’t miss out this talk, and we can’t wait to see what you all will be building!

Nicola Corti

November 26, 2023
Tweet

More Decks by Nicola Corti

Other Decks in Technology

Transcript

  1. • Android Dev @ Nextome • AppIntro Author, Piano Player

    • twitter.com/paolorotolo • github.com/paolorotolo NICOLA CORTI PAOLO ROTOLO • Kotlin GDE • twitter.com/cortinico • github.com/cortinico
  2. Learn to say no • Build elegant APIs • Do

    not break APIs • Future proof your APIs
  3. Learn to say no • Build elegant APIs • Do

    not break APIs • Future proof your APIs
  4. class DefaultIntro : AppIntro() { override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) addSlide(AppIntroFragment.createInstance( title = "Title 1", description = "Desc 1", imageDrawable = R.drawable.ic_slide1 )) addSlide(AppIntroFragment.createInstance( title = “Title 2", description = "Desc 2", imageDrawable = R.drawable.ic_slide2 )) setTransformer(AppIntroPageTransformerType.Fade) isWizardMode = true isColorTransitionsEnabled = true Build elegant APIs
  5. AppIntroScreen { slides { slide { title = "Title 1"

    description = "Desc 1" imageDrawable = R.drawable.image1 backgroundColor = R.color.orange } slide { title = "Title 2" description = "Desc 2" imageDrawable = R.drawable.image2 backgroundColor = R.color.blue } } settings { transformer = transformer { AppIntroPageTransformerType. } isWizardMode = true Fade Build elegant APIs
  6. /** * Sealed class to represent all the possible Page

    Transformers * offered by AppIntro. */ sealed class AppIntroPageTransformerType { object Flow : AppIntroPageTransformerType() object Depth : AppIntroPageTransformerType() object Zoom : AppIntroPageTransformerType() object SlideOver : AppIntroPageTransformerType() object : AppIntroPageTransformerType() class Parallax( val titleParallaxFactor: Double = 1.0, val imageParallaxFactor: Double = -1.0, val descriptionParallaxFactor: Double = 2.0, @IdRes val titleViewId: Int = R.id.title, @IdRes val imageViewId: Int = R.id.image, @IdRes val descriptionViewId: Int = R.id.description ) : AppIntroPageTransformerType() } Fade Build elegant APIs
  7. when (transformType) { AppIntroPageTransformerType.Flow -> { page.rotationY = position *

    FLOW_ROTATION_ANGLE } AppIntroPageTransformerType.SlideOver -> transformSlideOver(position, page) AppIntroPageTransformerType.Depth - > transformDepth(position, page) AppIntroPageTransformerType.Zoom -> transformZoom(position, page) AppIntroPageTransformerType.Fade -> transformFade(position, page) is AppIntroPageTransformerType.Parallax -> { titlePF = transformType.titleParallaxFactor imagePF = transformType.imageParallaxFactor descriptionPF = transformType.descriptionParallaxFactor transformParallax( position, page, transformType.titleViewId, transformType.imageViewId, transformType.descriptionViewId ) } } Build elegant APIs
  8. class DefaultIntro : AppIntro() { override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) addSlide( AppIntroFragment.createInstance( title = "Welcome!", description = "This is a demo of the AppIntro library", imageDrawable = R.drawable.ic_slide1 ) ) Build elegant APIs
  9. class DefaultIntro : AppIntro() { override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) addSlide( AppIntroFragment.createInstance( title = "Welcome!", description = "This is a demo of the AppIntro library", imageDrawable = R.drawable.ic_slide1 ) ) Build elegant APIs
  10. addSlide( AppIntroFragment.createInstance( title = "Welcome!", description = "This is a

    demo of the AppIntro library", imageDrawable = R.drawable.ic_slide1 ) ) Build elegant APIs
  11. Learn to say no • Build elegant APIs • Do

    not break APIs • Future proof your APIs
  12. abstract class AppIntro : AppIntroBase() { override val layoutId =

    R.layout.appintro_intro_layout /** * Override viewpager bar color * @param color your color resource */ fun setBarColor(@ColorInt color: Int) { val bottomBar = findViewById<View>(R.id.bottom) bottomBar.setBackgroundColor(color) } Do not break APIs
  13. public abstract class AppIntro : AppIntroBase() { override val layoutId

    = R.layout.appintro_intro_layout /** * Override viewpager bar color * @param color your color resource */ public fun setBarColor(@ColorInt color: Int) { val bottomBar = findViewById<View>(R.id.bottom) bottomBar.setBackgroundColor(color) } Do not break APIs Kotlin Explicit API mode
  14. public abstract class AppIntro : AppIntroBase() { override val layoutId:

    Int = R.layout.appintro_intro_layout /** * Override viewpager bar color * @param color your color resource */ public fun setBarColor(@ColorInt color: Int) { val bottomBar = findViewById<View>(R.id.bottom) bottomBar.setBackgroundColor(color) } Do not break APIs Kotlin Explicit API mode
  15. fun goToSlide( number: Int, animate: Boolean = false, ) {

    TODO() } void goToSlide(Integer number, boolean animate); Do not break APIs
  16. fun goToSlide( number: Int, animate: Boolean = false, ) {

    TODO() } void goToSlide(Integer number, boolean animate); @JvmOverloads void goToSlide(Integer number); Do not break APIs
  17. @Deprecated( level = DeprecationLevel.WARNING ) fun newInstance(sliderPage: SliderPage) = createInstance(sliderPage)

    replaceWith = ReplaceWith("createInstance(sliderPage)"), message = "`newInstance` is deprecated to support color resources instead of color int " + "for configuration changes and dark theme support", Do not break APIs
  18. @Deprecated( level = DeprecationLevel.WARNING ) fun newInstance(sliderPage: SliderPage) = createInstance(sliderPage)

    replaceWith = ReplaceWith("createInstance(sliderPage)"), message = "`newInstance` is deprecated to support color resources instead of color int " + "for configuration changes and dark theme support", Do not break APIs
  19. @Deprecated( level = DeprecationLevel.WARNING ) fun newInstance(sliderPage: SliderPage) = createInstance(sliderPage)

    replaceWith = ReplaceWith("createInstance(sliderPage)"), message = "`newInstance` is deprecated to support color resources instead of color int " + "for configuration changes and dark theme support", Do not break APIs
  20. @Deprecated( level = DeprecationLevel.WARNING ) fun newInstance(sliderPage: SliderPage) = createInstance(sliderPage)

    replaceWith = ReplaceWith("createInstance(sliderPage)"), message = "`newInstance` is deprecated to support color resources instead of color int " + "for configuration changes and dark theme support", Do not break APIs
  21. <?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http: // schemas.android.com/tools"> <string name="skip_button">SKIP </

    string> <string name="next_button">NEXT </ string> <string name="back_button">BACK </ string> <string name="done_button">DONE </ string> </ resources> Don’t forget about Resources Do not break APIs
  22. <?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http: // schemas.android.com/tools"> <string name="app_intro_skip_button">SKIP </

    string> <string name="app_intro_next_button">NEXT </ string> <string name="app_intro_back_button">BACK </ string> <string name="app_intro_done_button">DONE </ string> </ resources> Do not break APIs Don’t forget about Resources
  23. Learn to say no • Build elegant APIs • Do

    not break APIs • Future proof your APIs
  24. Retrofit Room Joda Time Timber Moshi Paging - > -

    > - > - > - > - > Ktor SQLDelight DataTime Kermit kotlinx-serialization Cash App’s Paging Future proof your APIs
  25. JitPack • Easy to use • Free for OSS •

    Good for starting • No support for artifacts signing • No support for Kotlin Multiplatform (yet)
  26. JitPack • Easy to use • Free for OSS •

    Good for starting • No support for artifacts signing • No support for Kotlin Multiplatform (yet) jitpack/jitpack.io #3853
  27. GitHub Packages • Easy integration with Actions • Code and

    artifacts on the same repo • Supports Kotlin Multiplatform • Users are required to login with a token
  28. dependencyResolutionManagement { repositories { google() mavenCentral() maven { name =

    "GitHubPackages" url = uri("https: // maven.pkg.github.com/Nextome/KmmBeacons") credentials { username = "gh_username" password = "gh_token" } } } } settings.gradle (user)
  29. dependencyResolutionManagement { repositories { google() mavenCentral() maven { name =

    "GitHubPackages" url = uri("https: // maven.pkg.github.com/Nextome/KmmBeacons") credentials { username = "gh_username" password = "gh_token" } } } } settings.gradle (user)
  30. Maven Central • Popular, included by default in Android projects

    • Open Source Software Repository Hosting (OSSRH) for free • More secure • Requires more effort and time to set up
  31. sourceControl { gitRepository(URI("https: // github.com/AppIntro/AppIntro.git")) { producesModule("com.github.AppIntro:AppIntro") rootDir = "appintro"

    } } settings.gradle implementation("com.github.AppIntro:AppIntro:SNAPSHOT") { version { branch = "main" } } build.gradle Gradle Source Dependencies
  32. sourceControl { gitRepository(URI("https: / producesModule("com.github.AppIntro:AppIntro") rootDir = "appintro" } }

    settings.gradle implementation("com.github.AppIntro:AppIntro:SNAPSHOT") { version { branch = "main" } } build.gradle Gradle Source Dependencies
  33. High User Growth Low User Growth High Contributor Growth Federations

    (Flutter) Low Contributor Growth Stadium (OkHTTP) Growth
  34. High User Growth Low User Growth High Contributor Growth Federations

    (Flutter) Clubs (Arrow) Low Contributor Growth Stadium (OkHTTP) Growth
  35. High User Growth Low User Growth High Contributor Growth Federations

    (Flutter) Clubs (Arrow) Low Contributor Growth Stadium (OkHTTP) Toys Growth
  36. • First, what’s your model? • Start a chat channel

    • KotlinLang Slack • Discord • Twitter pro fi le Grow your community
  37. • Run contests • Join Hacktoberfest • Organize Meet-ups •

    Swag-as-a-service Gamify your contributions