mirror of
https://github.com/LukeHagar/unicorn-utterances.git
synced 2025-12-09 21:07:49 +00:00
Made articles more consistent with their headings
This commit is contained in:
@@ -16,7 +16,7 @@ Tech recruiting is difficult. Interviews are tricky for candidates - and for int
|
||||
|
||||
This can be done in many ways, but let’s talk about a few today.
|
||||
|
||||
## Don’t Be Afraid To Help
|
||||
# Don’t Be Afraid To Help
|
||||
|
||||
Something to keep in mind while interviewing candidates is that they’re just like you and me: people. People that make mistakes from time-to-time or might get stuck on a certain phrasing of a question.
|
||||
|
||||
@@ -27,7 +27,7 @@ This is particularly beneficial for junior engineers, who’s interviews should
|
||||
|
||||
While this all might seem counterintuitive to assist a candidate (even in small ways) during an interview, you have to remember that they need support. In their future role with your company, they won’t (and shouldn’t) be working in isolation. Instead, they will have a team to lean on. By giving a small hint here and there, you’re able to understand how they receive feedback and when they need help.
|
||||
|
||||
## Allow for Resources
|
||||
# Allow for Resources
|
||||
|
||||
As mentioned earlier, candidates are just people. Because of that, you will never find an all-knowing candidate who only ever relies solely on their existing knowledge to fix an issue (no matter what big-ego Jim says). Time-and-time again I’ve heard from seasoned developers that research and cheat-sheets are part of their daily engineering work.
|
||||
|
||||
@@ -36,7 +36,7 @@ While it might not be immediately obvious, knowing how to search for and find th
|
||||
|
||||
After all, the point of coding interviews is to see how capable a developer is at the job they’re applying for. You want to test in real-world situations, not in an isolated environment that doesn’t represent the daily aspects of the job.
|
||||
|
||||
## Less Algorithms, More Demos
|
||||
# Less Algorithms, More Demos
|
||||
|
||||
Speaking of representing a job in a more realistic light: think about the last time you had a ticket in your backlog that required discussion of tree reversal (or similar algorithm). Now think of the last time you asked a question like that in your interviews. See where I’m going here? I’m not implying that algorithm questions are inherently bad for every position, but in this industry they’ve been used as a stop-gap for more relevant questions.
|
||||
|
||||
@@ -50,7 +50,7 @@ Not only is possible cheating unhelpful in reflecting a candidate’s problem so
|
||||
|
||||
Even with all of this, I’m only scratching the surface of the [issues with algorithm-based “whiteboarding” interviews that’ve been written about before](https://coderpad.io/blog/whiteboard-interview-guide/).
|
||||
|
||||
### Fixing the Problem
|
||||
## Fixing the Problem
|
||||
|
||||
That said, interview questions are important, so what should we be replacing these algorithm questions with technical assessments?
|
||||
|
||||
@@ -60,7 +60,7 @@ In other scenarios, where styling is important to the role, you might want to pr
|
||||
|
||||
While real-world code samples provide many upsides, setting up a real-world example may take a candidate a little longer to read through the code and orient themselves. How can we reduce the dead-air for interviewers and candidates alike?
|
||||
|
||||
## Take-Homes
|
||||
# Take-Homes
|
||||
|
||||
We at CoderPad are ***strong\*** advocates of take-home interviews for technical assessments. While [we’ve written about many of the benefits of take-homes before](https://coderpad.io/blog/hire-better-faster-and-in-a-more-human-way-with-take-homes/), we’ll touch on some of the advantages here:
|
||||
|
||||
@@ -70,7 +70,7 @@ We at CoderPad are ***strong\*** advocates of take-home interviews for technical
|
||||
- Removes biases
|
||||
- Kills dead air while candidate is doing initial thought process of a problem
|
||||
|
||||
### Level the Playing Field
|
||||
## Level the Playing Field
|
||||
|
||||
As mentioned previously, time isn’t the same for everyone. Some folks look for work while they’re still working full-time for another employer. Those same people may have kids or other priorities in their lives. This makes time a rare commodity for some.
|
||||
|
||||
@@ -82,13 +82,13 @@ CoderPad allows you to limit the amount of time a candidate is able to spend in
|
||||
|
||||
I encourage you to keep the time limit to 2 hours max. Any longer and you risk alienating the candidates without that much time to do assessments. Keep in mind - you’re likely not the only role that candidate has applied for.
|
||||
|
||||
## Find Their Speciality
|
||||
# Find Their Speciality
|
||||
|
||||
Not only do take-homes improve the balance of “stress” vs “capabilities” you look for in an interview, they also allow candidates with specialties focus on their strengths and show you what they’re most capable of.
|
||||
|
||||
Think about it this way: You’re hiring for a team - ideally a team should have a good balance of skills. Even in small-scale applications, there are lots of jobs to do: styling, internal documentation to write, code organization, boilerplate writing, application logic, testing, etc.. While it’s possible to have someone capable of doing more than one of these - it’s impossible to be a true universal expert at all of them at once. Engineers are likely to lean more in some directions than others. Don’t discourage this - it’s what allows a team of diverse people to build a better product through combined individual expertise.
|
||||
|
||||
## Wrap Up
|
||||
# Wrap Up
|
||||
|
||||
Most of these tips are meant to make your technical interviews more representative of real-world experiences and enable a more diverse pool of candidates to succeed in your process. I hope these tips help give you better insights into your candidates and enable a team that’s more well-rounded, ultimately bringing more value to your company and teams.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
}
|
||||
---
|
||||
|
||||
## Testing the limits of `firstBaselineToTopHeight` and `lastBaselineToBottomHeight` to deliver a perfect result.
|
||||
# Testing the limits of `firstBaselineToTopHeight` and `lastBaselineToBottomHeight` to deliver a perfect result.
|
||||
|
||||
_**I really care about implementation.**_ I obsess over it. I’m constantly thinking about it.
|
||||
|
||||
@@ -38,7 +38,7 @@ _[Plaid’s `BaselineGridTextView` library](https://github.com/android/plaid/blo
|
||||
|
||||
**If this isn’t good enough for you and you’d rather have control over every aspect of the UI, then come along.**
|
||||
|
||||
## Introduction
|
||||
# Introduction
|
||||
|
||||
Android has two main `TextView`s; one of them is `AppCompatTextView`, which has been available for quite a while, and `MaterialTextView` (which extends `AppCompatTextView`). They are identical, with the latter allowing a line-height attribute to be set in a `textAppearance` (if you don’t know what that means, no worries). _**Go with `MaterialTextView`**._
|
||||
|
||||
@@ -48,7 +48,7 @@ Shortly after, Google removed those API restrictions by backporting those featur
|
||||
|
||||
However, if you seek fidelity, you’ll find that `lineHeight` on Android differs from other platforms and most design tools.
|
||||
|
||||
## How is it any different?
|
||||
# How is it any different?
|
||||
|
||||
Let us take a look at some examples; one with a single line, then two lines, then three lines with line height set to `24pt/sp`.
|
||||
|
||||
@@ -78,7 +78,7 @@ Here’s a simple mockup, detailing the spacing between a title and a subtitle.
|
||||
|
||||
But even if it did have an effect, the problems wouldn’t stop there; the issue is more complex than that.
|
||||
|
||||
## What designers want, and what developers can do
|
||||
# What designers want, and what developers can do
|
||||
|
||||
Designers, like myself, like to see perfect alignment. We like consistent values and visual rhythm.
|
||||
|
||||
@@ -86,7 +86,7 @@ Designers, like myself, like to see perfect alignment. We like consistent values
|
||||
|
||||
Unfortunately, translating values from a design tool wasn’t possible. You had the option to either pixel nudge (pictured above, right), or forget about alignment altogether, thus leading to an incorrect implementation that would, yet again, be shorter than the mockups.
|
||||
|
||||
### …Until now!
|
||||
## …Until now!
|
||||
|
||||
_`firstBaselineToTopHeight`_ and _`lastBaselineToBottomHeight`_ are powerful tools for Android design. They do as the name suggests: If _`firstBaselineToTopHeight`_ is set to `56sp`, then that’ll become the distance between the first baseline and the top of a `TextView`.
|
||||
|
||||
@@ -100,7 +100,7 @@ This is something I’ve personally tested in an app I designed. [**Memoire**, a
|
||||
|
||||
*Memoire’s TextViews are all customized using these APIs.*
|
||||
|
||||
## What is the purpose of firstBaselineToTopHeight and lastBaselineToBottomHeight?
|
||||
# What is the purpose of firstBaselineToTopHeight and lastBaselineToBottomHeight?
|
||||
|
||||
In reality, the new attributes were actually made to be used when creating layouts: you want to make sure the baseline is a certain distance from another element, and it also helps to align the first and lastBaseline to a `4dp` grid. This mirrors the way iOS layouts are built.
|
||||
|
||||
@@ -132,7 +132,7 @@ The overrides will take precedence to whatever value you set in your **`styles.x
|
||||
|
||||
Implementing margins instead of overriding values also matches the way layouts work within Android Studio and design tools like Sketch and Figma. It also ensures that your layouts can scale well to different font sizes.
|
||||
|
||||
## So, how can you adapt your TextViews? Design goes first.
|
||||
# So, how can you adapt your TextViews? Design goes first.
|
||||
|
||||
It’s actually pretty simple. Let’s walk through how to adapt one of Material Design’s standard type sizes: Headline 6 — used inside AppBars and dialog titles.
|
||||
|
||||
@@ -202,7 +202,7 @@ You would need to find these values for every text style in your app, but if you
|
||||
|
||||

|
||||
|
||||
## How to implement these values (as a developer)
|
||||
# How to implement these values (as a developer)
|
||||
|
||||
All of them follow the same template.
|
||||
|
||||
@@ -229,7 +229,7 @@ Let’s use Memoire once again as an example.
|
||||
|
||||

|
||||
|
||||
### Each has a different function:
|
||||
## Each has a different function:
|
||||
|
||||
**`TextAppearance`:** Applied in styles to theme Material Components globally.
|
||||
|
||||
@@ -242,13 +242,13 @@ For example, _**`textAppearanceCaption`**_, _**`textAppearanceBody1`**_, etc.
|
||||
|
||||
*What happens to a `TextView` when a `TextStyle` is properly applied.*
|
||||
|
||||
## And now, a couple of warnings
|
||||
# And now, a couple of warnings
|
||||
|
||||
### Loss of vertical padding
|
||||
## Loss of vertical padding
|
||||
|
||||
When setting a style to a `TextView`, keep in mind that `firstBaseline` and `lastBaseline` are designed to replace vertical padding. This means that, whenever set, a `TextStyle` will nullify all vertical padding values.
|
||||
|
||||
### Do not apply `TextStyle` to Material Components. Use `TextAppearance` for those instances instead.
|
||||
## Do not apply `TextStyle` to Material Components. Use `TextAppearance` for those instances instead.
|
||||
|
||||
Applying a `TextStyle` to a component — instead of a `TextAppearance` — causes serious issues.
|
||||
|
||||
@@ -260,7 +260,7 @@ This happens because Material Components already have padding that _**IS NOT**_
|
||||
|
||||
As far as other issues, I haven’t been able to find any.
|
||||
|
||||
## Resources, resources, resources!
|
||||
# Resources, resources, resources!
|
||||
|
||||
Now that you’ve scrolled all the way down without reading a single word, here’s all the stuff you’ll need:
|
||||
|
||||
@@ -268,7 +268,7 @@ Now that you’ve scrolled all the way down without reading a single word, here
|
||||
|
||||
*Figma document with code and layout samples.*
|
||||
|
||||
### For designers: [Figma Document](https://www.figma.com/file/F1RVpdJh73KmvOi06IJE8o/Hard-Grid-—-Text-Components/duplicate)
|
||||
## For designers: [Figma Document](https://www.figma.com/file/F1RVpdJh73KmvOi06IJE8o/Hard-Grid-—-Text-Components/duplicate)
|
||||
|
||||
Document containing:
|
||||
|
||||
@@ -282,7 +282,7 @@ Document containing:
|
||||
|
||||
* Customizable code blocks for each style in a text box, so you can change each depending on your theme and hand it to developers
|
||||
|
||||
### For developers: [styles.xml](./styles.xml)
|
||||
## For developers: [styles.xml](./styles.xml)
|
||||
|
||||
A styles.xml file containing:
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ Interviewing for frontend engineering positions can be difficult. There’s a lo
|
||||
|
||||
While we’ve discussed [5 tips for tech recruiting](https://coderpad.io/blog/5-tips-for-tech-recruiting/), let’s take a look at some of the things we feel are more specific to a frontend technical screening.
|
||||
|
||||
## JavaScript Baseline
|
||||
# JavaScript Baseline
|
||||
|
||||
Whether you’re using a JavaScript framework or simply adding logic to a vanilla JS website, good frontend candidates need to know some basics of JavaScript in order to create business logic.
|
||||
|
||||
@@ -25,20 +25,20 @@ Regardless of the code style you utilize, you may want to ask about JavaScript b
|
||||
|
||||
Likewise, “gotcha” questions specifically designed to be confusing or obtuse are unhelpful in gauging real-world problem-solving in any technical interview.
|
||||
|
||||
## Design Focus
|
||||
# Design Focus
|
||||
|
||||
As a frontend engineer, being able to build an app to match designs is important. If your interview process includes a design for your candidate to build, check to see how consistent their implementation is to your design. While minor changes in element sizing can be easily glossed over, they can impact a user’s view of the product. Ideally, you want to include a way to analyze the provided design for the engineer to check the values of padding and other measurements. Oftentimes in technical interviews, the only provided design is a screenshot - having to guess-and-check the pixel values proves a frustrating candidate experience.
|
||||
|
||||
If your exercise doesn’t include a design to utilize, I wouldn’t evaluate their skills based on the UX or UI. Unless the role you’re hiring for is a mixture of designer and engineering, keep in mind - engineers don’t necessarily need to have design skills.
|
||||
|
||||
### Different Screens
|
||||
## Different Screens
|
||||
|
||||
With mobile devices becoming more and more predominant in today’s society, it’s important to know that your candidate can scale the application’s UI to non-desktop devices. Even if a design isn’t provided, ask your candidate to include a view for smaller screens as well.
|
||||
|
||||
|
||||
While JavaScript is more than able to conditionally render logic based on screen size, it’s suggested to utilize CSS’s media queries whenever possible. This allows your app to adjust to various-sized screens (and often helps with SEO).
|
||||
|
||||
## Frameworks
|
||||
# Frameworks
|
||||
|
||||
Most modern frontend applications are written with a framework like React or Vue. Luckily for engineers looking to switch roles, the core concepts of many of these frameworks are similar in nature.
|
||||
|
||||
@@ -46,25 +46,25 @@ You may want lead engineer or senior developer candidates to have in-depth knowl
|
||||
|
||||
Because of this, there are some things you can ask of a candidate that may have had more experience in a different frameworks.
|
||||
|
||||
### Lifecycles
|
||||
## Lifecycles
|
||||
|
||||
Even frameworks that have recently moved away from highlighting lifecycle methods (such as React with Hooks), most frameworks have some concept of a lifecycle. Being able to have a candidate explain when and why a component will do its initial data capture, re-render, and un-render can help accentuate their core framework knowledge.
|
||||
|
||||
### Unidirectionality
|
||||
## Unidirectionality
|
||||
|
||||
While frameworks like Vue or Angular allow you to bubble events up from a child component to a parent, it’s generally accepted that keeping your application architecture unidirectional is the best practice. We’ve [written a bit about what that means in practice](https://coderpad.io/blog/master-react-unidirectional-data-flow/), but it’s something to be cognizant of when evaluating a candidate’s code submission.
|
||||
|
||||
## Empathy
|
||||
# Empathy
|
||||
|
||||
A major part of collaborating within a team effectively is a candidate’s empathy for others. Remember, engineering often utilizes significantly more interpersonal skills than most give credit. It’s important that your engineers are able to communicate with one another effectively. Great engineers will even consider users in their daily work and raise concerns or thoughts when they see something that misaligns with the user’s experience in your app.
|
||||
|
||||
### Accessibility
|
||||
## Accessibility
|
||||
|
||||
Part of a front-end engineer’s role is to make sure that the application they’re building is usable by all users. Making sure that users with screen-readers, color blindness, or other impairments are able to use your application as easily as other users is important.
|
||||
|
||||
This could mean bringing up problems with color contrasts in a provided design, making sure that the candidate is using semantic HTML, or even that they’re utilizing the right `aria` attributes. Don’t forget that CSS can impact screen-reader support through properties like flexbox’s “[order](https://developer.mozilla.org/en-US/docs/Web/CSS/order)”
|
||||
|
||||
### Documentation
|
||||
## Documentation
|
||||
|
||||
Empathy isn’t just something that should be expressed to users. After all, communication within a team is extremely important and much more frequent. One way that communication can be increased within a codebase is documentation. While many tend to think of documentation as dedicated docs pages shared with engineers, documentation can take many forms.
|
||||
|
||||
@@ -73,7 +73,7 @@ For example, code comments that explain how a particular bit of code can be a fo
|
||||
|
||||
In other scenarios, creating example projects that showcase component’s design can help be a bridge of communication between designers, engineers, and product managers. You can help encourage candidates to add a development route that acts as a showcase of their UI components.
|
||||
|
||||
## Ignore Style Differences
|
||||
# Ignore Style Differences
|
||||
|
||||
While most of this article has been focused on things ***to\*** do and look for in an interview, let’s look at something that shouldn’t be focused on: code style. While there are certainly instances where `for` loops make more sense than `forEach`, or `function() {}` declarations are required instead of `() => {}` functions, most of the time they shouldn’t be taken as a positive or negative indicator of code quality. Some engineers might prefer newer syntaxes such as object destructuring but other engineers might have experience prior to the introduction of those syntax introductions.
|
||||
|
||||
@@ -81,6 +81,6 @@ There are exceptions to this - using a single `for` loop may be more performant
|
||||
|
||||
Regardless of code preferences, make sure to communicate expectations with your candidate ahead of time. There’s nothing worse than shifting goalposts when working on a project - especially during an interview
|
||||
|
||||
## Conclusion
|
||||
# Conclusion
|
||||
|
||||
While we feel that these points provide a good baseline of evaluation for technical screenings, interviews are dynamic and change from company to company. What are the things you look for during a frontend interview? Let us know [on Twitter](https://twitter.com/coderpad) or [on our community Slack](https://bit.ly/coderpad-slack) - we’d love to see you there!
|
||||
@@ -33,7 +33,7 @@ The first thing I do, _before looking at any tech whatsoever is think about it f
|
||||
|
||||
These are all of the questions I layout before even thinking about coding. I first start by white-boarding these things, explaining them to both myself and my partners, and generally doing my due-diligence concerning project planning.
|
||||
|
||||
## Wholistic Vision {#whats-your-vision}
|
||||
# Wholistic Vision {#whats-your-vision}
|
||||
|
||||
My holistic vision would consist of:
|
||||
|
||||
@@ -47,7 +47,7 @@ My holistic vision would consist of:
|
||||
|
||||
While the first point doesn't inform us of much at this early stage (we'll touch on UI tooling selection later), we can glean from the second point that we'll have to maintain some kind of storage layer. This will be something we'll need to keep in mind as we structure our goals.
|
||||
|
||||
## Target Audience {#who-are-you-targetting}
|
||||
# Target Audience {#who-are-you-targetting}
|
||||
|
||||
In this case, the groups of people I would want to appeal to are:
|
||||
|
||||
@@ -57,7 +57,7 @@ In this case, the groups of people I would want to appeal to are:
|
||||
|
||||
This potentially broad appeal might be able to drive a lot of business, but without a focused plan and a solid profit model, the project would fall flat.
|
||||
|
||||
## Profit Model {#layout-your-profit-model}
|
||||
# Profit Model {#layout-your-profit-model}
|
||||
|
||||
We'd plan to drive revenue by using the following profit model:
|
||||
|
||||
@@ -65,7 +65,7 @@ We'd plan to drive revenue by using the following profit model:
|
||||
- No students would pay for accounts but might pay for a subscription to course content
|
||||
- We'd likely take a cut of the subscription or charge for course features in some way
|
||||
|
||||
## Budget {#define-your-budget}
|
||||
# Budget {#define-your-budget}
|
||||
|
||||
Finally, none of this can be done without resources. These resources should be budgeted upfront, so what have we got? We have:
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@ If you're new to web development, it can be difficult to figure out when (and ho
|
||||
|
||||
In this article, we'll outline what Node and npm are, how to use both `npm` and `yarn` to install dependencies for your project, and point out some "gotcha's" that are good to keep in mind while using them.
|
||||
|
||||
## What's Node and `npm`, anyway? {#what-are-they}
|
||||
# What's Node and `npm`, anyway? {#what-are-they}
|
||||
|
||||
If you're new to web development - well, firstly, welcome! - you may wonder what Node and `npm` are. Great questions!
|
||||
|
||||
### Node {#whats-node}
|
||||
## Node {#whats-node}
|
||||
|
||||
Let's start with Node. Node is a [JavaScript runtime](/posts/how-computers-speak/#compiled-vs-runtime) that allows you to run JavaScript code on your machine without having to run your JavaScript in a browser. This means that you can write JavaScript that interacts with your computer in ways your browser cannot. For example, you can host a REST web server from Node, write files to your hard drive, interact with operating system APIs (like notifications), and more!
|
||||
|
||||
@@ -26,7 +26,7 @@ Let's start with Node. Node is a [JavaScript runtime](/posts/how-computers-speak
|
||||
|
||||
Node also comes with an advantage over browsers for running JavaScript: you can interface with lower-level programming languages such as C via [Node's N-API](https://nodejs.org/api/n-api.html#n_api_node_api). This means that libraries you rely on can build on top of this N-API to provide a way to do things like send native desktop notifications, show something particular in your taskbar, or any other action that would require lower-level access to your local machine than JavaScript typically provides.
|
||||
|
||||
### `npm` {#whats-npm}
|
||||
## `npm` {#whats-npm}
|
||||
|
||||
Any sufficiently useful programming language needs an ecosystem to rely on. One of the primary elements for an ecosystem is a collection of libraries that you can use to build out your own libraries and applications.
|
||||
|
||||
@@ -41,7 +41,7 @@ When, say, Facebook wants to publish a new version of `react`, someone from the
|
||||
|
||||
While the registry is vital for the usage of the CLI utility, most of the time we say `npm` in this article, we're referring to the CLI tool. We'll make sure to be explicit when talking about the registry itself
|
||||
|
||||
## Setting Up Node {#setup-node}
|
||||
# Setting Up Node {#setup-node}
|
||||
|
||||
Before we explain how to install Node, let's explain something about the release process of the software.
|
||||
|
||||
@@ -57,7 +57,7 @@ The "current" release, on the other hand, usually sees new features of JavaScrip
|
||||
|
||||
NodeJS switches back and forth between LTS and non-LTS stable releases. For example, Node 12 and 14 were LTS releases, but Node 13 and 15 were not. You can [read more about their release cycle on their website](https://nodejs.org/en/about/releases/)
|
||||
|
||||
### Installing Node {#installing-node}
|
||||
## Installing Node {#installing-node}
|
||||
|
||||
You can find pre-built binaries ready-to-install from [NodeJS' website](https://nodejs.org/en/download/). Simply download the package you want and install it.
|
||||
|
||||
@@ -67,7 +67,7 @@ Node installs come pre-packaged with their own version of `npm`, so don't worry
|
||||
|
||||
However, the process of upgrading and changing version of NodeJS can be difficult. This is why I (and many others) recommend using NVM to manage your Node versions.
|
||||
|
||||
#### NVM {#nvm}
|
||||
### NVM {#nvm}
|
||||
|
||||
While Node has a fairly stable API (and their LTS releases are often supported for many years at a time), there may be instances where it's benificial to have the ability to quickly upgrade and change the currently installed Node versions.
|
||||
|
||||
@@ -85,7 +85,7 @@ Additionally, you can (and, in order to use `nvm`, **must** use `nvm` to do so)
|
||||
nvm install --lts
|
||||
```
|
||||
|
||||
##### Switching Node Versions {#nvm-switch-node-ver}
|
||||
#### Switching Node Versions {#nvm-switch-node-ver}
|
||||
|
||||
NVM is a useful tool to switch Node versions, but there is something that should be noted before you do so. When you switch Node versions, it also resets the globally installed packages. This means that if you ran:
|
||||
|
||||
@@ -97,7 +97,7 @@ On Node 12, when you switch to Node 14, and attempt to run a `create-react-app`
|
||||
|
||||
It's also worth noting that some packages (like `sass`) have native dependencies. This means that they need to run specific commands on install depending on the version of Node you have installed. Because of this, if you switch from Node 12 to Node 14, you may need to re-run `npm i` on your packages before you attempt to re-run your applications.
|
||||
|
||||
##### Windows NVM {#windows-nvm}
|
||||
#### Windows NVM {#windows-nvm}
|
||||
|
||||
It's worth noting that the Windows variant of `nvm` does not support the same commands as the macOS and Linux variants. As such, when you find instructions for `nvm` online, you may have to find the alternative versions of those commands for the Windows version
|
||||
|
||||
@@ -113,7 +113,7 @@ Then, simply declare it as your main version of node:
|
||||
nvm use 12.16.3
|
||||
```
|
||||
|
||||
#### Upgrading NPM {#upgrading-npm}
|
||||
### Upgrading NPM {#upgrading-npm}
|
||||
|
||||
The version of `npm` that's shipped with Node is typically good enough for 99.99% of use-cases. Like any other software, however, bug fixes and features are added to new versions of `npm`. You can follow [the official `npm` blog](https://blog.npmjs.org/) to read about new features and bug fixes the versions introduce.
|
||||
|
||||
@@ -125,7 +125,7 @@ npm i -g npm@latest
|
||||
|
||||
> Keep in mind that if you switch Node versions using `nvm`, you will need to re-run this command on every version of installed Node, as switching Node also switches the installed version of `npm`.
|
||||
|
||||
### Yarn {#yarn}
|
||||
## Yarn {#yarn}
|
||||
|
||||
`npm` isn't the only game in town when it comes to installing packages for use in webdev. One of the biggest alternatives to `npm` is the `yarn` package manager.
|
||||
|
||||
@@ -149,7 +149,7 @@ However, the ways `npm` and `yarn` install packages on your local machine are di
|
||||
|
||||
> Want to learn the differences between `npm` and `yarn` yourself? We're working on an article that covers that exact topic in-depth, both for newcomers and experiences devs alike. Be sure to subscribe to our update emails (at the bottom of the page right above the comments) to catch when that article lands!
|
||||
|
||||
### Installing Yarn {#install-yarn}
|
||||
## Installing Yarn {#install-yarn}
|
||||
|
||||
Once you have node and npm installed, installing yarn is as simple as:
|
||||
|
||||
@@ -159,7 +159,7 @@ npm i -g yarn
|
||||
|
||||
It's worth noting that, just like `npm` and any other globally installed packages, [when you change Node version using `nvm`, you'll need to re-run this command](#nvm-switch-node-ver). However, if you're able to natively install `yarn`, you can sidestep this issue and have `yarn` persist through `nvm` version changes.
|
||||
|
||||
#### macOS {#yarn-mac}
|
||||
### macOS {#yarn-mac}
|
||||
|
||||
If you're using macOS and want to utilize `nvm`, you can also use Homebrew (a third party package manager for Macs) to install `yarn` natively:
|
||||
|
||||
@@ -169,7 +169,7 @@ brew install yarn
|
||||
|
||||
> There are other methods to install Yarn on macOS if you'd rather. [Look through `yarn`'s official docs for more](https://classic.yarnpkg.com/en/docs/install/#mac-stable)
|
||||
|
||||
#### Windows {#yarn-windows}
|
||||
### Windows {#yarn-windows}
|
||||
|
||||
Just as there's a method for installing `yarn` natively on macOS, you can do the same on Windows using [the same third-party package manager we suggest using for installing and maintaining Windows programs on your machine, Chocolatey](https://unicorn-utterances.com/posts/ultimate-windows-development-environment-guide/#package-management):
|
||||
|
||||
@@ -181,7 +181,7 @@ choco install yarn
|
||||
> There are other methods to install Yarn on Windows if you'd rather. [Look through `yarn`'s official docs for more](https://classic.yarnpkg.com/en/docs/install/#windows-stable)
|
||||
|
||||
|
||||
## Using Node {#using-node}
|
||||
# Using Node {#using-node}
|
||||
|
||||
Now that you have it setup, let's walk through how to use Node. First, start by opening your terminal.
|
||||
|
||||
@@ -211,7 +211,7 @@ From here, you can type in JavaScript code, and hit "enter" to execute:
|
||||
|
||||
This view of Node - where you have an interactive terminal you can type code into - is known as the REPL.
|
||||
|
||||
### Executing JS Files {#node-run-file}
|
||||
## Executing JS Files {#node-run-file}
|
||||
|
||||
While Node's REPL is super useful for application prototyping, the primary usage of Node comes into effect when running JavaScript files.
|
||||
|
||||
@@ -278,7 +278,7 @@ You'll need to re-start Node to catch that update.
|
||||
|
||||
The way you restart a Node process is the same on Windows as it is on macOS - it's the same way you stop the process. simply type Ctrl+C in your terminal to stop the process running. Then, re-run your Node command.
|
||||
|
||||
#### Hot Reload on File Edit {#nodemon}
|
||||
### Hot Reload on File Edit {#nodemon}
|
||||
|
||||
Node being able to run JavaScript files is useful once you have a finished product ready-to-run. However, while you're actively developing a file, it can be frustrating to manually stop and restart Node every time you make a change. I've had so many instances where I've Googled "NodeJS not updating JavaScript file" at some point in my debugging, only to realize that I'd forgotten to restart the process.
|
||||
|
||||
@@ -292,7 +292,7 @@ npm i -g nodemon
|
||||
|
||||
Then, simply replace your `node index.js` command with `nodemon index.js`.
|
||||
|
||||
## Using NPM/Yarn {#using-pkg-manager}
|
||||
# Using NPM/Yarn {#using-pkg-manager}
|
||||
|
||||
With basic Node usage established, we can expand our abilities by learning how to use `npm`/`yarn` efficiently.
|
||||
|
||||
@@ -334,7 +334,7 @@ Or:
|
||||
yarn init
|
||||
```
|
||||
|
||||
### Dependencies {#deps}
|
||||
## Dependencies {#deps}
|
||||
|
||||
Most projects you'll run into will have at least one dependency. A dependency is a library that your project depends on for it's functionality. For example, if I use the [`classnames` library](https://www.npmjs.com/package/classnames) to generate CSS-friendly class names from a JavaScript object:
|
||||
|
||||
@@ -380,7 +380,7 @@ yarn add classnames
|
||||
|
||||
> Just because using `classnames` as an example here, doesn't mean you have to. You can use the name of whatever dependency you're wanting to add.
|
||||
|
||||
#### Semantic Versioning {#semver}
|
||||
### Semantic Versioning {#semver}
|
||||
|
||||
For each dependency listed, there is a number with three dots associated with it. These numbers represent the version of the library to install when running commands like `npm i`.
|
||||
|
||||
@@ -408,7 +408,7 @@ Because minor and patch releases do not contain breaking changes (when following
|
||||
|
||||
Again, this isn't the _only_ way to version a library, but it is an increasingly common method for making sure that new versions won't break your project's functionality.
|
||||
|
||||
##### SemVer Setting {#package-json-semver}
|
||||
#### SemVer Setting {#package-json-semver}
|
||||
|
||||
How can we leverage SemVer in our `package.json`? If you looked at the `dependencies` object in our example previously, you may have noticed an odd character that's not a number: `^`.
|
||||
|
||||
@@ -462,7 +462,7 @@ This can be useful when a package isn't following SemVer and instead includes br
|
||||
|
||||
There are other modifiers you can use such as version ranges that cross-over major releases, pre-release versions, and more. To learn more about these additional modifiers and to experiment with the tilde and caret modifiers, [NPM has setup a website that teaches you and lets you visually experiment with the modifiers](https://semver.npmjs.com/).
|
||||
|
||||
#### Dev Dependencies {#dev-deps}
|
||||
### Dev Dependencies {#dev-deps}
|
||||
|
||||
Let's take a closer look at the `package.json` we were using as an example.
|
||||
|
||||
@@ -487,7 +487,7 @@ If you include `prettier` and other tools you use to develop the library, it blo
|
||||
|
||||
**`devDependency` allows you to keep a list of tools you'll utilize when developing, but which your code itself does not rely on to run.**
|
||||
|
||||
#### Peer Dependencies {#peer-deps}
|
||||
### Peer Dependencies {#peer-deps}
|
||||
|
||||
While dependencies are incredibly useful, if you're using a framework like React, having every dependency in your project install a separate version of React would potentially cause issues. Each dep would have a different version, which may act differently, and your `node_modules` would be bloated.
|
||||
|
||||
@@ -508,7 +508,7 @@ This would allow you to have `react` installed on your project and able to share
|
||||
|
||||
It's worth noting in that in `npm 6`, you used to have to install these yourselves. However, `npm 7` made the change such that peer deps are installed automatically. If you see an error from a package saying that your peer dep doesn't match, find the project and make a pull request to add the correct versions of the peer deps. These warnings were not significant with `npm 6`, but with `npm 7`, these matter substantially more.
|
||||
|
||||
### Ignoring `node_modules ` {#gitignore}
|
||||
## Ignoring `node_modules ` {#gitignore}
|
||||
|
||||
Once you have your packages installed (either by using `yarn` or `npm`), **it's important that you _do not commit_ your `node_modules` folder** to your code hosting. By commiting `node_modules`, you:
|
||||
|
||||
@@ -526,7 +526,7 @@ node_modules/
|
||||
|
||||
Worried that your dependencies might not resolve the same version on systems like CI where having replicable stable dependency installs matter a lot? That's where lock files comes into play
|
||||
|
||||
### Lock Files {#package-lock}
|
||||
## Lock Files {#package-lock}
|
||||
|
||||
Once you run `npm i` on a project with dependencies, you'll notice a new file in your root folder: `package-lock.json`. This file is called your **"lockfile"**. **This file is auto-generated by `npm` and should not be manually modified.**
|
||||
|
||||
@@ -540,7 +540,7 @@ While it's imperative not to track your `node_modules` folder, you **want to com
|
||||
|
||||
> Something to keep in mind is that different major versions of `npm` use slightly differently formatted lock files. If part of your team is using `npm 6` and the other part uses `npm 7`, you'll find that each team replaces the lockfile every single time `npm i` is installed. To avoid this, make sure your team is using the same major version of `npm`.
|
||||
|
||||
### Scripts {#npm-scripts}
|
||||
## Scripts {#npm-scripts}
|
||||
|
||||
You'll notice that the above `package.json` has a `start` script. When `npm run start` or `yarn start` is ran, it will execute `node index.js` to run the file with Node. While `node` usage is common, you're also able to leverage any command that's valid on your machine. You could have:
|
||||
|
||||
@@ -570,7 +570,7 @@ You're not limited to a single command, either. Most projects will have "scripts
|
||||
}
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
# Conclusion
|
||||
|
||||
While there's always more to learn when it comes to developmental tooling, this has been an introduction to `npm`, `yarn`, and `node`! With this information, you should have more context when it comes to how dependency management and basic JavaScript usage are utilized in web projects. We suggest taking a look through some open-source projects on GitHub to see how they're doing things.
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ Unicorn Utterances is here to help. Let's start at the beginning.
|
||||
|
||||
At a very basic level, all websites are constructed from three foundational technologies: HTML, CSS, and JavaScript. Every other concept or technology related to front-end web development is either based on or works with one of these three building blocks. Getting a firm grasp on HTML, CSS, and JavaScript is critical to understanding web development and learning how to create your own websites and web applications.
|
||||
|
||||
|
||||
## HTML
|
||||
# HTML
|
||||
|
||||
HTML stands for [HyperText Markup Language](https://developer.mozilla.org/en-US/docs/Web/HTML), and the latest release is HTML5. HTML is the foundation of the modern internet. Every website you visit or web application you use is built on a structure created by an HTML document. A great way to visualize what HTML does is to think of a building under construction. All buildings have a frame, whether steel or wood, that creates the basic structure of the building from the foundation to the roof. This structure dictates the size and shape of the building. No matter what the building looks like when it is finished or the purpose it serves, everything will be built around that steel structure.
|
||||
|
||||
@@ -35,8 +34,7 @@ If you want to see the HTML of a website, all you have to do is access the devel
|
||||
|
||||
HTML also serves as the foundation for web accessibility for people with disabilities. [Screen readers](https://www.afb.org/blindness-and-low-vision/using-technology/assistive-technology-products/screen-readers) allow people who are blind or have low vision to use the internet in a primarily audio format. They work by reading the HTML directly and are programmed to follow its structure and pick up on information included in the element tags. Practicing good [web accessibility standards](https://www.w3.org/WAI/standards-guidelines/wcag/) is important for a modern web developer and makes websites that are easier to use for everyone.
|
||||
|
||||
|
||||
## CSS
|
||||
# CSS
|
||||
|
||||
CSS, or [Cascading Style Sheets](https://developer.mozilla.org/en-US/docs/Web/CSS), is what web developers use to control the look and feel of the websites they create.
|
||||
|
||||
@@ -48,8 +46,7 @@ In modern web development, CSS is written in a separate document called a "styl
|
||||
|
||||
CSS is a big topic, and you will very likely be learning more about it as long as you do web development. There are pre-made frameworks, preprocessors, and lots of other CSS-related goodies out there to dive into as you learn your new craft. The important thing at the outset is to learn the basics well so that you know what to expect as you write your own CSS stylesheets.
|
||||
|
||||
|
||||
## JavaScript
|
||||
# JavaScript
|
||||
|
||||
JavaScript is the programming language of the internet and web browsers. When you are shopping online, JavaScript is happening. When you check your local weather radar, JavaScript is happening. When you use social media, JavaScript is happening. Basically, JavaScript is what gives websites and web applications the ability to actually do something.
|
||||
|
||||
@@ -63,8 +60,7 @@ To go back to our building construction analogy… well, it starts to break down
|
||||
|
||||
JavaScript is a powerful tool that can be used to create everything from useful applications, to convenient shopping experiences, and even games. JavaScript is complex, and there is a lot to learn, but there are a lot of great free [resources](https://developer.mozilla.org/en-US/docs/Web/JavaScript) out there to learn the basics and get started writing code from scratch.
|
||||
|
||||
|
||||
## Conclusion
|
||||
# Conclusion
|
||||
|
||||
Now you should have a better conceptual understanding of the primary web technologies, what they do, and how they work together to create the internet that we see and use every day. Once you learn the basics of HTML, CSS, and JavaScript, you will have a firm foundation to build on to create your own websites and applications.
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ If you are completely new to Android development, I would recommend following th
|
||||
["Build your first app"](https://developer.android.com/training/basics/firstapp/) tutorial before
|
||||
reading this article.
|
||||
|
||||
## Contexts
|
||||
# Contexts
|
||||
|
||||
In Android, a `Context` is a general class that... facilitates your app's interaction with the
|
||||
Android system? I'm not sure how to best explain it, but it essentially gives you access to
|
||||
@@ -46,7 +46,7 @@ For more about Drawables and other image assets, see
|
||||
A general overview of app resources can be found
|
||||
[here](https://developer.android.com/guide/topics/resources/providing-resources).
|
||||
|
||||
## Intents
|
||||
# Intents
|
||||
|
||||
Every component inside of an Android app is started by an `Intent`. Components declared in an
|
||||
app's manifest can typically be invoked from _anywhere in the system_, but you can define
|
||||
@@ -64,7 +64,7 @@ context.startActivity(new Intent(context, ActivityClass.class));
|
||||
This call to `startActivity` sends the `Intent` to the Android system, which is then in charge of
|
||||
creating and opening the activity that you have specified.
|
||||
|
||||
### Starting an Unknown Activity
|
||||
## Starting an Unknown Activity
|
||||
|
||||
You do not always need to specify an explicit class to start a new activity, though. How would your
|
||||
app start an activity in another application? You don't know what its class name is, and if you did,
|
||||
@@ -89,7 +89,7 @@ _[so ridiculously slow](https://issuetracker.google.com/issues/68393945)_ is tha
|
||||
these lists, it has to query every single activity on the device asking "will you accept this Intent?"
|
||||
to make a list for the user to choose from.
|
||||
|
||||
### Sending Data to Activities
|
||||
## Sending Data to Activities
|
||||
|
||||
In order for an Activity to have any dynamic functionality, you will need to have some way of sending
|
||||
information to it. When you create an Intent to a new Activity, you should _never_ have an instance of
|
||||
@@ -99,7 +99,7 @@ circumstances this may not present any obvious issues, there are situations wher
|
||||
reliable way to tell an activity what to display while abiding by the laws of the system. There are two
|
||||
main ways of doing this, both of which have their own advantages and disadvantages:
|
||||
|
||||
#### 1. Create your own state / data provider.
|
||||
### 1. Create your own state / data provider.
|
||||
|
||||
This indirectly relies on having access to an instance of the activity, though it should not fail if
|
||||
it does not obtain an instance; rather than relying on the started activity being created, it acts
|
||||
@@ -113,7 +113,7 @@ very robust solution that will make your application much easier to maintain in
|
||||
it can be a little bit complicated, especially if you are writing a simple application that
|
||||
only needs to manage a small amount of information.
|
||||
|
||||
#### 2. Use Intent extras.
|
||||
### 2. Use Intent extras.
|
||||
|
||||
The other, much simpler method of transferring data between activities is to simply include the
|
||||
data in the Intent. This data will be passed with the Intent to the Android system when it starts
|
||||
@@ -144,7 +144,7 @@ such as Bitmaps or Drawables.
|
||||
For more information about Intents, there is a more descriptive summary of them in the
|
||||
[Android Developer Documentation](https://developer.android.com/reference/android/content/Intent).
|
||||
|
||||
### Note: More about Contexts
|
||||
## Note: More about Contexts
|
||||
|
||||
When your application is opened by the system, the first components to be created will be the
|
||||
`Application`, then the `Activity` - which will have a different Context from the Application
|
||||
@@ -159,7 +159,7 @@ of your app's `Application` class (which should be referenced from the manifest)
|
||||
Activities and other parts of your application look there for the information. The Application
|
||||
class can be obtained from any `Context` instance by calling `context.getApplicationContext()`.
|
||||
|
||||
## Activity Lifecycle
|
||||
# Activity Lifecycle
|
||||
|
||||
Activities are big and complicated things, and many events can occur during their use as a result
|
||||
of user interaction or just weird Android memory-saving things. However, it is important to know
|
||||
@@ -196,14 +196,14 @@ When the application is completely closed by the user, then you will receive:
|
||||
A more comprehensive overview of the Activity lifecycle can be found
|
||||
[here](https://developer.android.com/guide/components/activities/activity-lifecycle).
|
||||
|
||||
## More...
|
||||
# More...
|
||||
|
||||
What about tasks that you want to exist beyond the Activity lifecycle? Maybe you want music to
|
||||
keep playing after the user leaves the app, or you just want to perform a short action without
|
||||
opening an activity when a certain event occurs. There are two other components that can receive
|
||||
intents for this purpose: `Service` and `BroadcastReceiver`.
|
||||
|
||||
### Services
|
||||
## Services
|
||||
|
||||
Services can run in the background without being attached to a user interface for longer periods
|
||||
of time, for tasks such as playing music, downloading large files, or other potentially lengthy
|
||||
@@ -211,7 +211,7 @@ operations that shouldn't be terminated when the user leaves the app.
|
||||
|
||||
See: [Service documentation](https://developer.android.com/reference/android/app/Service).
|
||||
|
||||
### Broadcast Receivers
|
||||
## Broadcast Receivers
|
||||
|
||||
A broadcast receiver can be seen as more of an "event" that occurs once and is over. They can run
|
||||
independently from a UI, the same a Service, but only for a short period of time (I believe they are
|
||||
@@ -225,7 +225,7 @@ again when they are no longer needed.
|
||||
|
||||
See: [BroadcastReceiver documentation](https://developer.android.com/reference/android/content/BroadcastReceiver).
|
||||
|
||||
## Fin
|
||||
# Fin
|
||||
|
||||
That's all for now! This was not a very thorough overview of Android development, and I feel like
|
||||
I left a lot of holes and exceptions to what I mentioned here, but hopefully it is useful to someone.
|
||||
|
||||
@@ -22,7 +22,7 @@ TypeScript's popularity cannot be understated. Either you likely know someone wh
|
||||
|
||||
These goodies are enabled by the TypeScript compiler, which takes your TypeScript source code and output JavaScript source code, capable of running in any JavaScript environment.
|
||||
|
||||
### Doesn't JavaScript Have Types Already? {#javascript-types}
|
||||
## Doesn't JavaScript Have Types Already? {#javascript-types}
|
||||
|
||||
While JavaScript _does_ have a loose understanding of types, they're not strictly enforced.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
Internet Relay Chat is a difficult thing to get used to, especially for people who were born into this world of full graphical interfaces and messaging web apps that handle user interaction seamlessly. IRC is a little bit different, though it still has a lot of the functionality that conventional messengers do: group chats / channels, admin (operator) permissions, user ban lists, private messages, and _quite a bit more_. However, a lot of this functionality may seem obscured to new users, as most IRC clients don't have the fancy menus, dropdowns, or simple toggles and check box elements that are often taken for granted - they use more of a command line-like interface, having users remember the commands to execute a specific action instead, like `/motd` or `/whois fennifith`.
|
||||
|
||||
## Choosing a Client
|
||||
# Choosing a Client
|
||||
|
||||
The first thing that you'll want to do before logging into freenode is choose an IRC client to connect with. I've compiled a list of the ones that I have tried below.
|
||||
|
||||
@@ -36,13 +36,13 @@ The first thing that you'll want to do before logging into freenode is choose an
|
||||
- [Kiwi IRC](https://kiwiirc.com/)
|
||||
- [The Lounge](https://demo.thelounge.chat/)
|
||||
|
||||
## Connecting to Freenode
|
||||
# Connecting to Freenode
|
||||
|
||||
Connect to the freenode servers by specifying `chat.freenode.net` as the server, and either port `6697` if your client supports SSL/TLS connections, or `6667` if it does not. Many clients have a preset option for connections to freenode, for example in `irssi` you can simply type `/CONNECT Freenode` to connect to a freenode server without needing to configure anything else.
|
||||
|
||||
For a more detailed explanation of connecting to freenode, [Freenode's documentation](https://freenode.net/kb/answer/chat) might be useful.
|
||||
|
||||
## Registering a Nickname
|
||||
# Registering a Nickname
|
||||
|
||||
First, you'll want to choose a nick. This will be something that all users will see and address you by, so it should be easy to remember. If you have a twitter or github handle, it is best to make it as similar as possible to that in order to stay consistent. In the following steps, replace the information surrounded by `<>` with the relevant data.
|
||||
|
||||
@@ -55,11 +55,11 @@ First, you'll want to choose a nick. This will be something that all users will
|
||||
|
||||
Each time you reconnect to freenode, you will need to log in. [Freenode's registration docs](https://freenode.net/kb/answer/registration) have more information on this, but it is possible to simply run `/msg NickServ IDENTIFY <username> <password>` each time you connect.
|
||||
|
||||
## Joining a Channel
|
||||
# Joining a Channel
|
||||
|
||||
On most IRC servers, you can run `/list` to display a list of all of the channels on the server that you can join. However, as freenode has just shy of 50000 channels, this command will generate quite a large output that may not be to your liking. Two options here: you can either use a web index, such as [irc.netsplit.de](http://irc.netsplit.de/channels/?net=freenode), to view a list of channels in a more usable format, or you can use freenode's [alis tool](https://freenode.net/kb/answer/findingchannels) to search through the list with a query such as `/msg alis LIST programming`. Alis has quite a few other options to trim down the search results, and I reccomend taking a look at `/msg alis HELP LIST` before you start scrolling through 1000+ search results to look for a particular topic.
|
||||
|
||||
## General Use
|
||||
# General Use
|
||||
|
||||
By now, you've probably gotten a decent feel for how IRC chat works - most commands handle faulty input fairly gracefully and let you know what they're doing and how to use them properly. Most commands and usernames are case insensitive, and help can usually be found by simply adding `help` after the root command, ex: `/msg NickServ HELP VERIFY`. If you haven't come across them already, here is a list of various useful commands and what they do:
|
||||
|
||||
@@ -76,7 +76,7 @@ By now, you've probably gotten a decent feel for how IRC chat works - most comma
|
||||
|
||||
More commands, along with basic descriptions of how they work and examples of their use, can be found [here](https://www.livinginternet.com/r/r.htm).
|
||||
|
||||
## Policies
|
||||
# Policies
|
||||
|
||||
Last, but certainly not least, I recommend that you scroll through [freenode's policies](https://freenode.net/policies) to get an idea of the purpose of the project and what is deemed acceptable use of their servers. Most channels have their own code of conduct to go along with these policies, which you should review to make sure that you aren't unknowingly violating any rules when contributing to a discussion. The [channel guidelines](https://freenode.net/changuide) also list more definitions of what is considered to be acceptable behavior on IRC (and really any social network).
|
||||
|
||||
|
||||
@@ -18,9 +18,7 @@ But there are a few pitfalls out there for the unwary new React developer. One o
|
||||
|
||||
What does that mean?
|
||||
|
||||
|
||||
|
||||
## Browsing in Public {#public}
|
||||
# Browsing in Public {#public}
|
||||
|
||||
Well, as it turns out, anything that happens in the browser basically happens out in the open. Anyone who knows how to open a developer console can see the output of the JavaScript console, the results of network requests/responses, and anything hidden in the HTML or CSS of the current page. While you are able to mitigate this type of reverse-engineering by randomizing variable names in a build step (often called "Obfuscating" your code), even a fairly quick Google session can often undo all of the efforts you took to muddy the waters. The browser is a terrible place to try to store or use secret information like unencrypted passwords or API keys - and React runs in the browser!
|
||||
|
||||
@@ -30,27 +28,19 @@ So, what is the answer? How do you keep your API keys from falling into the hand
|
||||
|
||||
We can't keep things like API keys a secret in React because it runs in the browser on the user's computer. The solution is to make sure your React application never sees the API key or uses it all - that way, it is never sent to the user's local machine. Instead, we have to get a proxy server to make our API calls and send the data back to the React app.
|
||||
|
||||
|
||||
|
||||
## What is a Proxy Server? {#proxy}
|
||||
# What is a Proxy Server? {#proxy}
|
||||
|
||||
If you are unfamiliar with the term "proxy server", that's alright! If you think about how a React app would typically interface with an API, you'd have a `GET` call to the API server in order to get the data you want from the API. However, for APIs that require an API key of "client_secret", we have to include an API key along with the `GET` request in order to get the data we want. This is a perfectly understandable method for securing and limiting an API, but it introduces the problem pointed out above: We can't simply bundle the API key in our client-side code. As such, we need a way to keep the API key out of reach of our users but still make data accessible. To do so, we can utilize another server (that we make and host ourselves) that knows the API key and uses it to make the API call _for_ us. Here's what an API call would look like without a proxy server:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
Meanwhile, this is what an API call looks like with a proxy server:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
As you can see, the proxy server takes calls that you would like to make, adds the API key, and returns the data from the API server. It's a straightforward concept that we can implement ourselves.
|
||||
|
||||
|
||||
|
||||
## How to use a Proxy Server {#how-to-use}
|
||||
# How to use a Proxy Server {#how-to-use}
|
||||
|
||||
It might make more sense to talk about things the other way around and start with the front end. Instead of using React to make a direct request to an API for information, we tell React to send an HTTP request to our proxy server. Since we are writing our front end application in JavaScript, it makes life a little easier to write our server in Node, though you could use Ruby or Python or any other back end friendly language if you want.
|
||||
|
||||
@@ -122,9 +112,7 @@ app.get('/', async (req, res) => {
|
||||
|
||||
With that out of the way, let's get to the good part - keeping your API keys out of your source code!
|
||||
|
||||
|
||||
|
||||
## Environmental Variables and You {#environment}
|
||||
# Environmental Variables and You {#environment}
|
||||
|
||||
Most of the time, we want to keep things like API keys and other credentials out of the source code of an app. There are some very good security reasons for this practice. For one thing, if your project is open source and hosted on a place like GitHub, it will be exposed to anyone browsing the website, not to mention the fact that there are some less-than-savory people out there who have written web scraping scripts to look for publicly exposed API keys and exploit them. Furthermore, even for private projects API keys integrated into the source code is a potential security vulnerability. A hacker could find a way into your system and compromise the usage of the API key. Being able to hide them away in a more configurable manner might keep things safer.
|
||||
|
||||
@@ -150,9 +138,7 @@ The other potential "gotcha" is to make sure to include the `.env` file in your
|
||||
|
||||
Now, It's true that you can use environmental variables in React. But they [will not keep your secrets](https://create-react-app.dev/docs/adding-custom-environment-variables/) the way they do in Node! Those variables will be embedded into your React build, which means that anyone will be able to see them.
|
||||
|
||||
|
||||
|
||||
## Conclusion {#conclusion}
|
||||
# Conclusion {#conclusion}
|
||||
|
||||
Now you know how to whip up a simple Node server and use environmental variables to keep your secrets when making API calls with front end libraries/frameworks like React. It's actually pretty easy and can serve as an introduction to the basics of Node and Express if you haven't had a reason to use them before.
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ The answer is mostly through remote access. Fear not, dear reader, we’ll try t
|
||||
|
||||
 on [Unsplash](https://unsplash.com/s/photos/magic-keyboard?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)](cover.jpeg)
|
||||
|
||||
## External Monitor
|
||||
# External Monitor
|
||||
|
||||
I was lucky enough to have an [LG Ultrafine 4K](https://www.apple.com/shop/product/HMUA2VC/A/lg-ultrafine-4k-display) display in my possession for use with the MacBook Pro. These fancy displays are designed hand-in-hand with Apple are compatible with both Thunderbolt devices like the MacBook and with standard USB-C devices like the iPad. However, you can’t use the same cable, so if you are planning to buy this one, make sure you’re using the one with an iPad label on it before returning it out of frustration!
|
||||
|
||||
@@ -39,7 +39,7 @@ And in a clever workaround, a popular app called [Shiftscreen](http://shiftscree
|
||||
|
||||
I have to say, it’s an excellent option for specific tasks, but after a while I just learned to love the mirrored interface. Now I’m rarely spending my time in apps that provide full monitor support. App Switching via Cmd+Tab or the gestures is hugely satisfying, plus I don’t think it has brought my productivity down at all. In fact, it might have improved my focus on the task at hand.
|
||||
|
||||
## External Keyboard and Mouse/Trackpad
|
||||
# External Keyboard and Mouse/Trackpad
|
||||
|
||||
Speaking of hitting keys and performing gestures. While the iPad itself has an incredibly mobile form-factor, we owe ourselves a decent desk setup. After all, we are turning it into our personal workstation.
|
||||
|
||||
@@ -51,7 +51,7 @@ Now even cheaper options are possible, such as just getting a good regular Bluet
|
||||
|
||||
*Update: After quite some time with the Magic Keyboard for iPad on my desk, I revised the setup, with the tablet laying flat under the monitor to quickly take handwritten notes or drawings, with the Keychron K2 + Magic Trackpad 2 as input devices. We’ll see if it sticks!*
|
||||
|
||||
## Software Dev: Remote Server and Native Apps
|
||||
# Software Dev: Remote Server and Native Apps
|
||||
|
||||
Let’s get into some real engineering tools. As I'm sure you already know, there’s no walled garden like iOS/iPadOS. Apps are fully contained, *Files* is some kind of file explorer yet remains very limited, and firing up a local command line is pure fantasy.
|
||||
|
||||
@@ -75,7 +75,7 @@ The example below shows VS Code running in Safari for iPad. It would be a great
|
||||
|
||||
Side note: there is no such thing as a system clipboard manager on iPadOS, which is quite inconvenient when coming from other desktop platforms. While there are various paid solutions that offer cloud clipboard syncing and beyond, the non-iPad-optimized [Clipboard++](https://apps.apple.com/ca/app/clipboard/id854707788) brought me exactly what I needed: a way to automatically save my Cmd+Cs so I don’t have to worry about overwriting the system buffer. Neat!
|
||||
|
||||
## Software Dev (Bonus): Local Raspberry Pi
|
||||
# Software Dev (Bonus): Local Raspberry Pi
|
||||
|
||||
The folks for whom a cloud connection isn’t an option shouldn't lose hope: the most recent iteration of the sweet [Raspberry Pi](https://www.raspberrypi.org/products/raspberry-pi-4-model-b/) comes with a USB-C power and data port. Hence it’s possible to both run it and give it ethernet access from the iPad with one cable.
|
||||
|
||||
@@ -83,7 +83,7 @@ This enables a lot of possibilities, including local SSH, code-server-ing and ac
|
||||
|
||||
A great way to make sure some coding work gets done when we get to travel again ✈️
|
||||
|
||||
## Hardware Dev: Remote Server and Remote Desktop
|
||||
# Hardware Dev: Remote Server and Remote Desktop
|
||||
|
||||
Here comes the harder part: hardware (*easy)*.
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ Now that we have that covered let's generate our project!
|
||||
ng new my-scully-blog
|
||||
```
|
||||
|
||||
|
||||
We'll want to choose `y` when it asks us to add routing. The second question that will be raised is regarding what flavor of CSS you'd like. I like `SCSS`, so I chose that, but you're free to select any of the options that you deem fit for your blog.
|
||||
|
||||
If we pause here and run `ng serve`, we'll find ourselves greeted with the default generated app screen from the Angular core team upon visiting the `localhost:4200` URI in our browser.
|
||||
|
||||
@@ -15,7 +15,7 @@ As with any form of programming, there are dozens of ways to manage data inside
|
||||
|
||||
Today, we'll be covering one of the most important structural best practices to follow when building your React applications: Unidirectional data flow.
|
||||
|
||||
## What is Unidirectional Data Flow?
|
||||
# What is Unidirectional Data Flow?
|
||||
|
||||
Unidirectional data flow is the idea that components should only raise data in one direction. Child components should only call functions from parent components, while parent components should only set/pass data to their children.
|
||||
|
||||
@@ -23,7 +23,7 @@ Unidirectional data flow is the idea that components should only raise data in o
|
||||
|
||||
In order to explain how both of these look in real code, let's start with how a properly unidirectional parent and child component would be written.
|
||||
|
||||
## Unidirectional Demo
|
||||
# Unidirectional Demo
|
||||
|
||||
A great example of a set of components we'll use to demonstrate unidirectionality is a parent "App" component and a child "Form" component.
|
||||
|
||||
@@ -39,7 +39,7 @@ This is what a set of proper React components *should* look like. This pattern o
|
||||
|
||||
Now that we have a better understanding of the patterns to follow let's take a look at the wrong way to do things.
|
||||
|
||||
## Breaking from Suggested Patterns
|
||||
# Breaking from Suggested Patterns
|
||||
|
||||
Now that we've "lifted" the state, let's drop back down into `SimpleForm`. We'll start by changing `SimpleForm` to a class component and adding state.
|
||||
|
||||
@@ -98,7 +98,7 @@ export default function App() {
|
||||
|
||||
This code works, but has some inherent complexity issues. When you start expanding this component, this idea of separating your state and having to inspect the child reference from the parent makes development more difficult. Let's take a look visually how following the application logic is now more difficult with this pattern.
|
||||
|
||||
## Visualizing the Problem
|
||||
# Visualizing the Problem
|
||||
|
||||
First, let's start by taking a look at the `simpleRef` component, where the state is "lowered down" to the `SimpleForm` component:
|
||||
|
||||
@@ -127,7 +127,7 @@ As you can see, while the number of steps is similar between these methods (but
|
||||
|
||||
This is why the React core team (and the community at large) strongly suggests you use unidirectionality as often as possible.
|
||||
|
||||
## Conclusion & Challenge
|
||||
# Conclusion & Challenge
|
||||
|
||||
Understanding unidirectionality is integral to scaffolding scalable React applications. Unidirectionality doesn't just apply to React, either - Angular and Vue applications often require similar patterns for large scale codebases to be easier to follow and more performant.
|
||||
|
||||
|
||||
@@ -12,11 +12,8 @@
|
||||
|
||||
Every new C/C++ programmer will eventually reach the point at which they are forced to work with pointers and will undoubtedly realize that they extremely dislike using them because they are a little complex. Today, we'll be looking at what pointers are, deconstructing their usage, and hopefully, making the usage of pointers easier to grok.
|
||||
|
||||
|
||||
# What is a Pointer? {#what-is-a-pointer}
|
||||
|
||||
|
||||
|
||||
A pointer is simply a variable or object that instead of holding a value, holds a memory address to another spot in memory. You will commonly see a pointer being most recognizable by their declaration including the **\*** operator, also known as the **dereference operator**. This operator is called the dereference operator because when you try to access the value that the pointer is referencing, you have to use the **\*** operator to "de-reference" the value. Which is just a fancy way of saying, "go to that reference".
|
||||
|
||||
Here's an example:
|
||||
@@ -56,7 +53,6 @@ As you can see, the pointer p holds the memory address of num, and when you use
|
||||
|
||||
Pointers can also get a lot more complex and must be used in certain situations. For example, if you put an object on the heap (Check out my article on Virtual Memory to learn more about heap memory) then you will have to use a pointer because you can't access the heap directly. So, instead of having a pointer to an address on the stack, it will point to an address on the heap. You might even find yourself using double or triple pointers as you get more used to them.
|
||||
|
||||
|
||||
# What is a Reference? {#what-is-a-reference}
|
||||
|
||||
In simple terms, a reference is simply the address of whatever you're passing. The difference between a pointer and a reference lies in the fact that a reference is simply the **address** to where a value is being stored and a pointer is simply a variable that has it's own address as well as the address it’s pointing to. I like to consider the **&** operator the "reference operator" even though I'm pretty sure that's not actually what it is called. I used this operator in the last example, and it's pretty straightforward.
|
||||
@@ -143,8 +139,6 @@ As you can see, when passed by reference, the local value is changed, but when i
|
||||
|
||||
This gets confusing after a while if you're not paying attention to your outputs. In fact, Python gets even more confusing, but that's a topic for another day; be sure to sign up for our newsletter to see when that lands 😉
|
||||
|
||||
|
||||
|
||||
# Review/Conclusion {#conclusion}
|
||||
|
||||
Pointers and References are extremely important in your day to day work in languages like C/C++. C++ gives you a lot of manual control with the most common being memory. Knowing how each one of your pointers or variables are stored will help you write code faster and more efficiently. Knowing also how parameters are passed to your functions as well as how they are updated, will make your life **so** much easier.
|
||||
|
||||
@@ -17,7 +17,7 @@ But what does Rust do that has garnered such a positive response from the commun
|
||||
|
||||
One of the many features that highlights Rust’s capabilities is its handling of enums and matching.
|
||||
|
||||
## Enums
|
||||
# Enums
|
||||
|
||||
Like many languages with strict typings, Rust [has an enum feature](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html). To declare an enum is simple enough, start with `pub enum` and name the values.
|
||||
|
||||
@@ -49,7 +49,7 @@ fn get_version(_lang: CodeLang) -> &'static str {
|
||||
|
||||
While this code *works*, it’s not very functional. If you pass in “CodeLang::JavaScript”, the version number isn’t correct. Let’s take a look at how we can fix that in the next section.
|
||||
|
||||
## Matching
|
||||
# Matching
|
||||
|
||||
While you *could* use `if` statements to detect which enum is passed in, like so:
|
||||
|
||||
@@ -90,7 +90,7 @@ fn get_version(lang: CodeLang) -> &'static str {
|
||||
|
||||
If you’re familiar with a programming language that has a feature similar to “[switch/case](https://www.tutorialspoint.com/cprogramming/switch_statement_in_c.htm)”, this example is a close approximation of that functionality. However, as you’ll soon see, `match` in Rust is significantly more powerful than most implementations of switch/case.
|
||||
|
||||
## Pattern Matching
|
||||
# Pattern Matching
|
||||
|
||||
While most implementations of `switch/case` only allow simple primitives matching, such as strings or numbers, Rust’s `match` allows you to have more granular control over what is matched and how. For example, you can match anything that isn’t matched otherwise using the `_` identifier:
|
||||
|
||||
@@ -116,7 +116,7 @@ fn get_version<'a>(lang: CodeLang, other_lang: CodeLang) -> (&'a str, &'a str) {
|
||||
|
||||
This shows some of the power of `match` . However, there’s more that you’re able to do with enums.
|
||||
|
||||
## Value Storage
|
||||
# Value Storage
|
||||
|
||||
Not only are enums values within themselves, but you can also store values within enums to be accessed later.
|
||||
|
||||
@@ -164,7 +164,7 @@ fn get_version(lang: CodeLang) -> &'static str {
|
||||
|
||||
Not all enums need to be manually set, however! Rust has some enums built-in to the language, ready for use.
|
||||
|
||||
## Option Enum
|
||||
# Option Enum
|
||||
|
||||
While we’re currently returning the string `”Unknown”` as a version, that’s not ideal. Namely, we’d have to do a string comparison to check if we’re returning a known version or not, rather than having a value dedicated to a lack of value.
|
||||
|
||||
@@ -216,11 +216,11 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
## Operators
|
||||
# Operators
|
||||
|
||||
While the above code functions as intended, if we add more conditional logic, we may find ourselves wanting to make abstractions. Let’s look at some of these abstractions Rust provides for us
|
||||
|
||||
### Map Operator
|
||||
## Map Operator
|
||||
|
||||
What if we wanted to convert `rust_version` to a string, but wanted to handle edge-cases where `None` was present.
|
||||
|
||||
@@ -274,7 +274,7 @@ pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
|
||||
|
||||
As you can see, we matched our implementation very similarly, matching `Some` to another `Some` and `None` to another `None`
|
||||
|
||||
### And Then Operator
|
||||
## And Then Operator
|
||||
|
||||
While the automatic wrapping of the `.map` function return value into a `Some` can be useful in most instances, there may be times where you want to conditionally make something inside the `map`
|
||||
|
||||
@@ -302,7 +302,6 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Which we can rewrite using Rust’s `and_then` operator:
|
||||
|
||||
```rust
|
||||
@@ -335,7 +334,7 @@ pub fn and_then<U, F: FnOnce(T) -> Option<U>>(self, f: F) -> Option<U> {
|
||||
}
|
||||
```
|
||||
|
||||
## Putting it Together
|
||||
# Putting it Together
|
||||
|
||||
Now that we’re familiar with the Option enum, operators, and pattern matching let’s put it all together!
|
||||
|
||||
@@ -485,7 +484,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
## Conclusion & Challenge
|
||||
# Conclusion & Challenge
|
||||
|
||||
All of these features are used regularly in Rust applications: enums, matching, option operators. We hope that you can take these features and utilize them in your applications along your journey to learn Rust.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Last week, I started setting up continuous integrations for some of my projects.
|
||||
|
||||
A small preface, make sure that you create your account on [travis-ci.com](https://travis-ci.com/), not [travis-ci.org](https://travis-ci.org/). Travis previously had their free plans on their .org site and only took paying customers on .com, but they have since begun [migrating all of their users](https://docs.travis-ci.com/user/open-source-on-travis-ci-com/) to travis-ci.com. However, for some reason they have decided _not to say anything about it_ when you create a new account, so it would be very easy to set up all of your projects on their .org site, then (X months later) realize that you have to move to .com. This isn't a huge issue, but it could be a little annoying if you have _almost 100 repositories_ like I do which you would have to change (though I have only just started using Travis, so it doesn't actually affect me). Just something to note.
|
||||
|
||||
## Step 1: Start your first build
|
||||
# Step 1: Start your first build
|
||||
|
||||
There are a few basic things to do in order to build your project. Assuming that you have already [set up your account](https://docs.travis-ci.com/user/tutorial/) and authenticated it with your GitHub, you will next want to create a file named `.travis.yml` in your project's root directory. One thing to keep in mind here is that the YAML format in this file is heavily dependent on whitespace; tab characters are invalid, indents must be made only in spaces, and a sub-section or parameter **must** be indented or it will not be treated as such. To start, let's write a basic file that should properly build most up-to-date Android projects.
|
||||
|
||||
@@ -39,7 +39,7 @@ You will want to update the `android` and `build-tools` versions to match the re
|
||||
|
||||
Now, when you commit this file to your repository (the branch should not make a difference), Travis should build your project and notify you of the result.
|
||||
|
||||
## Step 2. Signing APKs
|
||||
# Step 2. Signing APKs
|
||||
|
||||
So Travis _can_ successfully build your APK, but that itself is not very useful. It can do something with debug APKs, sure, but deploying them won't be very useful as they won't be under the same signature, and users won't be able to update from the existing application. So... we need a way to sign the application using an existing keystore that Travis has access to.
|
||||
|
||||
@@ -53,13 +53,13 @@ No, they can't. This is because the values passed to the command are two [enviro
|
||||
|
||||
If you are worried about security (or if you aren't worried enough), I highly recommend that you read [Travis's documentation](https://docs.travis-ci.com/user/best-practices-security/#Steps-Travis-CI-takes-to-secure-your-data) on best practices regarding secure data.
|
||||
|
||||
### Part A. Encrypting files
|
||||
## Part A. Encrypting files
|
||||
|
||||
You can go about this two ways: a difficult way, or a difficult way. You can either install [Travis's CLI tool](https://docs.travis-ci.com/user/encrypting-files/) for the sole purpose of logging in, encrypting your file, and setting its environment variables, or you can just do it yourself. I will provide instructions for both. Do what you like.
|
||||
|
||||
Note that if you want to automatically deploy your builds to Google Play, you may want to come back here and go through the exact same process later on, so you might want to skip this for now. If you don't, or want to do it twice anyway... carry on...
|
||||
|
||||
#### Using Travis's CLI
|
||||
### Using Travis's CLI
|
||||
|
||||
First, install it. Assuming you have Ruby set up, you'll want to run `gem install travis`. Since not everyone has Ruby set up, [here are their installation instructions](https://www.ruby-lang.org/en/documentation/installation/). A bit of a pain for something that you can just write yourself in my opinion, but hey, anything to avoid writing more code.
|
||||
|
||||
@@ -71,7 +71,7 @@ Assuming you have named your keystore `key.jks`, you will want to run `travis en
|
||||
|
||||
Side-note: if your keystore is a `.keystore` file, it shouldn't make a difference - just replace `key.jks` with `key.keystore` (or whatever it is named) whenever it appears.
|
||||
|
||||
#### Doing It Yourself
|
||||
### Doing It Yourself
|
||||
|
||||
Pick a key and a password. They shouldn't be excessively long, but not tiny either. Do not use special characters. In this example, I will use "php" as the key and "aaaaa" as the password.
|
||||
|
||||
@@ -93,11 +93,11 @@ before_install:
|
||||
|
||||
That's it! Push your changes to `.travis.yml` as well as `key.jks.enc`, and Jekyll should build your project.
|
||||
|
||||
### Part B. Dummy files
|
||||
## Part B. Dummy files
|
||||
|
||||
This isn't entirely necessary, but you can use some fake "dummy" files to add to version control alongside the "real" encrypted ones. When Travis decrypts your encrypted files, they will be overwritten, but otherwise they serve as quite a nice substitute to prevent anyone from getting their hands on the real files (and to prevent you from uploading the real ones by accident). You can find a few (`key.jks`, `service.json`, and `secrets.tar`) in the sample project [here](/redirects/?t=github&d=TravisAndroidExample).
|
||||
|
||||
### Part C. Signing the APK
|
||||
## Part C. Signing the APK
|
||||
|
||||
Now we want to actually use the key to sign our APKs. This requires a few changes to our app's build.gradle. Specifically, we need to specify a `signingConfig` that ONLY exists on Travis - we don't want our local builds (or the builds of other contributors) to be affected by this. Luckily, not only can we read environment variables from our `build.gradle` file using `System.getenv`, Travis automatically creates a nice "CI" variable to tell us that the build is happening in a Continuous Integration, so why don't we use that.
|
||||
|
||||
@@ -137,7 +137,7 @@ script:
|
||||
|
||||
Now it will create a proper release using these signing configs. Push everything to git and it should build a properly signed APK. Yay.
|
||||
|
||||
## Step 3. Deploying to github releases
|
||||
# Step 3. Deploying to github releases
|
||||
|
||||
This part is fairly simple, as Travis provides its own deployment functionality for this purpose. According to [their documentation](https://docs.travis-ci.com/user/deployment/releases/), for the bare minimum functionality all that you will need is to add the following to your `.travis.yml`...
|
||||
|
||||
@@ -156,7 +156,7 @@ Now, you _could_ follow this exactly and place your GitHub token directly in you
|
||||
|
||||
This should now create a release with a built (and signed) APK each time there is a new tag. Fair enough; all you have to do for it to deploy is create a new tag.
|
||||
|
||||
### Part A. Creating tags
|
||||
## Part A. Creating tags
|
||||
|
||||
What if you're lazy like me, though? What if you want to create a new release on each push to the master branch? (I have two branches in most of my projects, `develop` and `master`, for this purpose - only the commits currently in production are in the `master` branch)
|
||||
|
||||
@@ -171,7 +171,7 @@ deploy:
|
||||
|
||||
Well, it almost does the trick. The thing is, since we haven't created a tag, Travis doesn't know what version number we want to use. It just creates a new release using the commit hash as a title. That isn't very good. I wonder if we could somehow get the version number from our build.gradle file and use that instead...
|
||||
|
||||
### Part B. Version numbers
|
||||
## Part B. Version numbers
|
||||
|
||||
Let's write a gradle task to print our version number! Place the following in your app's `build.gradle`.
|
||||
|
||||
@@ -210,17 +210,17 @@ deploy:
|
||||
|
||||
Yay! Now we have fully automated releases on each push to master. Because of the `overwrite` parameter, it will overwrite existing releases if the version number has not been changed (a new release will be created if it has), so they will always be up to date.
|
||||
|
||||
## Step 4. Deploying to the Play Store
|
||||
# Step 4. Deploying to the Play Store
|
||||
|
||||
Travis doesn't have a deployment for the Play Store, so we will have to use a third party tool. I found [Triple-T/gradle-play-publisher](https://github.com/Triple-T/gradle-play-publisher/), which should work, except there isn't an option to deploy an existing APK without building the project. Not only would a deployment that requires building a project _twice_ be super wasteful and take... well, twice as long, [I ran into problems signing the APK](https://jfenn.me/redirects/?t=twitter&d=status/1061620100409761792) when I tried it, so... let's not. Instead, we'll modify the `script` to run the `./gradlew publish` command when a build is triggered from the master branch.
|
||||
|
||||
### Part A. Setup
|
||||
## Part A. Setup
|
||||
|
||||
Setup is fairly simple; just follow the directions in the plugin's readme. However, what should we do with the JSON file? PLEASE DO NOT ADD IT TO GIT. ANYONE WITH THIS FILE HAS ACCESS TO YOUR PLAY CONSOLE. WE'RE ENCRYPTING IT.
|
||||
|
||||
You can either encrypt it as a separate file, or you can put them both in a tar (`tar -cvf secrets.tar key.jks service.json`), encrypt that, and run `tar -xvf secrets.tar` once it has been decrypted. I am not sure if either will affect how secure they are. I have opted for the tar method as it gives me less things to keep track of.
|
||||
|
||||
### Part B. Publishing
|
||||
## Part B. Publishing
|
||||
|
||||
Now we can modify the `script` section of our `.travis.yml` to run the `./gradlew publish` command when a build is triggered from the master branch. This can be done using the "TRAVIS_BRANCH" environment variable which Travis handily creates for us. In other words...
|
||||
|
||||
@@ -231,7 +231,7 @@ script:
|
||||
|
||||
This should build a signed APK and upload it to the Play Store whenever a push is made to the `master` branch, then deploy the same APK to GitHub if it was built successfully. Important to note that using this method, the build will also fail if it has failed to upload the APK to the Play Store - so it _might_ not be an issue with your project if it results in a failure unexpectedly.
|
||||
|
||||
### Part C. Changelogs
|
||||
## Part C. Changelogs
|
||||
|
||||
Now, gradle-play-publisher requires you to specify a changelog at `app/src/main/play/release-notes/en-US/default.txt` for it to publish an APK. What if we want to use the same changelog for GitHub releases? We'll add another line to the `before_deploy` section and GitHub deployment to do so.
|
||||
|
||||
@@ -245,7 +245,7 @@ deploy:
|
||||
body: "$APP_CHANGELOG"
|
||||
```
|
||||
|
||||
## Finish
|
||||
# Finish
|
||||
|
||||
Hopefully this blog has gone over the basics of using Travis to deploy to GitHub and the Play Store. In later blogs, I hope to also cover how to implement UI and Unit tests, though I have yet to actually use them myself so I cannot yet write an article about them.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user