iOS-Based Smart Mirror
Joe Angell
I had previously built a Magic Mirror, which I’ve documented on this site. I had a few problems since the setup, mostly hardware related. The power supply on the TV died and needed to be replaced, I found linux on the Raspberry Pi difficult to use and replaced it with an Intel Compute Stick running Windows, and when that died another Atom-based compute stick. When that also failed, I decided to finally learn some iOS development and take an entirely different approach.
My new smart mirror is built with SwiftUI on iOS 14. This app runs on a used seventh generation iPod Touch with a Lightning to HDMI adaptor that plugs into the TV.
Why iOS?
It mostly comes down to having iOS devices and wanting to learn iOS development. This seemed like a good first project.
Beyond that, iOS is very mature, with easy-to-use, high-level APIs, and an excellent development environment. Apple supports the latest version of iOS on fairly old devices. Used Apple devices are easy to get, and older ones are quite affordable.
Since I’m not running a browser, I don’t have to worry about cross-site API requests (although the newer node.js versions of the original Magic Mirror project don’t have that problem either), I don’t have to deal with maintaining Apache or node.js on a machine, I don’t need to figure out how to get the rest of the normal desktop UI out of the way (like the mouse cursor or full-screen mode), and so on.
Another nice bonus is that the device’s screen can be used for configuration, and while the TV is only used for the mirror display. I also don’t need to have a keyboard and mouse somewhere for it, since I can just use the touch screen directly. And I don’t have to edit any configuration files to make it work.
When I need to update the app, I can simply unplug the iOS device and bring it to my computer. I don’t have to copy files over the network or onto an SD card or whatever. If I put it on the App Store I can just download the update directly from the device.
iOS vs Pi vs Compute Stick
Here are my feelings on how an iOS device, a Raspberry Pi and an Intel Compute Stick running Windows compare for this purpose. This assumes the Pi and Intel Compute Stick are running a Magic Mirror variant that uses a web browser and Apache or node.js server. This is very much a “to each his own” kind of thing; your mileage may vary.
iOS
Pros:
A complete device with a powerful processor and almost everything you need standard (except a built-in HDMI output)
Very low maintenance, automatic updates, etc.
Excellent development environment and debugging tools.
Built in display and input (touch screen) for device setup, with automatic backups to make it easy to replace the device in the future.
All configuration (device and application) can be done from a graphic interface, without resorting to editing configs.
Cons:
Cost, especially for a new device, but used devices are quite affordable while still supporting the latest OS.
Need a Lightning to HDMI adaptor that is limited to 1080p (until USB-C is common on iOS devices), which further increases the cost.
Provisioning certificates that expire yearly to install on a device without going through the App Store
Have to write everything yourself, since you’re making your own app and not using an existing codebase like the Magic Mirror
Raspberry Pi
Pros:
Very inexpensive.
No special process for running on the device (like provisional certificates or app stores).
Connects directly to an HDTV via HDMI in up to 4k.
Magic Mirror provides a lot of plug-ins and customizations via editing code.
Cons:
Linux. Yes, this is a con, at least for me.
Configuration and maintenance are annoying/complicated due to Linux.
Need to run and manage a web server/browser.
Need a separate keyboard/mouse for configuration and maintenance.
More complicated to set up, debug and develop.
Configuration involves lots of config file editing.
Intel Computer Stick
Pros:
Windows (if you’re going to run Linux, you might as well use a Pi).
Configuration is mostly GUI-based.
Connects directly to an HDTV via HDMI in up to 4k.
Magic Mirror provides a lot of plug-ins and customizations via editing code.
Cons:
Cost; compute sticks are around $150, or as much as an iOS device and the HDMI adaptor.
Need to run and manage a web server/browser.
Need a separate keyboard/mouse for configuration and maintenance.
More complicated to set up, debug and develop.
Choosing an iOS Device
I had a seventh-generation iPod touch lying around from another project, and decided to use that. I had bought it on eBay for about $100. It was in very good condition and worked perfectly for my purposes. An iPhone 6s or 7 would have worked fine, too. The more recent it is, the longer it will be supported, and Apple supports their devices for quite a while. For example, at the time of this writing the iPhone 12 is the latest iPhone, but even the 6-year-old iPhone 6s will be able to run the (at the time) upcoming iOS 15.
The battery on this iPod touch failed about 8 months after I set it up. It swelled and popped the screen out. After failing to find a replacement battery, I just bought an iPhone 6s for $68 on eBay. There are a couple of scratches on the glass, a bright spot in the middle of the screen, and the battery doesn’t last very long (under an hour), but since it will always be plugged in and the screen will only used for configuration, none of that matters — it’s the perfect device for a smart mirror.
That brings us to the other nice feature of using iOS: it’s easy to switch to a new device. The process was basically:
Set up the new iOS device. I restored from my iPod touch backup, which included all my app settings.
Add the device to my Apple provisioning profile so that apps I build can run on it.
Rebuild the app in Xcode and push it to the device.
This was pretty painless. It took about an hour only because I needed to do an iOS update before restoring the backup, and because I hadn’t provisioned a device for development in a while had had to remember how to do that, but it was very little actual work to get it transferred over.
Update, March 2023: the battery on the 6s failed after 2 years, so I got an iPhone 8 for $85 on eBay. It already has a small nick in the glass and the battery is reading less than 80%, but it supports the latest iOS 16 and, like the 6s, will be plugged in all the time, so it should be fine.
Again, it was pretty much trivial to switch to the iPhone 8. It took maybe 20 minutes total, and it was plugged back into the mirror and working properly.
HDMI Output
To plug it into a TV I needed a Lightning to HDMI adaptor. The Apple ones are $50, which is pretty steep, but wound up being worth it. I first tried a $20 third-party one, and it basically didn't work at all. It wanted me to use an app, which didn’t make sense, and it displayed a message on the external display suggesting that it was for some streaming device instead of an HDMI output device. It doesn’t matter if it’s cheaper if it doesn’t actually work. I returned it and bought the Apple one, which worked perfectly.
The HDMI adaptor also has a separate Lightning connection so that you can charge the phone while it’s plugged into the TV, which I also took advantage of.
Power
I originally used an old 1 amp iPhone power supply, but it would stop charging the iPod when I plugged it into the HDMI adaptor. Apparently, the adaptor draws too much current to both run the HDMI output and charge the iPod. Switching to an old 2 amp USB power supply that I had laying around fixed the problem.
Features
I mimicked the original features of my old customized Magic Mirror implementation, often adding a number of tweaks and new features:
Current date and time.
Upcoming calendar events, but now using my iCloud calendar. This makes it easy to update from the Calendar app on any of my Apple devices, and I can select the calendars I want to see directly from the app. The calendar color is drawn as a bar next to that calendar, and the number of entries to show is adjustable from the app.
RSS feeds, thanks to FeedKit. Feeds are added directly from the app.
MBTA alerts. Which lines to show alerts for are selectable from the app, and the refresh frequency and if ongoing alerts should be shown are set in the app.
Independent and overall scaling for the UI components.
Hourly and daily weather. With Apple buying Dark Sky, I switched to OpenWeatherAPI and its OneCall interface, which adds much of what Dark Sky supported. The graph shows all the data it did with Dark Sky, but also includes humidity (both current and as a purple line on the graph), rain accumulation in addition to snow accumulation, options for the hot line and cold line temperatures, an option to prefer the “feels like” temperature instead of the real temperature, it shows cloud cover and the sun and moon, and the user can set how many hours to show on the hourly graph.
Live on-device preview for testing purposes when not plugged into the TV.
Currently, the API keys for OpenWeatherMap and the MBTA app are entered into the app directly by the user. If I ever get around to setting up an API Gateway or HTTP proxy I’ll move them there and put this on the App Store, probably.
Limitations
Customization and Distribution
The biggest limitation is that I’m the only one who can make changes to the functionality of the mirror, because it’s a compiled app. Distribution would be through the App Store, most likely, or it could be open sourced and others could make changes to it, but you’re not going to be able to easily add plug-in modules like you can with the Magic Mirror project. This isn’t a problem for me — I built it for exactly what I need.
Since this is not yet on the App Store, I’m using developer keys that expire yearly (App Store distribution keys don’t expire like that). That’s not much of an issue for me, since I can just plug it back into my computer, rebuild and upload a new version. It’s slightly annoying, though.
I’m also not sure if I even can put it on the App Store, since it requires people to generate and enter their own API keys, and that might violate some App Store rules or something.
HDMI Output and Resolution
Currently, the Lightning to HDMI adaptor has a max resolution of 1080p. I don’t expect this to change, but rather that Apple will replace Lightning with USB-C and support 4K displays directly in future devices. The iPad Pro with USB-C already does this, so it seems reasonable to expect this someday. It’ll eventually be relevant to me when I build a new mirror with a 4K display, but for now my 1080p display is working out fine.