Classic Computer Science Problems in Swift is Released!

Last week, Classic Computer Science Problems in Swift was released in print by Manning. It’s now in general availability from both Manning and Amazon. It’s a great book for students enrolled in a computer science program, professionals looking to learn a few computational problem solving techniques they’ve always been curious about, people preparing for job interviews, or anyone who already knows Swift and would like a very tutorial-oriented intro to some basic algorithms from AI.

The book aims for breadth instead of depth. There are chapters on search (including depth-first search, breadth-first search, and A*), graph algorithms (including Prim’s algorithm and Dijkstra’s algorithm), genetic algorithms, constraint-satisfaction problems, k-means clustering, neural networks, and more. It’s not a data structures & algorithms textbook. Instead, it’s a concise set of hands-on tutorials starting from scratch in each subject area. It’s also not an app-development book. The reader will not find any UI code. Instead, it focuses on solving several classic problems one would typically find in an undergraduate CS curriculum as concisely as possible in Swift Playgrounds.

The book assumes the reader is an intermediate Swift programmer. It is not appropriate for absolute Swift beginners. You can watch my short intro video explaining the book on YouTube and you can find all of its accompanying source code (tested against Swift 4.1 Playgrounds in Xcode 9.3) on GitHub. Most of the code will work in any Swift project, including projects for Linux.

If you read the description in the YouTube video, you will find a code to get a significant discount on the book from Manning. You can also download both Chapter 2 (Search) and Chapter 4 (Graph Algorithms) for free from the book’s page on Manning’s website. I think Chapter 4 (Graph Algorithms) is a really great chapter. In it you solve several problems related to building out a Hyperloop network in the United States while developing a graph data structure programmed in a protocol-oriented style.

Writing the book was mostly a joy. I finished the manuscript in September 2017. Delays in Manning’s production department led to the April 2018 release. I apologize to readers who pre-ordered the book and had to wait so long for the final version. I am not terribly happy with the formatting of the .mobi (Kindle) and .epub (iBooks) editions, which unfortunately is out of my control. I recommend picking up either the PDF from Manning or the print edition. When you purchase the eBook from Manning, you get the PDF, Kindle, and iBooks versions together.

Thank you in advance for reading. Feedback from readers in Manning’s early access program has been very positive. Pre-order sales of the book have also been strong. Let me know your feedback (both positive and negative) about the book on Twitter. And Amazon reviews (especially positive ones 😀) are appreciated. We are probably going to “port” the book to other programming languages, but those new books are likely a long-time away from release. I will also update the GitHub repository with an updated PlayGround when Swift 5 ships in final form.

Posted in , , , , | Leave a comment

Core Image for Swift Review

I recently completed reading Core Image for Swift by Simon Gladman. The book is available for free on iBooks. I had little previous exposure to Core Image, nor writing GLSL (the language Core Image filters are defined in) and I found this book a thorough and well written introduction.

To my knowledge, this is one of only two full-length book about Core Image. I have not read the other, but since its publication date (2012) predates Core Image for Swift (2016) by four years, I imagine it is somewhat out of date. Gladman is comprehensive in his coverage of the technology. He covers the built-in filters, creating various kinds of filters from scratch, image display options like GLView and MetalView, and peripheral technologies like vImage. In short, everything you would need to get started using Core Image for almost any purpose is covered in this book.

Gladman’s approach is one of using copious source code examples. Some people learn better from English explanations and some people learn better from code. This book will mostly appeal to the latter kind of reader. With that said, Gladman does take the time to explain basic information that the uninitiated in the digital image manipluation world will likely find helpful. Such as, how does a convolution filter work? The difference between various techniques and why you would use each is made clear throughout.

I read the book from cover-to-cover, which is probably not how most people would approach this book. The chapters are self-contained enough that you could probably just read the first few and then pick and choose chapters on topics that interest you. In my opinion each chapter is a more cogent explanation of the topic at hand than Apple’s documentation provides.

The biggest downside of Core Image for Swift is that it was written in the Swift 2 era, meaning its source code is now outdated and requires translation. Most of the source code in the book is provided in online repositories. However the repositories themselves have not been updated in over a year. Since Mr. Gladman began working at Apple last year, I think there is little hope the book itself will be updated either. We cannot blame book authors for writing books that get outdated. I just finished writing Classic Computer Science Problems in Swift, and with a fast moving target like Swift, it is likely that my book too will have some syntax incompatibilities with the latest version of Swift in a couple years. Xcode 9 is able to translate most of the code from Core Image for Swift into Swift 4 with little manual intervention beyond a lot of clicking “fix.”

Overall, Core Image for Swift is an excellent introduction to Core Image and I highly recommend it to anyone interested in this technology. Since it is free, it is a no brainer to at least download it and checkout the first chapter. Mr. Gladman’s blog is also work checking out. Many of the older posts are about Core Image.

Posted in , , , | 1 Comment

Annoucing Classic Computer Science Problems in Swift

I am pleased to announce the availability of my second book, Classic Computer Science Problems in Swift. Starting today, you can purchase early access to the book from Manning (use promo code mlkopec for 50% off through June 27th). I have completed drafts of the first six of eight chapters; three of which are available today from Manning, with the next three to follow over the summer. The final version of the book will arrive in Fall 2017, shortly after the release of Swift 4.

Classic Computer Science Problems in Swift is a great “second book on Swift.” It is aimed at those with some basic knowledge of Swift’s syntax who want to delve deeper into the covered problem solving techniques, brush up on core algorithms, or learn more Swift using problems familiar to them from other languages. It is suitable for professionals looking to deepen their understanding of the covered topics, students with some programming background looking to expand their computer science knowledge, and anyone preparing for coding interviews.

What is a “Classic Computer Science Problem?” It is a problem one typically finds in an undergraduate computer science curriculum. The topics covered in the book span the gamut from core computer science algorithms you would find in a data structures & algorithms class to artificial intelligence and its sub-discipline machine learning. There are both practical and whimsical problems. They include classic search problems, constraint satisfaction problems, graph algorithms, genetic algorithms, k-means clustering, simple neural networks, and more!

The book is not your typical app development book. You will not find any UIKit, App Kit, or full blown apps. Instead, you will be learning problem solving techniques (in Swift) that will make your apps more powerful, more efficient, and more featureful. All of the code in the book is Linux friendly (server-side Swift friendly) and available for easy perusal via a Playground on GitHub.

To learn more, checkout the Classic Computer Science Problems in Swift page on Manning’s website where you will find a full table of contents and free access to the Introduction and Chapter 2 (Search Problems). If the table of contents looks appealing to you, dive in with promo code mlkopec which will give you 50% off through June 27th!

Remember that you are purchasing a pre-release version of the book, so you will be joining me on the journey to its final release in the fall. You will be receiving rough drafts of chapters before they have been fully developed. I encourage you to send me your feedback, but keep-in mind that these are early days and everything is not yet perfect. You will receive the final version of the book upon publication.

Posted in , , , , , , | 3 Comments

Five Months on the Apple TV App Store

Apple launched the fourth generation Apple TV on October 29th, 2015. That was also the first day of the Apple TV App Store and the first day that my tvOS app, Chess TV!, was available. Chess TV! is an app that I created as a side-project in about 30 hours of my time after receiving a free Apple TV dev kit from Apple prior to the official launch of the device. I have been pleasantly surprised by its sales. This is the story of its first five months.

Building Chess TV!

I have quite a bit of experience with chess programming. I studied chess AI as my culminating experience in graduate school and I have developed prior chess apps as well as the main (only?) chess programming library for the Dart programming language. Yet, I felt there was no reason to reinvent the wheel. My goal was to create a great big screen chess playing experience, not to create a cutting edge AI.

To that end I searched through the repositories of the many open source chess engines. All of them (even the weak ones) are strong enough to be competitive for all but the most elite players. My criteria instead was:

  • Will this engine be easy to integrate with my code?
  • Is this engine well documented (or its source easy to understand enough), so that I will have no problem utilizing its public interface to implement features I want like undo/redo, algebraic move output, etc.?
Surprisingly, many of the popular engines are so concerned with performance that things like ease of integration and documentation take a backseat. I ultimately settled on integrating Mister Queen by Michael Fogleman. Mister Queen is not a well known engine by any means, but in my testing using The Bratko-Kopec Test it scored a 13/24 when provided 8 seconds per position (12 at 4 seconds and still 13 at 16 seconds) which indicates it’s about expert strength (ELO ~2000, stronger than 99+% of users).

Mister Queen is written in clear, concise, C. For a one-man project, as with most of Michael Fogleman’s projects, it’s surprising for its completeness and depth. I think I made a great choice, as it’s been a joy to integrate and has provided several features off-the-shelf (like built-in testing positions (more about that later)) that I would have otherwise needed to write myself. I do not regret at all choosing Mister Queen over the more popular alternatives. Sometimes in programming software, just as in using it, ease-of-use trumps all else.

Because Mister Queen is written in C, I chose to use Objective-C, instead of Swift, in writing Chess TV!. Although Swift has good support for C, it can’t beat Objective-C on that front! Working with UIKit on tvOS is almost identical to working with it on iOS. The main difference is thinking about focus (what UI element is currently selected by the Apple TV’s trackpad like remote). Once I had implemented a chess board using custom UIViews for each square with intelligent focusing behavior, I chose to use off-the-shelf widgets for the rest of the interface. The result is a UI that looks plain and no-nonsense, but also fits well with the rest of tvOS.

I’ve noticed a lot of bugs in other apps with regards to how the remote’s menu button operates. Misbehavior in this regard can even lead to rejections by app review. By using a standard tab bar controller, I’ve managed to avoid this.

I wanted Chess TV! to be more than just a chess playing app. Conveniently, Mister Queen is bundled with ~400 famous chess puzzles (positions where you choose the best move) from famous chess books (you cannot copyright a chess position - that was settled in the legal system). So, I built Chess TV! so that in addition to its playing mode it also has a puzzle mode, something none of its competitors have…

Competition

When the app store launched, Chess TV! was the only chess app on the store. By the second day of the store a competitor, also priced at $0.99, appeared. We dueled it out for chess app supremacy. Because there were so few apps on the store at the time, throughout November it was not unusual to find both apps in the top paid apps list.

In the last couple of months two free competitors have also appeared on the store as well as two apps related to chess (a chess clock and a chess watching app). For whatever reason, the two free apps do not seem to have diminished my sales. I have done zero marketing of Chess TV!. I think it may be that users are trying out the other apps and then deciding they want something more substantial. It’s possible that my higher price point (see next section) has actually signalled to the market place that Chess TV! is a premium product. If that theory is true, then the marketing efforts of the free competitors may actually be boosting Chess TV! sales.

I will say that I think Chess TV! is the most featureful (especially considering the puzzles) tvOS chess app. There are a couple features (3D, saving and resuming games) that other apps have that Chess TV! does not. Since launch, I have added features like support for background music, board flipping, and alternative board patterns based on feedback from users and the inclusion of these in competitors’ apps. I have also added Spanish localization, but it’s unclear how much this has boosted sales.

Pricing & Sales

Chess TV! launched at $0.99 cents and sat there for several months. I have experimented with prices ranging from $2.99 to $4.99. I tried each price out for about a week and compared sales to the prior week. Eventually I settled at $3.99, not based on much science other than these weekly examinations. I’m not sure it’s the ideal price, but I do think the signalling of Chess TV! as a premium product has helped it. Units are of course down from the $0.99 price point, but sales (in dollars) are actually stable or up.

Sales average $150-$200/month (before Apple’s cut). It’s not going to make me rich, but if these sales continue for the rest of the year (a big if), then for something I did as a hobby in about 30–40 hours of time (including updates, excluding me thinking about it, talking about it, and checking up on it) I would consider the sales more than satisfactory. They’re certainly higher than I expected, especially considering I’ve done zero marketing.

The tvOS App Store Needs Work

One reason I’ve done zero marketing is that as of now, there’s no way to link to a tvOS only app’s store page (or purchase an app for that matter) from any other device. That’s right, unlike iOS apps which you can link to the store page of in a web browser and purchase through iTunes, there’s no equivalent for tvOS only apps. This is a huge problem. What do you link to (for people to purchase from) on a marketing page? I have a simple page for Chess TV! that I don’t send to anyone since it doesn’t have real links.

I’ve brought up this issue with Apple developer relations and also at the Apple TV Tech Talk in NYC. Apple seemed sympathetic to the issue, but it’s been five months and there’s been no resolution. It’s a major sore point for anyone who wants to develop and market a tvOS only app.

When the tvOS app store launched in October it definitely was not ready for prime time. It didn’t even have categories! Now it has some rudimentary categories, but still not subcategories. This is despite the fact that we are able to select a subcategory for our tvOS apps when we submit them. Perhaps Apple is embarassed by the lack of apps that would appear in some of these subcategories. It definitely hurts Chess TV! which would likely dominate the board game subcategory if the subcategory was browsable by users.

Apple has never featured Chess TV!, which I understand since it’s not graphically impressive nor does it utilize any of the unique features of the Apple TV. The least they could do for us lowly chess developers would be to give us store page links and subcategories.

Summary

In building Chess TV! I was able to leverage a great open source chess engine, Apple’s free dev kit (to get a jump on the competition), and my UIKit skills from iOS, to build an app in a relatively short amount of time that resonates with more users than I expected. Thanks must go to Apple, Michael Fogleman, and all of the early adopters who took a chance on Chess TV!. Yet, the tvOS app store needs some tweaks before it will be truly developer friendly.

Posted in , , , , , , , , , , , | 3 Comments

Three Options for App Owners Facing the End of Parse

I originally wrote this post for Crew’s Backstage blog and it originally appeared there on March 1, 2016. Special thanks to Jory Mackay for his many superb edits and improvements. Note that the original audience is non-technical app owners. I have made a few minor edits from the original post here.
If you’re one of the 500,000+ app owners that use Parse—the most popular mobile backend-as-a-service (BaaS)—to power your app, January 28th was a particularly bad day. After half a decade providing app developers with flexible data storage, and everything from user authentication to push notifications, the team behind Parse announced they’d be shutting down their services.

Which should’ve come as a surprise. Since Facebook acquired Parse in 2013, they have only provided positive indications about their commitment to Parse. If you have an app developed using a Parse backend, it’s time you started looking for an alternative.

First off, it needs to be stated plainly that this most likely won’t be a quick and easy fix. The backend is a major component of your app. In many cases, the backend and the code that connects to the backend can make up as much as 50% of your app, especially if your app is a social network, messaging app, or other app centered on storing large amounts of user created data.

Changing backends is obviously a big endeavor. Luckily, you have both time and options. Parse has provided a one year window (until January 28th, 2017) in which its services will continue to operate. If you feel that your app has not yet gained enough traction to warrant a partial rewrite, consider taking a wait-and-see approach. You could wait until the late Spring/Summer with the existing backend and then begin the rewrite if your app has taken off. But keep in mind that the longer you wait, the more dangerous your situation becomes (software development is a fickle business).

So, if it’s time to move, what are your best options?

Option 1: Open Source Parse Server (or the Path of Least Resistance)

Despite shutting Parse down, Facebook has graciously open sourced a large amount of its technology. This will allow you to run your own ‘mini-Parse’ on your own hardware or your own virtualized server (through AWS, Heroku, Azure, Linode, Digital Ocean, etc…).

Unfortunately, it’s not as feature rich as Parse was/is, and of course it comes with no formal support contract. You’ll also have to maintain your own server or virtualized server, which is more than some developers/clients want to do.

But, ultimately, this option will entail the least development work/code changes.

Pros:
  • Free
  • Complete control of your own destiny
  • Fewest code changes

Cons:
  • No formal support
  • Have to maintain your own server or virtualized environment
  • Still missing some features other BaaS providers have

Option 2: Switch to Another Established BaaS Provider (CloudKit & Firebase)

Apple provides a comprehensive BaaS known as CloudKit. It offers many of the same services that Parse offered and its resource limits are very similar to Parse.

CloudKit backs Apple’s own services like iCloud Drive and iCloud Photo Library, so you know the infrastructure is industrial strength and unlikely to go away in the near future. There are ample tutorials and book chapters to help developers get up to speed on it.

But, there is a catch. CloudKit has no native SDK for Android (or Windows, Linux, etc…). While the recently introduced webhooks may allow you to cobble together a cross-platform solution, CloudKit is clearly designed only for iOS, Mac, and Web apps (an official JavaScript SDK does exist). If your app is only on these platforms then it may make a lot of sense.

Firebase, acquired by Google in 2014, is perhaps the BaaS most similar to Parse. Like Parse it’s backed by a big company with a great reputation in the field of big data, is feature rich, and works cross-platform.

Firebase offers realtime capabilities that make it ideal for messaging apps and also offers clearly tiered pricing that makes sense for most startups. Firebase is relatively popular and there are many more tutorials available surrounding it than some of the lesser known BaaS providers, so it is likely one of the easier options for a developer to get up to speed on. However, some developers dislike Firebase’s API.

Pros:
  • Free for apps with a small userbase
  • Backed by Apple or Google
  • Feature rich
  • Well documented
  • Realtime messaging support (Firebase)

Cons:
  • iOS/Mac/Web only (CloudKit)
  • No built-in support for push notifications (Firebase)
  • At the mercy of another tech giant (like Parse was to Facebook)

Option 3: Roll Your Own Backend

Writing a custom backend can be as daunting a task as writing a mobile app, depending on the complexity of that backend. There are a myriad of frameworks, languages, servers, and development environments to choose from. If your developer does not have previous experience writing a backend, it’s likely to be a laborious, error-prone task.

However, it’s also the only path that puts you completely in control of your own destiny (even beyond the open source Parse Server).

Your custom backend is built specifically for your app. It can include whatever features you need and you can scale it however suits your growth. However, it also means maintaining your own servers. You can somewhat get a jumpstart by utilizing cloud data stores provided by services like Azure, AWS, and Google Cloud Platform.

Pros:
  • Complete control of your app’s destiny
  • Customized for the features your app needs
  • Cheaper to scale
  • Can use cloud data stores from AWS, Azure, and Google

Cons:
  • Unsupported by a big company (no Apple or Google backing your server code)
  • Have to maintain your own servers or virtualized environments
  • Highest development cost
  • Longest development time

So what’s the best option?

Choosing what direction to take your Parse-powered app is ultimately both a technical and a business decision. Utilizing Parse’s open source solution will be the least costly in terms of both programmatic effort and maintenance costs. Porting your app to CloudKit, Firebase, or a lesser-known BaaS Provider will be costly and time consuming but will provide you with comprehensive support and eliminate the need to maintain your own server. Finally, building your own custom backend provides the ultimate in control but also will be the most costly and time consuming to develop.

If none of these sound right for you, there are a myriad of lesser known BaaS providers like Kii, Kinvey, FatFractal, and many more out there. The risk, however, is that you build against one of them and then they cease to exist. Using one of these options may make sense for an MVP but is unlikely to make sense for something you expect to scale.

You must consider the development work involved, platform needs, pricing, and your timeline. As well as the future of the platform that you’re building upon.

No option is perfect, but continuing with Parse Server on your own server probably makes sense as the path of least resistance for most apps. If you’re interested in porting your app from Parse, get in touch with me.

Posted in , , , , , , , , , | 1 Comment

The Real Estate Market does not Reflect Global Warming

Whether or not you believe the phenomenon of global warming* is man-made, there's no doubt the Earth has been getting warmer over the past century. Naturally, a warmer Earth leads to melting ice which as a consequence creates higher sea levels. The only point of contention is how fast sea levels will rise. There are many models.

The most extreme of these models expect major devastation in rich and highly populated areas in the United States like Miami by as soon as 2050. Yet Miami seems almost oblivious to the threat. The real estate market in Miami continues to boom.

How can respected scientists' prediction that an area will be under water not change the value of its real estate? There are three possibilities:

  • The players in the market do not believe in sea level rise/global warming.
  • The chronologic window for the devastation is still too great to affect prices. Perhaps when predicted calamity is within the window of a 30 year mortgage, banks will think twice.
  • Market actors do believe in sea level rise, but act irrationally through some sort of group think.

Markets are not always great predictors of long term trends.

*I use the term "global warming" because I feel "climate change" is a kind of doublespeak. Global warming describes the Earth on average getting warmer, whilst the now more politically correct climate change can be deemed an accurate prediction regardless of what happens to the Earth's average global temperature. Yes I understand its defense is that global warming will not be a universal trend - some areas will get colder. I still think of it as doublespeak though.

Posted in , , , , , , , | 4 Comments

American Customer Service Downfall

I'm not an expert on customer service. On some level, though, we're all experts on customer service because we all interact with hundreds of different service workers in our regular day-to-day errands. Anecdotally, I believe customer service generally used to be better here in the States. When I was growing up, I feel like service with a smile was the norm, not the exception.

I worked retail at music & movie chain Sam Goody when I was 17 for 7 months for $6/hour (I got a raise to $6.24 near the end). As soon as the holiday season started and I needed to quit for tennis season anyway, I did. Even though Sam Goody's prices were a rip off and the management sucked, the floor people there genuinely cared about the customer experience. Everyone worked hard to make customers happy and "the customer is always right" was nearly true.

I've had an incredibly bad customer service experience amongst six different organizations during the past 24 hours. Again these are just anecdotes, but I believe them to be part of a larger pattern. It all started when I decided I need a new Windows PC. Most of my work is on the Mac, but I have a little bit that crosses over onto Windows. TigerDirect was having an incredible Black Friday sale on DIY bundles, so I purchased one that included the AMD A10-7870K APU.

The shipment email (as you can see in this screenshot)
shows I ordered a bundle with the A10-7870K a part worth about ~$130. The shipment arrived yesterday by UPS with the wrong microprocessor (A8-7670K instead). It turns out the email for the shipment already showed them sending me a different CPU .
What's nefarious is not the mistake, but that TigerDirect changed the bundle (without changing the bundle's item number) and then shipped me the new bundle, even though my order email proves I bought the original bundle. The customer service agent that I waited 65 minutes on the phone to talk to (TigerDirect does not accept returns by chat/email for this part) acknowledged the mistake. The entire conversation was 98 minutes.

She acknowledged that they changed the bundle after I had already ordered. It's a true bait-and-switch! They said they had no more A10-7870K's in stock and wouldn't for a while. So they offered me to send it back and they would refund me for the cost of the A8. The A8 is $25 cheaper and that's not what I paid for! $25 is not going to make or break me, but it's the principle. I contacted Google Trusted Stores and someone higher up at TigerDirect is supposedly going to contact me according to the original customer service rep. I'm not holding my breath.

So to go with this new computer I also ordered several items from Amazon. USPS claims they were delivered yesterday at 6:49 PM (strange because that's after when the mail comes). I told Amazon they haven't been delivered even though USPS has marked them as delivered and they told me to wait a couple more days. So when they say "Thursday delivery guaranteed" they don't mean it.

I went to the UPS Store today to ship back the A8 to TigerDirect. The first customer service rep barked at me "need a receipt"? But I couldn't understand what he was saying. When I  asked for clarification, he didn't reply. The second scanned it in and seemed friendly enough, but as I was leaving I distinctly heard him toss my A8 package across the room.

I then went to the Apple Store to return an item. There are no lines at the Apple Store, you just walk up to someone and ask for help. I asked someone who turned out to be a manager and he found the most disinterested employee you can imagine. The guy was playing on his phone and the manager actually had to chastise him for being spaced out. The guy hardly communicated with me/made eye contact as he returned my item.

I went to T-Mobile on the way out of the mall to change a cell phone plan. Not only did the service representative assume I wasn't a T-Mobile customer, he also told me their cheapest plan for my configuration was $120 (he was hoping to up-sell me it seems), and I had to correct him that it was indeed $90, but he also then acted almost with disdain that I was bothering him to do this switch for me. Then he espoused more misinformation about the plan I had switched to not including Binge-On, even though it does, to again try to up-sell me to a higher priced plan.

I went to MicroCenter to get the right A10 APU. I went to the notoriously commission driven "Build Your Own PC" center. I was obviously lost looking for their poorly marked "processors" section. Nobody came to my aid. Maybe I don't look like the typical nerd, or maybe they were all too busy chatting and on their cell phones. I found a guy and asked him for help. I think he was on his cell phone too. He told me to wait for him at a glass case he pointed in the direction of. I waited quite a bit even though he didn't seem to be helping any other customer. Then he walked with me to the checkout (security procedure there for high price items) and took another call on his cell phone.

I understand being a service worker in retail is a low-paid job with few benefits. But having these kind of experiences is certainly another incentive to shop online. The TigerDirect bait-and-switch, changing the components of the bundle after I had already ordered and paid, is completely unacceptable. That they further refused to refund me for the value of the original item is so ridiculous it's hard to believe. If this post got at least one person to not shop there, that's justice.

Posted in , , , , , , , , , , , , | 1 Comment
Copyright 2012-2016 David Kopec. Powered by Blogger.

Search

Swedish Greys - a WordPress theme from Nordic Themepark. Converted by LiteThemes.com.