Saturday, January 7, 2023
In Remix, a child route can contribute content to a layout route via a handle
named export object.
Through this we can have the child route return a chunk of UI based on it’s data loader. In the example below, we might have
a child route contribute a link that a layout route might use to show breadcrumbs.
// Child Route File
export const loader = async ({ request, params }: LoaderArgs) => {
return { username: "Reese", userId: 1 };
};
export default function ChildRoute() {
return <p>My Content</p>;
}
export const handle = {
breadcrumb: (routeMatch: RouteMatch) => {
const { username, userId } = routeMatch.data;
return <Link to={`/users/${userId}`}>{username}</Link>;
},
};
// Layout Route File
export default function Layout() {
const matches = useMatches();
const breadcrumbs = matches
.filter((match) => match.handle && match.handle.breadcrumb)
.map((match, index) => {
return <li key={index}>{match.handle!.navMenuItem(match)}</li>;
});
return <ul>{breadcrumbs}</ul>;
}
Typesafety
In the example above we have a typesafety issue - anything we access on routeMatch.data
will be an any
type even
though we know the it will match the result of our loader.
Here’s how we can do this with some Typescript utilities… we’ll do this in a few steps to understand why each
utility is needed since our loader is a function that returns a Promise.
// The type here is a RouteData
// This is essentially an any type
const attemptOne = routeMatch.data;
// The type here is a function
// What we need is the type of the result of that function
const attemptTwo = routeMatch.data as typeof loader;
// The type here is a promise
// The loader returns a Promise but the data we get in routeMatch is the resolved data from that Promise.
const attemptThree = routeMatch.data as ReturnType<typeof loader>;
// The type here is the result of the promise that is returned by the loader function.
const attemptFour = routeMatch.data as Awaited<ReturnType<typeof loader>>;
Summary
As we see with using RouteData, there are times where we want to get the type of the
resolved result of a Remix loader function. There are two layers to get through to get the result.
- The loader is a function so we use the
ReturnType<>
Typescript utility to get the result type of the function
- The function returns a promise so we use the
Awaited<>
Typescript utility to get the result type of the Promise.
Putting it all together we end up with:
const data = routeMatch.data as Awaited<ReturnType<typeof loader>>;
Now our data has the proper type. As our loader changes we can be sure that anything relying on its data will fail to compile
if we try to access parts of that data that are no longer available.
Wednesday, February 28, 2018
When I was in school to be a teacher, the first iPhone came out and about a year later the first SDK came out. I knew immediately that I wanted to build something for it. The first app I (poorly) wrote was to help me check answers for some students I was tutoring. But I kept learning from my mistakes and read a ton.
Back then it was a lot easier to be a single developer on the App Store so I was able to make some extra money on the side while building a portfolio at the same time. That portfolio got me my foot in the door as a developer as I transitioned away from being a teacher. Having projects that were available for employers to see and play with that I could say I built from the ground up was vital to the transition.
Building something to solve a problem that you are facing or in a domain you know a lot about is important. It keeps you motivated and lets you focus on tackling the next technical issue rather than trying to solve for a problem you don’t know about. Start simple and keep making it better and better.
Some caveats to learning on your own… it takes a lot of time and effort. You have to be prepared to fail a lot and be willing to keeping coming back. I was lucky to be able to spend time during college and then having a half-time schedule my first few years as a teacher. I was privileged to not have to work full-time in college and had no kids or dependents to take care of.
Now some benefits to learning on your own… you are going to have different skills than someone coming from a comp-sci background. As a teacher I facilitated discussions and lessons with 20-30 students all with different needs, interests, and skills. That translates directly to being able to talk with your team to figure out what you are building and how it should be built. Coming in and being to scaffold conversations and identify where someone is coming from is a huge advantage. That’s not to say it’s impossible to come from a comp-sci background with these skills but I don’t think there is emphasis on these more ”human” skills.
These skills are more important than ever. In the last few years, I think we’ve seen that tech sector get into trouble when we forget that when we build something it gets put into a world of humans. Some examples of this failure:
- Fake news comes from the fact that Facebook and Twitter enabled everyone to communicate with each other directly (which is good) but failed to give the tools to discern who should be trusted (or who is even real).
- Jeff Bezos asks for ideas of how to use his fortune to help the world, but doesn’t see that on of the biggest things he could do is pay his warehouse workers more and let them unionize.
- Uber and Lyft offer lower prices than existing taxis but run at a loss and only exist on the bet that self driving cars come before the VC spigot runs out
Computer science education needs to put an emphasis on re-humanizing the field. For now, having some experience outside of it has some advantages.
Wednesday, April 12, 2017
TLDR: Check out Blueprint, a Visual Studio Code extension that lets you create files from templates.
After almost 9 years of iOS development, my full-time job is now as a web developer; an Angular (2) developer to be specific.
A year ago I wouldn’t have believed that I’d be working in a text editor made by Microsoft everyday, yet Visual Studio Code has turned into one of my favorite developer tools ever. I’m not an Xcode hater, but it is a bit strange that I get more consistent code completion from TypeScript/JavaScript than I do from a compiled language like Swift.
One thing that I do miss from Xcode is its new file templates. Creating a view controller and xib with everything hooked up is just a few clicks. In Angular, when dealing with components, there are actually 4 separate files to create—doing this manually is a pity.
Visual Studio Code extensions to the rescue! Along with Duy Nguyen, we have built Blueprint which lets you build out files and a folder of files quickly from templates in your project. Simply install Blueprint, put your templates in a folder named “blueprint-templates” at the root of your project, and then right-click where you would like your file created.
Templates are simply folders of files using a Handlebars syntax and an optional file named “manifest.json” (lets you add additonal options like whether a folder should be created). There are helpers for different casing-types (kebab, snake, Pascal, and camel) for transforming your input within the template. Check out some examples.
If you ever find yourself creating the same set of files over and over again in a project try out Blueprint and see if it makes things easier for you.
Sunday, February 28, 2016
Continuing after last week’s failure due to rate limiting of the Let’s Encrypt API, this site is now being served using https.
Using this guide, a cron job runs a script to check if the certificate will expire in less than 30 days. If it will, the script uses the Let’s Encrypt client to renew it.
Pretty neat.
Wednesday, February 24, 2016
Over the weekend I tried to setup Let’s Encrypt.
Let’s Encrypt works by providing short term SSL certificates (currently 90 days) along with a client that allows you to renew them with one simple command. With a renewal script you have a pretty nice - free - solution to enabling https.
The initial setup of the certificate went fine, but when setting up the auto-renewal script I decided to change to only allow the non-www domain. This caused the Let’s Encrypt renew script to not use the previous settings. So rather than using reesemclean.com as the certificate name it used reesemclean.com-0001.
I wiped out both of these files and tried to restart. Turns out there are some other files that Let’s Encrypt uses for renewal information. So this time I see reesemclean.com-0002.
This goes on a couple more times as I try to find all the files to delete. Finally, I removed them all and tried it one more time. Oops! I got rate limited! You can only request a certificate for a domain five times in a week.
Little did I know, there is a sandbox environment you can use to avoid the rate limit. No way around it at this point so I will try again next weekend.
Wednesday, February 17, 2016
At some point I want to write a static blogging engine for a this site in Swift – dipping my toes into the non-iOS world of Swift.
What might the basic architecture of this look like?
First thing to think about is how to get writing into it. One thought is to have the server act as a Git repository; I create/edit/delete a post, commit it locally and then push it to the repo where a post commit hook would trigger the generator to rerun.
That’s one option - the iPhone would require a Git client which doesn’t seem like the best writing environment (I could copy and paste from a dedicated writing app but that adds a step to the process).
Another option would be to create an API for just myself to use which would allow me to upload/edit/read posts. More complex and would require some custom apps to write in. Maybe at some point but the first option seems best for now.
The Generator
So I have some files and a way to trigger an update. The input to the generator is going to be something like this:
Input
Posts
Drafts
Published
Pages
Drafts
Published
Templates
Assets
Images
SASS
The generator takes all this and spits out a folder containing the site. Templates would written using something like Handlebars or Mustache. SASS would get compiled to CSS. Drafts would either be ignored or served at a non-public location.
Serving the Site
The output gets moved to the location my HTTP server is serving from. Optionally, it could move it a CDN and have it hosted there. At this point it would be just a folder full of html and assets.
Thursday, February 11, 2016
Inspired by the Unit Testing portion of of this post by Gergely Orosz I decided to take another look at unit testing view models in the app I work on in my day job.
Most of our view models are created with an instance of our API/data controller (practicing good dependency injection). Previously when doing view model testing, we would create an instance of the data controller using mock credentials and a mock set of permissions. Then we would need to stub our network call using OHHTTPStubs to mock any API calls that would be made.
This works, but means we basically had code running through every layer of our app just to test one view model.
If we were using Objective-C we could use a mocking library and do this a little better by mocking the methods we were depending on the data controller for. So far, nothing like this exists when working with Swift-only classes/structs.
Instead, taking cues from Gergely’s post, I instead decided to work with a subclass of our data controller. For each of the methods that our view model relies on we can create a version that returns what we want.
Let’s say we have a view model that exposes a method to start refreshing it’s list of people (this could just be locally or it could be calling our API and getting totally new objects — it doesn’t matter to the view model). All we care about is that when this method is called, our data controller’s “fetchData()” method is called and if that returns an error the view model returns an appropriate error.
Here’s a simple example of this:
class ViewModel {
let dataController: DataController
init(dataController: DataController) {
self.dataController = dataController
}
func refreshData() -> Error? {
let result = self.dataController.fetchData()
switch result {
case .Data:
...do something with the data
return nil
case let .Error(error):
return error
}
}
}
To check whether the correct behavior happens, we can add a couple variables to our mock data controller to keep track of what is going on and what data should be returned.
class DataController {
func fetchData() -> Result {
...implementation
}
}
class MockDataController: DataController {
var fetchDataCalled = false
var dataToReturnOnFetch = .Error
override func fetchData() -> Result {
fetchDataCalled = true
return dataToReturnOnFetch
}
}
Now when we are creating our test, we can create a MockDataController, set the data we want to return (or an error), and then pass this mock object when creating the view model.
A test might look like this:
func testViewModelReturnAnErrorWhenFetchingFails() {
// arrange
let mockDataController = MockDataController()
mockDataController.dataToReturnOnFetch = .Error
let viewModel = ViewModel(dataController: mockDataController)
// act
let error = viewModel.refreshData()
// assert
expect(fetchDataCalled) = true
expect(error) != nil
}
There is one caveat to this… currently if a method is incompatible with Objective-C, you are unable to override it if that method was declared in an extension (see here for more on this).
We have been using extensions to organize related code within a file. We’ll have to adjust this for any methods that we want to be available to override for testing.
Wednesday, February 10, 2016
If you are reading this you may see some older/newer or previous/next buttons at the bottom of this page. These are laid out in a Handlebars template like so:
<footer class="pagination">
{{#prev_post}}
<a href="{{url}}">« Previous</a>
{{/prev_post}}
{{#next_post}}
<a href="{{url}}">Next »</a>
{{/next_post}}
</footer>
So for an individual post, if there is a older one it adds a link to it and if there is an newer one it adds a link for that.
Using flexbox to lay this out looks something like this:
footer {
width: 100%;
display: flex;
justify-content:space-between; //Horizontal
align-items:center; //Vertical
}
The footer takes up the whole width, and presses the ends to each side and spaces the items equally in the horizontal direction. This works in all but one case: when there are no previous post and only a newer post, the newer link is left aligned rather than right aligned.
To solve this I need to give the right aligned items an override. So they get a CSS class and we give them the following style:
footer.pagination .rightAlignedFooterLink {
align-self: flex-end;
}
And with that newer/next links are now right aligned when there is no older/previous link.
As a note, I originally had misread the attribute name for overriding flex items and used:
footer.pagination .rightAlignedFooterLink {
align-items: flex-end;
}
This worked… sometimes. At this point with CSS, I’m never quite sure if I’m slightly off or just completely backwards — I’m sure with experience that will change. It seems similar to when you first start with Auto Layout in iOS land — at first it was baffling but after just a little bit it became my go to way of laying views out.
Tuesday, February 9, 2016
This will be the first of many posts about the setup behind this blog. Today’s post is on code blocks; since I want to talk about code quite a bit this seems like a good place to start.
Again, I’m coming to HTML/CSS/JS as pretty much a complete beginner. As I went into researching this I had just a couple of goals (ordered by importance):
- Fast/Small download
- Easy to use
- Good looking
- Syntax highlighting
After doing some research it looks like syntax highlighting requires either Javascript (e.g. Prism) or the ability to process files through a HTML generator (e.g. Pygments or Rouge).
Javascript
I want to stay with vanilla HTML and CSS/SASS as long as possible. Plus some of the Javascript includes would increase the current page size by four times. At some point this might be revisited but for now this is out.
HTML Generator
Currently I am using Ghost as my blogging engine. At some point I would like to look into creating a static blogging engine using Swift, but for now using HTML generation seems to be out.
Compromise
For now I will sacrifice syntax highlighting and go with a simple styled code block. Here’s the SASS it’s styled with shown using a code block (how meta!):
pre {
border: thin solid $code-block-border-color;
}
code {
background-color: $code-block-background-color;
border-left: thick solid $code-block-left-line-color;
display: block;
overflow: auto;
padding: 8px 16px 8px 16px;
}
At some point I’ll look to revisit this, but for now I think this works.
Monday, February 8, 2016
This is the first in what I hope will be many posts.
Some of the reasons I want to begin blogging:
- Learn html/css
- Improve communication skills
- Document my learning and personal projects
The first thing I’ll be talking about is setting up this blog: figuring out the CSS, setting up the server, etc.
I’ve done lots of iOS programming but this is the first time I’ve done even a little bit with html/css. This is my first attempt to expand my knowledge into that area.