{ "version": "https://jsonfeed.org/version/1", "user_comment": "This feed allows you to read the posts from this site in any feed reader that supports the JSON Feed format. To add this feed to your reader, copy the following URL -- https://furbo.org/feed/json/ -- and add it your reader.", "home_page_url": "https://furbo.org", "feed_url": "https://furbo.org/feed/json/", "title": "furbo.org", "description": "by Craig Hockenberry", "items": [ { "id": "https://furbo.org/2023/01/15/the-shit-show/", "url": "https://furbo.org/2023/01/15/the-shit-show/", "title": "The Shit Show", "content_html": "\n
Well, it happened.
\n\n\n\nWe knew it was coming.
\n\n\n\nA prick pulled the plug. And what bothers me most about it is how Space Karen did it.
\n\n\n\nMy mom passed away just before Christmas. Her decline was something everyone in the family saw coming and we prepared for her demise. It still hurts like hell, but she left with love and dignity. That makes all the difference when it comes to coping with loss.
\n\n\n\nTwitterrific is something that we’ve all poured our love into for the past 16 years. I’m not usually one to toot my own horn, but we literally crafted the early experience on the service. We often hear that folks joined up because of our app. Our work was definitive and groundbreaking. We loved this app like I loved my mom.
\n\n\n\n(Note today’s date and the one on our announcement – the fuckwads missed our 16th anniversary by a couple of days! King Shithead probably thought Friday the 13th was lol. I’d love some proof that the API went down at 04:20 in UTC +1.)
\n\n\n\nLike my mom, the API has been declining for awhile. Endpoints were removed, new features were unavailable to third parties, and rate limiting restricted what we could do. And like my mom, we struggled on and did the best we could, trying to stay upbeat about it all.
\n\n\n\nWhat bothers me about Twitterrific’s final day is that it was not dignified. There was no advance notice for its creators, customers just got a weird error, and no one is explaining what’s going on. We had no chance to thank customers who have been with us for over a decade. Instead, it’s just another scene in their ongoing shit show.
\n\n\n\nBut I guess that’s what you should expect from a shitty person.
\n\n\n\nPersonally, I’m done. And with a vengeance.
\n\n\n\nFirst, arrogant bastards love seeing their names on tweets and other media. I want to starve him of the things that money can’t buy: respect and attention. Do the same by simply ignoring him and his kingdom.
\n\n\n\nSecondly, for the past several months I’ve been thinking about where we go from here. When you see decline, you plan for a demise. It was the last thing mom taught me.
\n\n\n\nI’ve been active on Mastodon since the billionaire bozo took over. And it makes me think.
\n\n\n\nOne thing I’ve noticed is that everyone is going to great lengths to make something that replaces the clients we’ve known for years. That’s an excellent goal that eases a transition in the short-term, but ignores how a new open standard (ActivityPub) can be leveraged in new and different ways.
\n\n\n\nFederation exposes a lot of different data sources that you’d want to follow. Not all of these sources will be Mastodon instances: you may want to stay up-to-date with someone’s Micro.blog, or maybe another person’s Tumblr, or someone else’s photo feed. There are many apps and servers for you to choose from.
\n\n\n\nIt feels like the time is right for a truly universal timeline. That notion excites me like the first time I posted XML status to an endpoint.
\n\n\n\nOne thing I remember from these early days: no one had any idea what they were doing. It was all new and things like @screen_name, \u00a0#hashtags, or RT hadn’t been invented yet.\u00a0Heck, we didn’t even call them “tweets” or use a bird icon at first!\u00a0The best ideas came from people using the service: all of the things mentioned above grew organically from a need.
\n\n\n\nThat’s where I want to be in the future. Exploring unknown territory that empowers others and adapts to the needs of a community.
\n\n\n\nThere’s no sense in clinging to the personal whims of a clown leading a shit show. Especially when his circus will end up being a $44 billion version of MySpace.
\n", "date_published": "2023-01-15T20:22:56+00:00", "date_modified": "2023-01-15T20:22:56+00:00", "author": { "name": "Craig Hockenberry" } }, { "id": "https://furbo.org/2022/12/20/simbuddy-your-simulators-bff/", "url": "https://furbo.org/2022/12/20/simbuddy-your-simulators-bff/", "title": "SimBuddy \u2013 Your Simulator\u2019s BFF", "content_html": "\nHave you ever added code like this to your app?
\n\n\n\nprint(Bundle.main.resourcePath!)\nprint(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.path)\n\n\n\n
Or maybe you\u2019ve been frustrated that you can’t add that code because you\u2019re in the middle of debugging?
\n\n\n\nYeah, me too. Many times.
\n\n\n\nThe locations show above, and many others, are available from Xcode using the xcrun simctl
command. Every application on every device on every platform can be queried. But these lookups are difficult for developers because the information is structured around automatically generated GUIDs. The GUID you\u2019re looking for changes every time a new OS is available, a device is added, or an application is installed. And we do that a lot!
There are other tools available to help you navigate the Simulator, but they all do much more than I really need and take up space in my menu bar even though they are used infrequently. Additionally, none of these tools help find the “On My iPhone/iPad” container used by the Files app: a folder that I use whenever I\u2019m testing import and export code.
\n\n\n\nBy now, you probably know where this is going: yes, I wrote my own utility and call it\u00a0SimBuddy. It\u2019s a\u00a0FREE download\u00a0from the\u00a0Iconfactory.
\n\n\n\n\n\n\n\nSimBuddy\u00a0uses two popup menus for navigation: the top one shows which devices are running in the\u00a0Simulator and the one below shows all the applications installed on that device (your apps are listed first). Once you make a choice with those popups, you can use the buttons at the bottom of the window to navigate in the Finder. If you are using app group containers for sharing information between an extension/widget and your main app, you open those folders by selecting the ID and using “Open”.
\n\n\n\nIf the Terminal is more your thing, you can hold down the option key while clicking a button and a path to the folder is put on the clipboard. Paste that into a command line and away you go!
\n\n\n\nIt\u2019s not a complicated app, as you can see from the source code, but it\u2019s one that I\u2019m very happy to have in my developer toolbox now. I hope you enjoy it, too!
\n\n\n\nP.S. I love putting Easter eggs in apps. This time it\u2019s in the app icon.
\n", "date_published": "2022-12-20T19:54:15+00:00", "date_modified": "2022-12-20T19:54:15+00:00", "author": { "name": "Craig Hockenberry" } }, { "id": "https://furbo.org/2022/11/09/managing-xcode-downloads/", "url": "https://furbo.org/2022/11/09/managing-xcode-downloads/", "title": "Managing Xcode Downloads", "content_html": "\nBeginning with Xcode 14, the Simulators for watchOS and tvOS are available as separate downloads (iOS and macOS are still “built-in”). This reduces the app download size significantly, but it also means that you now have to manage these large (3-4 GB) components yourself.
\n\n\n\nWhen you launch Xcode 14 the first time, you are prompted to download additional platforms. Another prompt is displayed when you try to run a target for a platform without a runtime.
\n\n\n\nBut what are these downloads and where are they stored?
\n\n\n\nThe first hint is when you look at Disk Utility. You’ll see a bunch of new “Simulator” volumes mounted under Disk Images:
\n\n\n\n\n\n\n\nWhen you select these volumes, you’ll see that they all mount at /Library/Developer/CoreSimulator/Volumes
. Within each volume you’ll find a legal PDF and a path to a .simruntime
package in a Runtimes
directory. This structure is the same as additional iOS runtimes in /Library/Developer/CoreSimulator/Profile/Runtimes
. These .simruntime
packages contain all the information needed to simulate the device.
Now that you know what Xcode is using, you’ll wonder where it’s getting the disk image. It’s located in a sibling directory: /Library/Developer/CoreSimulator/Images
. That folder also contains an images.plist
file that contains metadata for the disk images. There are only a handful of files there, but on my Mac they use 13 GB of disk space.
And up until a couple of hours ago, that folder contained 7 GB of data that was incompatible with the current version of Xcode. I had to delete these files manually. But how?
\n\n\n\nThe simplest way to manage this space is using the new Platforms panel in Xcode preferences:
\n\n\n\n\n\n\n\nThis window also shows when you last used the runtime: in the screenshot above it’s clear that I can get rid of the iOS 14 and tvOS 16.0 runtimes and save about 25 GB of storage. It’s easy to get those runtimes back if needed, just press the + button. (After downloading a new runtime, it can be used in the Devices & Simulator windows to create a new test device.)
\n\n\n\nIf the command line is more your thing, you can use xcrun
to gather the same information:
$ xcrun simctl runtime list\n\n\n\n
Add a -v
option there if you want more details (from the images.plist
mentioned above). To delete any one of the items listed, use the listed GUID in this command:
$ xcrun simctl runtime delete <GUID>\n\n\n\n
In the end, this short post saved me 32 GB of disk space. If you’re developing for platforms other than the current iOS, you’ll likely see something similar. As time passes, you’ll need to manually keep an eye on this stuff: Xcode can’t clean things up for you because it has no idea what you need.
\n\n\n\nFor additional details, check out Apple’s documentation for installing and managing Simulator runtimes. Thanks go to Jason Yao for helping me figure out a bunch of this stuff!
\n", "date_published": "2022-11-10T00:35:35+00:00", "date_modified": "2022-11-10T00:35:35+00:00", "author": { "name": "Craig Hockenberry" } }, { "id": "https://furbo.org/2022/09/13/behind-the-app-wallaroo/", "url": "https://furbo.org/2022/09/13/behind-the-app-wallaroo/", "title": "Behind the App: Wallaroo", "content_html": "\nIt’s been awhile since I’ve done one of these deep dives on what goes on behind the scenes during the development of an Iconfactory app. There’s a common thread to each one: I feel the need to document our work when there’s a major change in how we build user interfaces.
\n\n\n\nThe first one was for the “flattening” of Twitterrific 5, a task that preceded Apple’s work in iOS 7 by six months. The next one was for Flare 2, when the Aqua face of macOS began a dramatic evolution in Yosemite.
\n\n\n\nWith Wallaroo, there’s another major change that may not be noticeable on the surface: it’s the Iconfactory’s first app written completely in SwiftUI.
\n\n\n\nIt all started while I was working on Shortcuts support in Tot. During March of this year, I noticed that there was an action to “Set Wallpaper”. I also learned how Shortcuts could be downloaded, installed, and managed using a URLs.
\n\n\n\nThe Iconfactory has been making wallpaper images since the dawn of time, but it never made sense to make an app because changing your wallpaper was a manual task. Shortcuts radically changed this calculus and the idea for an app was born. I threw together a quick prototype that let you set two wallpapers. Sean and I had the beginnings of Wallaroo.
\n\n\n\nOur wallpaper prototype became one of those “we’ll do it some day” projects. Then something important happened: WWDC 2022. After Lock and Home Screen customization was announced, the idea immediately became a “we need to do this before September!” project.
\n\n\n\nWe built Wallaroo from scratch in a little over two months.
\n\n\n\nThe project started with a couple of wrinkles: the “Set Wallpaper” action didn’t work with the new features on iOS 16, so we filed FB10377111 on June 6th (a couple of hours after the keynote ended). We placed our faith in the abilities of the Shortcuts team and decided to carry on in spite of this setback. (We wish everyone at Apple wrote release notes like they do!)
\n\n\n\nThe other wrinkle was that we all had work-in-progress that needed to be finished up. We knew that the short timeframe meant that this was an “all hands on deck” situation, so it wasn’t until the end of June before we all freed up. We put the prototype on TestFlight and got to work.
\n\n\n\nThere were three major areas where we focused our attention:
\n\n\n\nGed, Anthony, Dave, and Talos immediately got to work on the first bullet, but without a backend server, there was no place to put files and metadata. So we made a Numbers spreadsheet and shared it in Dropbox along with the source images. Our Slack channel for the project was filled with “I’m going in” and “I’m out!” to avoid write conflicts. (S.W.A.T. = Software Write Avoidance Technique)
\n\n\n\nI was responsible for the development of the backend. Importing a spreadsheet CSV file gave us our initial database and images in Dropbox let me manually generate thumbnails and other content that would be needed in the app.
\n\n\n\nSean took the lead on the app. We’ve been holding back on SwiftUI due to its immaturity, but the changes in iOS 16 looked great, so we went all in (the only UIKit/AppKit code is in delegate connectors). The data in the spreadsheet was massaged again to give him some real data to use.
\n\n\n\nTalos took the lead on the app architecture and the wireframe was finished on July 5th. A week later we had enough working code to make a Git repository. A few days later Sean showed us his Captain Pike Appreciation App:
\n\n\n\n\n\n\n\nWe were on our way, but had less than two months before an iPhone announcement. Time to kick butt.
\n\n\n\nJuly was a blur. Progress was quick and everyone was heads down on their app responsibilities.
\n\n\n\nRemember that bug in Shortcuts that prevented Set Wallpaper from working in iOS 16? It was still around and we were starting to get worried. When iOS 16 beta 5 dropped on August 8th, we rejoiced when our test ran. The shortcut action worked perfectly!
\n\n\n\nOur last update to the shared spreadsheet was on August 10th. From that point on, we were able to use our new content management system to add and update wallpaper. There was still a ton of work needed to clean up metadata and fine tune each wallpaper release.
\n\n\n\nWith the new server up and running, we started testing push notifications. Since Sean’s focus was still in the primary user interface, I started working on the SwiftUI views and models that talked to our backend server. That work continued with integrating the Patreon API and hooking up StoreKit2 for subscriptions. I also had a blast doing the User License and Credits screens.
\n\n\n\nWe started our first beta test with Patreon supporters on August 25th. We were going to make the mid-September release date!
\n\n\n\nLooking back on the development, I think there were two things working in our favor: experience and SwiftUI.
\n\n\n\nWe’ve made a lot of apps and have an instinctual knowledge on how to build them. But no matter how little friction there is in working together, you still have to put the pieces together.
\n\n\n\nSwiftUI is incredibly good at doing that.
\n\n\n\nKeep in mind that neither Sean or I had created a full-fledged app using SwiftUI (widgets don’t count). We had to learn the idioms and best practices, but once that was overcome, development happened at a lightning pace.
\n\n\n\nWe encountered roadblocks, of course. Tracking down memory leaks was harder than UIKit because of the abstractions. Figuring out how to share an image was a huge head scratcher. Implementing parallax in a ScrollView was many days of hard work. And you should see the comments in our PagingView!
\n\n\n\nBut overall, the experience was extremely positive. If you’ve been on the fence with this technology, iOS 16 feels like a turning point in SwiftUI’s evolution.
\n\n\n\nNow that you’ve read about how we made it, take Wallaroo for a spin. It’s a FREE download and a fun little app for iOS 16. And a great example of what you can do with SwiftUI.
\n", "date_published": "2022-09-13T18:49:34+00:00", "date_modified": "2022-09-13T18:49:34+00:00", "author": { "name": "Craig Hockenberry" }, "attachments": [ { "url": "https://furbo.org/stuff/Captain-Pike-Appreciation-App.mp4", "mime_type": "video/mp4", "size_in_bytes": 1494887 } ] }, { "id": "https://furbo.org/2022/06/03/lame-until-it-isnt/", "url": "https://furbo.org/2022/06/03/lame-until-it-isnt/", "title": "Lame, Until it Isn’t", "content_html": "\nWhere there\u2019s smoke, there\u2019s fire. And as we approach WWDC 2022, there\u2019s a lot of smoke around AR and VR. In some ways, this is going to be a huge inflection point, in other ways, it\u2019s probably going to be a letdown.
\n\n\n\nRemember when the iPod was announced? Some folks called it lame because it didn\u2019t meet their expectations.
\n\n\n\nThe same thing will be true of anything Apple wants us to put on our face. It\u2019s going to less impressive technically than any of the currently shipping products. And that\u2019s good, because you don\u2019t make fundamental changes by tweaking existing technologies. A Nomad audio player was a tweak. An Oculus headset is a tweak.
\n\n\n\nEverything we\u2019ve seen to date with VR has been an attempt to bring information to a 3D world. Headsets are just a means to project that 3D environment so our eyes can see it.
\n\n\n\nI think Apple\u2019s approach with AR will be completely different: they will bring 3D to an information world.
\n\n\n\nWe all have the greatest source of information humankind has ever known in our pocket or purse. Much of that information relates to the world around us: weather, transportation, shopping, dining, etc. Relating that data to our physical space will be a powerful tool.
\n\n\n\nAll the AR examples we\u2019ve seen on Apple\u2019s devices hint at this direction. They take the information on our phone and place it at derived 3D coordinates. Where people get tripped up in these demos is where the results are shown: on a standard screen.
\n\n\n\nI don\u2019t think that\u2019s Apple\u2019s final goal, because any current screen technology will block your view of the real world. It\u2019s also why I think 3D headsets will remain a niche technology: people have innate need to see what\u2019s going on around them.
\n\n\n\nOur current screens also use a lot of power. And that means batteries. And that means weight. Not what I want on my face, for sure.
\n\n\n\nApple knows this and that\u2019s why I think a new display system is the thing they\u2019re taking time to get right. We may or may not see this new display at WWDC. I can remember a time when all we had for an iPad was a simulator.
\n\n\n\nThe changes caused by a new display will be incremental. There will certainly be technical limitations in the product that are imposed by size and weight: Apple will improve on those things as components allow.
\n\n\n\nOther changes will happen because no one, including Apple, really knows how this display will be used by normal folks (we, I should note, are not normal folks). The first Apple Watch tried to do a lot of things: iteration got rid of things no one used, and improved the things everyone wanted.
\n\n\n\nIt\u2019s likely that a first iteration will also be a \u201csatellite device\u201d where the iPhone does the heavy lifting. Much like the original iPod relied on a Mac. The realityOS could be nothing more than widgets for a new display on your face.
\n\n\n\nThat will feel lame until you realize something else: after two decades, the basic form factor and functionality of that first iPod is now an essential part of our lives and we call it an iPhone. Don\u2019t underestimate Apple\u2019s ability to iterate.
\n", "date_published": "2022-06-03T17:21:43+00:00", "date_modified": "2022-06-03T17:21:43+00:00", "author": { "name": "Craig Hockenberry" } }, { "id": "https://furbo.org/2022/01/11/consistency-sin/", "url": "https://furbo.org/2022/01/11/consistency-sin/", "title": "Consistency Sin", "content_html": "\nThis past summer we narrowly avoided a major user interface regression on Apple devices. The story ended well, but I think it\u2019s important to look back on the situation and ask a simple question:
\n\n\n\nWhy did this happen in the first place?
\n\n\n\nMy answer is something I call \u201cconsistency sin\u201d. Understanding the cause lets us avoid similar situations in the future.
\n\n\n\nYour first reaction to this nomenclature may be, “Isn\u2019t consistency a good thing in user interfaces?”
\n\n\n\nAbsolutely! Colors, fonts, and other assets should be similar within an app. Combined they help give the user a sense of place and act as a guide through an interface. And in many, cases these similarities should be maintained across platforms. There\u2019s no sin there.
\n\n\n\nBut you can get into trouble when this consistency starts to affect the user experience.
\n\n\n\n\n\n\n\nDesign is not how it looks, it\u2019s how it works.
Steve Jobs said a lot of smart things, but I use this advice most often.
\n\n\n\nThe roots of consistency sin take hold when folks disregard the inherent differences between platforms. A greater importance is placed on making sure things match visually: how a person uses that design takes a back seat.
\n\n\n\nPlatform controls and interface elements can differ at a fundamental level. The mouse is optimized for indirect interaction while a screen is optimized for direct interaction.
\n\n\n\nFor the most part, developers are shielded from these details through the use of standard components that conform to the Human Interface Guidelines.
\n\n\n\nHigher level interactions are driven by the type and quantity of information the user is working with.
\n\n\n\nTo use Safari as an example, I can have hundreds of tabs open while I work on my Mac; on iOS it\u2019s usually less than a dozen. Safari on iOS is also a full screen experience, while multiple windows and interactions with other applications are common on macOS.
\n\n\n\nSafari\u2019s new tab design on iOS works great for me: swiping between tabs of fullscreen content is a better interaction for a limited number of pages. The grid of pages as a fallback for selection also works well for managing what I want to keep around.
\n\n\n\nThe consistency sin in Safari was to come up with a good design for iOS and assume that it would also work well on iPadOS and macOS. It practice, these new tabs were difficult to use in a different work environment.
\n\n\n\nLuckily the folks working on Safari did the smartest thing possible: they listened to feedback and fixed the issues before shipping. That\u2019s an important thing for a product that every Apple customer uses every day on every device.
\n\n\n\nIt\u2019s one thing to make a mistake, it\u2019s a wholly different thing to deny that anything’s wrong. So let\u2019s take a look at another example.
\n\n\n\nNotifications also suffer from consistency sin.
\n\n\n\nI look at my iPhone Lock Screen dozens of times each day, and sometimes just to just view a reminder or some other short notification. It’s quick, simple, and minimizes distractions.
\n\n\n\nOn my Mac I see the Lock Screen only once or twice per day for just a few seconds as I enter a password. That means notifications occur while I’m actively working.
\n\n\n\nAgain, consistency sin looks for a single solution that ignores my needs. On macOS I don\u2019t want a minimal solution that is suitable for a mobile device. I want options that let me quickly dismiss or defer an item that\u2019s interrupting my work. (And I certainly don\u2019t want to hunt around in the window for a hidden control that lets me access a function.)
\n\n\n\nPlacement is also an issue: on iOS controls tend toward the bottom of the screen (for reachability). The opposite is true on macOS where they tend toward the top of the screen so they\u2019re closer to the menu bar and window controls. Consistency sin says that notifications should always be at the top of the screen.
\n\n\n\nWith iOS, there\u2019s a nice visual and functional separation between app interactions in the lower half of the screen and notifications in the upper half. On the Mac, notifications are just another thing fighting for real estate at the top of the screen.
\n\n\n\nThe bad news is that we\u2019re likely to have more consistency sin in our future, thanks to Electron and other cross platform frameworks.
\n\n\n\nWhile development teams try to attain feature parity, experience parity will suffer. Everyone who\u2019s used an iOS app and immediately thought \u201cthis is a web page\u201d will know what that means.
\n\n\n\nThere is a long history of user interface frameworks that make work easier for a product team. Every time, these solutions end up being a least common denominator that makes it more difficult for customers. Don\u2019t be surprised when they complain: as they did for Safari, and as they do for Notifications.
\n\n\n\nIf you\u2019re a designer or developer, it\u2019s your job to push back on the notion of consistency when it begins to affect a user’s experience. Remember design is how it works, and work is not the same on every device.
\n", "date_published": "2022-01-11T23:26:23+00:00", "date_modified": "2022-01-23T20:59:44+00:00", "author": { "name": "Craig Hockenberry" } }, { "id": "https://furbo.org/2021/08/30/introducing-av-remote/", "url": "https://furbo.org/2021/08/30/introducing-av-remote/", "title": "Introducing AV Remote", "content_html": "\nIn my ongoing quest to release FREE apps that are useful in my daily life, I’m proud to announce the release of AV Remote. It’s a very simple remote for Denon and Marantz AV receivers.
\n\n\n\n\n\n\n\nThe official apps for controlling these receivers work fine, but have way too many features for my needs. I don’t need to control zones, select inputs, or adjust equalizer presets. I just want to control the power and volume.
\n\n\n\nWhile using early versions of the app, I realized that I have different “listening levels”. As a result, AV Remote makes it easy to select a quiet volume for background music or something much louder for watching a movie:
\n\n\n\n\n\n\n\nAV Remote works with recent models of both Denon and Marantz receivers. If your device supports HEOS, then the app can discover the receiver on your network and begin controlling it. There’s no setup other than allowing access to your local network with Bonjour.
\n\n\n\nAnd if you’re looking for some good background music to put on your Apple TV, I have a solution for that too!
\n", "date_published": "2021-08-30T17:22:01+00:00", "date_modified": "2021-08-30T17:36:13+00:00", "author": { "name": "Craig Hockenberry" } }, { "id": "https://furbo.org/2021/08/25/jsc-my-new-best-friend/", "url": "https://furbo.org/2021/08/25/jsc-my-new-best-friend/", "title": "jsc: My New Best Friend", "content_html": "\nA friend of mine recently pointed me at a well hidden command line tool. In the JavaScript framework used by Safari and other parts of Apple’s products, there is a tool called jsc
. It’s a command line interface for JavaScript that uses the same code as the rest of the system.
You can find the binary at /System/Library/Frameworks/JavaScriptCore.framework/Versions/Current/Helpers/jsc
. That path is unwieldy, so I have an alias set up that lets my just type jsc
in the Terminal.
So what can you do with jsc
? Pretty much anything you can do with JavaScript in a browser with the caveat that there aren’t document
and window
instances.
If you run jsc -h
, you’ll see a lot of options for testing and profiling JavaScript. It’s clear that the WebKit team uses this internally for running tests. But we can also use it for trying out ideas and running simple utilities.
A picture is worth a thousand words, so let me show you how it can be used to solve a simple problem. I recently needed to convert some strings in our Turkish localization of Frenzic to uppercase: the lowercase “i” was getting converted to the dotless version.
\n\n\n\nJavaScript’s toLocaleUpperCase()
function is the perfect way to do this, so I pulled jsc
out of my tool bag and got to work. The first challenge was getting input.
Luckily, there is a readline()
function that takes keyboard input and returns a value. Unluckily, that input isn’t in the encoding you’d expect it to be: characters are returned in ISO-8859-1 (Latin-1), not UTF-8. Remember, there’s no document instance so the default encoding is used.
To workaround this limitation, you can percent escape the characters to UTF-16 and then decode them back into UTF-8 with this technique:
\n\n\n\nvar text = decodeURIComponent(escape(readline()));\n\n\n\n
(If any WebKit engineers are reading this, it would be nice to have a command line option like --encoding=utf-8
.)
Generating output is a bit different than a browser, too. You’ll be using print()
instead of console.log()
. To convert the text input and display it, I used this:
print(text.toLocaleUpperCase('tr-TR'));\n\n\n\n
There are a few more built-in functions that may prove useful, but so far, I’ve only needed to read and write text. It’s undocumented, but jsc
also takes standard input and can be used as a shebang:
$ echo \"print(1+2);\" | jsc\n3\n\n\n\n
Since this is likely code I’ll have to use again, I created a Turkish.js file:
\n\n\n\nwhile (true) {\n print('Turkish text?');\n var text = decodeURIComponent(escape(readline()));\n print(text.toLocaleUpperCase('tr-TR'));\n print('-------------');\n}\n\n\n\n
I can now run this any time with jsc Turkish.js
. And you also get to see how having JavaScript in a command line can be handy. Enjoy!
It’s common wisdom that you should release a software product when it is minimally viable: get an early version out in the world as soon as it can perform a useful task for a customer.
\n\n\n\nWhen that product is for someone who is a developer that’s been coding since the dawn of time, the equation gets flipped on its head. Waterscope is a Maximally Viable Product and the customer is me.
\n\n\n\nThe app got its start when Swift 1.0 was announced back in 2014: I wanted to build something with the new language. About that same time, I had also started learning about tides and how they are predicted. It’s a complex problem that has been vexing scientists since the three-body problem was first proposed by Newton with the publication of the Principia in 1687. Like determining the time and place for a lunar eclipse, we rely on derived approximations.
\n\n\n\nFor learning a new language, tide prediction provided a lot of interesting work: data collection, complex calculations, graphical presentation, and animatable data. It also let me know when it was a good time for a dog walk.
\n\n\n\nAs an ocean swimmer, I also wanted my weather app to provide information about water conditions. It turns out the scientists at the National Oceanic and Atmospheric Administration (NOAA) have that all figured out. As do the meteorologists at the National Weather Service (NWS) with their API for weather observations and forecasts. There are even high resolution images from environmental satellites launched by NASA. The United States government provides a treasure trove of data; the challenge with Waterscope was to organize and present it in a consistent manner.
\n\n\n\n\n\n\n\nWhich leads to a secondary goal for this app: to make it completely by myself. I work with some incredibly talented designers, but I wanted Waterscope to be uniquely my own. All the design, for better or worse, was created by my own hand. (The only exception is the use of SF Symbols when showing weather conditions.)
\n\n\n\nSo not only was I learning Swift, I was also learning Sketch and, of course, how much time it takes to get something to feel right. Like coding, it’s not as easy as we sometimes make it look. Don’t take your designers for granted :-)
\n\n\n\nAlong the way, there were some interesting hurdles. Some visual, some simple, and some complex. Many of the things I wanted to do required learning about astronomy and orbital mechanics. My sketches from Linea will give you an idea of the breadth of the challenges. (And being the day after the solstice, that first sketch is particularly relevant.)
\n\n\n\n\n\n\n\nHere I am, six years later. I’ve learned a lot, but as with my first app in Objective-C, the most important thing about this exercise was how not to use Swift. It will probably take me another 18 years to come to terms with this new language, and feel like I’ve mastered it, but a journey can’t start without the first steps. Another insight is that a programming language is just a means to an end: the hard part is not the code, it’s understanding what needs to be done.
\n\n\n\nI’m releasing Waterscope today because there are certainly other folks who will benefit from my personal weather app. There may even be some educational value in seeing how I approached a data-rich user interface (hint: Edward Tufte’s books taught me). Information can be dynamic and beautiful.
\n\n\n\nAt the same time, if you’re outside the U.S., it’s unlikely to be a satisfying experience: most of the data sources and their presentation are oriented towards North America. An example: in the southern hemisphere your view of the sun and moon’s orbit is in a counterclockwise direction as you look north. Waterscope displays a clockwise orbit.
\n\n\n\nBut the good news is that Waterscope, like the data it uses, is FREE to download and use. Enjoy!
\n", "date_published": "2020-12-22T20:01:44+00:00", "date_modified": "2020-12-23T20:10:50+00:00", "author": { "name": "Craig Hockenberry" } }, { "id": "https://furbo.org/2020/12/01/codesign-the-saga-continues/", "url": "https://furbo.org/2020/12/01/codesign-the-saga-continues/", "title": "Codesign: The Saga Continues", "content_html": "\nI have a long history of writing about code signing in macOS. When Big Sur was released, I thought “Finally!”
\n\n\n\nI was wrong.
\n\n\n\nThis time around I was tripped up by Safari, of all things. It doesn’t open app archives like other parts of macOS.
\n\n\n\nThis story began with customer reports of xScope being a “damaged app” on Big Sur. This was surprising because I had been downloading and testing the app on Big Sur for several months without issue.
\n\n\n\nI was also doing all this work on Apple Silicon using the DTK. And since Google Chrome wasn’t yet working on this device, all my testing was limited to Safari. Safari’s default setting is to open “safe” files after download, so I left that alone (as most customers would).
\n\n\n\nThis is where I shot myself in the foot. At no point did my downloads touch the Archive Utility. And I had no idea that Safari’s code is different than the system utility.
\n\n\n\nWhen I checked the signature of the app downloaded with Safari, everything looks good:
\n\n\n\n$ codesign -vvvv ~/Downloads/xScope.app\n...\n/Users/CHOCK/Downloads/xScope.app: valid on disk\n/Users/CHOCK/Downloads/xScope.app: satisfies its Designated Requirement\n\n\n\n
Things were very different when using Google Chrome:
\n\n\n\n$ codesign -vvvv ~/Downloads/xScope.app\n/Users/CHOCK/Downloads/xScope.app: unsealed contents present in the root directory of an embedded framework\nIn subcomponent: /Users/CHOCK/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework\n\n$ codesign -vvvv ~/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework\n/Users/CHOCK/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework: a sealed resource is missing or invalid\nfile added: /Users/CHOCK/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework/Versions/Current/Resources/._fr_CA.lproj\nfile added: /Users/CHOCK/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework/Versions/Current/Resources/Updater.app/Contents/Resources/._fr_CA.lproj\nfile added: /Users/CHOCK/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework/Versions/Current/Resources/Updater.app/Contents/Resources/._pt.lproj\nfile added: /Users/CHOCK/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework/Versions/Current/Resources/._pt.lproj\n\n\n\n
It turns out all these folks complaining about a “damaged app” were either using Chrome or had Safari’s “safe” file handling turned off. The damaged archive wasn’t getting repaired automatically by Safari.
\n\n\n\nThe root of the problem is localization in the Sparkle framework. There are two symlinks with extended attributes (the “._” is where macOS stores things like Finder information). The intent of the symlink was to say that French Canadian is the same as French, and Portuguese is the same as Brazilian Portuguese.
\n\n\n\nSince macOS automatically makes this inference, it’s safe to just delete the scripts that create the symlinks. In your Sparkle project, find any occurrences of \u201cRun Script: Link fr_CA to fr\u201d and \u201cRun Script: Link pt to pt_BR\u201d in your Target Build Phases and remove them. I had them in “Sparkle”, “SparkleCore”, and “Installer Progress”.
\n\n\n\nAfter you build and notarize, you’ll see that your app is “valid on disk” no matter how it’s unarchived.
\n\n\n\nI’ve also submitted this information to Apple’s Product Security group. As I said in my email, the biggest problem here is expectations:
\n\n\n\n\n\n\n\nThe reason I\u2019m writing is because Safari\u2019s implementation for opening \u201csafe\u201d files is somehow bypassing a code signing check or repairing the downloaded package. The Archive Utility does not. Customer and developer expectations for unzipping archives is that they are not modified and behave the same way across all Apple products.
If you’re a Mac developer who’s using Sparkle and distributing your product via a web download, now’s a good time to check how things work in a variety of browsers. I’ve heard that we’re not the only ones affected.
\n\n\n\nAnd if you encounter a download that’s damaged because of these Sparkle symlinks, this quick fix in the Terminal will set things right:
\n\n\n\n$ ditto xScope.app xScopeFixed.app\n$ rm -rf xScope.app\n$ mv xScopeFixed.app xScope.app\n\n\n\n
The ditto
command strips the extended attributes that are causing the issue. This may, in fact, be what Safari is doing for “safe” files.
All that’s left to do now is wonder what surprises codesign
has in store for next year’s release of macOS\u2026