WWDC'19 Summary

This was a big one.

The dev conference started with a TV series announcement trailer 😑


tvOS 13

  • PS4/Xbox One controller support
  • New wallpapers

watchOS 6

  • New watch faces
  • Hourly beeping sound
  • Audiobooks, voice memos, calculator
  • Independent standalone apps
  • Audio streaming API
  • Own App Store
  • Activity trends
  • Noise app to monitor noise pollution
  • Cycle tracking
  • Health summaries
  • New bands

iOS 13

  • Speed improvements
  • Dark mode
  • Improved Safari, Mail, Notes, Reminders
  • Improved Maps with Street View (SF only as usual?)
  • ‘Just once’ location access option
  • Sign in with Apple - private social login
  • One-time email address generation
  • HomeKit secure video
  • iMessage avatar pictures
  • Enhanced Memoji with earrings, teeth, eyeshades, etc.
  • Memoji stickers
  • Improvements to photo capture and post-production
  • Rotate videos, filters and effects for videos
  • ML to remove photo dupes and choose the best of them
  • Improved Photos.app to remove clutter and let focus on important moments
  • AirPods reading and replying to messages
  • AirPods audio sharing
  • HomePod handoff
  • Siri live radio support with iHeartRadio, TuneIn, etc. 100k stations.
  • Updated CarPlay
  • CarPlay integration with 3rd party apps
  • Neural TTS - much better sounding Siri voice

iPadOS 1?

  • Much improved multitasking
  • Multi-window support for the same app
  • Improved Files.app
  • iCloud Drive folder sharing
  • SMB File sharing
  • Mass Storage Device support
  • External device support via usb-c
  • Desktop class browsing on Safari iPad
  • Download manager
  • Custom fonts
  • Working with text is now much easier
  • Apple Pencil latency down to 9ms from 20ms
  • New notes app
  • PencilKit

New Mac Pro

  • 28-core Xeon CPU
  • 12 DIMM slots
  • PCIe expansions
  • Special dual-core Vega II GPU
  • Custom designed video processing card
  • 1.4kW PSU
  • Optional wheels
  • New 6k, HDR, anti-reflective, 1000 nits display
  • Mac pro starts at $5999, new display at $3999/$4999

MacOS 10.15 Catalina

  • iTunes now broken down into Apple Music, Podcasts and Apple TV apps
  • iPhone sync moves to Finder
  • Podcasts will have content search
  • Apple TV 4k HDR playback on Mac
  • Sidecar - wireless connectivity tablets as second screens/tablets
  • Voice control
  • Improved find my Mac even when it’s offline
  • ScreenTime

Dev Tools

  • iPad apps migration tool to Mac
  • RealityKit - kit for building photorealistic scenes, includes 3d scene tools
  • ARKit3 with real-time people occlusion, motion capture
  • Real-world Minecraft
  • SwiftUI - new UI framework written in Swift - less code, live preview in sim and device (sic!)
  • Native UI framework for watchOS

It was probably the most packed WWDC I've seen. I hope all of the new stuff will be working reliably when it's out 🙂

Support indie developers

Two years ago I supported BetterTouchTool (Mac OS tool for extended keyboard/mouse/touchpad control) with a whooping $5 😃


And today after another update out of the very many updates in the last years the developer told that the older license won't work with the latest version and you'll have to buy a new one for $6.50.

I use the app only for one single feature - long time ago I assigned three finger press on the touchpad to simulate middle click which allows me open Safari links in background tabs and close them as well by 'middle-cliking' them everywhere in the tab and not only on the small, hard to target X button.

And even though I use the app for only a small single reason, I bought the new for few simple reasons:

  1. The developer was constantly updating the app through the years
  2. He never even peeped about asking more than my initial $5 for all those updates
  3. He still continues refining and improving the app, which means he still pours a lot of effort into the app
  4. The app saved me thousands of additional clicks for those simple operations I use it for

I constantly hear people complaining about how developers are trying to rip them off. But those people forget that developers are like everyone else - they need food, shelter, provide for their families. And getting $5 in two years is far from what they need to ship a quality product on a constant basis. So even when some of them switch to subscriptions (which I don't like as anyone else) I still understand their reasoning and buy not the product itself but more and more I invest for great people constantly refining them. And I wish more people would understand that as well and appreciate the hard work done to bring us, customers those refined products we use daily.

That's why please support indie devs when that support costs you that famous cup of coffee everyone's comparing it to. Because you will drink that cup and that's it. But the piece of software you buy will make your life easier day after day.

Google I/O '19 Summary

Today was Google's dev conference, and here are the main takeaways.



  • 'Full coverage' from Google News comes to search
  • Podcasts are coming to search as well
  • AR search results you can see on your phone
  • Google lens shows popular dishes in the restaurant's menu
  • Duplex not only will make reservations over the phone but now over web too
  • Assistant will now have more personalized recommendations
  • Assistant comes to Waze
  • More transparency and control over privacy
  • Incognito mode in Maps, YouTube

Android Q


  • Foldable screen support & continuity
  • 5G support
  • Offline auto-captions for videos
  • Smart Reply for IMs
  • Dark mode

Security & privacy

  • Privacy settings
  • Reminders for location usage
  • Faster security updates without device reboots

Digital well-being

  • Focus mode
  • Extended Parental Controls

‘Helpful home’ devices gathered under the Nest name

  • Easy to use
  • Personal for everyone via Google Assistant
  • High privacy

Smart home:

  • Nest Hub Max - touchscreen device to control your home aka Alexa with screen
  • Previous Nest Hub got a discount

Google Pixel 3a and 3a XL

  • Starts at $399
  • Has a headphone jack
  • AR maps navigation
  • AI enhanced camera with Night Sight
  • Unlimited Google Photos storage
  • 30h stand-by time
  • 3 years of updates


  • For researchers
  • In healthcare
  • To foresee floods

Satisfy the market or leave it

I had the first Pebble watch from a company who was the first to raise $10M on Kickstarter and then the first to raise $20M with their second watch. Few years later this seemingly popular and successful company with all their assets, intellectual property and strong interest first had few layoffs eventually was sold for just $23M to Fitbit. How did that happen?


It's not because they got fierce competition or were too early to the market - smart watches from big brands weren't there yet and the wearables sector had healthy demand already as well. The problem was that Pebble tried so hard to please their core audience - early adopters that their products never came out from serving that niche to catch the broader market.

Early adopters are a good way of taking your product off the ground: your customers are willing to pay extra to be the first to get your product even if it means dealing with the potential production delay which is common on Kickstarter. But the early adopters are ready to sacrifice their time and money for that new thing on the market and are also much more forgiving to any imperfections and roughness of product's first versions than any regular user would be.

The problem comes after. Since the early adopters is the first and smallest user group in the product's life cycle, holding on to them will never let you move forward and gather necessary resources to do so. After getting your product, early adopters can't wait you to make it better usually for no extra cost. Moreover, each backer thinks his or her new idea/feature should definitely be implemented to the next revision of the product and also retain all previous features as well.

Company's early adopters probably also won't jump into buying the next revision of their product considering the changes aren't significant enough to update and demanding unlimited support for the first version. Also paying a full extra price for something which is similar to what you have (usual iteration of products) especially with similar features is not what early adopters do.

And by sticking to the requests of those first customers, treasuring them companies continue building products which almost no one would buy - for previous customers there won't be much better/newer than what they have, and for the mass market the upcoming products without major changes will be still too finicky, techy and not very appealing. That's why companies which don't adapt to the new users by trying to satisfy the first ones go out from business just not reaching that bigger mass market group.

In this video the author explains the situation in more details with examples that include Pebble's. He also touches OnePlus which for me is the most interesting one and where I myself have proof of the early adopters theory. A friend of mine who is a strong OnePlus supporter is very judgmental about OnePlus' moves in the recent years. For him it's unacceptable for the company leaving out the headphone jack, raising the prices and overall following the industry leaders - everything opposite the brand was famous for when it was listening to their users' opinions back in the day.[1]

But at the same time my friend bought only 2 out of OnePlus' 9 models (ignore the 10th McLaren 6T) literally supporting with money his favorite company only twice! No wonder they had to adapt to the mass market and leave the core loyal fans behind. I bet Apple is successful just because except of pure specs they are selling the image of being an iPhone user so successfully that those users fuel up the company with yearly (2 years max) upgrades. This is where OnePlus is now going and where every other company which overcame the first stage should go to stay in business. Otherwise by sticking to the early adopters you won't be able to ramp up to the next stages.

  1. At some point OnePlus actually had a poll whether to include or not the headphone jack. Unfortunately the poll was ignored and the 6T was released without a headphone jack. ↩︎

Podcast generator / getid3 issue with php 7.1

Yesterday my belowed Podcast generator (where I'm hosting private audio recordings to listen to conveniently) had an issue. After uploading an episode its web interface was stuck at a blank white page and the episode was never added (though the file was actually uploaded).


After looking into nginx's error logs (nano /var/log/nginx/error.log on Ubuntu) I found this PHP Error:
Fatal error: Uncaught Error: [] operator not supported for strings in getid3/module.audio.mp3.php(1088): getid3_mp3->decodeMPEGaudioHeader

Previously, running all the same on PHP 7.0 had no issues whatsoever. But when I moved the podcast generator to another server, it had PHP 7.1 and that what caused the issue.

Thankfully I was able to overcome it by updating all the files in the getid3/ folder of the web app with a newer version of getid3 (responsible for generating id3 tags from audiofiles). So if you have the problem with podcast generator or getid3 in any other app, now you know how to fix it 🙂

Tracking AVPlayer starting stream playback

If you ever worked with streaming audio on iOS you definitely used AVAudioPlayer or AVPlayer as one of the low-hanging and robust options provided by Apple.


Outside of the stellar performance and versatility the problem with both of them is a weak delegation system, especially for medium/pro use. It's true that you can go far with AVPlayer and even use its basic status changes to react to its behavior but they have few limitations:

  1. You actually have to work with them via KVO (Key-Value Observation) which is not ideal
  2. KVO still doesn't give you answers to everything you need

I had this basic need for a while: do some stuff in the app once the AVPlayer starts playing music from a stream. KVO'ing 'status', 'rate' and 'currentItem.status' was advised everywhere but none of them worked properly. And relying on AVPlayer's 'readyToPlay' status was incorrect as well - the player changes its status to it as soon as possible and not when actually starting playing the stream.

Thankfully I got to know about another key to look after, the 'currentItem.loadedTimeRanges'. And here's how you use it:

First, you add the observer (and then don't forget to remove it at the end of the player's lifecycle!):

avPlayer.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)

And then you just observe it!

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if object as? AVPlayer === avPlayer && keyPath == "currentItem.loadedTimeRanges" {
            if let timeRanges = avPlayer.currentItem?.loadedTimeRanges, timeRanges.count > 0 {
                let timeRange = timeRanges.first as! CMTimeRange
                let currentBufferDuration = CMTimeGetSeconds(CMTimeAdd(timeRange.start, timeRange.duration))
                let duration = avPlayer.currentItem!.asset.duration
                let seconds = CMTimeGetSeconds(duration)
                if currentBufferDuration > 2 || currentBufferDuration == seconds {

Next the player calls its delegate when the stream actually started playing. Neat! 🙂

How to search in Firebase

In case you've been wondering (I was) how to seach for stuff in Firebase's Cloud Firestore I found it randomly today and would like to share it:


How to increase upload file size limit

I'm using a self-hosted podcast web app (PodcastGenerator) to serve and listen audiobooks in Overcast with Smart Speed and position tracking. In order to get new books in my podcast feed I upload them via the built-in web admin panel. But since the audiobook files can be as large as 200-300MB, uploading them via a browser hits the default upload file limit of both Nginx (my http server) and PHP (which my podcast web app uses) pretty soon.


In order to increase the upload file size limit, do this:

  1. Edit Nginx's config
    sudo nano /etc/nginx/nginx.conf
    and add client_max_body_size 2000M; into the 'http' block
    Save and exit

  2. Edit php's config
    sudo nano /etc/php/7.1/fpm/php.ini (or track down your php.ini file by creating a *.php file with contents and run it)
    Find and change upload_max_filesize and post_max_size as well to = 2000M

  3. Finally restart Nginx (or your http server) via
    service nginx reload
    and also you can restart php to be safe via
    service php7.0-fpm restart

All my commands are meant for Ubuntu I'm running, for your OS you'll have use your own alternatives.

Happy uploading! 🙂

It's not worth securing in-app purchases

Few years ago, while working on one iOS app we had a problem with in-app piracy. At that time there were and now there are several ways of keeping track of users' purchases on your own server and validate each of them to stop pirates of using your app's unlockable features with their faked unauthorized purchases.


But I remember it was a hassle to build such a validating system. And I also remember reading about even bigger companies discontining their own billing systems like that because it was not worth the hassle for dealing and supporting 95% fair users and their purchases because of 5% scammers.

In order to hack in-apps, your iPhone has to be jailbroken. Then you can use a manual like this to get an in-app for free. The latest info on the amount of jailbroken iPhones I found was from the end of 2016 with only 0.4% of iOS devices being jailbroken which might have the potential to hack anything. From what I’ve read and heard over the years that percentage may be even lower now as less and less people jailbreak, because the main reason for it (I can confirm myself) was to get new functionality. And with the latest few iOS updates we got so much new stuff, there are almost no reasons left to jailbreak, at least not for the new features.

iOS devs still can pass receipts and validate them on servers, but what would be the main reason for it? Receipts are passed and then matched for non-consumable purchases: the user unlocked a part of the functionality once and then when the user restores it, his receipt is matched with the receipt stored on the server to avoid unauthorized functionality unlocks. So if someone can fake unlocking non-consumables you either can build a wall around it... or just give it away. If a person tries to hack your purchase - he's definitely not going to buy it, so just embrace it! Replace a mandatory in-app with free with ads model, or offer even more compelling features in your app and introduce non-hackable subscriptions!

If you sell consumables - even better, there's even less reason to secure them. A person who buys a consumable uses it right away, essentially not leaving much to himself anyway.

As for Android - that’s another story. On Android I found at least one, two and three articles on ways how to hack in apps even without root (analog of jailbreaking).

Fix AirPrint -9923 scanner error on MacOS Mojave

After upgrading to MacOS Mojave last autumn I actually had no problem using my Brother DCP-L2532DW via AirPrint. But everything changed when 10.14.3 rolled out few weeks ago.


My Mac did see the printer on the network but when I tried to scan it gave me the -9923 error and obviously nothing happened afterwards.

The solution was actually simple: to turn off IPv6 in my printer's settings, switching it to IPv4 only. If your printer doesn't have settings, try adjusting your router's settings to disable IPv6 and have each wireless devices (including the printer) to obtain an IPv4-only IP address.

Now I wonder when IPv6 becomes reliable enough for regular household items 🙂