Eddie Hinkle

Login

tech

Thinking through Reactive Programming using RxJS

State can be a hard, difficult and bug inducing thing to track and manage. The best way to simplify state is to reduce it. Because of that, at my job, we try to embrace using Observable streams whenever we can. The difference between imperative programming and reactive (using Observables) programming is that in imperative programming you have to continually add a lot of logic around state and determining when and how state changes and making sure that state is reflected properly in the UI.

Reactive programming, focuses on streams of data (in our case called Observables). These streams of data can come from anywhere: mouse event, keyboard event, network request or even a timer running in the background. These streams can trigger events, return data, combine streams, split streams and transform existing data into new shapes and forms. But these streams (much like real life streams of water) have singular sources that are far away up at the top of the mountain. As such, when you are down stream, you aren’t having to make sure the water keeps running, you just install a hydroelectric damn to power your house.

Here is an example, that powers an auto-complete or auto-search function:

  1. this.autoResults$ = merge(of(undefined), fromEvent(this.searchField.nativeElement, ‘input’))
  2. .pipe(
  3. map(e => e !== undefined ? e.target.value : undefined), // Get value from input box
  4. debounceTime(350), // Prevent too many queries back to back
  5. distinctUntilChanged(), // Don’t run duplicate requests
  6. tap(() => { this.loading = true }), // show loading mask, this has to be in curly braces to avoid returning any data
  7. switchMap(searchTerm => this.service.search(searchTerm)), // Search for terms
  8. tap(() => { this.loading = false }),
  9. shareReplay(1) // Prevent different subscribers from creating duplicate parallel async requests
  10. );

The item above starts by merging two Observable streams. The first, is an Observable stream that will emit a single value: undefined. This is for one reason, and one reason alone. On the component loading, we want to immediately kick off a search for all the unfiltered results. So in the example above, undefined means we don’t have any search terms and we just want ALL of the results. It loads on component load, but if we enter keys into the search field, it will automatically update. We don’t have to assign any logic to watching the input field and changing a search term state when it’s been modified and then determining when and how to launch the search logic after the search term state is modified.

Instead, it’s a single stream that starts with events being emitted from the search field, and then all of our logic is built alongside that stream. To simplify things, If we ONLY want search results to show if we have entered search results, then it would look like this:

  1. this.autoResults$ = fromEvent(this.searchField.nativeElement, ‘input’)
  2. .pipe(
  3. map(e => e !== undefined ? e.target.value : ‘’), // Get value from input box
  4. filter(e => e.length === 0) // Don’t proceed if the string is empty
  5. debounceTime(350), // Prevent too many queries back to back
  6. distinctUntilChanged(), // Don’t run duplicate requests
  7. tap(() => { this.loading = true }), // show loading mask, this has to be in curly braces to avoid returning any data
  8. switchMap(searchTerm => this.service.search(searchTerm)), // Search for terms
  9. tap(() => { this.loading = false }),
  10. shareReplay(1) // Prevent different subscribers from creating duplicate parallel async requests
  11. );

Here, we start with an event, the search field’s input. We then pipe it through a series of filters, actions and transformations.

Step 1: Map. we convert the data object from a keyboard event into the value of the search field. If the event is undefined for some reason, we get an empty string.

Step 2: Filter. We filter based on the length of the string. We only want to search IF there are characters in the string. If we wanted we could make this set a minimum character limit to 2 or 3 so you don’t search after just 1 or 2 characters.

Step 3: Debounce. To prevent firing a bunch of requests WHILE we are typing the term, we only let the last emitted value through within a given time period. So in this case if during 200ms you type “Hello”. There were 5 emitted values in the stream.

H

He

Hel

Hell

Hello

In this instance, at 350ms, a single value is emitted, which is the last value received: “Hello”.

Step 4: DistinctUntilChanged. Suppose your search term is “Hello”. The results are showing. If you remove the last two characters “lo”, and then within 350ms you will have 4 new values emitted, but only one will make it past the Debounce: “Hello”. This is the same term you already have! Because of that, DistinctUntilChanged will prevent us from running the search logic on the same value.

Step 5: Tap. Tap is essentially to just run an action based on the data coming in. You can use it for all sorts of things: Logging, Analytics, or this case, updating a loading UI.

Step 6: SwitchMap. With a Switch Map we are switching from one observable stream to another in its same place. So in this instance, we are replacing the search term observable with the results from the search service observable.

Step 7: Tap. This tap isn’t called until the step before it is complete, which means when we get here, we have loaded all data from the network, so we can hide our loading UI.

Step 8: ShareReplay. Typically every Observable subscriber is subscribing to their own independent stream of data. Sometimes that is good, but in the regard of a network request like this that is always going to be the same data, it’s better to share the Observable among various subscribers. The “Replay” part of this is the fact that it will return the last value emitted to any late subscribers. This means if one subscriber starts following the stream and the search results happen, but after they are returned another component subscribes to the stream, it will get the last search result that happened (as well as any future results).

This is just a brief overview of one type of reactive/Observable pattern, but it's one I found helpful and thought I would share.

😔 sigh.

The call a software engineer never wants to make: the ISP (internet service provider). Somehow even when you understand how software and the internet works, you can never understand how everything with an ISP is clunky, slow and error prone.

72.94 ℉🌒Frederick, Marylandtechpersonal
posted using indigenous.abode.pub
Yay, my website celebrates my birthday 🙂 that’s so nice of it! Go enjoy some confetti, today!
I felt like I needed to refresh my home screen in a practical way. So I looked at Screen Time’s “Top apps after pick up” and “Most time in app” lists in order from bottom right to top left. The most used apps are the most access for my thumb (ex: Slack, Messages and Spark)
IndieWebKit is 78% complete! I've gotten majority of features done for IndieAuth, Micropub and Microsub. some more work for parsing responses in Microsub, uploading files in Micropub, and fix an Authentication Services issue
89.41 ℉Frederick, Marylandindiewebkitswifttech
posted using quill.p3k.io

Swift Question: Swift Package error when relying on AuthenticationServices

Okay, so for anyone reading that is familiar with Swift/Apple development and more importantly, with Xcode 11 beta.

I am building a Swift Package using Xcode 11 beta. It's called IndieWebKit, but the issue I'm running into is one of the classes relies on AuthenticationServices because it uses OAuth 2.0 and Apple rejects any app that doesn't use ASWebAuthenticationSession for OAuth 2.0

So here is the issue, when build the package and run it's tests, it errors out because it says "Library Not Loaded" for AuthenticationServices.

Below are what I've implemented to try to get it to work:

I added #if canImport(AuthenticationServices) around import AuthenticationServices to try to avoid importing it if it's not available. I've also set the Swift package platforms to macOS(v10_15) and iOS(v12).

I also tried adding @available(iOS 12.0, macOS 10.15, *) at the beginning of the function that utilizes ASWebAuthenticationSession.

In the test suite, I DO NOT call the function that uses ASWebAuthenticationSession within the tests.

Despite all of this, I can ONLY run my tests if I have commented out the import and the usage of ASWebAuthenticationSession. This works for now while I'm doing initial development, but eventually I need to be able to run these tests AND have the package contain the ASWebAuthenticationSession call.

I believe the reason it's doing this is because I'm still on macOS 10.14 (which doesn't have AuthenticationServices), but I feel like if I'm add these various "available if and can import" statements, I should be able to run tests that don't run the function that requires those services.

Any thoughts or ideas would be super helpful. Thanks!

88.08 ℉🌧Frederick, Marylandtechswiftquestion
posted using quill.p3k.io
I’m pretty excited about adding unofficial HomeKit support to my bed mattress. Right now I can just turn the heater off and on. Next up I think I should be able to add a presence indicator if we are in bed or not.
71.04 ℉☁️Frederick, Marylandtechpersonal
posted using indigenous.abode.pub
Indigenous has been stuck for a bit due to computer, time and focus issues. Unable to sleep tonight I was able to put my thinking cap on regarding several aspects of Indigenous and I think I’ve got some good ideas regarding the future of Indigenous. Now I just need some time...
A preview of my new homepage!
17.21 ℉Frederick, Marylandtechcolophon
posted using indigenous.abode.pub
Yesterday and Today I took my homepage through a refresh. My goal was to make my website feel less stoic and plain and more fun! I haven't finished the colors but I'm pretty excited about the direction! Interestingly this time last year, I was also re-thinking my homepage! Must be the cold weather of winter!

https://eddiehinkle.com/2018/03/06/6/article/

Excited for IndieWebCamp Online 2019 this coming weekend! If you haven't signed up yet, sign up today! Also, we're hosting a Virtual IndieWeb Meetup tonight, we'd love to have you join us!
9 years ago today, I was about to start my job at Apple Retail! Wow, feels like an eternity ago!

https://eddiehinkle.com/2010/03/04/1/note/

Two Dads Talking, Episode 4: I Made a Pikachu Pancake

Your hosts are back, and they recap Jonathan’s vacation to Maui, discuss sharing special places with family, debate hotels vs. resorts vs. condos, follow-up on some tech topics, and finally talk about balanced parenting. Oh, and Eddie’s cat happens.

Map of Maui

You can find out more about Eddie and Jonathan at their respective websites:

For past episodes or to subscribe to future episodes, visit us at twodads.fm.

Had a great time chatting with Greg planning the upcoming IndieWebCamp Online conference. It seems like just the other day it was months away! Now it’s just under two weeks! Interested in embracing a healthier form of social media? Join us!
Registration for IndieWebCamp Online 2019 is open!, it's the first IndieWebCamp based on the internet since 2014 and we're experimenting with really embracing the internet medium for everything it has. Come experiment with us?
7 Years Ago I was worried my Home Button wasn’t going to survive until the next iPhone came out. Man am I glad that Home Buttons no longer exist!!
Just posted a podcast about my 2019 yearly theme : Clarity, and what it means to me, my projects and my podcasts.
I set up automatic podcast episode importing for my website based on my podcasts' RSS feeds today. You can check out the script here, it's tools like microformats, Micropub, RSS, etc that make me love having a website nowadays!

Passive Tracking > Active Tracking

In the most recent episode of Two Dads Talking, Jonathan and I talk about tracking our information and whether we are using active or passive applications. Jonathan is using mostly passive applications that collect his behavior and translate that into posts on his website. I, on the other hand, spend way too much time tracking all sorts of information: what I watch, what I listen to, where I go, not to mention what I eat and drink. Essentially everything I log is manual, so in 2019 I have a goal to move as much as I can to automated and that which can't be automated to defer to a time when I have free time.

Step one is my listen posts. Currently after every podcast I listen to, I have to remember to activate the share sheet in my Podcast app (Castro) and share it with my app, Indigenous, which will share the url to my website and I will process it for the episode's information. Jonathan shared in the episode that he came up with a script that can pull in his play history from his Overcast account and publish it to his website. It hasn't been approved by Overcast's owner yet and he ran into some issues so he's waiting on those to be resolved but it's do-able. Because of this I switched from Castro back to Overcast yesterday and am looking forward to setting up a similar system so that I can have listen posts be passively tracked just by being played in my podcast player of choice.

Step two are my watch posts. Currently, every time I watch something and want to track it, I have to run a command line script which require me connecting to my server, running a snippet from an app on my iPhone and entering the episode number that I just watched. It's fairly low maintenance but it still requires more thought than I would like. I initially thought I wouldn't be able to use Trakt like Jonathan because I track who I watched it with. I realized, I already use an app to track what I want to watch that is compatible with Trakt, so I can set my site up to automatically import watch posts from Trakt, and then after the fact I can update who I watched it with, thus combining my two goals of 2019: automate and defer. This takes me from opening an app, connecting to my server, selecting the right snippet and entering the episode number into opening an app, tapping on the checkmark for the right show in my watch list. Then, later, I can sit down and add who I watched each show or movie with since I'm not likely to forget that.

I don't have a plan for the rest of my items: ate, drank, checkins, trips, etc. but I'm going to be thinking through them. Right now I have an app that automatically tracks my location everywhere and it logs when I've been in one location for more than 10 minutes and my speed, so I'm thinking I can build something that can analyze that information and present suggestions as to where I might need to checkin and how I might have traveled (walking, driving, biking, etc.) and then just provide the ability for me to modify that information if anything is incorrect and publish it to my site.

Additionally, there is no easy way to automatically track my food, but I can either work on making sure the app I use is as seamless and quick as possible to enter it and finish it, or I can try to defer it by creating an interface that makes it really easy for me to enter a entire day's worth of meals at one time at the end of the day and then submit that.

Any thoughts? Do you track information? Which do you track actively and which do you track passively?

Switching from Castro back to Overcast

As mentioned on the recent episode of Two Dads Talking, I found myself really enjoying Castro, but wanting to shift back to Overcast. I finally figured out a workflow that works for me on Overcast! I have three playlists: Queue (manual), Priority (Smart), All (Smart).

Queue is a manual playlist that I can add podcast episodes to and I always listen to episodes from the queue.

Priority is a smart playlist that lists episodes from my most important podcasts I follow.

All episodes is a smart playlist that includes all podcasts but excludes the podcasts that are in the priority smart playlist.

So my workflow is I listen to podcasts in the Queue, however when the queue runs low, I check my priority smart playlist and from there I can delete an episode I’m not interested in, or add that episode to the queue if I’m ready to listen to it. When I add it to the queue I also remove it from the smart playlist so that I know anything in the smart playlist doesn’t exist in the queue yet.

The best thing is that I can move individual episodes from priority to queue OR priority to all episodes if I feel like I don’t want to lose an episode but I don’t want it clogging up my priority list. The multiple playlists allow for more flexibility than I had with Castro while still allowing me the spirit of what I had in Castro a type of filtering system so I don’t feel I have to listen to every episode of a podcast.

The benefits of switching back to Overcast are also that Jonathan wrote a script that I can use to automatically import listen posts to my website rather than having to do it manually. Also, I’m looking forward to being able to try out Overcast’s Apple Watch app, not to mention I really appreciate Marco’s design aesthetic and attention to detail.

Please note: This site is in an active redesign. Some things might be a little off 🧐