Tag Archives: large

Thumbnail

A Guide To Embracing Challenges And Excelling At Your UX Design Internship




A Guide To Embracing Challenges And Excelling At Your UX Design Internship

Erica Chen



This is the story about my user design internship. I’m not saying that your internship is going to be anything like mine. In fact, if there’s one thing I can say to shape your expectations, it would be this: be ready to put them all aside. Above all else, remember to give yourself space and time to learn. I share my story as a reminder of how much I struggled and how well everything went despite my difficulties so that I’ll never stop trying and you won’t either.

It all started in May 2018, when I stepped off the plane in Granada, Spain, with a luggage at my side, laptop on my back, and some very rusty Spanish in my head. It was my first time in Europe and I would be here for the next three months doing an internship in UX design at Badger Maps. I was still pretty green in UX, having been learning about it for a barely a year at this point but I felt ready and eager to gain experience in a professional setting.

Follow along as I learned how to apply technical knowledge to complete the practical design tasks assigned to me:

  • Create a design system for our iOS app using Sketch;
  • Design a new feature that would display errors occurring in data imports;
  • Learn the basics HTML, CSS, and Flexbox to implement my design;
  • Create animations with Adobe Illustrator and After Effects.

This article is intended for beginners like me. If you are new to UX design looking to explore the field — read on to learn if a UX design internship is the right thing for you! For me, the work I ended up completing went well beyond my expectations. I learned how to a design system, how to compromise design with user needs, the challenges of implementing a new design, and how to create some “moments of delight.” Every day at the internship presented something new and unpredictable. At the conclusion of my internship, I realized I had created something real, something tangible, and it was like everything I had struggled with suddenly fell into place.

Recommended reading: How To Land A First-Rate Graphic Design Internship

Chapter 1: Legos

My first task was to create a design system for our existing iOS app. I had created design systems in the past for my own projects and applications, but I had never done them retrospectively and never for a design that wasn’t my own. To complete the assignment, I needed to reverse engineer the mockups in Sketch; I would first need to update and optimize the file in order to create the design system.


Screenshot of organizing a design file in the program Sketch.


Working with organizing the Sketch file to create a design system. (Large preview)

It was also at this opportune moment when I learned the Sketch program on my computer had been outdated for about a year and a half. I didn’t know about any of the symbols, overrides and other features in the newer versions. Lesson learned: keep your software updated.


Footer symbols and overrides in the program Sketch.


Creating footers and working with overrides in Sketch. (Large preview)

Before worrying about the symbols page, I went through the mockups artboard by artboard, making sure they were updated and true to the current released version of the application. Once that was done, I began creating symbols and overrides for different elements. I started with the header and footer and moved on from there.

As a rule of thumb, if an element showed up in more than one page, I would make it a symbol. I added different icons to the design system as I went, building up the library. However, it quickly became clear that the design system was evolving and changing faster than I could try to organize it. Halfway through, I stopped trying to keep the symbols organized, opting instead to go back and reorganize them once I had finished recreating each page. When I stopped going back and forth between mockups and symbols and worrying about the organization for both, I could work more efficiently.

It was easy to come to appreciate the overrides and symbols in Sketch. The features made the program much more powerful than what I was used to and increased the workability of the file for future designs. The task of creating the design system itself challenged me to dive deep into the program as well as understand all the details of the design of our application. I began to notice small inconsistencies in spacing, icon size, or font sizes that I was able to correct as I worked.


A description of what the image shows for alt text


A caption to be shown below the image. (Large preview)

The final step was to go back into the symbols page and organize everything. I weeded through all the symbols, deleted those not in use and any replicas. Despite being a little tedious, this was a very valuable step in the process. Going through the symbols after working through the document gave me a chance to reevaluate how I had created the symbols for each page. Grouping them together forced me to consider how they were related throughout the app.

By going through this thought process, I realized how challenging it was to create a naming system. I needed to create a system broad enough to encompass enough elements, specific enough to avoid being vague, and that could easily be understood by another designer. It took me a few tries before I landed upon a workable system that I was happy with. Ultimately, I organized elements according to where they were used in the application, grouping pieces like lists together. It worked well for an application like Badger that had distinct designs for different features in the app. The final product was a more organized file that would be a lot easier to work with for any future design iterations.


New design with larger headers, inspired by native apple apps.


Modernizing the design with new header designs. (Large preview)

As a capstone to this project, I experimented with modernizing the design. I redesigned the headers throughout the app, drawing on native apple apps for inspiration. Happily, the team was excited about it as well and are considering implementing the changes in future updates to the app.

Overall, working a Sketch file to such detail was an unexpectedly helpful experience. I left with a much greater fundamental understanding of things like font size, color, and spacing by virtue of redoing every page. The exercise of copying existing design required a minute attention to detail that was very satisfying. It was like putting together a Lego model: I had all the pieces and knew what the end product needed to look like. I just needed to organize everything and put them together to create the finished product. This is one of the reasons why I enjoy doing UX design. It’s about the problem solving and piecing together a puzzle to create something that everyone can appreciate.


Final design for a new feature for the badger maps web application.


Dashboard design for the Badger web application. (Large preview)

Chapter 2: The Design

The next part of my internship allowed me to get into the weeds with some design work. The task: to design a new import page for the Badger web application.

The team was working on redesigning the badger to CRM integration to create a system that allowed users to view any data syncs and manage their accounts themselves. The current connection involves a lot of hands-on work from badger CSAs and AEs to set up and maintain. By providing an interface for users to directly interact with the data imports, we wanted to improve the user experience for our CRM integration.


Current design for the import process.


Existing process: Users currently integrating Badger with their Salesforce accounts can’t manage the flow of information between the two. They can’t view any errors in data being imported to Badger or easily see the status of their import. To the right is the existing errors view for users importing via spreadsheets. We want to improve this user experience and make it accessible to Salesforce-integrated users as well. (Large preview)

My goal was to design a page that would display errors occurring in any data imports that also communicated to users how and where to make the necessary changes to their data. If there were more errors associated with a single import or users would like to view all errors at once, they should be able to download an excel file of all that information.

Objectives

  1. Create an import page that informs the user on the status of an import in process;
  2. Provide a historical record of account syncs between Badger and the CRM with detailed errors associated with each import;
  3. Provide links to the CRM for each account that has an import error in Badger;
  4. Allow users to download an excel file of all outstanding errors.

User Stories

Badger customer with CRM account:
As a customer with a CRM, I want to be able to connect my CRM to my badger and visualize all data syncs so that I’m aware of all errors in the process and can make changes as necessary.

Badger:
As a badger, I want users to be able to manage and view the status of their CRM integration so that I can save time and manual work helping and troubleshooting users syncing their badger to their CRM accounts.

Before I really delved into the design, we needed to go through some thinking to decide what information to show and how:

  1. Bulk versus continuous imports
    Depending on the type of user, there are two ways to import data to Badger. If done through spreadsheets, the imports would be batched and we would be able to visualize the imports in groups. Users integrated with their CRMs, however, would need to have their Badger data updated constantly as they made changes within their CRM. The design needed to be able to handle both use cases.
  2. Import records
    Because this was a new feature, we weren’t absolutely sure of the user behavior. Thus, deciding how to organize the information was challenging. Should we allow users to go for an infinity scroll in a list of their history? How would they search for a specific import? Should they be able to? Should we show the activity day-by-day or month by month?

Ultimately, we were only able to make a best guess for each of these issues — knowing that we could make appropriate adjustments in the future once users began using the feature. After thinking these issues out, I moved into wireframing. I had the opportunity to design something completely different and this was both liberating and challenging. The final design was a culmination of individual elements from various designs that were created along the way.

Design Process

The hardest part of this process was learning to start over. I eventually learned that forcing something into my design for solely aesthetic purposes was not ideal. Understanding this and letting my ideas go was key to arriving at a better design. I needed to learn how to go start over again and again to explore different ideas.


Three design explorations.


First few iterations: Experimenting with the placement of the header, buttons, and list design. Feedback at this point and for the next few days was consistently as it should be: ‘let’s see what else we can do.’ But the advantages to running like a headless chicken was that I occasionally stumbled upon some corn kernels of gold that I used in the final design. (Large preview)


A blue themed design exploration.


One design exploration that stretched a little too far from the badger application. After this, I circled back a little but the final design really benefited from exploring such different ideas. (Large preview)

Challenges

1. Using white space

Right off the bat, I needed to explore what information we wanted to show on the page. There were many details we could include — and definitely the room to do it.


A dashboard design showing a lot of excess information.


Initially, I was very intimidated at the prospect of having a lot of white space and a minimalistic design so tried really hard to come up with filler information, 75% of which our users wouldn’t really need. Then I crammed it all into my design, permitting minimal breathing room. A very good attitude for a city planner in San Francisco; not so much for creating user centric design. (Large preview)

All the unnecessary information added way too much cognitive load and took away from what the user was actually concerned about. Instead of trying to get rid of all the white space, I needed to work with it. With this in mind, I eventually chucked out all the irrelevant information to show only what we expect our users to be most concerned about: the errors associated with data imports.

This was the final version:


Final design featuring a streamlined table design with activity organized by month.


Imports organized according to day and month. This was a more logical organization for our purposes, especially because synchronizations between the CRM and Badger were continuous, not just on demand. (Large preview)

2. Navigation

The next challenge was deciding between a sidebar versus a header for displaying information. The advantages to the sidebar was that the information would be consistently visible as the user scrolled. But we also had to ensure that the information contained in the sidebar was logically related to what was going on in the rest of the page.

The header offered the advantage of a clean, single column design. The downside was that it took up a lot of vertical real estate depending on how much information was contained in the header. It also visually prioritized the contents of the header over what was below it for the user.


Design exploration with a top navigation.


Iteration exploring the top navigation. Cons: users would scroll through the list of imports to view errors and have to scroll back up to see the summary. The contents and location of the two cells to the right was also confusing. It didn’t make sense for the two cells to scroll with the rest of the page because they were a summary of all information to its left. But it would make for a confusing user experience if they didn’t scroll. Overall, the organization of the information on the page was misaligned with the design. (Large preview)

Once I worked out what information to display where, the sidebar navigation became the more logical decision. We expect users to be primarily concerned with the errors associated with their imports and with a large header, too much of that information would fall below the fold. The sidebar could then be a container for an import and activity summary that would be visible as the user scrolled.

Sidebar design: After I decided on having a sidebar, it came down to deciding what information to include and how to display it.


Five different sidebar design explorations.


Different sidebar design explorations. The design became increasingly simple as I narrowed in on the information the users wanted to see. (Large preview)

I struggled to create a design that was visually interesting because there was little information to show. For this reason, I once again found myself adding in unnecessary elements to fill up the space although I wanted to prioritize the user. I experimented with different content and color combinations, trying to find the compromise between design and usability. The more I worked with it, the more I was able to parse down the design to the bare bones. It became easier to differentiate useful information from fillers. The final product is a streamlined design with just a few summary statistics. It also offers great flexibility to include more information in the future.


Final design for a new feature for the badger maps web application.


Final design: Subtext beneath the buttons removed and the accounts created/accounts updated information is placed in its own container and shifted down to add visual interest. (Large preview)

Import process: The import progress page was created after the design for the import page was finalized. The biggest design challenge here was deciding how to display the in-progress import sync. I tried different solutions from pop-ups and overlays but ultimately settled with showing the progress in the sidebar. This way, users can still resolve any errors and see the historical record of their account data while an import is in progress. To prevent any interruptions to the import, the ‘Sync data’ and ‘Back to Badger’ buttons are disabled so users can’t leave the page.


Final design with the sync data and back to badger buttons disabled.


Sync data and Back to Badger buttons disabled to prevent users from interrupting the sync and going back to the application. (Large preview)

With the designs done, I moved onto HTML and CSS.


Screenshot of the sketch program and visual studio code with the code for the design.


Beginning to code my design. (Large preview)

Chapter 3: HTML/CSS

This project was my first experience with any type of coding. Although I had tried to learn HTML and CSS before, I had never reached any level of proficiency. And what better way to start than with a mockup of one’s own design?

Understanding the logic of organizing an HTML document reminded me of organizing the Sketch document with symbols and overrides. However, the similarities ended there. Coding felt like a very alien thing that I was consistently trying to wrap my head around. As my mentor would say, “You’re flexing very different muscles in programming than you are in design.” With the final product in hand now, I’m fully convinced that learning to code is the coolest thing I’ve learned to do since being potty trained.

The first challenge, after setting up a document and understanding the basics, was working with Flexbox. The design I had created involved two columns side by side. The right portion was meant to scroll while the left remained static. Flexbox seemed like a clean solution for this purpose, assuming I could get it to work.

Implementing Flexbox consisted of a lot of trial and error and blind copying of code while I scrambled through various websites, reading tutorials and inspecting code. With guidance from my mentor through this whole process, we eventually got it to work. I will never forget the moment when I finally understood that by using flex-direction: column I would get all of the elements into a single column, and flex-direction: row helped placed them in one row.

It makes so much sense now, although my initial understanding of it was the exact opposite (I thought flex-direction: column would put elements in columns next to each other). Surprisingly, I didn’t even come to this realization until after the code was working. I was reviewing my code and realized I didn’t understand it at all. What tipped me off? In my CSS, I had coded flex-direction: row into the class I named column. This scenario was pretty indicative of how the rest of my first coding experience went. My mental model was rarely aligned with the logic of the code, and they often clashed and went separate ways. When this happened, I had to go back, find my misconceptions, and correct the code.

After setting up Flexbox, I needed to figure out how to get the left column to stay fixed while the right portion scrolled. Turns out this couldn’t be achieved with a single line of code as I had hoped. But working through this helped me understand the parent-child relationship that aided me immensely with the rest of the process.


Table of imports design showing the timeline and calendar icons


Vertical timeline with calendar icons. (Large preview)

Coding the vertical timeline and the dial was also a process. The timeline was simpler than I had originally anticipated. I was able to create a thin rectangle, set an inner shadow and a gradient filling to it, and assign it to the width of each activity log.

The dial was tricky. I tried implementing it with pure CSS with very little success. There were a few times I considered changing the design for something simpler (like a progress bar) but I’m quite happy I stuck with it.


Image showing the original and final dial designs.


Original and final dial designs. (Large preview)

A major struggle was getting outside progress dial to overlap the background circle along the border. This was where I changed the design a little bit — instead of having the unloaded portion of the progress dial cut out, it overlaps all around. It was a compromise between my design and code that I was initially unwilling to make. As it turns out, however, I was satisfied with the final result and once I realized this, I was happy to make that compromise. The final dial was implemented via JavaScript.

There was a moment in my coding process where I threw every line of code I’d ever written into every class to try to make it work. To make up for this lack of hindsight, I needed to spend quite a while going through and inspecting all the elements to remove useless code. I felt like a landlord kicking out the tenants who weren’t paying rent. It was most definitely a lesson learned in maintaining a level of housekeeping and being judicious and thoughtful with code.

The majority of the experience felt like blind traversing and retrospective learning. However, nothing was more satisfying than seeing the finished product. Going through the process made me interact with my work in a way I had never done before and gave me insight into how design is implemented. In all of my expectations for the internship, I never anticipated being able to code and create one of my own designs. Even after being told I would be able to do so on my first day, I didn’t believe it until after seeing this page completed.

Chapter 4: Working With Baby Badgers

As part of the process integrating Badger users with their CRM accounts, we needed our users to sign into their CRM — requiring us to redirect them out of badger to the native CRM website. To prevent a sudden, jarring switch from one website to another, I needed to design intermediate loading pages.


Original design for the redirection page with the badger maps logo and “See ya later!” message.


One of the first mockups of a sample static redirection page. It was simple and fulfilled its purpose but did little else. (Large preview)

I started out with your run-of-the-mill static redirection page. They were simple and definitely fulfilled their purpose, but we weren’t quite happy with them.

The challenge was to create something simple and interesting that informed the user they were leaving our website in just a few seconds it was visible. The design would need to introduce itself, explain why it was there, and leave before anyone got tired of looking at it. It was essentially an exercise in speed dating. With that in mind, I decided to try animations — specifically that of a cheeky little badger, inspired by the existing logo.


Image showing 7 iterations of the badger design and how it changed.


The evolution of “baby badger”. (Large preview)

Using the badger logo as a starting reference point, I created different badger characters in Adobe Illustrator. The original logo felt a little too severe for a loading animation, so I opted for something a little cuter. I kept the red chest and facial features from the original logo for consistency and worked away at creating a body and head around these elements. The head and stripes took a while to massage into shapes that I was happy with. The body took the form a little easier, but it took a little longer to find the right proportion between the size of the head and the body. Once I nailed that down, I was ready to move onto animating.


Stop animation frames animating the baby badger.


My attempt at stop animation. (Large preview)

My first instinct was to try a stop-motion animation. I figured it was going to be great — a lá Wallace and Gromit. But after the first attempt and then the second, and all the ensuing ones, it became clear that watching that show as a child had not fully equipped me with the skills required to do a stop-motion animation.

I just wasn’t able to achieve the smoothness I wanted, and there were small inconsistencies that felt too jarring for a very short loading animation. Animation typically runs at 23 frames per second, and my badger animation only had about 15 frames per second. I considered adding more frames, but upon suggestion from my mentor, decided to try character animation instead.

This was the first time I had animated anything that was more than 5 moving parts and there was definitely a learning curve to understanding how to animate a two-dimensional character in a visually satisfying way. I needed to animate the individual elements to move by themselves independent of the whole in order to make the motion believable. As I worked on the animation, the layers I imported became increasingly granular. The head went from being one layer to five as I learned the behavior of the program and how to make the badger move.

I anchored each limb of the body and set each body part as a child to the parent layer of the body. I set the anchor points accordingly at the top of the thighs and shoulders to make sure they moved appropriately and then, using rotations and easing, simulated the movement of the body parts. The head was a tad bit tricky and required some vertical movement independent of the body. To make the jump seem more realistic, I wanted the head to hang in space a little before being pushed up by the rest of the body, and to come down just slightly after the rest of him. I also adjusted the angle I tried to make him seems as if he were leading with his nose, pointing up during the jump, and straightforward while he ran.

The overly anthropomorphic feet were abandoned from the original designs. They were one of the last changes made to baby badger. I hadn’t considered how odd human toes looked like on a badger.

The animation featured on the page redirecting the user back to badger displayed the baby badger running back to badger with a knapsack full of information from the CRM.

Animation of baby badger running back to the badger application.

And finally: the confused badger. This was done for the last page I needed to create: an error page notifying the user of unexpected complications in the integration process. And what better way to do that then a sympathetic, confused badger?


Image showing four iterations of the baby badger face.


Design exploration of the baby badger face. (Large preview)

The tricky part here was combining the side profile of the existing cartoon badger and the logo to create a front-facing head shape. Before beginning this project, I had never once seen a real live badger. Needless to say, Badger has found its way into my google image searches this month. I was surprised to see how flat the head of a badger actually is. In my first few designs, I tried to mimic this but wasn’t satisfied with the result. I worked with the shape some more, adjusting the placement of the nose, the stripes, and the ears to achieve the final result:

Swirly eyes inspired by the possum from the movie Fantastic Mister Fox.

This animation process has forced me to take my preexisting knowledge to a higher level. I needed to push myself beyond what I knew rather than limiting myself with what I thought I could do. I originally started with the stop-motion animation because I didn’t trust myself to do character animation. By giving myself the chance to try something new and different, I was able to achieve something that exceeded my own expectations.


Four cartoon-style designs based off the baby badger animation.


Designs expanded from the original baby badger to be printed and used around the office and on marketing material. (Large preview)

Conclusion

The three months I spent at my internship were incredibly gratifying. Every single day was about learning and trying something new. There were challenges to everything I did — even with tasks I was more familiar with such as design. Every time I created something, I was very insecure and apprehensive about how it would be received. There was a lot of self-doubt and lots of discarded ideas.

For that reason, it was incredible to be part of a team and to have a mentor to lead me in the right direction. Being told to try something else was often the only encouragement I needed to try something else and achieve something bigger and better. I like to picture myself as a rodent in a whack-a-mole game, being hit on the head over and over but always popping up again and again. Now the struggles and challenges have come to an end, I only want to do it all over again.

I appreciate what I’ve learned and how I was pushed to go beyond what I thought I could do. It’s crazy to see how far I’ve come in a few months. My understanding of being a UX designer has grown immensely, from figuring out the features, to hammering out the design, and then writing front-end code to implement it. This internship has taught me how much more I have to learn and has motivated me to keep working. I’ve come to understand that what I can do should never be limited by what I know how to do.


badger mascot

Smashing Editorial
(mb, ra, yk, il)


From:

A Guide To Embracing Challenges And Excelling At Your UX Design Internship

Thumbnail

Building A PWA Using Angular 6




Building A PWA Using Angular 6

Ahmed Bouchefra



In this tutorial, we’ll be using the latest Angular 6 to build a PWA by implementing the core tenets that make a PWA. We’ll start by creating a front-end web application that consumes a JSON API. For this matter, we’ll be using the Angular HttpClient module to send HTTP requests to a statically JSON API generated from the Simplified JavaScript Jargon GitHub repository. We’ll also use Material Design for building the UI via the Angular Material package.

Next, we’ll use the “Audits” panel (Lighthouse) from Chrome DevTools to analyze our web application against the core tenets of PWAs. Finally, we’ll explain and add the PWA features to our web application according to the “Progressive Web App” section in the Lighthouse report.

Before we start implementing our PWA, let’s first introduce PWAs and Lighthouse.

Recommended reading: Native And PWA: Choices, Not Challengers!

What’s A PWA?

A Progressive Web App or PWA is a web application that has a set of capabilities (similar to native apps) which provide an app-like experience to users. PWAs need to meet a set of essential requirements that we’ll see next. PWAs are similar to native apps but are deployed and accessible from web servers via URLs, so we don’t need to go through app stores.

A PWA needs to be:

  • Progressive
    Work for every user, regardless of browser choice, because they are built with progressive enhancement as a core tenet.
  • Responsive
    Fit any form factor, desktop, mobile, tablet, or whatever is next.
  • Connectivity independent
    Enhanced with service workers to work offline or on low-quality networks.
  • App-like
    Use the app-shell model to provide app-style navigation and interactions.
  • Fresh
    Always up-to-date thanks to the service worker update process.
  • Safe
    Served via HTTPS to prevent snooping and ensure content has not been tampered with.
  • Discoverable
    Are identifiable as “applications” thanks to W3C manifests and service worker registration scope allowing search engines to find them.
  • Re-engageable
    Make re-engagement easy through features like push notifications.
  • Installable
    Allow users to “keep” apps they find most useful on their home screen without the hassle of an app store.
  • Linkable
    Easily share via URL and not require complex installation.

Introducing Lighthouse

Lighthouse is an open-source auditing tool created by Google which can be used to audit websites and applications for accessibility performance, SEO, best practices and PWA features.

You can access Lighthouse from the Audit tab in Chrome DevTools as a module in Node.js or as a CLI tool. You can use Lighthouse by providing an URL and then running the audits which will provide you with a report containing the auditing results which are basically suggestions on how you can improve your web application.

Installing Angular CLI v6 And Generating A Project

In this section, we’ll install the latest version of Angular CLI then we’ll use it to create a new Angular 6 project.

Angular CLI requires Node.js >= 8.9+ so first make sure you have the required version installed by running the following command:

$ node -v

Node.js version


Checking Node version. (Large preview)

In case you don’t have Node.js installed, you can simply head on to the official Node download page and grab the Node binaries for your system.

Now, you can go ahead and install the latest version of Angular CLI by running:

$ npm install -g @angular/cli 

Note: Depending on your npm configuration, you may need to add _sudo_ to install packages globally.

You can generate your Angular 6 project by running the following command in your terminal:

$ ng new pwademo

This will create a project with a structure that looks like:


Angular project structure


Angular project structure. (Large preview)

Most work that’s done will be inside the src/ folder that contains the source code of the application.

Creating The Angular Application

After generating a project, we’ll build a web application that consumes a JSON API and displays the items on the home page. We’ll use the HttpClient service for sending HTTP requests and Angular Material for building the UI.

Adding Angular Material

Thanks to Angular CLI v6 and the new ng add command, adding Angular Material to your project is only one command away. You just need to run the following command from your terminal:

$ cd pwademo
$ ng add @angular/material

Adding Angular Material


Adding Angular Material. (Large preview)

You can see from the screenshot that the command installs the required package from npm and update a bunch of files for setting up Angular Material in your project which previously needed manual updates.

Setting Up HttpClient And Consuming The JSON API

Now, let’s setup the Angular project to use HttpClient for sending HTTP requests. First, you need to import the HttpClientModule module in the main application module in the src/app/app.module.ts file:

/*...*/
import  HttpClientModule  from  '@angular/common/http';
@NgModule(
declarations: [
AppComponent
],
imports: [
/*...*/
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
)
export  class  AppModule  

That’s it. We can now inject and use HttpClient in any component or service that belongs to the main module.

For demo purposes, we’ll consume a statically generated JSON API from the Simplified JavaScript Jargon GitHub repository. If you are consuming any other resource, make sure you have CORS enabled so the browser doesn’t disallow reading the remote resource due to the Same Origin Policy.

Let’s create a service that interfaces with the API. Inside your project folder, run:

$ ng g service api

This will create a service called ApiService in the src/app/api.service.ts file.

Now open the src/app/api.service.ts file and update it to reflect the following changes:

import  Injectable  from  '@angular/core';
import  HttpClient  from  '@angular/common/http';
import  Observable  from  'rxjs';

export  interface  Item
name:  string;
description:  string;
url:  string;
html:  string;
markdown:  string;


@Injectable(
providedIn:  'root'
)

export  class  ApiService 
private  dataURL:  string  =  "https://www.techiediaries.com/api/data.json";
constructor(private  httpClient:  HttpClient) 
fetch():  Observable<Item[]>
return <Observable<Item[]>this.httpClient.get(this.dataURL);

}

We first imported the HttpClient and Observable classes then injected the HttpClient in the constructor as httpClient and added a fetch() method which calls the get() method of HttpClient (for sending an HTTP GET request to our JSON endpoint) and returns an Observable that we can subscribe to later.

We also declared an Item interface which represents a single item of the returned JSON data.

Next import this service from the application component. Open the src/app/app.component.ts file and add:

import  Component, OnInit  from  '@angular/core';
import  ApiService  from  './api.service';
import  Item  from  './api.service';

@Component(
selector:  'app-root',
templateUrl:  './app.component.html',
styleUrls: ['./app.component.css']
)
export  class  AppComponent  implements  OnInit
title  =  'pwademo';
items:  Array<Item>;
constructor(private  apiService:  ApiService)

ngOnInit()
this.fetchData();

fetchData()
this.apiService.fetch().subscribe((data:  Array<Item>)=>
console.log(data);
this.items  =  data;
, (err)=>
console.log(err);
);
}
}

We import the ApiService that we created before and we inject it as apiService, we also import the Item class which represents a single item of our JSON data and we declare the items variable of type Array<Item> which will hold the fetched items.

Next, we add a fetchData() method which calls our fetch() method that we defined in the ApiService which returns an Observable. We simply subscribe to this observable in order to send a GET request to our JSON endpoint and get the response data that we finally assign to the items array.

We call the fetchData() method in the ngOnInit() life-cycle event so it will be called once the AppComponent component is initialized.

Adding The Application UI

Our application UI will consist of a navigation bar and the skeleton of the page which will be created with Angular Material.

Before using an Angular Material component, you’ll need to import its module. Each Material component belongs to its own module.

Open the src/app/app.module.ts file and add the following imports:

/*...*/
import  MatToolbarModule  from  '@angular/material/toolbar';
import  MatCardModule  from  '@angular/material/card';
import  MatButtonModule  from  '@angular/material/button';

@NgModule(
declarations: [
AppComponent
],
imports: [
/*...*/
MatToolbarModule,
MatCardModule,
MatButtonModule
],
providers: [],
bootstrap: [AppComponent]
)
export  class  AppModule  

We import modules for toolbar, card and button components and we add them to the imports array of the AppModule.

Next, open the src/app/app.component.html file, delete what’s in there and add:

<mat-toolbar  color="primary">
<mat-toolbar-row>
<span>JS-jargon</span>
</mat-toolbar-row>
</mat-toolbar>
<main>
<mat-card *ngFor="let item of items">
<mat-card-header>
<mat-card-title>item.name}</mat-card-title>
</mat-card-header>
<mat-card-content>
item.description}
</mat-card-content>
<mat-card-actions>
<a  mat-raised-button  href="item.url}"  color="primary">More</a>
</mat-card-actions>
</mat-card>
</main>

We use Material components to create the UI. The <mat-toolbar> component is used to create a Material toolbar and the <mat-card> component is used to create a Material card etc.

We iterate over the items array which gets populated by the fetchData() method when the component is initialized, and display items as Material cards. Each card contains the name, description and a link for more information (The link is styled as a Material button using the mat-raised-button directive).

This is a screenshot of the application:


Demo Application


Demo Application. (Large preview)

Building The Application For Production

Typically, when checking your application for PWA features you should first build it for production because most PWA features are not added in development. For example, you don’t want to have service workers and caching enabled in development since you will periodically need to update the files.

Let’s build the application for production using the following command:

$ ng build --prod

The production build will be available from the dist/pwademo folder. We can use a tool like http-server to serve it.

First, install http-server using the following command:

$ npm i -g http-server

You can then run it using the following command:

$ cd dist/pwademo
$ http-server -o

The -o option will automatically open the default browser in your system and navigate to the http://127.0.0.1:8080/ address where our web application is available.

Analyzing The Application Using Lighthouse

Let’s now analyze our application using Lighthouse. First, launch Chrome and visit our application address http://127.0.0.1:8080/.

Next, open Developer Tools or press Ctrl + Shift + I and click on the Audit panel.


Perform an audit


Perform an audit. (Large preview)

You preferably need to set the Emulation to Mobile instead of Desktop to emulate a mobile environment. Next, click on Perform an audit… blue button. You’ll have a dialog opened in which you need to choose the types of the audits you want to perform against your web application. Un-check all types but Progressive Web App and click the Run audit button.


Progressive Web App Audits


Progressive Web App Audits. (Large preview)

Wait for the Lighthouse to generate the report. This is a screenshot of the result at this stage:


Initial PWA Report


Initial Report. (Large preview)

Lighthouse performs a series of checks which validate the aspects of a Progressive Web App specified by the PWA Checklist.
We get an initial score of 36100 that’s because we have some audits passed.

Our application has 7 failed audits mainly related to Service Workers, Progressive Enhancement, HTTPS and Web App Manifest which are the core aspects of a PWA.

Registering A Service Worker

The first two failed audits (“Does not register a service worker” and “Does not respond with a 200 when offline”) are related to Service Workers and caching. So what’s a service worker?

A service worker is a feature that’s available on modern browsers which can be used as a network proxy that lets your application intercept network requests to cache assets and data. This could be used for implementing PWA features such as offline support and Push notifications etc.

To pass these audits we simply need to register a service worker and use it to cache files locally. When offline, the SW should return the locally cached version of the file. We’ll see a bit later how to add that with one CLI command.

Recommended reading: Making A Service Worker: A Case Study

Progressive Enhancement

The third failed audit (“Does not provide fallback content when JavaScript is not available”) is related to Progressive Enhancement which is an essential aspect of a PWA and It simply refers to the capability of PWAs to run on different browsers but provide advanced features if they’re available. One simple example of PE is the use of the <noscript> HTML tag that informs users of the need to enable JavaScript to run the application in case It’s not enabled:

<noscript>
Please enable JavaScript to run this application.
</noscript>

HTTPS

The fourth failed audit (“Does not redirect HTTP traffic to HTTPS”) is related to HTTPS which is also a core aspect of PWAs (service workers can be only served from secure origins, except for localhost). The “Uses HTTPS” audit itself is considered as passed by Lighthouse since we’re auditing localhost but once you use an actual host you need a SSL certificate. You can get a free SSL certificate from different services such as Let’s Encrypt, Cloudflare, Firebase or Netlify etc.

The Web App Manifest

The three failed audits (“User will not be prompted to Install the Web App”, “Is not configured for a custom Splash Screen” and “Address bar does not match brand colors”) are related to a missing Web App Manifest which is a file in JSON format that provides the name, description, icons and other information required by a PWA. It lets users install the web app on the home screen just like native apps without going through an app store.

You need to provide a web app manifest and reference it from the index.html file using a <link> tag with rel property set to manifest. We’ll see next how we can do that automatically with one CLI command.

Implementing PWA Features

Angular CLI v6 allows you to quickly add PWA features to an existing Angular application. You can turn your application into a PWA by simply running the following command in your terminal from the root of the project:

$ ng add @angular/pwa

The command automatically adds PWA features to our Angular application, such as:

  • A manifest.json file,
  • Different sizes of icons in the src/assets/icons folder,
  • The ngsw-worker.js service worker.

Open the dist/ folder which contains the production build. You’ll find various files but let’s concentrate on the files related to PWA features that we mentioned above:

A manifest.json file was added with the following content:


    "name": "pwademo",
    "short_name": "pwademo",
    "theme_color": "#1976d2",
    "background_color": "#fafafa",
    "display": "standalone",
    "scope": "/",
    "start_url": "/",
    "icons": [
        
        "src": "assets/icons/icon-72x72.png",
        "sizes": "72x72",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-96x96.png",
        "sizes": "96x96",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-128x128.png",
        "sizes": "128x128",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-144x144.png",
        "sizes": "144x144",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-152x152.png",
        "sizes": "152x152",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-192x192.png",
        "sizes": "192x192",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-384x384.png",
        "sizes": "384x384",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-512x512.png",
        "sizes": "512x512",
        "type": "image/png"
    
    ]
}

As you can see, the added manifest.json file has all the information required by a PWA such as the name, description and start_url etc.


Angular project structure


Angular project structure. (Large preview)

The manifest.json file, links to icons with different sizes, that were also added automatically in the assets/icons folder. You will, of course, need to change those icons with your own once you are ready to build the final version of your PWA.


Angular project structure


Angular project structure. (Large preview)

In the index.html file, the manifest.json file is referenced using:

<link  rel="manifest"  href="manifest.json">

The ngsw-worker.js file, was also automatically added, which contains the service worker. The code to install this service worker is automatically inserted in the src/app/app.module.ts file:

...
import  ServiceWorkerModule  from  '@angular/service-worker';

@NgModule(
declarations: [
AppComponent
],

imports: [
...
ServiceWorkerModule.register('/ngsw-worker.js',  enabled:  environment.production )
],

The @angular/service-worker is installed by the ng add command and added as a dependency to pwademo/package.json:

"dependencies": 
...
"@angular/service-worker": "^6.1.0"

The service worker build support is also enabled in the CLI. In the angular.json file a "serviceWorker": true configuration option is added.

In the index.html file a meta tag for theme-color with a value of #1976d2 is added (It also corresponds to the theme_color value in the manifest.json file):

<meta  name="theme-color"  content="#1976d2">

The theme color tells the browser what color to tint UI elements such as the address bar.

Adding the theme color to both the index.html and manifest.json files fixes the Address Bar Matches Brand Colors audit.

The Service Worker Configuration File

Another file src/ngsw-config.json is added to the project but It’s not a required file for PWAs. It’s a configuration file which allows you to specify which files and data URLs the Angular service worker should cache and how it should update the cached files and data. You can find all details about this file from the official docs.

Note: As of this writing, with the latest 6.1.3 previous ng add @angular/pwa command will fail with this error: Path “/ngsw-config.json” already exists so for now the solution is to downgrade @angular/cli and @angular/pwa to version 6.0.8.

Simply run the following commands in your project:

$ npm i @angular/cli@6.0.8
$ ng i @angular/pwa@6.0.8
$ ng add @angular/pwa

Now let’s re-run the audits against our local PWA hosted locally. This is the new PWA score:


Initial PWA Report


PWA Report. (Large preview)

The Angular CLI doesn’t automatically add the JavaScript fallback code we mentioned in the Progressive Enhancement section so open the src/index.html file and add it:

<noscript>
Please enable JavaScript to run this application.
</noscript>

Next, rebuild your application and re-run the audits. This is the result now:


Initial PWA Report


PWA Report. (Large preview)

We have only one failed audit which is related to HTTPS redirect. We need to host the application and configure HTTP to HTTPS redirect.

Let’s now run the audits against a hosted and secured version of our PWA.


PWA Final Report


PWA Final Report. (Large preview)

We get a score of 100100 which means we’ve successfully implemented all core tenets of PWAs.

You can get the final code of this demo PWA from this GitHub repository.

Conclusion

In this tutorial, we’ve built a simple Angular application and have turned it into a PWA using Angular CLI. We used Google’s Lighthouse to audit our application for PWA features and explained various core tenets of PWAs such as Service Workers for adding offline support and push notifications. The Web Manifest file for enabling add-to-home-screen and splash screen features, Progressive Enhancement as well as HTTPS .

You may also need to manually check for other items highlighted (under the “Additional items to manually check” section) but not automatically checked by Lighthouse. These checks are required by the baseline PWA Checklist by Google. They do not affect the PWA score but it’s important that you verify them manually. For example, you need to make sure your site works cross-browser and that each page has a URL which is important for the purpose of shareability on social media.

Since PWAs are also about other aspects such as better perceived performance and accessibility, you can also use Lighthouse for auditing your PWA (or any general website) for these aspects and improve it as needed.

Smashing Editorial
(rb, ra, yk, il)


View this article:  

Building A PWA Using Angular 6

Thumbnail

Flexbox: How Big Is That Flexible Box?




Flexbox: How Big Is That Flexible Box?

Rachel Andrew



This is the third part of my series on Flexbox. In the past two articles, we have looked at what happens when you create a flex container and explored alignment as it works in Flexbox. This time we are going to take a look at sizing. How do we control the size of our flex items, and what choices is the browser making when it controls the size?

Initial Display Of Flex Items

If I have a set of items, which have variable lengths of content inside, and set their parent to display: flex, the items will display as a row and line up at the start of that axis. In the example below my three items have a small amount of content and are able to display the content of each item as an unbroken line. There is space at the end of the flex container which the items do not grow into because the initial value of flex-grow is 0, do not grow.


Three items with space at the end


The flex items have room to each be displayed on one line (Large preview)

If I add more text to these items, they eventually fill the container, and the text begins to wrap. The boxes are assigned a portion of the space in the container which corresponds to how much text is in each box — an item with a longer string of text is assigned more space. This means that we don’t end up with a tall skinny column with a lot of text when the next door item only contains a single word.


Three items, the final item has longer text and the text wraps


The space is distributed to give more space to a longer item (Large preview)

This behavior is likely to be familiar to you if you have ever used Flexbox, but perhaps you have wondered how the browser is working that sizing out, as if you look in multiple modern browsers you will see that they all do the same thing. This is down to the fact that detail such as this is worked out in the specification, making sure that anyone implementing Flexbox in a new browser or other user agent is aware of how this calculation is supposed to work. We can use the spec to find this information out for ourselves.

The CSS Intrinsic And Extrinsic Sizing Specification

You fairly quickly discover when looking at anything about sizing in the Flexbox specification, that a lot of the information you need is in another spec — CSS Intrisnic and Extrinsic Sizing. This is because the sizing concepts we are using aren’t unique to Flexbox, in the same way that alignment properties aren’t unique to Flexbox. However, for how these sizing constructs are used in Flexbox, you need to look in the Flexbox spec. It can feel a little like you are jumping back and forth, so I’ll round up a few key definitions here, which I’ll be using in the rest of the article.

Preferred Size

The preferred size of a box is the size defined by a width or a height, or the logical aliases for these properties of inline-size and block-size. By using:

.box 
    width: 500px;

Or the logical alias inline-size:

.box 
    inline-size: 500px;

You are stating that you want your box to be 500 pixels wide, or 500 pixels in the inline direction.

min-content Size

The min-content size is the smallest size that a box can be without causing overflow. If your box contains text then all possible soft-wrapping opportunities will be taken.

max-content Size

The max-content size is the largest size the box can be to contain the contents. If the box contains text with no formatting to break it up, then it will display as one long unbroken string.

Flex Item Main Size

The main size of a flex item is the size it has in the main dimension. If you are working in a row — in English — then the main size is the width. In a column in English, the main size is the height.

Items also have a minimum and maximum main size as defined by their min-width or min-height on the main dimension.

Working Out The Size Of A Flex Item

Now that we have some terms defined, we can have a look at how our flex items are sized. The initial value of the flex properties are as follows:

  • flex-grow: 0
  • flex-shrink: 1
  • flex-basis: auto

The flex-basis is the thing that sizing is calculated from. If we set flex-basis to 0 and flex-grow to 1 then all of our boxes have no starting width, so the space in the flex container is shared out evenly, assigning the same amount of space to each item.

See the Pen Smashing Flexbox Series 3: flex: 1 1 0; by Rachel Andrew (@rachelandrew) on CodePen.

Whereas if flex-basis is auto and flex-grow: 1, only the spare space is distributed, taking the size of the content into account.

See the Pen Smashing Flexbox Series 3: flex: 1 1 auto short text by Rachel Andrew (@rachelandrew) on CodePen.

In situations where there is no spare space, for example when we have more content than can fit in a single line, then there is no space to distribute.

See the Pen Smashing Flexbox Series 3: flex: 1 1 auto long text by Rachel Andrew (@rachelandrew) on CodePen.

This shows us that figuring out what auto means is pretty important if we want to know how Flexbox works out the size of our boxes. The value of auto is going to be our starting point.

Defining Auto

When auto is defined as a value for something in CSS, it will have a very specific meaning in that context, one that is worth taking a look at. The CSS Working Group spend a lot of time figuring out what auto means in any context, as this talk for spec editor Fantasai explains.

We can find the information about what auto means when used as a flex-basis in the specification. The terms defined above should help us dissect this statement.

“When specified on a flex item, the auto keyword retrieves the value of the main size property as the used `flex-basis`. If that value is itself auto, then the used value is `content`.”

So if our flex-basis is auto, Flexbox has a look at the defined main size property. We would have a main size if we had given any of our flex items a width. In the below example, the items all have a width of 110px, so this is being used as the main size as the initial value for flex-basis is auto.

See the Pen Smashing Flexbox Series 3: flex items with a width by Rachel Andrew (@rachelandrew) on CodePen.

However, our initial example has items which have no width, this means that their main size is auto and so we need to move onto the next sentence, “If that value is itself auto, then the used value is content.”

We now need to look at what the spec says about the content keyword. This is another value that you can use (in supporting browsers) for your flex-basis, for example:

.item 
    flex: 1 1 content;

The specification defines content as follows:

“Indicates an automatic size based on the flex item’s content. (It is typically equivalent to the max-content size, but with adjustments to handle aspect ratios, intrinsic sizing constraints, and orthogonal flows”

In our example, with flex items that contain text, then we can ignore some of the more complicated adjustments and treat content as being the max-content size.

So this explains why, when we have a small amount of text in each item, the text doesn’t wrap. The flex items are auto-sized, so Flexbox is looking at their max-content size, the items fit in their container at that size, and the job is done!

The story doesn’t end here, as when we add more content the boxes don’t stay at max-content size. If they did they would break out of the flex container and cause overflow. Once they fill the container, the content begins to wrap and the items become different sizes based on the content inside them.

Resolving Flexible Lengths

It’s at this point where the specification becomes reasonably complex looking, however, the steps that need to happen are as follows:

First, add up the main size of all the items and see if it is bigger or smaller than the available space in the container.

If the container size is bigger than the total, we are going to care about the flex-grow factor, as we have space to grow.


flex items with spare space at the end


In the first case our items have available space to grow into. (Large preview)

If the container size is smaller than the total then we are going to care about the flex-shrink factor as we need to shrink.


flex items overflowing the container


In the second case our items are too large and need to shrink to fit into the container. (Large preview)

Freeze any inflexible items, which means that we can decide on a size for certain items already. If we are using flex-grow this would include any items which have flex-grow: 0. This is the scenario we have when our flex items have space left in the container. The initial value of flex-grow is 0, so they get as big as their max-width and then they don’t grow any more from their main size.

If we are using flex-shrink then this would include any items with flex-shrink: 0. We can see what happens in this step if we give our set of flex items a flex-shrink factor of 0. The items become frozen in their max-content state and so do not flex and arrange themselves to fit in the container.

See the Pen Smashing Flexbox Series 3: flex: 0 0 auto by Rachel Andrew (@rachelandrew) on CodePen.

In our case — with the initial values of flex items — our items can shrink. So the steps continue and the algorithm enters a loop in which it works out how much space to assign or take away. In our case we are using flex-shrink as the total size of our items is bigger than the container, so we need to take away space.

The flex-shrink factor is multiplied by the items inner base size, in our case that is the max-content size. This gives a value with which to reduce space. If items removed space only according to the flex-shrink factor then small items could essentially vanish, having had all of their space removed, while the larger item still has space to shrink.

There is an additional step in this loop to check for items which would become smaller or larger than their target main size, in which case the item stops growing or shrinking. Again, this is to avoid certain items becoming tiny, or massive in comparison to the rest of the items.

All that was simplified in terms of the spec as I’ve not looked at some of the more edge-casey scenarios, and you can generally simply further in your mind, assuming you are happy to let Flexbox do its thing and are not after pixel perfection. Remembering the following two facts will work in most cases.

If you are growing from auto then the flex-basis will either be treated as any width or height on the item or the max-content size. Space will then be assigned according to the flex-grow factor using that size as a starting point.

If you are shrinking from auto then the flex-basis will either be treated as any width or height on the item or the max-content size. Space will then be removed according to the flex-basis size multiplied by the flex-shrink factor, and therefore removed in proportion to the max-content size of the items.

Controlling Growing And Shrinking

I’ve spent most of this article describing what Flexbox does when left to its own devices. You can, of course, exercise greater control over your flex items by using the flex properties. They will hopefully seem more predictable with an understanding of what is happening behind the scenes.

By setting your own flex-basis, or given the item itself a size which is then used as the flex-basis you take back control from the algorithm, telling Flexbox that you want to grow or shrink from this particular size. You can turn off growing or shrinking altogether by setting flex-grow or flex-shrink to 0. On this point, however, it is worth using a desire to control flex items as a time to check whether you are using the right layout method. If you find yourself trying to line up flex items in two dimensions then you might be better choosing Grid Layout.

If your flex items are ending up an unexpected size, then this is usually because your flex-basis is auto and there is something giving that item a width, which is then being used as the flex-basis. Inspecting the item in DevTools may help identify where the size is coming from. You can also try setting a flex-basis of 0 which will force Flexbox to treat the item as having zero width. Even if this isn’t the outcome that you want, it will help to identify the flex-basis value in use as being the culprit for your sizing issues.

Flex Gaps

A much-requested feature of Flexbox is the ability to specify gaps or gutters between flex items in the same way that we can specify gaps in grid layout and multi-column layout. This feature is specified for Flexbox as part of Box Alignment, and the first browser implementation is on the way. Firefox expects to ship the gap properties for Flexbox in Firefox 63. The following example can be viewed in Firefox Nightly.

See the Pen Smashing Flexbox Series 3: flex-gaps by Rachel Andrew (@rachelandrew) on CodePen.


Three rows of items with gutter spacing between them


The image as seen in Firefox 63 (Large preview)

As with grid layout, the length of the gap is taken into account before space is distributed to flex items.

Wrapping Up

In this article, I’ve tried to explain some of the finer points of how Flexbox works out how big the flex items are. It can seem a little academic, however, taking some time to understand the way this works can save you huge amounts of time when using Flexbox in your layouts. I find it really helpful to come back to the fact that, by default, Flexbox is trying to give you the most sensible layout of a bunch of items with varying sizes. If an item has more content, it is given more space. If you and your design don’t agree with what Flexbox thinks is best then you can take control back by setting your own flex-basis.

Smashing Editorial
(il)


Link to article: 

Flexbox: How Big Is That Flexible Box?

Thumbnail

Smashing Book 6 Is Here: New Frontiers In Web Design




Smashing Book 6 Is Here: New Frontiers In Web Design

Vitaly Friedman



Imagine you were living in a perfect world. A world where everybody has fast, stable and unthrottled connections, reliable and powerful devices, exquisite screens, and capable, resilient browsers. The screens are diverse in size and pixel density, yet our interfaces adapt to varying conditions swiftly and seamlessly. What a glorious time for all of us — designers, developers, senior Webpack configurators and everybody in-between — to be alive, wouldn’t you agree?

Well, we all know that the reality is slightly more nuanced and complicated than that. That’s why we created Smashing Book 6, our shiny new book that explores uncharted territories and seeks to discover new reliable front-end and UX techniques. And now, after 10 months of work, the book is ready, and it’s shipping. Jump to table of contents and get the book right away.


Smashing Book 6: New Frontiers in Web Design

eBook

$19Get the eBook

PDF, ePUB, Kindle. Free for Smashing Members.

Hardcover

$39Get the Print (incl. eBook)

Printed, quality hardcover. Free airmail shipping worldwide.

About The Book

Finding your way through front-end and UX these days is challenging and time-consuming. But frankly, we all just don’t have time to afford betting on a wrong strategy. Smashing Book 6 sheds some light on new challenges and opportunities, but also uncovers new traps and pitfalls in this brave new front-end world of ours.

Our books aren’t concerned with short-living trends, and our new book isn’t an exception. Smashing Book 6 is focused on real challenges and real front-end solutions in the real world: from accessible apps to performance to CSS Grid Layout to advanced service workers to responsive art direction. No chit-chat or theory. Things that worked, in actual projects. Jump to table of contents.


Smashing Book 6


The Smashing Book 6, with 536 pages on real-life challenges and opportunities on the web. Photo by our dear friend Marc Thiele. (Large preview)

In the book, Laura and Marcy explore strategies for maintainable design systems and accessible single-page apps with React, Angular etc. Mike, Rachel and Lyza share insights on using CSS Custom Properties and CSS Grid in production today. Yoav and Lyza take a dive deep into performance patterns and service workers in times of Progressive Web Apps and HTTP/2.


Inner design of the Smashing Book 6.


Inner design of the Smashing Book 6. Designed by one-and-only Chiara Aliotta. Large view.

Ada, Adrian and Greg explore how to design for watches and new form factors, as well as AR/VR/XR, chatbots and conversational UIs. The last chapter will guide you through some practical strategies to break out of generic, predictable, and soulless interfaces — with dozens of examples of responsive art direction. But most importantly: it’s the book dedicated to headaches and solutions in the fragile, inconsistent, fragmented and wonderfully diverse web we find ourselves in today.

Table Of Contents

Want to peek inside? Download a free PDF sample (PDF, ca. 21 MB) with a chapter on bringing personality back to the web by yours truly. Overall, the book contains 10 chapters:

  1. Making Design Systems Work In Real-Life
    by Laura Elizabeth
  2. Accessibility In Times Of Single-Page Applications
    by Marcy Sutton
  3. Production-Ready CSS Grid Layouts
    by Rachel Andrew
  4. Strategic Guide To CSS Custom Properties
    by Mike Riethmueller
  5. Building An Advanced Service Worker
    by Lyza Gardner
  6. Loading Assets On The Web
    by Yoav Weiss
  7. Conversation Interface Design Patterns
    by Adrian Zumbrunnen
  8. Building Chatbots And Designing For Watches
    by Greg Nudelman
  9. Cross Reality And The Web (AR/VR)
    by Ada Rose Cannon
  10. Bringing Personality Back To The Web (free PDF sample, 21MB)
    by Vitaly Friedman
Laura Elizabeth
Marcy Sutton
Rachel Andrew
Mike Riethmuller
Lyza Danger Gardner
Yoav Weiss
Adrian Zumbrunnen
Greg Nudelman
Ada Rose Edwards
Vitaly Friedman
From left to right: Laura Elizabeth, Marcy Sutton, Rachel Andrew, Mike Riethmuller, Lyza D. Gardner, Yoav Weiss, Adrian Zumbrunnen, Greg Nudelman, Ada Rose Edwards, and yours truly.

  • 536 pages. Quality hardcover + eBook (PDF, ePUB, Kindle).
    Published late September 2018.
  • Written by and for designers and front-end developers.
    Designed with love from Italy by Chiara Aliotta.
  • Free airmail worldwide shipping from Germany.
    Check delivery times for your country.
  • If you are a Smashing Member, don’t forget to apply your Membership discount.
  • Good enough? Get the book right away.

Smashing Book 6: Covers of Chapter 1 and Chapter 10

eBook

$19Get the eBook

PDF, ePUB, Kindle. Free for Smashing Members.

Hardcover

$39Get the Print (incl. eBook)

Printed, quality hardcover. Free airmail shipping worldwide.

About The Designer

Chiara AliottaThe cover was designed with love from Italy by one-and-only Chiara Aliotta. She founded the design studio Until Sunday and has directed the overall artistic look and feel of different tech companies and not-for-profit organizations around the world. We’re very happy that she gave Smashing Book 6 that special, magical touch.

Behind The Scenes Of The Design Process

We asked Chiara to share some insights into the design process of the cover and the interior design and she was very kind to share some thoughts with us:

“It all started with a few exchanges of emails and a Skype meeting where Vitaly shared his idea of the book and the general content. I had a lot of freedom, which is always exciting and scary at the same time. The only bond (if we want to call it like this) was that the “S” of Smashing Magazine should be the main protagonist of the cover, reinvented and creatively presented as per all the other previous Smashing Books.




The illustration on paper. The cover sketched on paper. Also check the close-up photo. (Large preview)

I worked around few keywords that Vitaly was using to describe the book during our meetings and then developed an idea around classical novels of adventure where the main hero leaves home, encounters great hazards, risks, and then eventually returns wiser and/or richer than he/she was before.

So I thought of Smashing Book 6 as a way to propose this basic and mythic structure under a new light: through the articles of this book, the modern web designer will be experiencing true and deep adventures.

I imagined the “S” as an engine, the starting point of this experience, from where different worlds were creating and expanding. So the cover was the map of these uncharted territories that the book explores.

Every element on the cover has a particular meaning that constructs the S
Every element on the cover has a particular meaning that constructs the S. Large view.

I am a person who judges books by its cover and having read some of the chapters and knowing some of the well-established writers, I wanted to honour its content and their work by creating a gorgeous cover and chapter illustrations.

For this edition of Smashing Book, I imagined a textile cover in deep blue, where the graphic is printed using a very old technique, the hot gold foil stamping.

Together with Markus, part of the Smashing Magazine team and responsible for the publishing of all the Smashing Books, we worked closely to choose the final details of the binding and guarantee an elegant and sophisticated result, adding a touch of glam to the book.




Smashing Book 6 comes wrapped with a little bookmark. Photo by our dear friend Marc Thiele.

As a final touch, I added a paper wrap around the book that invites the readers to “unlock their adventure”, suggesting a physical action: the reader needs to tear off the paper before starting reading the book.
And for this only version, we introduced a customise Smashing Magazine bookmark, also in printed on gold paper. Few more reasons to prefer the paperback version over the digital ones!”

A huge round of applause to Chiara for her wonderful work and sharing the thoughts with us. We were remarkably happy with everything from design to content. But what did readers think? Well, I’m glad that you asked!




Sketches for chapter illustrations. (Large preview)

Feedback and Testimonials

We’ve sent the shiny new book to over 200 people to peek through and read, and we were able to gather some first insights. We’d love to hear your thoughts, too!

“Web design is getting pretty darned complicated. The new book from SmashingMag aims to bring the learning curve down to an accessible level.”

Aaron Walter, InVision

“Just got the new Smashing Book 6 by SmashingMag. What a blast! From CSS Grid Layout, CSS Custom Properties and service workers all the way to the HTTP/2 and conversational interfaces and many more. I recommend it to all the people who build interfaces.”

Mihael Tomić, Osijek, Croatia

“The books published by SmashingMag and team are getting better each time. I was thrilled to be able to preview it… EVERY CHAPTER IS GOOD! Having focused on a11y for much of my career, Marcy Sutton’s chapter is a personal favorite.”

Stephen Hay, Amsterdam, Netherlands


Smashing Book 6, a thank-you page


The Smashing Book 6, with 536 pages on real-life challenges and solutions for the web. Huge thank-you note to the smashing community for supporting the book and out little magazine all these years. (Large preview)

Thank You For Your Support!

We’re very honored and proud to have worked with wonderful people from the industry who shared what they’ve learned in their work. We kindly thank all the hard-working people involved in making this book reality. We kindly thank you for your ongoing support of the book and our little magazine as well. It would be wonderful if you could mention the book by any chance as well in your social circles and perhaps link to this very post.

We’ve also prepared a little media kit .zip with a few photos and illustrations that you could use if you wanted to — just sayin’!

We can’t wait to hear your thoughts about the book! Happy reading, and we hope that you’ll find the book as useful as we do. Just have a cup of coffee (or tea) ready before you start reading, of course, stay smashing and… meow!


Smashing Book 6: New Frontiers in Web Design

eBook

$19Get the eBook

PDF, ePUB, Kindle. Free for Smashing Members.

Hardcover

$39Get the Print (incl. eBook)

Printed, quality hardcover. Free airmail shipping worldwide.

Smashing Editorial
(ra, il)


Continued here:  

Smashing Book 6 Is Here: New Frontiers In Web Design

Thumbnail

Getting Started With Machine Learning




Getting Started With Machine Learning

Alvin Wan



The goal of machine learning is to find patterns in data and use those patterns to make predictions. It can also give us a framework to discuss machine learning problems and solutions — as you’ll see in this article.

First, we will start with definitions and applications for machine learning. Then, we will discuss abstractions in machine learning and use that to frame our discussion: data, models, optimization models, and optimization algorithms. Later on in the article, we will discuss fundamental topics that underlie all machine learning methods and conclude with practical guidance for getting started with using machine learning. By the end, you should have an understanding of how to advance your practice and study of machine learning.

Let’s begin.

So, What Exactly Is Machine Learning?

Machine learning is generically a set of techniques to find patterns in data. Applications range from self-driving cars to personal AI assistants, from translating between French and Taiwanese to translating between voice and text. There are a few common applications of machine learning that already or could potentially permeate your day-to-day.

  1. Detecting anomalies
    Recognize spikes in website traffic or highlight abnormal bank activity.
  2. Recommend similar content
    Find products you may be looking for or even Smashing Magazine articles that are relevant.
  3. Predict the future
    Plan the path of neighboring vehicles or identify and extrapolate market trends for stocks.

The above are few of many applications of machine learning, but most applications tie back to learning the underlying distribution of data. A distribution specifies events and probability of each event. For example:

  • With 50% probability, you buy an item $5 or less.
  • With 25% probability, you buy an item $5-$10.
  • With 24% probability, you buy an item $10-100.
  • With 1% probability, you buy an item > $100.

Using this distribution, we can accomplish all of our tasks above:

  1. Detecting anomalies
    With a $100 purchase, we can confidently call this an anomaly.
  2. Recommend similar content
    A purchase of $3 means we should recommend more items $5 or less.
  3. Predict the future
    Without any prior information, we can predict that the next purchase will be $5 or less.

With a distribution of data, we can accomplish a myriad of tasks. In sum, one goal in machine learning is to learn this distribution.

Even more generically, our goal is to learn a specific function with particular inputs and outputs. We call this function our model. Our input is denoted x. Say our model, which accepts input x, is

f(x) = ax

Here, a is a parameter of our model. Each parameter corresponds to a different instance of our model. In other words, the model where a=2 is different from the model where a=3. In machine learning, our goal is to learn this parameter, changing it until we do “well.” How do we determine which values of a do “well”?

We need to define a way to evaluate our model, for each parameter a. To start, the output of f(x) is our prediction. We will refer to y as our label, meaning the true and desired output. With our predictions and our labels, we can define a loss function. One such loss function is simply the difference between our prediction and our label, |f(x) - y|. Using this loss function, we can then evaluate different parameters for our model. Picking the best parameter for our model is known as training. If we have a few possible parameters, we can simply try each parameter and pick the one with the smallest loss!

However, most problems are not as simple. What happens if there are an infinite number of different parameters? Let’s say all decimal values between 0 and 1? Between 0 and infinity? This brings us to our next topic: abstractions in machine learning. We will discuss different facets of machine learning, to compartmentalize your knowledge into data, models, objectives, and methods of solving objectives. Beyond learning the right parameter, there are plenty of other challenges: how do we break down a problem as complex as controlling a robot? How do we control a self-driving car? What does it mean to train a model that identifies faces? The section below will help you organize answers to these questions.

Abstractions

There are countless topics in machine learning — at various levels of specificity. To better understand where each piece fits in the larger picture, consider the following abstractions for machine learning. These abstractions compartmentalize our discussion of machine learning topics, and knowing them will make it easier for you to frame topics. The following classifications are taken from Professor Jonathan Shewchuck at UC Berkeley:

  1. Application and Data
    Consider the possible inputs and the desired output for the problem.
    Questions: What is your goal? How is your data structured? Are there labels? Is it reasonable for us to extract output from the provided inputs?

    Example: The goal is to classify pictures of handwritten digits. The input is an image of a handwritten number. The output is a number.

  2. Model
    Determine the class of functions under consideration.
    Questions: Are linear functions sufficient? Quadratic functions? Polynomials? What types of patterns are we interested in? Are neural networks appropriate? Logistic regression?

    Example: Linear regression

  3. Optimization Problem
    Formulate a concrete objective in mathematics.
    Questions: How do we define loss? How do we define success? Should we apply additionally penalties to bias our algorithm? Are there imbalances in the data our objective needs to consider?

    Example: Find `x` that minimizes |Ax-b|^2

  4. Optimization Algorithm
    Determine how you will solve the optimization problem.
    Questions: Can we compute a solution by hand? Do we need an iterative algorithm? Can we convert this problem to an equivalent but easier-to-solve objective, and solve that one?

    Example: Take derivative of the function. Set it to zero. Solve for our optimal parameter.

Abstraction 1: Data

In practice, collecting, managing, and packaging data is 90% of the battle. The data contains samples in which each sample is a specific realization of our input. For example, our input may generically be images of dogs. The first sample is specifically a picture of Maxie, my Bernese Mountain dog-chow chow mix at home. The second sample is specifically a picture of Charlie, a young corgi.

While training your model, it is important to handle your data properly. This means separating our data accordingly and not peeking prematurely at any set of data. In general, our data is split into three portions:

  1. Training set
    This is the dataset you train your model on. The model may see this set hundreds of times.
  2. Validation set
    This is the dataset you evaluate your model on, to assess accuracy and tune your model or method accordingly.
  3. Test set
    This is the dataset you evaluate on to assess accuracy, once at the very end. Running on the test set prematurely could mean your model overfits to the test set as well, so run only once. We will discuss the notion of “overfitting” in more detail below.

Abstraction 2: Models

Machine learning methods are split into the following two:

Supervised Learning

In supervised learning, our algorithm has access to labeled data. Still, we explore the following two classes of problems:

  • Classification
    Determine which of k classes C_1, C_2, ... C_k to which each sample belongs, e.g. “Which breed of dog is this?” The dog could be one of "corgi", "bernese mountain dog", "chow chow"...
  • Regression
    Determine a real-valued output (which are often probabilities), e.g. “What is the probability this patient has neuroblastoma (eye cancer)?”
Unsupervised Learning

In unsupervised learning, our algorithm does not have access to labels, and we explore the following classes of problems:

  • Clustering
    Cluster samples into k clusters. We do not have a label for the resulting clusters. “Which DNA sequences are most similar?”
  • Dimensionality reduction
    Reduce the number of “unique” (linearly independent) features we consider. “What are common features of faces?”

Abstraction 3: Optimization Objective

Before discussing optimization objectives and algorithms, we’ll need an example to discuss. Least squares are the canonical example. We will restrict our attention to a specific form of least squares: Let us return to our grade-school problem of fitting a line to some points.

Let’s recall the equation of a line:

y = m * x + b

Assume we have such a line. This is the true underlying model.


 true model


True model. The line that generates our data. (Large preview)

Now, sample points from this line.


true data


True data. Data that is sampled from the true model. (Large preview)

For each point, jiggle it a little bit. In other words, add noise, which is random perturbations. This noise is due to real-world processes.


noise


Noise. Real-world perturbations that affect our data. This may be due to imprecision in measurements, lossy compression, and so on. (Large preview)

This gives us our observed data. We will call these points (x_1, y_1), (x_2, y_2), (x_3, y_3).... This is the training data we are given to train a model on. We do not have access to the underlying line that generated this data (the original green line).


observations


Observations. Our true data with noise and ultimately what we will use to train a model. (Large preview)

Say we have an estimate for the parameters of a line. In this case, the parameters are m and b. This gives us a predicted line, drawn in blue below.


proposed model


Proposed model. The result of training a model on our observations. (Large preview)

We wish to evaluate our blue line, to see how accurate it is. To start, we use m and b to estimate y. We compute a set of ŷ values.

ŷ_i = m * x_i + b

The error for a single predicted ŷ_i and true y_i is simply

(ŷ_i−y_i)^2

Our total error is then the sum of squared differences, across all samples. This yields our loss.

∑(ŷ_i−y_i)^2

Presented visually, this is the vertical distance between our observed points and our predicted line.


observed error


Observed error. The distance between our observed data and our proposed model. (Large preview)

Plugging in ŷ_i from above, we then have the total error in terms of m and b.

∑(m * x_i + b − y_i)^2

Finally, we want to minimize this quantity. This yields our objective function, abstraction 3 from our list of abstractions above.

min_m, b ∑(m * x_i + b−y_i)^2

The above states in mathematics that the goal is to minimize the loss by changing values of m and b. The purpose of this section was to motivate fitting a line of best of fit, a special case of least squares. Additionally, we showed examined the least squares objective. Next, we need to solve this objective.

Abstraction 4: Optimization Algorithm

How do we minimize this? We take the derivative with respect to m`, set to 0 and solve. After solving, we obtain the analytical solution. Solving for an analytical solution was our optimization algorithm, the fourth and final abstraction in our list of abstractions.

Note: The important portion of this section is to inform you that least squares have a closed form solution, meaning that the optimal solution for our problem can be computed, explicitly. To understand why this is significant, we need to examine a problem without a closed-form solution. For example, we could never solve x=logx for a standard base-10 logarithm. Try graphing these two lines, and we see that they never intersect. In which case, we have no closed-form solution. On the other hand, ordinary least squares have a closed-form — which is good news. For any problem reduced to least squares, we can then compute the optimal solution, given our data and assumptions.

Fundamental Topics

Before studying more methods, it is necessary to understand the undercurrents of machine learning. These will govern the initial study of machine learning:

Bias-Variance Tradeoffs

One of machine learning’s most dreaded evils is overfitting in which a model is too closely tailored to the training data. In the limit, the most overfit model will memorize the data. This might mean that if one does well on exam A, one repeats every detail for exam B — down to the duration of an inter-exam restroom trip and whether or not one used the urinal.

A related but less common evil is underfitting, where the model is not sufficiently expressive to capture important information in the data. This could mean that one looks only at homework scores to predict exam scores, ignoring the effects of reading notes, completing practice exams, and more. Our goal is to build a model that generalizes to new examples while making the appropriate distinctions.

Given these two evils, there are a variety of approaches to fighting both. One is modifying your optimization objective to include a term that penalizes model complexity. Another is tuning hyperparameters that govern either your objective or your algorithm, which may correspond to notions such as “training speed” or “momentum.” The bias-variance tradeoff gives us a precise way of defining and handling both overfitting and underfitting.

Maximum Likelihood Estimation (MLE) + Maximum A Posteriori (MAP)

Say we have ice cream flavors A, B, and C. We observe different recipes. Our goal is to predict which flavor each recipe produces.

One way to predict flavors based on recipes is to first estimate the following probability:

P(flavor|recipe)

Given this probability and a new recipe, how can we predict the flavor? Given a recipe, simply consider the probability of each of the flavors A, B, C.

P(flavor=A|recipe) = 0.4
P(flavor=B|recipe) = 0.5
P(flavor=C|recipe) = 0.1

Then, pick the flavor that has the highest probability. Above, flavor B has the highest probability, given our recipe. Thus, we predict flavor B. Restating the above rule in mathematics, we have:

argmax_flavor P(flavor|recipe)  # argmax means take the flavor that corresponds to the max value

However, the only information at our disposal is the reverse: the probability of some recipe given the flavor.

P(recipe|flavor)

For Maximum Likelihood Estimates, we make assumptions and find that the two values are proportional.

P(recipe|flavor) ~ P(flavor|recipe)

Since we’re only interested in the class with maximum probability P(flavor|recipe), we can simply find the class with maximum probability, for a proportional value P(recipe|flavor).

argmax_flavor P(recipe|flavor)

MLE offers the above objective as one way to predict, using the probability of data given the labels.

However, allow me to convince you that it’s reasonable to assume we have (x|y). We can estimate this from observed, real-world data. For example, say we wish to estimate the number of marbles each student in your class carries, based on the number of rubber ducks the student carries.

Each student’s number of rubber ducks is the data x, and the number of marbles she or he has is y. We will use this sample data below.

| x | y |
|---|---|
| 1 | 2 |
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 2 |
| 1 | 2 |

For every y, we can compute the number of x, given us P(x|y). For the first one, P(x=1|y=1), consider all of the rows where y=1. There are 2, and only one of them has x=1. Therefore, P(x=1|y=1) = 12. We can repeat this for all values of x and y.

P(x=1|y=1) = 1/2
P(x=2|y=1) = 1/2
P(x=1|y=2) = 3/4
P(x=2|y=2) = 1/4

Featurizations, Regularization

Least squares draw lines of best fit for us. Note that least squares can fit the model anytime the model is linear in its inputs x and outputs y.

Say m=1. We have the following equation:

y = x + b

However, what if we had data that doesn’t generally follow a line? Specifically, consider a set of data sampled along a circle. Recall that the equation for a circle is:

x^2 + y^2 = r^2

Can least squares fit this well? As it stands, no. The model is not linear in its inputs x and outputs y. Instead, the model above is quadratic in x and y. However, it turns out that we can use still use least squares, just with a modification. To accomplish this, we featurize our samples.

Consider the following: what if the input to our model was x_ = x^2 and y_ = y^2? Then, our model is trying to learn the following model.

x_ + y_ = r^2

Is this linear in the model’s input x_ and output y_? Yes. Note the subtlety. The current model is still quadratic in x,y but it is linear in x_,y_. This means that least squares can fit the data if we square x^2 and y^2 before training least squares.

More generally, we can take any non-linear featurization to apply least squares to labels that are non-linear in the features. This is a fairly powerful tool, known as featurization.

However, featurizations lead to more complex models. Regularization allows us to penalize model complexity, ensuring that we do not overfit the training data.

Conclusion

In this article, you’ve touched on major topics in the fundamentals of machine learning. Using the abstractions above, you now have a framework to discuss machine learning problems and solutions. Using the fundamental topics above, you now also have quintessential concepts to learn more about, giving you the necessary tools to evaluate risk and other concerns in a machine learning application.

Further Reading

We will continue to explore these topics in depth, both the undercurrents of machine learning and specific methods. In the interim, here are resources to further your study and exploration of machine learning:

Smashing Editorial
(ra, il)


Continue reading here: 

Getting Started With Machine Learning

Thumbnail

Preparing Your App For iOS 12 Notifications




Preparing Your App For iOS 12 Notifications

Kaya Thomas



In 2016, Apple announced a new extension that will allow developers to better customize their push and local notifications called the UNNotificationContentExtension. The extension gets triggered when a user long presses or 3D touches on a notification whenever it is delivered to the phone or from the lock/home screen. In the content extension, developers can use a view controller to structure the UI of their notification, but there was no user interaction enabled within the view controller — until now. With the release of iOS 12 and XCode 10, the view controller in the content extension now enables user interaction which means notifications will become even more powerful and customizable.

At WWDC 2018, Apple also announced several changes to notification settings and how they appear on the home screen. In an effort to make users more aware of how they are using apps and allowing more user control of their app usage, there is a new notification setting called “Deliver Quietly.” Users can set your app to Delivery Quietly from the Notification Center, which means they will not receive banners or sound notifications from your app, but they will appear in the Notification Center. Apple using an in-house algorithm, which presumably tracks often you interact with notifications, will also ask users if they still want to receive notifications from particular apps and encourage you to turn on Deliver Quietly or turn them off completely.

Notifications are getting a big refresh in iOS 12, and I’ve only scratched the surface. In the rest of this article, we’ll go over the rest of the new notification features coming to iOS 12 and how you can implement them in your own app.

Recommended reading: WWDC 2018 Diary Of An iOS Developer

Remote vs Local Notifications

There are two ways to send push notifications to a device: remotely or locally. To send notifications remotely, you need a server that can send JSON payloads to Apple’s Push Notification Service. Along with a payload, you also need to send the device token and any other authentication certificate or tokens that verify your server is allowed to send the push notification through Apple. For this article, we focus on local notifications which do not need a separate server. Local notifications are requested and sent through the UNUserNotificationCenter. We’ll go over later how specifically to make the request for a local notification.

In order to send a notification, you first need to get permission from the user on whether or not they want you to send them notifications. With the release of iOS 12, there are a lot of changes to notification settings and permissions so let’s break it down. To test out any of the code yourself, make sure you have the Xcode 10 beta installed.

Notification Settings And Permissions

Deliver Quietly

Delivery Quietly is Apple’s attempt to allow users more control over the noise they may receive from notifications. Instead of going into the settings app and looking for the app whose notification settings you want to change, you can now change the setting directly from the notification. This means that a lot more users may turn off notifications for your app or just delivery them quietly which means the app will get badged and notifications only show up in the Notification Center. If your app has its own custom notification settings, Apple is allowing you to link directly to that screen from the settings management view pictured below.


iPhone 8 Plus shown with Manage selected from notification which brings up the Deliver Quietly and Turn Off options.


Delivery quietly feature. (Large preview)

In order to link to your custom notification setting screen, you must set providesAppNotificationSettings as a UNAuthorizationOption when you are requesting notification permissions in the app delegate.

In didFinishLaunchingWithOptions, add the following code:

UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound, .providesAppNotificationSettings])  ... 

When you do this, you’ll now see your custom notification settings in two places:

  • If the user selects Turn Off when they go to manage settings directly from the notification;
  • In the notification settings within the system’s Settings app.

iPhone 8 Plus shown with Turn Off selected from notification which brings up the Turn Off All Notifications and Configure in NotificationTester options.


Deep link to to custom notification settings for NotificationTester from notification in the Notification Center. (Large preview)


iPhone 8 Plus shown with system Settings app open with Notifications screen for NotificationTester app.


Deep link to custom notification settings for NotificationTester from system’s Settings app. (Large preview)

You also have to make sure to handle the callback for when the user selects on either way to get to your notification settings. Your app delegate or an extension of your app delegate has to conform to the protocol UNUserNotificationCenterDelegate so you can then implement the following callback method:

func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) 
    let navController = self.window?.rootViewController as! UINavigationController
    let notificationSettingsVC = NotificationSettingsViewController()
    navController.pushViewController(notificationSettingsVC, animated: true)

Another new UNAuthorizationOption is provisional authorization. If you don’t mind your notifications being delivered quietly, you can set add .provisional to your authorization options as shown below. This means that you don’t have to prompt the user to allow notifications — the notifications will still show up in the Notification Center.

UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .provisional])  ... 

So now that you’ve determined how to request permission from the user to deliver notifications and how to navigate users to your own customized settings view, let’s go more into more detail about the actual notifications.

Sending Grouped Notifications

Before we get into the customization of the UI of a notification, let’s go over how to make the request for a local notification. First, you have to register any UNNotificationCategory, which are like templates for the notifications you want to send. Any notification set to a particular category will inherit any actions or options that were registered with that category. After you’ve requested permission to send notifications in didFinishLaunchingWithOptions, you can register your categories in the same method.

let hiddenPreviewsPlaceholder = "%u new podcast episodes available"
let summaryFormat = "%u more episodes of %@"
let podcastCategory = UNNotificationCategory(identifier: "podcast", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenPreviewsPlaceholder, categorySummaryFormat: summaryFormat, options: [])
UNUserNotificationCenter.current().setNotificationCategories([podcastCategory])

In the above code, I start by initiating two variables:

  • hiddenPreviewsPlaceholder
    This placeholder is used in case the user has “Show Previews” off for your app; if we don’t have a placeholder there, your notification will show with only “Notification” also the text.
  • summaryFormat
    This string is new for iOS 12 and coincides with the new feature called “Group Notifications” that will help the Notification Center look a lot cleaner. All notifications will show up in stacks which will be either representing all notifications from the app or specific groups that the developer has set for there app.

The code below shows how we associate a notification with a group.

@objc func sendPodcastNotification(for podcastName: String) 
let content = UNMutableNotificationContent()
content.body = "Introducing Season 7"
content.title = "New episode of (podcastName):"
content.threadIdentifier = podcastName.lowercased()
content.summaryArgument = podcastName
content.categoryIdentifier = NotificationCategoryType.podcast.rawValue
sendNotification(with: content)

For now, I’ve hardcoded the text of the notification just for the example. The threadIdentifier is what creates the groups that we show as stacks in the Notification Center. In this example, I want the notifications grouped by podcast so each notification you get is separated by what podcast it’s associated with. The summaryArgument matches back to our categorySummaryFormat we set in the app delegate. In this case, we want the string for the format: "%u more episodes of %@" to be the podcast name. Lastly, we have to set the category identifier to ensure the notification has the template we set in the app delegate.

func sendNotification(for category: String, with content: UNNotificationContent) 
let uuid = UUID().uuidString
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: uuid, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)

The above method is how we request the notification to be sent to the device. The identifier for the request is just a random unique string; the content is passed in and we create the content in our sendPodcastNotification method, and lastly, the trigger is when you want the notification to send. If you want the notification to send immediately, you can set that parameter to nil.


iPhone 8 Plus lock screen shown with a grouped notification stack from Notification Tester app.


Grouped notifications for NotificationTester. (Large preview)


iPhone 8 Plus lock screen shown with a grouped notification stack from Notification Tester app that has hidden content.


Notification grouped with previews turned off. (Large preview)

Using the methods we’ve described above, here’s the result on the simulator. I have a button that has the sendPodcastNotification method as a target. I tapped the button three times to have the notifications sent to the device. In the first photo, I have “Show Previews” set to “Always” so I see the podcast and the name of the new episodes along with the summary that shows I have two more new episodes to check out. When “Show Previews” is set to “Never,” the result is the second image above. The user won’t see which podcast it is to respect the “No Preview” setting, but they can still see that I have three new episodes to check out.

Notification Content Extension

Now that we understand how to set our notification categories and make the request for them to be sent, we can go over how to customize the look of the notification using the Notification Service and Notification Content extensions. The Notification Service extension allows you to edit the notification content and download any attachments in your notification like images, audio or video files. The Notification Content extension contains a view controller and storyboard that allows you to customize the look of your notification as well as handle any user interaction within the view controller or taps on notification actions.

To add these extensions to your app go File → New → Target.


Xcode shown after selecting from menu to add a new target, Notification Content Extension is highlighted.


Adding new target to app for the Notification Content Extension. (Large preview)

You can only add them one at a time, so name your extension and repeat the process to add the other. If a pop-up appears asking you to activate your new scheme, click the “Activate” button to set it up for debugging.

For the purpose of this tutorial, we will be focusing on the Notification Content Extension. For local notifications, we can include the attachments in the request, which we’ll go over later.

First, go to the Info.plist file in the Notification Content Extension target.


Info.plist file for Notification Content Extension shown in Xcode.


Info.plist for the Notification Content Extension. (Large preview)

The following attributes are required:

  • UNNotificationExtensionCategory
    A string value equal to the notification category which we created and set in the app delegate. This will let the content extension know which notification you want to have custom UI for.
  • UNNotificationExtensionInitialContentSizeRatio
    A number between 0 and 1 which determines the aspect ratio of your UI. The default value is 1 which will allow your interface to have its total height equal to its width.

I’ve also set UNNotificationExtensionDefaultContentHidden to “YES” so that the default notification does not show when the content extension is running.

You can use the storyboard to set up your view or create the UI programmatically in the view controller. For this example I’ve set up my storyboard with an image view which will show the podcast logo, two labels for the title and body of the notification content, and a “Like” button which will show a heart image.

Now, in order to get the image showing for the podcast logo and the button, we need to go back to our notification request:

guard let pathUrlForPodcastImg = Bundle.main.url(forResource: "startup", withExtension: "jpg") else  return 
let imgAttachment = try! UNNotificationAttachment(identifier: "image", url: pathUrlForPodcastImg, options: nil)

guard let pathUrlForButtonNormal = Bundle.main.url(forResource: "heart-outline", withExtension: "png") else  return 
let buttonNormalStateImgAtt = try! UNNotificationAttachment(identifier: "button-normal-image", url: pathUrlForButtonNormal, options: nil)

guard let pathUrlForButtonHighlighted = Bundle.main.url(forResource: "heart-filled", withExtension: "png") else  return 
let buttonHighlightStateImgAtt = try! UNNotificationAttachment(identifier: "button-highlight-image", url: pathUrlForButtonHighlighted, options: nil)

content.attachments = [imgAttachment, buttonNormalStateImgAtt, buttonHighlightStateImgAtt]

I added a folder in my project that contains all the images we need for the notification so we can access them through the main bundle.


Project navigator shown in Xcode.


Xcode project navigator. (Large preview)

For each image, we get the file path and use that to create a UNNotificationAttachment. Added that to our notification content allows us to access the images in the Notification Content Extension in the didReceive method shown below.

func didReceive(_ notification: UNNotification) {
self.newEpisodeLabel.text = notification.request.content.title
self.episodeNameLabel.text = notification.request.content.body

let imgAttachment = notification.request.content.attachments[0]
let buttonNormalStateAtt = notification.request.content.attachments[1]
let buttonHighlightStateAtt = notification.request.content.attachments[2]

guard let imageData = NSData(contentsOf: imgAttachment.url), let buttonNormalStateImgData = NSData(contentsOf: buttonNormalStateAtt.url), let buttonHighlightStateImgData = NSData(contentsOf: buttonHighlightStateAtt.url) else  return 

let image = UIImage(data: imageData as Data)
let buttonNormalStateImg = UIImage(data: buttonNormalStateImgData as Data)?.withRenderingMode(.alwaysOriginal)
let buttonHighlightStateImg = UIImage(data: buttonHighlightStateImgData as Data)?.withRenderingMode(.alwaysOriginal)

imageView.image = image
likeButton.setImage(buttonNormalStateImg, for: .normal)
likeButton.setImage(buttonHighlightStateImg, for: .selected)
}

Now we can use the file path URLs we set in the request to grab the data for the URL and turn them into images. Notice that I have two different images for the different button states which will allow us to update the UI for user interaction. When I run the app and send the request, here’s what the notification looks like:


iPhone 8 Plus shown with custom notification loaded after force touching the notification.


Content extension loaded for NotificationTester app. (Large preview)

Everything I’ve mentioned so far in relation to the content extension isn’t new in iOS 12, so let’s dig into the two new features: User Interaction and Dynamic Actions. When the content extension was first added in iOS 10, there was no ability to capture user touch within a notification, but now we can register UIControl events and respond when the user interacts with a UI element.

For this example, we want to show the user that the “Like” button has been selected or unselected. We already set the images for the .normal and .selected states, so now we just need to add a target for the UIButton so we can update the selected state.

override func viewDidLoad() 
super.viewDidLoad()
// Do any required interface initialization here.
likeButton.addTarget(self, action: #selector(likeButtonTapped(sender:)), for: .touchUpInside)
@objc func likeButtonTapped(sender: UIButton) 
likeButton.isSelected = !sender.isSelected

Now with the above code we get the following behavior:

Gif of iPhone 8 Plus with custom notification loaded and like button being selected and unselected.
Selecting like button within notification. (Large preview)

In the selector method likeButtonTapped, we could also add any logic for saving the liked state in User Defaults or the Keychain, so we have access to it in our main application.

Notification actions have existed since iOS 10, but once you click on them, usually the user will be rerouted to the main application or the content extension is dismissed. Now in iOS 12, we can update the list of notification actions that are shown in response to which action the user selects.

First, let’s go back to our app delegate where we create our notification categories so we can add some actions to our podcast category.

let playAction = UNNotificationAction(identifier: "play-action", title: "Play", options: [])
let queueAction = UNNotificationAction(identifier: "queue-action", title: "Queue Next", options: [])
let podcastCategory = UNNotificationCategory(identifier: "podcast", actions: [playAction, queueAction], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenPreviewsPlaceholder, categorySummaryFormat: summaryFormat, options: [])

Now when we run the app and send a notification, we see the following actions shown below:


iPhone 8 Plus with custom notification loaded with an options to Play or Add to Queue.


Notification quick actions. (Large preview)

When the user selects “Play,” we want the action to be updated to “Pause.” If they select “Queue Next,” we want that action to be updated to “Remove from Queue.” We can do this in our didReceive method in the Notification Content Extension’s view controller.

func didReceive(_ response: UNNotificationResponse, completionHandler completion:
(UNNotificationContentExtensionResponseOption) -> Void) {
guard let currentActions = extensionContext?.notificationActions else  return 

if response.actionIdentifier == "play-action" 
let pauseAction = UNNotificationAction(identifier: "pause-action", title: "Pause", options: [])
let otherAction = currentActions[1]
let newActions = [pauseAction, otherAction]
extensionContext?.notificationActions = newActions

 else if response.actionIdentifier == "queue-action" 
let removeAction = UNNotificationAction(identifier: "remove-action", title: "Remove from Queue", options: [])
let otherAction = currentActions[0]
let newActions = [otherAction, removeAction]
extensionContext?.notificationActions = newActions

  else if response.actionIdentifier == "pause-action" 
let playAction = UNNotificationAction(identifier: "play-action", title: "Play", options: [])
let otherAction = currentActions[1]
let newActions = [playAction, otherAction]
extensionContext?.notificationActions = newActions

 else if response.actionIdentifier == "remove-action" 
let queueAction = UNNotificationAction(identifier: "queue-action", title: "Queue Next", options: [])
let otherAction = currentActions[0]
let newActions = [otherAction, queueAction]
extensionContext?.notificationActions = newActions

completion(.doNotDismiss)
}

By resetting the extensionContext?.notificationActions list to contain the updated actions, it allows us to change the actions every time the user selects one. The behavior is shown in the gif below.

Gif of iPhone 8 Plus with custom notification loaded and the quick actions being changed from Play to Pause and Add to Queue to Remove from Queue.
Dynamic notification quick actions. (Large preview)

Summary

There’s a lot to do before iOS 12 launches to make sure your notifications are ready. The steps vary in complexity and you don’t have to implement them all. Make sure to first download XCode 10 beta so you can try out the features we’ve gone over. If you want to play around with the demo app I’ve referenced throughout the article, check it out on Github.

For Your Notification Permissions Request And Settings, You’ll Need To:

  • Determine whether or not you want to enable provisional authorization and add it to your authorization options.
  • If you have already have a customized notification settings view in your app, add providesAppNotificationSettings to your authorization options as well as implement the call back in your app delegate or whichever class conforms to UNUserNotificationCenterDelegate.

For Notification Grouping:

  • Add a thread identifier to your remote and local notifications so your notifications are correctly grouped in the Notification Center.
  • When registering your notification categories, add the category summary parameter if you want your grouped notification to be more descriptive than “more notifications.”
  • If you want to customize the summary text even more, then add a summary identifier to match whichever formatting you added for the category summary.

For Customized Rich Notifications:

  • Add the Notification Content extension target to your app to create rich notifications.
  • Design and implement the view controller to contain whichever elements you want in your notification.
  • Consider which interactive elements would be useful to you, i.e. buttons, table view, switches, etc.
  • Update the didReceive method in the view controller to respond to selected actions and update the list of actions if necessary.

Further Reading

Smashing Editorial
(ra, yk, il)


Follow this link: 

Preparing Your App For iOS 12 Notifications

Thumbnail

Get Your Mobile Site Ready For The 2018 Holiday Season




Get Your Mobile Site Ready For The 2018 Holiday Season

Suzanne Scacca



After reading the title of this article, it might seem like it’s jumping the gun, but with retailers turning on holiday music and putting out holiday-related displays earlier and earlier every year, your consumers are primed to start thinking about the holidays earlier, too. In fact, a study done by the Tampa Bay Times revealed that in-store shoppers were exposed to holiday music as early as October 22 in 2017.


Holiday music in retail


Results from TBT’s survey on when holiday music starts (Source: Tampa Bay Times) (Large preview)

Of course, e-commerce handles the holiday season a bit differently than brick-and-mortar. It’s not really necessary to announce promotions or run sales in late October or early November. However, that doesn’t mean you should wait until the last minute to prepare your mobile website for the holidays.

In this article, I’m going to give you a quick rundown of what happened during the 2017 holiday sales season and, in particular, what role mobile played in it. Then, we’re going to dig into holiday design and marketing tactics you can use to boost sales through your mobile website for the 2018 holiday season.

Recommended reading: How Mobile Web Design Affects Local Search (And What To Do About It)

A Recap Of The 2017 Holiday Sales Season

Before we get started, I want to quickly add a disclaimer:

This particular section focuses on e-commerce statistics because this kind of data is readily available. Something like the total number of page visits, subscribed readers, and leads generated… well, it’s not.

So, although I only use data to express how important mobile was to 2017 holiday sales, keep in mind that the tips that follow pertain to all websites. Even if your site doesn’t expressly sell goods or services, blogs and other content-driven sites can take advantage of this, too!

Now, let’s take a look at the numbers:

Total Retail Sales

The National Retail Federation calculated the total amount of retail sales–online and in-store–to be $691.9 billion between November and December, a 5.5% bump from 2016.

Total e-Commerce Sales

Adobe put the total amount of e-commerce sales during that same timeframe at $108.15 billion in 2017.


2017 holiday e-commerce revenue


Adobe’s stats on 2016 and 2017 holiday e-commerce revenue (Source: Adobe) (Large preview)

e-Commerce Sales By Device

Adobe takes it even further and breaks down the share of revenue by device:


Device-specific sales during 2017 holidays


Breakdown of desktop, smartphone and tablet sales for 2017 holiday season (Source: Adobe) (Large preview)

e-Commerce Sales vs. Traffic

While smartphone and tablet sales still trail those on desktop, there are a couple interesting things to note here. For starters, desktop revenue has mostly flatlined year-over-year whereas mobile continues to grow. In addition, there’s an interesting disparity between how much traffic comes from each device and what percentage of revenue it generates:


Traffic vs. revenue breakdown


Traffic vs. revenue for desktop, smartphone, tablet (Source: Adobe) (Large preview)

Pay close attention to desktop and smartphone. As you can see, more visits stem from smartphones than any other device and, yet, desktop leads the way in conversions:


Conversion rates by device


Statista shows the breakdown between desktop, smartphone, and tablet conversions in Q1 2018 (Source: Statista) (Large preview)

Is this indicative of a lack of trust in smart devices to handle purchases?

In all likelihood, it probably isn’t. Data from other sources indicates that on holidays, in particular, mobile reigns supreme in terms of visits and conversions:

  • Thanksgiving Day: 62% of traffic / 46% of purchases.
  • Christmas Day: 68% of traffic / 50% of purchases.

Also, let’s not forget to take into account the strengths of mobile devices within the shopper’s experience. According to the four micro-moments as defined by Google, a large number of mobile users commonly search for the following:

  • “I want to know.”
  • “I want to go.”
  • “I want to do.”
  • “I want to buy.”

The second and third are clearly indicative of a searcher’s desire to find something outside their devices (and their homes) to spend money on. That might even be so for the fourth, though it could also be an indication that they want to do their research on mobile and complete the purchase on desktop.

Either way, we know that smartphones tend to be a primary facilitator in the customer’s journey and not something that’s putting an end to the shopping experience as a whole.

Recommended reading: Designing For Micro-Moments

5 Tips To Prepare Your Mobile Site For The 2018 Holiday Season

While the overall numbers indicate that desktop is the leading platform for holiday sales, it’s not a universal rule that can be applied to each and every day in November and December. This is why your own data will have to play a big role in the design choices you make for your mobile site this season.

You have to admit, no matter how stressed or unhappy you might feel around the holidays, there is something nice about encountering just the right hint of holiday “cheer”. And that’s one of the keys to doing this right: finding the right amount of holiday flavor to infuse into your website.

Before we get into what you can do to spruce up your mobile web design, I want to remind you that security and speed are critical elements to check off your list before November gets here. These might not be in your realm of responsibilities, but that doesn’t mean you shouldn’t keep an eye on them.

If you’re doing all this design work in anticipation of boosting conversions over the holidays, don’t let it all be for nothing by forgetting about performance and security essentials. To protect your site from potentially harmful traffic surges, start with this front-end performance checklist. With regards to security, you can use these security improvement tips.

Now, let’s talk about the five ways in which you can prepare your mobile website for the 2018 holiday season:

1. Study Last Year’s Data

If your website has been live and actively doing business for more than a year, you need to start with the data from 2017. Using Google Analytics and your CRM platform, locate answers to the following questions:

What was the prominent device that generated traffic? Sales?

Google Analytics allows you to divvy up traffic based on technology in a number of ways:

Under Browser & OS, you can sort visitors by browser:


Google Analytics browser data


Google Analytics shows which browsers users visited from (Source: Google Analytics) (Large preview)

There is a small tab at the top of the table for “Operating System”. Click that to reveal which OS were used:


Google Analytics operating system data


Google Analytics breaks down traffic by operating system (Source: Google Analytics) (Large preview)

You can use the MobileOverview tab to look at the simple breakdown between desktop, mobile, and tablet users.


Google Analytics device data


Google Analytics division between device traffic (Source: Google Analytics) (Large preview)

Really, your goal here is to weed out desktop users so you can focus strictly on mobile traffic as you assess the following data points.

When did your site experience an increase in traffic in November or December?

Every website’s holiday traffic history will look a little different. Take mine, for example:


A sample Google Analytics holiday traffic chart


An example of holiday traffic up and downs in Google Analytics (Source: Google Analytics) (Large preview)

My business really isn’t affected by the holidays at all… except that I know things are going to be super quiet on and around Thanksgiving and the major holidays in December. This is still important information for me to have.

For businesses that directly sell products or services through their site or content-based sites that plan publication schedules based on traffic, you’ll likely see a different trajectory in terms of highs and lows.

When did sales start to increase (if they don’t coincide with traffic)?

Again, for some of you, the matter of sales is irrelevant if you don’t offer any through your site. For everyone else, however, use the Google Analytics Conversions tab along with sales logged through your payment gateway or CRM to check this number.

Just remember that you have to activate the Conversions module in Google Analytics if you want it to track that data. If you didn’t remember last year, put it in place for this year.

Did the holiday uptick remain consistent until the end of the season or were there temporary dropoffs?

Much of this has to do with how you promote holiday-related events, promotional offers or content through your website. If you consistently market around the holidays from November 1 to the end of the year, you should see relatively steady traffic and sales.

Some days, of course, may be slower than others (like during workdays or earlier in the season), so it’s good to get a sense for the ebb and flow of your site’s holiday traffic. On the other hand, your website might be a major draw only on special sales days and the holidays themselves, so you can use this data to harness your energy for a big push on the days when it’ll have the greatest impact.

Try to identify patterns, so you can plan your design and marketing strategy accordingly.

When did traffic and sales return to their usual amount?

At some point, your site is going to see a dip in activity. There are some businesses that embrace this.

Let’s use Xfinity as an example. Around mid-November of last year, this is the holiday-centric message the top of the home page was pushing:


Xfinity holiday promotion


Xfinity promotes ways to make your home holiday ready (Source: Xfinity) (Large preview)

A month later, on December 9, any mention of the holidays was gone and replaced by a promotion of the upcoming Olympic Winter Games.


Xfinity December promotion


Xfinity stops promoting holidays in December (Source: Xfinity) (Large preview)

One can only assume that a major sporting event like the Olympics helps Xfinity sign more subscribers than trying to capture last-minute sales for the holidays.

Logically, this makes sense. December is a busy time for families. They’re planning travel, purchasing gifts and running around town in preparation for the upcoming celebrations. Most people probably don’t have time to set up a new cable or Internet package and wait around for Xfinity to configure it then.

Bottom line: it’s okay if your holiday-related traffic and sales drop off earlier than December 31. Study your data and let your user behavior guide you in your mobile design and promotion strategy.

What were the most popular sources for mobile traffic?

It’s actually not enough to identify the most popular sources of mobile traffic for your site. Sure, you want to know if organic SEO and social media promotional efforts worked to bring traffic to it… but it won’t really matter if those visitors abandoned the site without taking action.

When you start digging through the ways in which you acquired mobile visitors, make sure to review the sources and keywords used against other telling metrics, like:

  • Bounce rate
  • Time on site
  • Pages visited

This will give you a good sense for what sources — e.g. keywords, PPC ads, social media content, promotional backlinks from other sites — that attracted high-quality leads to it during the holiday season.

What were the most/least successful promotions?

One more thing to look at is what exactly performed the best between November and December with mobile visitors.

Did you run a pop-up promoting free shipping that was dismissed by most mobile visitors, but greatly taken advantage of by those on desktop? Did your custom home page banner touting an upcoming Black Friday sale get more clicks than the home page banner otherwise does at other times of the year? And what pathway resulted in the most conversions?

Dig into what exactly it was that appealed to your mobile visitors. Then, as you work on this year’s plan, focus on reproducing that success.

2. Assess The Navigation

The navigation plays two important roles on a website:

  1. High-level tabs inform visitors on what they’ll find on the site; essentially answering the question, “Is this of relevance to me?”
  2. The navigation itself provides visitors with shortcuts to parts of the site that matter most to them, simplifying their pathway to conversion.

When reviewing your navigation in the context of holiday traffic, you must ensure that it fulfills both of these roles.

Let’s look at two websites that provide relevant links during the holidays while also streamlining the visitors’ journey from entry to holiday-related pages.

Food52 is an online hub for people who enjoy cooking. You can buy kitchen gadgets from the site and peruse a whole bunch of content related to food and cooking.

I want to call out a number of things Food52 does especially well in terms of navigation:


Thanksgiving categories on Food52


The Food52 home page includes Thanksgiving-related categories (Source: Food52) (Large preview)

  1. The hamburger menu is prominently displayed in the top-left, which is exactly where visitors’ eyes will go as they follow the Z-shaped pattern for reading.
  2. The shopping cart, search bar shortcut and profile link are also displayed in the top header, making it easy to navigate to elements that support the shopping experience.
  3. If you scroll down on the home page (as I’ve done in the screenshot above), Food52 includes a good mix of Thanksgiving-related content along with its standard fare. In addition, it includes categories that help users filter through content that’s most relevant to them.

One other thing I’d like to point out is the navigation itself:


Simplified mobile navigation from Food52


Simplified and customized navigation from Food52 for Thanksgiving (Source: Food52) (Large preview)

There are a number of things you’ll notice:

  • The mobile navigation is quite simplified. Despite how many categories and types of pages the site has, the navigation keeps this from being an overwhelming choice.
  • There are special tabs for Thanksgiving and Holiday. This will get users directly to content related to the holiday they’re cooking for.
  • The Hotline — which is its customer service forum — is also featured in the mobile navigation. This element is especially important around the holidays when visitors have questions they need answered quickly.

L.L.Bean is another website that handles mobile navigation well.


L.L.Bean Navigation


L.L.Bean puts the essentials in the navigation (Source: L.L.Bean) (Large preview)

As you can see, there are four buttons located within the mobile header:

  • Hamburger navigation icon: bolded and well-placed;
  • L.L.Bean logo for easy backtracking to the home page;
  • A shopping cart icon which will keep stored items top-of-mind with mobile users;
  • An ever-present search bar to speed up navigation even further.

Once a mobile user expands the hamburger navigation, they encounter this:


L.L.Bean hamburger navigation


L.L.Bean prioritizes customer service and gifts around the holidays (Source: L.L.Bean) (Large preview)

As you can see, “Call Us” is the first option available within the mobile navigation. Again, with people in a rush and trying to get purchases done right over the holidays, having a direct line of communication to the company is important. The account link and “Ship To” personalization are also nice touches as these icons keep conversion top-of-mind.

Now, looking down the navigation, you’ll see this is a pretty standard mega menu. However, take note that at the very top of this category (as is the case for all others) appears a page for “Gifts”. This is not something you see the rest of the year, so that’s another holiday-related touch meant to streamline searches and sales.

3. Use Add-ons At Checkout

Here is everything you need to know to optimize conversions at mobile checkout. If I can add an additional two cents to this matter, though, I’d like to briefly talk about add-ons at checkout… but only around the holidays.

Typically, I believe that a fully streamlined checkout process is essential to capturing as many conversions as possible on mobile devices. It’s hard enough typing out all that information (if it doesn’t auto-populate) and trusting that devices and websites will keep payment information secure.

However…

When it comes to designing the checkout for holiday shoppers, I think it’s at least worth experimenting with add-ons. For example:

  • Promo codes
  • Free delivery options
  • Shorter, but more premium delivery or pick up in store options
  • Gift wrapping.

Nordstrom doesn’t even wait for visitors to get to the checkout to promote this.


Nordstrom free shipping


Nordstrom promotes free shipping and returns right away (Source: Nordstrom) (Large preview)

The very top of the site has a sticky bar promoting the free shipping and returns offer. This way, visitors are already in the mindset that they can get their Black Friday purchases or holiday gifts for even cheaper than planned.

Fitbit has another example of this I really like:


Fitbit holiday promotions


Fitbit promotes sales and free expedited shipping (Source: Fitbit) (Large preview)

The top-half of the Fitbit homepage gets visitors into the mindset that there are cost savings galore here. Not only are items on sale, but certain orders come with free and expedited shipping. And the site clearly states when the sale ends, which will keep customers from getting upset if gifts don’t arrive on time. (It will also probably motivate them to get their shopping done sooner if they want to cash in on the sale.)

So all appropriate expectations regarding pricing and shipping are set right from the very get-go, making checkout go more smoothly.

I know that some may argue these will be bad for UX (and normally I’d join them), but I don’t see them as distractions during the holidays. This is an expensive and busy time of year.

Anything you can add to checkout that says, “Hey, we’re thinking about you and want to make this holiday season go just a little more smoothly” would go over well with your users.

4. Give Images A Seasonal Touch

Images are a tricky thing this time of year. You want to use them to appeal to holiday-minded visitors, but you don’t want to overdo it because images add a lot of pressure to your server. You need your site running fast, so be smart about what you do with them.

  1. Resize them before you ever add them to your site. There’s no need to use oversized images if they’re going to appear smaller online.
  2. Optimize your images with compression tools before and after they’re added to the design. This will free up some space they would otherwise take.
  3. If your users’ journey starts above-the-fold, you might want to consider lazy-loading images.

That said, images can go a long way in communicating to visitors that your site and business are ready to spread some holiday cheer without having to ever explicitly say it. This might be the ideal choice for those of you who design websites for global audiences. Perhaps you’d rather use an image that evokes a festive feeling because you don’t want to unintentionally offend anyone who doesn’t celebrate the holiday your copy calls express attention to.

Here is a great example from Uncommon Goods:


Uncommon Goods holiday home page


Uncommon Goods holiday home page (Source: Uncommon Goods) (Large preview)

I wouldn’t necessarily say the images used here are festive, but there are unique elements that evoke a certain association with the holidays. Like the color green used within the photos. Or the partial glances of what appear to be snow globes. They’re seasonal elements, but not necessarily relegated to Christmas, Hanukkah or Kwanzaa.

Then, there’s the United States Postal Service (USPS) website. Granted, this website targets visitors within the United States, but it remains mindful of the differences in religions practiced and holidays celebrated.


USPS festive image


USPS uses a non-denominational image to promote the holidays (Source: USPS) (Large preview)

The message remains neutral as does the image itself. The USPS is simply trying to help people quickly and festively send holiday cards, gifts and other items to distant relatives and friends.

5. Review The Customer Journey

The factor of speed is a big one when it comes to designing the customer journey. While the navigation cuts down on any unnecessary steps that might be taken when visitors can afford a more leisurely pace, your design should expedite the rest.

In other words:

  • Start talking about holiday-related content, products, pages and links right on the home page.
  • Make sure you have at least one mention above-the-fold, whether it’s in the navigation, in a blog link or in a seasonal promo.
  • Use the data from last year to streamline the ideal pathway from the home page to conversion.
  • Walk through that pathway as a visitor on both desktop and mobile. Is it as clear, concise and direct as possible?
  • Check the responsiveness of the pathway. Your site, in general, needs to be responsive, but if you’re optimizing a certain journey for visitors and you want them to convert on mobile, then extra care needs to be taken.

Below is another example from the Food52 website from the holidays. As you can see in this snippet, two kinds of holiday-related content are promoted. What’s cool about them, though, is that it’s not necessarily in-your-face.


Food52’s festive home page design


Food52 adds a holiday touch to its home page design and copy (Source: Food52) (Large preview)

The relish recipe could easily be used any time of the year. However, because pomegranates are often considered a winter food, this falls into the category of holiday-related content. The second post is more blatant about attracting holiday readers.

The final element in this screenshot is also worth taking note of. To start, it appears they’ve customized the copy specifically for this time of year. All it takes is one addition of the word “joyfully” to let visitors know that Food52 took time to make its site just a little more festive.

I also want to give them kudos for including a newsletter subscription box here and in other key areas of the site.

If the research from Adobe is right and only about half of mobile visitors convert, then this is a smart design choice. This way, Food52 can collect visitor information on mobile and contact them later. When interested visitors receive the reminder at a more convenient time and place, they can hop onto their desktop or other preferred device and finish the conversion process.

Another site which I think handles the customer journey optimization well is Cracker Barrel.


Cracker Barrel home page design


Cracker Barrel home page design (Source: Cracker Barrel) (Large preview)

Cracker Barrel doesn’t overdo it when it comes to designing for the holidays. Instead, it’s developed a series of calls-to-action that set certain types of visitors on the right path.

The first one features an image of what looks like a holiday feast with the CTA “Order Heat N’ Serve”. That’s brilliant. If people are taking the time to visit this site right before Thanksgiving, it’s probably to see if they can get help preparing their major feast… which it appears they can.

The second section sort of looks festive, though I’d still say they play it safe with choice of color, texture and gift card image. With a CTA of “Buy Gift Cards”, they’re now appealing to holiday shoppers. Not only can you get a whole feast conveniently prepared by Cracker Barrel, but you can buy gifts here, too.

Sometimes designing for the holidays isn’t about the blatant use of snowflake imagery or promoting recipes for cooking a turkey. Sometimes it’s about understanding what your users’ particular needs are at that time and helping setting them on that exact journey right away.

Wrap-Up

I understand that there are ways to add a dancing Santa to a site or to spruce up pop-ups with animated text and images, but I think subtler is better.

It’s kind of like the whole holiday music and decorations thing. How many times have you gone to your local drug store at the end of October for the purposes of getting Halloween candy, only to be met by an entire aisle full of holiday decorations? Or maybe you entered a department store like Macy’s in November, thinking you’ll beat the crazy holiday crowds. And, yet, holiday music is already playing. It’s overkill.

If you want to impress mobile visitors with your website around the holidays, focus on making this a worthwhile experience. Optimize your server for high volumes of traffic, put extra security in place, reorganize the navigation and add some small festive touches to your design that call attention to the most relevant parts of your site at this time of year.

Smashing Editorial
(ra, yk, il)


Excerpt from: 

Get Your Mobile Site Ready For The 2018 Holiday Season

Thumbnail

A Brief Guide About Competitive Analysis




A Brief Guide About Competitive Analysis

Mayur Kshirsagar



In this article, I will introduce the subject of competitive analysis, which is basically a method to determine how well your competitors are performing. My aim is to introduce the subject to those of you who are new to the concept. It should be useful if you are new to product design, UX, interaction or digital design, or if you have experience in these fields but have not performed a competitive analysis before.

No prior knowledge of the topic is needed because I’ll be explaining what the term means and how to perform a competitive analysis as we go. I am assuming some basic knowledge of the design process and UX research, but I’ll provide plenty of practical examples and reference links to help with any terms and concepts you might be unfamiliar with.

Note: If you are a beginner in UX and interaction design, it would be good to know the basics of the design process and to know what is UX research (and the methods used for UX research) before diving into the article’s main topic. Please read the next section carefully because I’ve added reference links to help you get started.

Recommended reading: Standing Out From The Crowd: Improving Your Mobile App With Competitive Analysis

Competitive Analysis, Service Design Cycle, Five-Stages Design Process

If you are a UX designer, then you might be aware of the service design cycle. This cycle contains four stages: discover, explore, test and listen. Each one of these stages has multiple research methods, and competitive analysis is part of the exploration. Susan Farrell has very helpfully distinguished different UX research methods and activities that can be performed for your project. (You can check this detailed segregation in her “UX Research Cheat Sheet”.)

The image below shows the four steps and the most commonly used methods in these steps.




(Large preview)

If you are new to this concept, you might first ask, “What is service design?” Shahrzad Samadzadeh explains it very well in her article, “So, Like, What Is Service Design?.”

Note: You can also learn more about service design in Sarah Gibbons’s article, “Service Design 101.”

Often, UX designers follow the five-stages design process in their projects:

  1. empathize,
  2. define,
  3. ideate,
  4. prototype,
  5. test.

The five-stages design process.


The five-stages design process. (Large preview)

Please don’t confuse the five-stages design process with the service design cycle. Basically, they serve the same purpose in the design thinking process, but are explained in different styles. Here is a brief explanation of what these five stages contain:

  • Empathize
    This stage involves gaining a clear understanding of the problem you are trying to solve from the user’s point of view.
  • Define
    This stage involves defining the correct statement for the problem you are trying to solve, using the knowledge you gained in the first stage.
  • Ideate
    In this stage, you can generate different solution ideas for the problem.
  • Prototype
    Basically, a prototype is an attempt to give your solution some form so that it can be explained to others. For digital products, a prototype could be a wireframe set created using pen and paper or using a tool such as Balsamiq or Sketch, or it could be a visual design prototype created using a tool such as Sketch, Figma, Adobe XD or InVision.
  • Test
    Testing involves validating and evaluating all of your solutions with the users.

You can perform UX research at any stage. Many articles and books are available for you to learn more about this design process. “Five Stages in the Design Thinking Process” by Rikke Dam and Teo Siang is one of my favorite articles on the topic.


The most frequent methods used by UX professionals during the exploration stage of the design life cycle


The most frequent methods used by UX professionals during the exploration stage of the design life cycle. (Nielsen Norman Group, “User Experience Careers” survey report) (Large preview)

According to Nielsen Norman Group’s “User Experience Careers” survey report, 61% of UX professionals prefer to do the competitive analysis for their projects. But what exactly is competitive analysis? In simple language, competitive analysis is nothing but a method to determine how your competitors are performing, what they are offering and how well they are doing it.

Sometimes, competitive analysis is referred as competitive usability evaluation.

Why Should You Do A Competitive Analysis?

There are many reasons to do a competitive analysis, but I think the most important reason is that it helps us to understand the rights and wrongs of our own product or service.

Using competitive analysis, you can make decisions based on knowledge of what is currently working well for your users, rather than based on guesses or intuition. In doing competitive analysis, you can also identify risks in your product or service and use those insights to add value to it.

Recently, I was working on a project in which I did a competitive analysis of a feature (collaborative meeting note-taking) that a client wanted to introduce in their web app. Note-taking is not exactly a new or highly innovative thing, so the biggest challenge I was facing was to make this functionality simpler and easier to handle, because the product I was working on was in the very early stages of development. The feature, in a nutshell, was to create a simple text document where some interactive action items could be added.

Because a ton of apps are out there that allow you to create simple text documents, I decided to do a competitive analysis for this functionality. (I’ll explain this process in more detail later in the section “Five Easy Steps to Do a Competitive Analysis”.)

How To Find The Right Competitors?

Basically, there are two types of competitors: direct and indirect. As a UX designer, your role is to study the designs of these competitors.

Jaime Levy gives very good definitions of direct and indirect competitors in her book UX Strategy. You can learn more about competitive analysis (and types of competitors) in chapter 4 of the book, “Conducting Competitive Research”.


Types of competitors


Types of competitors. (Large preview)

Direct competitors are the ones who offer the same, or a very similar, set of features to your current or future customers, which means they are solving a similar problem to the one you are trying to solve, for a customer base that you are targeting as well.

Indirect competitors are the ones who offers a similar set of features but to a different customer segment; or, they target your exact customer base without offering the exact same set of features, which means indirect competitors are solving the same problem but for a different customer base, or are solving the same problem but offer a different solution.

You can search for these types of competitors online (by doing a simple web search), or you can directly ask your current and potential customers what they are using already. You can also look for your direct and indirect competitors on websites such as Crunchbase and Product Hunt, and you can search for them in the Google Play and the iOS App Store.

Five Easy Steps To Do A Competitive Analysis

You can perform a competitive analysis for your existing or new product using the following five-step process.


5 steps to do a competitive analysis


5 steps to do a competitive analysis. (Large preview)

1. Define And Understand The Goals

Defining and understanding the goal is an integral part of any UX research process. You must define an accurate goal (or set of goals) for your research; otherwise, there is a chance you’ll get the wrong outcome.

Draft all of your goals right before starting your process. When defining your goals, consider the following questions: Why are you doing this competitive analysis? What kind of outcome do you expect? Will this analysis affect UX decisions?

Remember: When setting up goals for any kind of UX research, be as specific as possible.

I mentioned earlier that I recently performed a competitive analysis for a collaborative meeting note-taking feature, to be introduced in the app that I was developing for a client. The goals for my research were very general because innumerable apps all provide this type of functionality, and the product I was working on was in the very early stages of development.

Even though your research goals might be simple, make them as specific as possible, and write them all down. Writing down your goals will help you stay on the right track.

The goals for my analysis were more like questions for which I was trying to find the answers. Here is the list of goals I set for this research:

  • Which apps do users prefer for note-taking? And why do they prefer them?
    Goal: To find out the user’s behavior with these apps, their preferences and their comfort zone.
  • What is the working mechanism of these apps?
    Goal: To find how out competitors’ apps work, so that we can identify their pros and cons.
  • What are the “star” features of these apps?
    Goal: To identify functionalities that we were trying to introduce as well, to see whether they already exist and, if they exist, how exactly they were implemented.
  • How comfortable does a user feel when using these apps?
    Goal: To identify user loyalty and engagement in the apps of our competitors.
  • How does collaborative editing work in these competitive apps?
    Goal: To identify how collaborative-editing functionality works and to study its technical aspects.
  • What is the visual structure and user interface of these apps?
    Goal: To check the visual look and feel of the apps (user interface and interaction).

2. Find The Right Competitors

After setting the goals, go on a search and make a list of both direct and indirect competitors. It’s not necessary to analyze all of the competitors you find. The number is completely up to you. Some people suggest analyzing at least two to four competitors, while others suggest five to ten or more.

Finding the right competitors for my research wasn’t a hard task because I already knew many apps that provided similar features, but I still did a quick search on Google, and the results were a bit surprising — surprising because most of the apps I knew turned out to be more like indirect competitors to the app I was working on; and later, after a bit more searching, I also found the apps that were our direct competitors.

Putting each competitor in the right list is a very important part of competitive analysis because the features and functionality in your competitors’ apps are based on exactly what users of those apps want. Let’s assume you put one indirect competitor, XYZ, under the “direct competitors” list and start doing your analysis. While doing the research, you might find some impressive feature in XYZ’s app and decide to add a similar feature in your own app; then, later it turns out that the feature you added is not useful for the users you are targeting. You might end up wasting a lot of energy, time and money building something that is not at all useful. So, be careful when sorting your competitors.

For my research, the competitors were as follows:

  • Direct competitorsQuip, Cisco Spark Meeting Notes, Workboard, Lucid Meeting, Less Meeting, MeetingSense, Minute-it, etc.
    • All of the apps above provide the same type of functionality, which we were trying to introduce for almost the same type of user base.
  • Indirect competitorsEvernote, Google Keep, Google Docs, Microsoft Word, Microsoft OneNote and other traditional note-taking apps and pen-paper note-taking methods.
    • The user base for all of the above is not exactly different from the user base we were targeting, but most of the users we were targeting were using these apps because they were unaware of the more convenient ways to take meeting notes.

3. Make A Competitive Analysis Matrix

A competitive analysis matrix is not complex, just a simple spreadsheet. You can use Microsoft Excel, Google Sheets, Apple Numbers or any other tool you are comfortable with.

First, divide all competitors you’ve found into two groups (direct and indirect) and put them in a spreadsheet. Jamie Levy suggests making the following columns:

  1. competitor’s name,
  2. URL,
  3. login credentials,
  4. purpose,
  5. year founded.

Example of competitive analysis matrix spreadsheet from UX Strategy, Jaime Levy’s book.


Example of competitive analysis matrix spreadsheet from UX Strategy, Jaime Levy’s book. (Large preview)

I would recommend digging a bit deeper and adding a few more columns, such as for “unique features”, “pros and cons”, etc. It would help to summarize your analysis. It’s not necessary to set your columns exactly as mentioned above. You can modify the columns to your own research goals and needs.

For my analysis, I created only four columns. My competitive analysis matrix looked as follows:

  • Competitor nameIn this column, I put the names of all of the competitors.
  • URLThese are website links or app download links for these competitors.
  • Features/commentsIn this column, I put all of my comments, some ”star” features I needed to focus on, and the pros and cons of the competitor. I color-coded the cells so that later I (or anyone viewing the matrix) could easily identify the difference between them. For example, I used light yellow for features, light purple for comments, green for pros and red for cons.
  • Screenshots/video linksIn this column, I put all of the screenshots and videos related to the features and comments mentioned in the third column. This way, it became very easy and quick to understand what a particular comment or feature was all about.



(Large preview)

4. Write A Summary And An Analysis

Once you are done with the analysis matrix spreadsheet, move on and create a summary of your findings. Be as specific as possible, and try to answer all of your questions while setting up a goal or during the overall process.

This will help you and your team members and stakeholders make the right design and UX decisions. This summary will also help you find new design and UX opportunities in the product you’re building.

In writing the summary and the presentation for the competitive analysis that I did for this collaborative note-taking app, the competitive analysis matrix helped me a lot. I drafted a document with all of the high-level takeaways from this analysis and answered all of the questions that were set as goals. For the presentation, I shared the document with the client, which helped both the client and me to finalize the features, the flows and the end requirements for the product.

5. Presentation

The last step of your competitive analysis is the presentation. It’s not a typical slideshow presentation — rather, just share all of the data and information you collected throughout the process with your teammates, stakeholders and/or clients.

Getting feedback from everywhere you can and being open to this feedback is a very important part of the designer’s workflow. So, share all of your finding with your teammates, stakeholders and clients, and ask for their opinion. You might find some missing points in your analysis or discover something new and exciting from someone’s feedback.

Conclusion

We live in a data-driven world, and we should build products, services and apps based on data, rather than our intuition (or guesswork).

As UX designers, we should go out there and collect as much data as possible before building a real product. This data will help us to create a solid product that users will want to use, rather than a product we want or imagine. These kinds of products are more likely to succeed in the market. Competitive analysis is one of the ways to get this data and to create a user-friendly product.

Finally, no matter what kind of product you are building or research you are conducting, always try to put yourself in the users’ shoes every now and then. This way, you will be able to identify the users’ struggles and ultimately deliver a better solution.

I hope this article has helped you plan and make your first competitive analysis for your next project!

Further Reading

If you want to become a better UX, interaction, visual (UI) or product designer, there are a lot of sources from which you can learn — articles, books, online courses. I often check the following few: Smashing Magazine, InVision blog, Interaction Design Foundation, NN Group and UX Mastery. These websites have a very good collection of articles on the topics of UI and UX design and UX research.

Here are some additional resources:

Smashing Editorial
(mb, ra, al, yk, il)


Excerpt from – 

A Brief Guide About Competitive Analysis

Thumbnail

Building A Room Detector For IoT Devices On Mac OS




Building A Room Detector For IoT Devices On Mac OS

Alvin Wan



Knowing which room you’re in enables various IoT applications — from turning on the light to changing TV channels. So, how can we detect the moment you and your phone are in the kitchen, or bedroom, or living room? With today’s commodity hardware, there are a myriad of possibilities:

One solution is to equip each room with a bluetooth device. Once your phone is within range of a bluetooth device, your phone will know which room it is, based on the bluetooth device. However, maintaining an array of Bluetooth devices is significant overhead — from replacing batteries to replacing dysfunctional devices. Additionally, proximity to the Bluetooth device is not always the answer: if you’re in the living room, by the wall shared with the kitchen, your kitchen appliances should not start churning out food.

Another, albeit impractical, solution is to use GPS. However, keep in mind hat GPS works poorly indoors in which the multitude of walls, other signals, and other obstacles wreak havoc on GPS’s precision.

Our approach instead is to leverage all in-range WiFi networks — even the ones your phone is not connected to. Here is how: consider the strength of WiFi A in the kitchen; say it is 5. Since there is a wall between the kitchen and the bedroom, we can reasonably expect the strength of WiFi A in the bedroom to differ; say it is 2. We can exploit this difference to predict which room we’re in. What’s more: WiFi network B from our neighbor can only be detected from the living room but is effectively invisible from the kitchen. That makes prediction even easier. In sum, the list of all in-range WiFi gives us plentiful information.

This method has the distinct advantages of:

  1. not requiring more hardware;
  2. relying on more stable signals like WiFi;
  3. working well where other techniques such as GPS are weak.

The more walls the better, as the more disparate the WiFi network strengths, the easier the rooms are to classify. You will build a simple desktop app that collects data, learns from the data, and predicts which room you’re in at any given time.

Further Reading on SmashingMag:

Prerequisites

For this tutorial, you will need a Mac OSX. Whereas the code can apply to any platform, we will only provide dependency installation instructions for Mac.

Step 0: Setup Work Environment

Your desktop app will be written in NodeJS. However, to leverage more efficient computational libraries like numpy, the training and prediction code will be written in Python. To start, we will setup your environments and install dependencies. Create a new directory to house your project.

mkdir ~/riot

Navigate into the directory.

cd ~/riot

Use pip to install Python’s default virtual environment manager.

sudo pip install virtualenv

Create a Python3.6 virtual environment named riot.

virtualenv riot --python=python3.6

Activate the virtual environment.

source riot/bin/activate

Your prompt is now preceded by (riot). This indicates we have successfully entered the virtual environment. Install the following packages using pip:

  • numpy: An efficient, linear algebra library
  • scipy: A scientific computing library that implements popular machine learning models
pip install numpy==1.14.3 scipy
==1.1.0

With the working directory setup, we will start with a desktop app that records all WiFi networks in-range. These recordings will constitute training data for your machine learning model. Once we have data on hand, you will write a least squares classifier, trained on the WiFi signals collected earlier. Finally, we will use the least squares model to predict the room you’re in, based on the WiFi networks in range.

Step 1: Initial Desktop Application

In this step, we will create a new desktop application using Electron JS. To begin, we will instead the Node package manager npm and a download utility wget.

brew install npm wget

To begin, we will create a new Node project.

npm init

This prompts you for the package name and then the version number. Hit ENTER to accept the default name of riot and default version of 1.0.0.

package name: (riot)
version: (1.0.0)

This prompts you for a project description. Add any non-empty description you would like. Below, the description is room detector

description: room detector

This prompts you for the entry point, or the main file to run the project from. Enter app.js.

entry point: (index.js) app.js

This prompts you for the test command and git repository. Hit ENTER to skip these fields for now.

test command:
git repository:

This prompts you for keywords and author. Fill in any values you would like. Below, we use iot, wifi for keywords and use John Doe for the author.

keywords: iot,wifi
author: John Doe

This prompts you for the license. Hit ENTER to accept the default value of ISC.

license: (ISC)

At this point, npm will prompt you with a summary of information so far. Your output should be similar to the following.


  "name": "riot",
  "version": "1.0.0",
  "description": "room detector",
  "main": "app.js",
  "scripts": 
    "test": "echo "Error: no test specified" && exit 1"
  ,
  "keywords": [
    "iot",
    "wifi"
  ],
  "author": "John Doe",
  "license": "ISC"
}

Hit ENTER to accept. npm then produces a package.json. List all files to double-check.

ls

This will output the only file in this directory, along with the virtual environment folder.

package.json
riot

Install NodeJS dependencies for our project.

npm install electron --global  # makes electron binary accessible globally
npm install node-wifi --save

Start with main.js from Electron Quick Start, by downloading the file, using the below. The following -O argument renames main.js to app.js.

wget https://raw.githubusercontent.com/electron/electron-quick-start/master/main.js -O app.js

Open app.js in nano or your favorite text editor.

nano app.js

On line 12, change index.html to static/index.html, as we will create a directory static to contain all HTML templates.

function createWindow () 
  // Create the browser window.
  win = new BrowserWindow(width: 1200, height: 800)

  // and load the index.html of the app.
  win.loadFile('static/index.html')

  // Open the DevTools.

Save your changes and exit the editor. Your file should match the source code of the app.js file. Now create a new directory to house our HTML templates.

mkdir static

Download a stylesheet created for this project.

wget https://raw.githubusercontent.com/alvinwan/riot/master/static/style.css?token=AB-ObfDtD46ANlqrObDanckTQJ2Q1Pyuks5bf79PwA%3D%3D -O static/style.css

Open static/index.html in nano or your favorite text editor. Start with the standard HTML structure.

<!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>Riot | Room Detector</title>
    </head>
    <body>
      <main>
      </main>
    </body>
  </html>

Right after the title, link the Montserrat font linked by Google Fonts and stylesheet.

<title>Riot | Room Detector</title>
  <!-- start new code -->
  <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
  <link href="style.css" rel="stylesheet">
  <!-- end new code -->
</head>

Between the main tags, add a slot for the predicted room name.

<main>
  <!-- start new code -->
  <p class="text">I believe you’re in the</p>
  <h1 class="title" id="predicted-room-name">(I dunno)</h1>
  <!-- end new code -->
</main>

Your script should now match the following exactly. Exit the editor.

<!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>Riot | Room Detector</title>
      <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
      <link href="style.css" rel="stylesheet">
    </head>
    <body>
      <main>
        <p class="text">I believe you’re in the</p>
        <h1 class="title" id="predicted-room-name">(I dunno)</h1>
      </main>
    </body>
  </html>

Now, amend the package file to contain a start command.

nano package.json

Right after line 7, add a start command that’s aliased to electron .. Make sure to add a comma to the end of the previous line.

"scripts": 
  "test": "echo "Error: no test specified" && exit 1",
  "start": "electron ."
,

Save and exit. You are now ready to launch your desktop app in Electron JS. Use npm to launch your application.

npm start

Your desktop application should match the following.


home page with button


Home page with “Add New Room” button available (Large preview)

This completes your starting desktop app. To exit, navigate back to your terminal and CTRL+C. In the next step, we will record wifi networks, and make the recording utility accessible through the desktop application UI.

Step 2: Record WiFi Networks

In this step, you will write a NodeJS script that records the strength and frequency of all in-range wifi networks. Create a directory for your scripts.

mkdir scripts

Open scripts/observe.js in nano or your favorite text editor.

nano scripts/observe.js

Import a NodeJS wifi utility and the filesystem object.

var wifi = require('node-wifi');
var fs = require('fs');

Define a record function that accepts a completion handler.

/**
 * Uses a recursive function for repeated scans, since scans are asynchronous.
 */
function record(n, completion, hook) 

Inside the new function, initialize the wifi utility. Set iface to null to initialize to a random wifi interface, as this value is currently irrelevant.

function record(n, completion, hook) 
    wifi.init(
        iface : null
    );
}

Define an array to contain your samples. Samples are training data we will use for our model. The samples in this particular tutorial are lists of in-range wifi networks and their associated strengths, frequencies, names etc.

function record(n, completion, hook) 
    ...
    samples = []

Define a recursive function startScan, which will asynchronously initiate wifi scans. Upon completion, the asynchronous wifi scan will then recursively invoke startScan.

function record(n, completion, hook) 
  ...
  function startScan(i) 
    wifi.scan(function(err, networks) 
    );
  }
  startScan(n);
}

In the wifi.scan callback, check for errors or empty lists of networks and restart the scan if so.

wifi.scan(function(err, networks) 
  if (err 
});

Add the recursive function’s base case, which invokes the completion handler.

wifi.scan(function(err, networks) 
  ...
  if (i <= 0) 
    return completion(samples: samples);
  }
});

Output a progress update, append to the list of samples, and make the recursive call.

wifi.scan(function(err, networks) 
  ...
  hook(n-i+1, networks);
  samples.push(networks);
  startScan(i-1);
);

At the end of your file, invoke the record function with a callback that saves samples to a file on disk.

function record(completion) 
  ...


function cli() 
  record(1, function(data) 
    fs.writeFile('samples.json', JSON.stringify(data), 'utf8', function() );
  }, function(i, networks) 
    console.log(" * [INFO] Collected sample " + (21-i) + " with " + networks.length + " networks");
  )
}

cli();

Double check that your file matches the following:

var wifi = require('node-wifi');
var fs = require('fs');

/**
 * Uses a recursive function for repeated scans, since scans are asynchronous.
 */
function record(n, completion, hook) 
  wifi.init(
      iface : null // network interface, choose a random wifi interface if set to null
  );

  samples = []
  function startScan(i) 
    wifi.scan(function(err, networks) 
        if (err 
        if (i <= 0) 
          return completion(samples: samples);
        }
        hook(n-i+1, networks);
        samples.push(networks);
        startScan(i-1);
    });
  }

  startScan(n);
}

function cli() 
    record(1, function(data) 
        fs.writeFile('samples.json', JSON.stringify(data), 'utf8', function() );
    }, function(i, networks) 
        console.log(" * [INFO] Collected sample " + i + " with " + networks.length + " networks");
    )
}

cli();

Save and exit. Run the script.

node scripts/observe.js

Your output will match the following, with variable numbers of networks.

 * [INFO] Collected sample 1 with 39 networks

Examine the samples that were just collected. Pipe to json_pp to pretty print the JSON and pipe to head to view the first 16 lines.

cat samples.json | json_pp | head -16

The below is example output for a 2.4 GHz network.


  "samples": [
    [
      
        "mac": "64:0f:28:79:9a:29",
        "bssid": "64:0f:28:79:9a:29",
        "ssid": "SMASHINGMAGAZINEROCKS",
         "channel": 4,
         "frequency": 2427,
          "signal_level": "-91",
          "security": "WPA WPA2",
          "security_flags": [
           "(PSK/AES,TKIP/TKIP)",
          "(PSK/AES,TKIP/TKIP)"
        ]
      ,

This concludes your NodeJS wifi-scanning script. This allows us to view all in-range WiFi networks. In the next step, you will make this script accessible from the desktop app.

Step 3: Connect Scan Script To Desktop App

In this step, you will first add a button to the desktop app to trigger the script with. Then, you will update the desktop app UI with the script’s progress.

Open static/index.html.

nano static/index.html

Insert the “Add” button, as shown below.

<h1 class="title" id="predicted-room-name">(I dunno)</h1>
        <!-- start new code -->
        <div class="buttons">
            <a href="add.html" class="button">Add new room</a>
        </div>
        <!-- end new code -->
    </main>

Save and exit. Open static/add.html.

nano static/add.html

Paste the following content.

<!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>Riot | Add New Room</title>
      <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
      <link href="style.css" rel="stylesheet">
    </head>
    <body>
      <main>
        <h1 class="title" id="add-title">0</h1>
        <p class="subtitle">of <span>20</span> samples needed. Feel free to move around the room.</p>
        <input type="text" id="add-room-name" class="text-field" placeholder="(room name)">
        <div class="buttons">
          <a href="#" id="start-recording" class="button">Start recording</a>
          <a href="index.html" class="button light">Cancel</a>
        </div>
        <p class="text" id="add-status" style="display:none"></p>
      </main>
      <script>
        require('../scripts/observe.js')
      </script>
    </body>
  </html>

Save and exit. Reopen scripts/observe.js.

nano scripts/observe.js

Beneath the cli function, define a new ui function.

function cli() 
    ...


// start new code
function ui() 

// end new code

cli();

Update the desktop app status to indicate the function has started running.

function ui() 
  var room_name = document.querySelector('#add-room-name').value;
  var status = document.querySelector('#add-status');
  var number = document.querySelector('#add-title');
  status.style.display = "block"
  status.innerHTML = "Listening for wifi..."

Partition the data into training and validation data sets.

function ui() 
  ...
  function completion(data) 
    train_data = samples: data['samples'].slice(0, 15)
    test_data = samples: data['samples'].slice(15)
    var train_json = JSON.stringify(train_data);
    var test_json = JSON.stringify(test_data);
  }
}

Still within the completion callback, write both datasets to disk.

function ui() 
  ...
  function completion(data) 
    ...
    fs.writeFile('data/' + room_name + '_train.json', train_json, 'utf8', function() );
    fs.writeFile('data/' + room_name + '_test.json', test_json, 'utf8', function() {});
    console.log(" * [INFO] Done")
    status.innerHTML = "Done."
  }
}

Invoke record with the appropriate callbacks to record 20 samples and save the samples to disk.

function ui() 
  ...
  function completion(data) 
    ...
  
  record(20, completion, function(i, networks) 
    number.innerHTML = i
    console.log(" * [INFO] Collected sample " + i + " with " + networks.length + " networks")
  )
}

Finally, invoke the cli and ui functions where appropriate. Start by deleting the cli(); call at the bottom of the file.

function ui() 
    ...


cli();  // remove me

Check if the document object is globally accessible. If not, the script is being run from the command line. In this case, invoke the cli function. If it is, the script is loaded from within the desktop app. In this case, bind the click listener to the ui function.

if (typeof document == 'undefined') 
    cli();
 else 
    document.querySelector('#start-recording').addEventListener('click', ui)

Save and exit. Create a directory to hold our data.

mkdir data

Launch the desktop app.

npm start

You will see the following homepage. Click on “Add room”.




(Large preview)

You will see the following form. Type in a name for the room. Remember this name, as we will use this later on. Our example will be bedroom.


Add New Room page


“Add New Room” page on load (Large preview)

Click “Start recording,” and you will see the following status “Listening for wifi…”.


starting recording


“Add New Room” starting recording (Large Preview)

Once all 20 samples are recorded, your app will match the following. The status will read “Done.”




“Add New Room” page after recording is complete (Large preview)

Click on the misnamed “Cancel” to return to the homepage, which matches the following.


finished recording


“Add New Room” page after recording is complete (Large preview)

We can now scan wifi networks from the desktop UI, which will save all recorded samples to files on disk. Next, we will train an out-of-box machine learning algorithm-least squares on the data you have collected.

Step 4: Write Python Training Script

In this step, we will write a training script in Python. Create a directory for your training utilities.

mkdir model

Open model/train.py

nano model/train.py

At the top of your file, import the numpy computational library and scipy for its least squares model.

import numpy as np
from scipy.linalg import lstsq
import json
import sys

The next three utilities will handle loading and setting up data from the files on disk. Start by adding a utility function that flattens nested lists. You will use this to flatten a list of list of samples.

import sys

def flatten(list_of_lists):
    """Flatten a list of lists to make a list.
    >>> flatten([[1], [2], [3, 4]])
    [1, 2, 3, 4]
    """
    return sum(list_of_lists, [])

Add a second utility that loads samples from the specified files. This method abstracts away the fact that samples are spread out across multiple files, returning just a single generator for all samples. For each of the samples, the label is the index of the file. e.g., If you call get_all_samples('a.json', 'b.json'), all samples in a.json will have label 0 and all samples in b.json will have label 1.

def get_all_samples(paths):
  """Load all samples from JSON files."""
  for label, path in enumerate(paths):
  with open(path) as f:
    for sample in json.load(f)['samples']:
      signal_levels = [
        network['signal_level'].replace('RSSI', '') or 0
        for network in sample]
      yield [network['mac'] for network in sample], signal_levels, label

Next, add a utility that encodes the samples using a bag-of-words-esque model. Here is an example: Assume we collect two samples.

  1. wifi network A at strength 10 and wifi network B at strength 15
  2. wifi network B at strength 20 and wifi network C at strength 25.

This function will produce a list of three numbers for each of the samples: the first value is the strength of wifi network A, the second for network B, and the third for C. In effect, the format is [A, B, C].

  1. [10, 15, 0]
  2. [0, 20, 25]
def bag_of_words(all_networks, all_strengths, ordering):
  """Apply bag-of-words encoding to categorical variables.

  >>> samples = bag_of_words(
  ...     [['a', 'b'], ['b', 'c'], ['a', 'c']],
  ...     [[1, 2], [2, 3], [1, 3]],
  ...     ['a', 'b', 'c'])
  >>> next(samples)
  [1, 2, 0]
  >>> next(samples)
  [0, 2, 3]
  """
  for networks, strengths in zip(all_networks, all_strengths):
    yield [strengths[networks.index(network)]
      if network in networks else 0
      for network in ordering]

Using all three utilities above, we synthesize a collection of samples and their labels. Gather all samples and labels using get_all_samples. Define a consistent format ordering to one-hot encode all samples, then apply one_hot encoding to samples. Finally, construct the data and label matrices X and Y respectively.

def create_dataset(classpaths, ordering=None):
  """Create dataset from a list of paths to JSON files."""
  networks, strengths, labels = zip(*get_all_samples(classpaths))
  if ordering is None:
    ordering = list(sorted(set(flatten(networks))))
  X = np.array(list(bag_of_words(networks, strengths, ordering))).astype(np.float64)
  Y = np.array(list(labels)).astype(np.int)
  return X, Y, ordering

These functions complete the data pipeline. Next, we abstract away model prediction and evaluation. Start by defining the prediction method. The first function normalizes our model outputs, so that the sum of all values totals to 1 and that all values are non-negative; this ensures that the output is a valid probability distribution. The second evaluates the model.

def softmax(x):
  """Convert one-hotted outputs into probability distribution"""
  x = np.exp(x)
  return x / np.sum(x)


def predict(X, w):
  """Predict using model parameters"""
  return np.argmax(softmax(X.dot(w)), axis=1)

Next, evaluate the model’s accuracy. The first line runs prediction using the model. The second counts the numbers of times both predicted and true values agree, then normalizes by the total number of samples.

def evaluate(X, Y, w):
  """Evaluate model w on samples X and labels Y."""
  Y_pred = predict(X, w)
  accuracy = (Y == Y_pred).sum() / X.shape[0]
  return accuracy

This concludes our prediction and evaluation utilities. After these utilities, define a main function that will collect the dataset, train, and evaluate. Start by reading the list of arguments from the command line sys.argv; these are the rooms to include in training. Then create a large dataset from all of the specified rooms.

def main():
  classes = sys.argv[1:]

  train_paths = sorted(['data/{}_train.json'.format(name) for name in classes])
  test_paths = sorted(['data/{}_test.json'.format(name) for name in classes])
  X_train, Y_train, ordering = create_dataset(train_paths)
  X_test, Y_test, _ = create_dataset(test_paths, ordering=ordering)

Apply one-hot encoding to the labels. A one-hot encoding is similar to the bag-of-words model above; we use this encoding to handle categorical variables. Say we have 3 possible labels. Instead of labelling 1, 2, or 3, we label the data with [1, 0, 0], [0, 1, 0], or [0, 0, 1]. For this tutorial, we will spare the explanation for why one-hot encoding is important. Train the model, and evaluate on both the train and validation sets.

def main():
  ...
  X_test, Y_test, _ = create_dataset(test_paths, ordering=ordering)
  
  Y_train_oh = np.eye(len(classes))[Y_train]
  w, _, _, _ = lstsq(X_train, Y_train_oh)
  train_accuracy = evaluate(X_train, Y_train, w)
  test_accuracy = evaluate(X_test, Y_test, w)

Print both accuracies, and save the model to disk.

def main():
  ...
  print('Train accuracy ({}%), Validation accuracy ({}%)'.format(train_accuracy*100, test_accuracy*100))
  np.save('w.npy', w)
  np.save('ordering.npy', np.array(ordering))
  sys.stdout.flush()

At the end of the file, run the main function.

if __name__ == '__main__':
  main()

Save and exit. Double check that your file matches the following:

import numpy as np
from scipy.linalg import lstsq
import json
import sys


def flatten(list_of_lists):
    """Flatten a list of lists to make a list.
    >>> flatten([[1], [2], [3, 4]])
    [1, 2, 3, 4]
    """
    return sum(list_of_lists, [])


def get_all_samples(paths):
    """Load all samples from JSON files."""
    for label, path in enumerate(paths):
        with open(path) as f:
            for sample in json.load(f)['samples']:
                signal_levels = [
                    network['signal_level'].replace('RSSI', '') or 0
                    for network in sample]
                yield [network['mac'] for network in sample], signal_levels, label


def bag_of_words(all_networks, all_strengths, ordering):
    """Apply bag-of-words encoding to categorical variables.
    >>> samples = bag_of_words(
    ...     [['a', 'b'], ['b', 'c'], ['a', 'c']],
    ...     [[1, 2], [2, 3], [1, 3]],
    ...     ['a', 'b', 'c'])
    >>> next(samples)
    [1, 2, 0]
    >>> next(samples)
    [0, 2, 3]
    """
    for networks, strengths in zip(all_networks, all_strengths):
        yield [int(strengths[networks.index(network)])
            if network in networks else 0
            for network in ordering]


def create_dataset(classpaths, ordering=None):
    """Create dataset from a list of paths to JSON files."""
    networks, strengths, labels = zip(*get_all_samples(classpaths))
    if ordering is None:
        ordering = list(sorted(set(flatten(networks))))
    X = np.array(list(bag_of_words(networks, strengths, ordering))).astype(np.float64)
    Y = np.array(list(labels)).astype(np.int)
    return X, Y, ordering


def softmax(x):
    """Convert one-hotted outputs into probability distribution"""
    x = np.exp(x)
    return x / np.sum(x)


def predict(X, w):
    """Predict using model parameters"""
    return np.argmax(softmax(X.dot(w)), axis=1)


def evaluate(X, Y, w):
    """Evaluate model w on samples X and labels Y."""
    Y_pred = predict(X, w)
    accuracy = (Y == Y_pred).sum() / X.shape[0]
    return accuracy


def main():
    classes = sys.argv[1:]

    train_paths = sorted(['data/{}_train.json'.format(name) for name in classes])
    test_paths = sorted(['data/{}_test.json'.format(name) for name in classes])
    X_train, Y_train, ordering = create_dataset(train_paths)
    X_test, Y_test, _ = create_dataset(test_paths, ordering=ordering)

    Y_train_oh = np.eye(len(classes))[Y_train]
    w, _, _, _ = lstsq(X_train, Y_train_oh)
    train_accuracy = evaluate(X_train, Y_train, w)
    validation_accuracy = evaluate(X_test, Y_test, w)

    print('Train accuracy ({}%), Validation accuracy ({}%)'.format(train_accuracy*100, validation_accuracy*100))
    np.save('w.npy', w)
    np.save('ordering.npy', np.array(ordering))
    sys.stdout.flush()


if __name__ == '__main__':
    main()

Save and exit. Recall the room name used above when recording the 20 samples. Use that name instead of bedroom below. Our example is bedroom. We use -W ignore to ignore warnings from a LAPACK bug.

python -W ignore model/train.py bedroom

Since we’ve only collected training samples for one room, you should see 100% training and validation accuracies.

Train accuracy (100.0%), Validation accuracy (100.0%)

Next, we will link this training script to the desktop app.

In this step, we will automatically retrain the model whenever the user collects a new batch of samples. Open scripts/observe.js.

nano scripts/observe.js

Right after the fs import, import the child process spawner and utilities.

var fs = require('fs');
// start new code
const spawn = require("child_process").spawn;
var utils = require('./utils.js');

In the ui function, add the following call to retrain at the end of the completion handler.

function ui() 
  ...
  function completion() 
    ...
    retrain((data) => 
      var status = document.querySelector('#add-status');
      accuracies = data.toString().split('n')[0];
      status.innerHTML = "Retraining succeeded: " + accuracies
    );
  }
    ...
}

After the ui function, add the following retrain function. This spawns a child process that will run the python script. Upon completion, the process calls a completion handler. Upon failure, it will log the error message.

function ui() 
  ..


function retrain(completion) 
  var filenames = utils.get_filenames()
  const pythonProcess = spawn('python', ["./model/train.py"].concat(filenames));
  pythonProcess.stdout.on('data', completion);
  pythonProcess.stderr.on('data', (data) => 
    console.log(" * [ERROR] " + data.toString())
  )
}

Save and exit. Open scripts/utils.js.

nano scripts/utils.js

Add the following utility for fetching all datasets in data/.

var fs = require('fs');

module.exports = 
  get_filenames: get_filenames


function get_filenames() 
  filenames = new Set([]);
  fs.readdirSync("data/").forEach(function(filename) 
      filenames.add(filename.replace('_train', '').replace('_test', '').replace('.json', '' ))
  );
  filenames = Array.from(filenames.values())
  filenames.sort();
  filenames.splice(filenames.indexOf('.DS_Store'), 1)
  return filenames
}

Save and exit. For the conclusion of this step, physically move to a new location. There ideally should be a wall between your original location and your new location. The more barriers, the better your desktop app will work.

Once again, run your desktop app.

npm start

Just as before, run the training script. Click on “Add room”.


home page with button


Home page with “Add New Room” button available (Large preview)

Type in a room name that is different from your first room’s. We will use living room.


Add New Room page


“Add New Room” page on load (Large preview)

Click “Start recording,” and you will see the following status “Listening for wifi…”.




“Add New Room” starting recording for second room (Large preview)

Once all 20 samples are recorded, your app will match the following. The status will read “Done. Retraining model…”


finished recording 2


“Add New Room” page after recording for second room complete (Large preview)

In the next step, we will use this retrained model to predict the room you’re in, on the fly.

Step 6: Write Python Evaluation Script

In this step, we will load the pretrained model parameters, scan for wifi networks, and predict the room based on the scan.

Open model/eval.py.

nano model/eval.py

Import libraries used and defined in our last script.

import numpy as np
import sys
import json
import os
import json

from train import predict
from train import softmax
from train import create_dataset
from train import evaluate

Define a utility to extract the names of all datasets. This function assumes that all datasets are stored in data/ as <dataset>_train.json and <dataset>_test.json.

from train import evaluate

def get_datasets():
  """Extract dataset names."""
  return sorted(list(path.split('_')[0] for path in os.listdir('./data')
    if '.DS' not in path))

Define the main function, and start by loading parameters saved from the training script.

def get_datasets():
  ...

def main():
  w = np.load('w.npy')
  ordering = np.load('ordering.npy')

Create the dataset and predict.

def main():
  ...
  classpaths = [sys.argv[1]]
  X, _, _ = create_dataset(classpaths, ordering)
  y = np.asscalar(predict(X, w))

Compute a confidence score based on the difference between the top two probabilities.

def main():
  ...
  sorted_y = sorted(softmax(X.dot(w)).flatten())
  confidence = 1
  if len(sorted_y) > 1:
    confidence = round(sorted_y[-1] - sorted_y[-2], 2)

Finally, extract the category and print the result. To conclude the script, invoke the main function.

def main()
  ...
  category = get_datasets()[y]
  print(json.dumps("category": category, "confidence": confidence))

if __name__ == '__main__':
  main()

Save and exit. Double check your code matches the following (source code):

import numpy as np
import sys
import json
import os
import json

from train import predict
from train import softmax
from train import create_dataset
from train import evaluate


def get_datasets():
    """Extract dataset names."""
    return sorted(list(path.split('_')[0] for path in os.listdir('./data')
        if '.DS' not in path))


def main():
    w = np.load('w.npy')
    ordering = np.load('ordering.npy')

    classpaths = [sys.argv[1]]
    X, _, _ = create_dataset(classpaths, ordering)
    y = np.asscalar(predict(X, w))

    sorted_y = sorted(softmax(X.dot(w)).flatten())
    confidence = 1
    if len(sorted_y) > 1:
        confidence = round(sorted_y[-1] - sorted_y[-2], 2)

    category = get_datasets()[y]
    print(json.dumps("category": category, "confidence": confidence))


if __name__ == '__main__':
    main()

Next, we will connect this evaluation script to the desktop app. The desktop app will continuously run wifi scans and update the UI with the predicted room.

Step 7: Connect Evaluation To Desktop App

In this step, we will update the UI with a “confidence” display. Then, the associated NodeJS script will continuously run scans and predictions, updating the UI accordingly.

Open static/index.html.

nano static/index.html

Add a line for confidence right after the title and before the buttons.

<h1 class="title" id="predicted-room-name">(I dunno)</h1>
<!-- start new code -->
<p class="subtitle">with <span id="predicted-confidence">0%</span> confidence</p>
<!-- end new code -->
<div class="buttons">

Right after main but before the end of the body, add a new script predict.js.

</main>
  <!-- start new code -->
  <script>
  require('../scripts/predict.js')
  </script>
  <!-- end new code -->
</body>

Save and exit. Open scripts/predict.js.

nano scripts/predict.js

Import the needed NodeJS utilities for the filesystem, utilities, and child process spawner.

var fs = require('fs');
var utils = require('./utils');
const spawn = require("child_process").spawn;

Define a predict function which invokes a separate node process to detect wifi networks and a separate Python process to predict the room.

function predict(completion) 
  const nodeProcess = spawn('node', ["scripts/observe.js"]);
  const pythonProcess = spawn('python', ["-W", "ignore", "./model/eval.py", "samples.json"]);

After both processes have spawned, add callbacks to the Python process for both successes and errors. The success callback logs information, invokes the completion callback, and updates the UI with the prediction and confidence. The error callback logs the error.

function predict(completion) 
  ...
  pythonProcess.stdout.on('data', (data) => 
    information = JSON.parse(data.toString());
    console.log(" * [INFO] Room '" + information.category + "' with confidence '" + information.confidence + "'")
    completion()

    if (typeof document != "undefined") 
      document.querySelector('#predicted-room-name').innerHTML = information.category
      document.querySelector('#predicted-confidence').innerHTML = information.confidence
    
  });
  pythonProcess.stderr.on('data', (data) => 
    console.log(data.toString());
  )
}

Define a main function to invoke the predict function recursively, forever.

function main() 
  f = function()  predict(f) 
  predict(f)
}

main();

One last time, open the desktop app to see the live prediction.

npm start

Approximately every second, a scan will be completed and the interface will be updated with the latest confidence and predicted room. Congratulations; you have completed a simple room detector based on all in-range WiFi networks.

demo
Recording 20 samples inside the room and another 20 out in the hallway. Upon walking back inside, the script correctly predicts “hallway” then “bedroom.” (Large preview)

Conclusion

In this tutorial, we created a solution using only your desktop to detect your location within a building. We built a simple desktop app using Electron JS and applied a simple machine learning method on all in-range WiFi networks. This paves the way for Internet-of-things applications without the need for arrays of devices that are costly to maintain (cost not in terms of money but in terms of time and development).

Note: You can see the source code in its entirety on Github.

With time, you may find that this least squares does not perform spectacularly in fact. Try finding two locations within a single room, or stand in doorways. Least squares will be large unable to distinguish between edge cases. Can we do better? It turns out that we can, and in future lessons, we will leverage other techniques and the fundamentals of machine learning to better performance. This tutorial serves as a quick test bed for experiments to come.

Smashing Editorial
(ra, il)


This article is from - 

Building A Room Detector For IoT Devices On Mac OS

Thumbnail

Making Distributed Product Teams Work More Efficiently With monday.com




Making Distributed Product Teams Work More Efficiently With monday.com

Nick Babich



(This is a sponsored article.) The way that product teams work is changing: The software industry is quickly moving to remote work. In the US alone, 43% of employed Americans have spent at least some time working remotely, and that number has steadily increased in recent years. Many successful digital products on the market today were designed and developed by a distributed team. Such teams don’t have an office in the traditional sense. Everyone chooses to work from where they like, both geographically and functionally (in a coworking space, coffee shop, home office, etc.).

While a distributed product team might sound tempting to you, creating an effective design process on such a team requires a lot of effort. Collaboration and communication are two of the most significant challenges distributed teams face. Managing a distributed team requires an understanding of how the individuals on your team operate, as well as requires a digital toolset that makes the team’s operations as efficient as possible. That’s why investing in the right remote tools and technology is so critical for product managers.

If you’re a team manager who is looking to establish a robust design process for a distributed team, then this article for you. You’ll find seven of the most common challenges distributed product teams should overcome and learn how a team-management tool called monday.com (formerly dapulse) can help them with that.

1. Build A Shared Understanding Of A Project’s Goals

When it comes to organizing a work process on a remote team, one of the key goals is to keep the whole team on the same page. Management needs to set goals and make sure everyone on the team understands and accepts them. Building understanding is especially important on remote teams because interaction tend to be more sporadic. Ensure that everyone on the team knows the following:

  • What are the project’s overall goals? When a team clearly understand’s the product strategy (what they want to build and why), that understanding motivates engagement.

  • What is expected of them, and how do they fit in the bigger picture? People want to know their role in the process. Even though every team member will be deep in the details when working on a project, understanding the big picture will help them to focus on what’s really important.

  • What are other people involved in the project doing? Each team member should have visibility on what the other team members are working on.

The more everyone knows, the better they can work as a team.

Visualize The Product Development Process

Helping everyone on the team know what is expected of them and when is possible using monday.com’s feature named the “timeline.” The timeline makes tasks more visual — team members will be able to see when each task is scheduled for, how long it will take and how it fits in the entire project. The tool enables you to see not only what tasks your team members are working on, but also how those tasks are distributed over time. It is great for when some activities depend on others (for example, developers are waiting on mockups from designers).


The timeline enables team members to see a high-level roadmap.


The timeline enables team members to see a high-level roadmap. (Large preview)

2. Manage The Team’s Workload

As anyone who has ever worked on a remote team will tell you, remote working is quite different from working face to face. Many project managers find it hard to manage the team’s workload.

Most product teams use project-tracking software to plan and estimate their work. Usually, a team will prepare all of the work in a task list, in which each task has a text description and a time estimate. The biggest downside of this approach is that it’s not very representative. For example, Kanban boards, used by many product teams today, are not very representative — it’s almost impossible from a glance at the board to understand the order in which tasks should be completed, especially when they have dependencies.


Using a Kanban board might make it hard to see how tasks should be distributed in time.


Using a Kanban board might make it hard to see how tasks should be distributed in time. (Image source) (Large preview)

Track Everything Your Team Is Working On

Interaction cost (i.e. the cognitive or physical effort required to complete an action) plays a vital role in the user experience of a product. The more effort required to complete an operation, the less usable the interface becomes for the end user. If the project manager has to switch to different products to see the team’s progress, that will create unnecessary friction and hinder the team from working efficiently.

monday.com assembles and displays progress data in a logical and understandable way. The tool has a feature called a board. The board is where all team members can track everything the team is working on. The main advantage of the board is that it enables product managers to monitor the team’s progress in real time and instantly see who is working on what and see where things stand.


monday.com gives you a clear sense of what needs to get done and who is responsible for what. The board provides in-depth insight into a project and its tasks.


monday.com gives you a clear sense of what needs to get done and who is responsible for what. The board provides in-depth insight into a project and its tasks. (Large preview)

Communicate Current Status

Each team needs a mechanism that makes it easy to understand what’s going on at a glance.

One way to solve this problem is to use color coding for different elements. Color coding speeds up visual search because it allows users to quickly filter a particular object (or objects) by knowing the color associated with it. monday.com uses color coding to indicate the current status of a task. For example, it’s easy to see where things have gotten stuck just by looking at the board and finding all tasks colored in red.


Status updates can be color coded.


Status updates can be color coded. (Large preview)

Create, Modify And Assign Tasks In A Few Clicks

Adding tasks in a project-management tool doesn’t sound very exciting. Generally, the more time it takes, the less happy the product manager will be.

monday.com simplifies the process of data input. Managers can quickly add rows to the board — monday.com calls them pulses. Pulses can be tasks, projects, missions, to-do items, etc. Creating a pulse requires just a few clicks.


Monday.com simplifies the process of data input. Managers can quickly add rows to the board — Monday.com calls them pulses. Pulses can be tasks, projects, missions, to-do items, etc. Creating a pulse requires just a few clicks.


(Large preview)

After you create a pulse, simply assign it to a team member.


Assign teammates to particular tasks or projects.


Assign teammates to particular tasks or projects. (Large preview)

Tailor The Platform To Your Needs

There’s no such thing as a universal design process. Every project is different and requires its own design process. A product-management tool should be very adaptive to change; the product team should be able to customize the process according to their needs, without having to put much effort into customization.

monday.com is extremely customizable and lets the user configure almost any option. You can customize monday.com to manage any workflow or process, to address any challenge and to manage basically anything.

When it comes to creating a board, you don’t need to start from scratch. A multitude of templates allow you to start quickly. For example, the “Team Tasks” template would be very useful for product teams.


Finding the right template for your activity is really simple because all templates are visualized.


Finding the right template for your activity is really simple because all templates are visualized. (Large preview)

After selecting a template for your needs, you can customize it by manipulating different sections. Product teams often need to combine task into groups, whereby each group represents a milestone (for example, “Release 1”, “Release 2”, etc.). Doing this in monday.com is relatively simple. As a board owner, you can have as many groups as you want.


Easy to organize tasks. You can have as many groups as you want.


Easy to organize tasks. You can have as many groups as you want. (Large preview)

But it doesn’t stop there. You can use the checklist feature to break down tasks even further. For example, each task can be broken down into smaller to-do steps. This feature is handy when a few activities need to get done before the task can be completed — for example, if a product specification needs to be approved by a few designers before it can be handed over to the development team. The checklist sits within a pulse, in the “Updates” section, and can help create a structure for each pulse.

The checklist sits within a pulse, in the “Updates” section. This feature can help create a structure for each pulse.
The checklist sits within a pulse, in the “Updates” section. This feature can help create a structure for each pulse. (Large preview)

Plan The Team’s Workload Visually

Designers, developers and managers often work with compressed timeframes and simultaneous projects. A team must be able to respond quickly to feedback on their product from stakeholders and users. Following the build-measure-learn cycle, a product team should be really flexible; it should be ready to implement feedback from testing sessions and adjust the design process according to the new information. The same level of flexibility should be in all products the team uses.

Using monday.com’s timeline, it’s possible to make corrections and improve the team’s efficiency. The visual editor makes the process of managing tasks easy. The product manager can see where each project is at each point, and can see and focus on areas of struggle, quickly and effectively.

The timeline makes it possible to see each team member’s capacity over a set period of time (say, the next few weeks), seeing where they have room to take on more work and where they need to delegate tasks to others.

Change the time range in the timeline. The time range is updated in real time.
Change the time range in the timeline. The time range is updated in real time. (Large preview)

3. Create Effective Internal Communications

Communication plays a critical role in the design process. When it comes to product design, it’s essential for all team members to be on the same page. Unlike colocated teams, a distributed team won’t have an opportunity to arrange regular face-to-face meetings. When you take out face-to-face interaction, you can’t expect things to just work the same way. Poorly established communication patterns can lead to some team members feeling like they’re working in a vacuum.

Tools matter more in remote work because they are the foundation for communication. The goal is to make sure everyone on the team feels connected.

Centralize All Communication

In today’s world, we communicate with a variety of tools: from traditional email to online messengers such as Skype, WhatsApp, Slack and Facebook Messenger. Having to switch from a task-management tool to another tool for communication can be stressful. Worse, some information can get lost during the transition (for example, an email inbox can fill up to the point that a team member can overlook a critical email).

Product teams can use monday.com as a single communication platform for their workplace. And it would be a much better solution because it allows for communication in the context of each task. With monday.com, you no longer need to use email for internal communication. When a team member clicks on a pulse on any board, a box opens to the right of the screen, showing the “updates”. Simply mention a person’s username (“@johndoe”), and send your message. The great thing is that the chat thread stays with that task, so finding a conversation after a while is relatively easy.

Cut Down On Meetings And Optimize Required Meetings

Meetings are an essential part of the communication process. When it comes to reviewing plans and brainstorming on design decisions, there’s no substitute for a meeting. But for a distributed team, the number of potential hours available for real-time meetings can be limited, so it’s essential to make the best use of that time. A distributed team should continually try to reduce their number of meetings and maximize the effectiveness of the time that team members have together.

Take a weekly kickoff meeting as an example. This meeting happens on a Monday, and team members come together to discuss plans for the week. For many teams, such meetings are rarely productive. Quite often, the information shared in a weekly kickoff meeting becomes outdated shortly after the meeting, and team members need to reprioritize tasks.

monday.com saves the team vast amounts of time in meetings. Instead of discussing the plan for the week, the product manager can break down complex tasks into weekly achievable goals. This will help team members plan the week based on what they need to get done.


Create a weekly task board.


Create a weekly task board. (Large preview)

Share Valuable Resources With The Entire Team, Not Individual Members

Imagine you’ve found a really valuable resource and want to share it with your peers. You tweet about it and send a link to a group chat. You get feedback like, “Awesome resource! Thanks!” from some people in the chat. Shortly after, most of your peers forget about the resource, especially if they can’t use it in the work they’re doing right now. Sad, right? We can do better.

Instead of sending a link to a group chat, share all resources you find on a separate board. monday.com has a template named “Design Inspiration & Resources”. The great thing about this approach is that it’ll be much easier for team members to find a particular resource when they actually need it.


Instead of sending a link to a group chat, share all resources you find on a separate board.


(Large preview)

Organize Better Planning And Brainstorming Sessions

Task prioritization is a typical activity in agile project management. Team members get together, discuss tasks and vote on what to implement in the next sprint.

monday.com incorporates voting. Team members can use the voting column when they want to decide on something together as a team. Simply add a voting column to a board, and team members will be able to cast their vote in one click.


Vote for ideas during brainstorming and planning sessions.


Vote for ideas during brainstorming and planning sessions. (Large preview)

Notify Team Members In Real Time

Fear of missing out (FOMO) is a common problem on distributed teams. When working remotely, team members might be afraid to miss an important piece of information. As a result, they spend a lot of time in communication tools, checking mail and messengers. This can get really distracting. Team members should spend less time in communication tools and more time in tools they use to design (tools for prototyping and development). It’s all too easy to waste the day reading messages and replying.

A communication tool should serve vital information just when team members need it; it should have an effective mechanism of notification. monday.com notifies users via desktop and mobile in real time. The platform has an app for iOS and Android. The app allows team members to stay connected on their phone or tablet and to respond quickly from anywhere. It’s also possible to customize notification rules. For example, you can manage which activity triggers an email.


Mention people or entire teams.


(Large preview)

Create A Work Schedule For Your Team

If your team is distributed across the globe and you need to arrange a meeting, you have to be sure that it won’t happen at awkward hours (such as in the middle of the night). It would be great to see the team members’ working hours.

The work schedule board is a cornerstone of your business operations. Team members in each time zone can commit to the times that work for them. This helps product managers schedule meetings at times that work for everybody.


The work schedule board shows when team members will be online and available for chat.


The work schedule board shows when team members will be online and available for chat. (Large preview)

4. Involve Users In The Design Process

Most commercially successful products were created with a strong focus on the target audience. Designers know that if they want to release a successful product, they need to introduce real users to the design process. User involvement is most efficient and influential in the early stages of product development, because the cost of making changes increases as the system develops. Generally, the earlier you create a strong feedback loop, the better the final product will be.

Share Designs With Users And Gather A Valuable Feedback

The feedback that a product team gets from users is extremely valuable. It can validate that the design team is moving in the right direction.

On monday.com, users can create a board and choose whom to share it with. For example, if you are working with a client, you can set up a board for their project and invite them to work as a guest. The board could include key features you want to work on. As soon as you share the board, the client will get a notification and then can open the board, review the plan and request modifications.

5. Find All Required Information Easily

Documentation is another challenge. Distributed teams don’t have a physically shared space where they can share product documentation. Information might be stored in many different places: email, cloud drives, local computers, etc. It could lead to team members missing an important piece of information and being unaware of it. This leads to fragmented knowledge.

Centralize All Documents

Having all documents in one place is critical to success. monday.com syncs all information in a single accessible hub. All team members can store all relevant discussions in a searchable database. The platform provides an option to upload different types of files simply by dragging and dropping. The next time a designer needs to share a product’s specifications, all they need to do is upload a file to the platform.


Upload all assets by dragging and dropping.


Upload all assets by dragging and dropping. (Large preview)

Search Anything And Everything

Anyone who has ever worked with a knowledge base will tell you how critical search functionality is. Without proper search, your chance of finding information decreases significantly.

monday.com allows you to quickly find anything your team has ever worked on, including images, updates, projects and assignments. Your work becomes a rich knowledge base.


monday.com allows you to quickly find anything your team has ever worked on, including images, updates, projects and assignments. Your work becomes a rich knowledge base.


(Large preview)

For example, when you need to find the latest version of a product’s specification, all you need to do is click the search box, select the “Files” tab and enter the project’s name as a search query.


When you need to find the latest version of a product’s specification, all you need to do is click the search box, select the ‘Files’ tab and enter the project’s name as a search query.


(Large preview)

6. Make The Collaboration Tool A Natural Part Of The Team

The platform you choose for team management should feel like second nature. Technology should work for you and your team, not the other way around.

Minimize The Time Required To Learn A Tool

When you introduce a new tool in the design process, one goal should be to have total agreement to work using this tool. This agreement is not always easy to come by because team members are usually skeptical about the next “magical tool that will solve all of their problems”. What’s worse is that they have to spend extra time learning how to use it. Nobody wants to learn new software.

One of the most significant advantages of monday.com is its intuitiveness. Regardless of whether you’ve used a similar app before, monday.com can be picked up with no training. Team members will be able to understand how to use the tool without preparation.


monday.com provides basic onboarding to help users get started.


monday.com provides basic onboarding to help users get started. (Large preview)

Scalable

When companies select a collaboration tool, they often think of it as an investment. They want a tool that will scale with the business.

monday.com is suitable for any sized team, from two freelancers working together to thousands collaborating across the globe. The tool scales with you, from simplicity to complexity, with total ease. Also, as your business expands, monday.com makes it painless to shift to a premium version (Standard, Pro or Enterprise) and get more of the platform’s premium features.

Integrate The Platform With Existing Tools

A task-management tool is essential for any team hoping for good results. But the team’s toolbox also needs to support the design process (for prototyping and development) and the collection of design artifacts (for example, on Google Drive or Dropbox). It’s essential that the team-management tool integrates seamlessly with other tools the team uses.

When it comes to integration, monday.com does a lot to be part of the established software ecosystem. It can connect to Dropbox, Zapier, Google Drive and other sharing tools. As a team member, you can attach a mockup file to your updates, sharing it in the context of the tasks it relates to.

monday.com also comes with an open API architecture, which lets developers build their own integrations.


Monday also comes with an open API architecture, which lets developers build their own integrations.


(Large preview)

7. Keep The Team Motivated

Having the right atmosphere is extremely important. Team leaders should not only be in tune with each person on the team, but should continually look for ways to increase engagement.

Celebrate Successes With Team Members

It’s natural for people to seek acknowledgment. The need for social approval drives us to look for confirmation from people we know (parents, friends, colleagues). When someone recognizes our results by saying something as simple as “Great job!”, we feel motivated to work towards our goals. It’s essential for team players to get acknowledged, especially when working remotely.

monday.com has a few features that help create a sense of acknowledgment. The first one is the thumb-up feature, which is basically a positive reaction to an activity. Most people are familiar with this from social networks. People are used to measuring the effect of a post by the number of likes they get. monday.com allows you to give a thumb up to your teammates’ work.


Monday has a few features that help create a sense of acknowledgment.


(Large preview)

Another nice feature are the animated GIFs. You can liven up comments with GIFs. monday.com lets you pick from thousands of GIFs when responding to teammates, which will add a bit of personality to your comments.

You can liven up comments with GIFs. Monday lets you pick from thousands of GIFs when responding to teammates, which will add a bit of personality to your comments.
(Large preview)

Last but not least, monday.com has a confetti feature. As soon as a designer completes their last “in progress” task on a board, they will see an animated confetti effect. This subtle detail adds a bit of delight and motivates team members to have an all-green board.

Monday has a confetti feature
(Large preview)

Conclusion

Establishing an effective process on a distributed team is hard. What works for a colocated team won’t necessarily work for a distributed team, and what works for one distributed team won’t necessarily work for another.

Build a remote-friendly work culture by focusing on following priorities:

  • Prioritize transparency.
    Keep important information accessible to everyone.

  • Stay on top of the team’s activity.
    Understand what every member of your team is doing and where the team is in the process at a glance.

  • Build an effective communication system.
    The foundation of distributed teams is communication. Create a healthy system of meetings and habits to keep people communicating.

  • Lower the barrier to entry.
    Choose a team-collaboration tool that will be the least painful for everyone to get on board with. It should be a reference point that brings everything together.

Smashing Editorial
(ms, ra, il, al)


Continue reading here:

Making Distributed Product Teams Work More Efficiently With monday.com