Quantcast
Channel: NSHipster
Viewing all 71 articles
Browse latest View live

Secret Management on iOS

$
0
0

One of the great unsolved questions in iOS development is,“How do I store secrets securely on the client?”

Baked into this question is the assumption that, without taking proper measures, those secrets will be leaked in one way or another — either in source revision control (GitHub and the like), from analysis tools running on .ipa files distributed through the App Store, or some other way.

Although credential security is often a second-tier concern at best, there’s a large and growing body of academic work that compels us, as developers, to take these threats seriously.

For instance, researchers at North Carolina State Universityfound that thousands of API keys, multi-factor secrets, and other sensitive credentials are leaked every day on GitHub.(Meli, McNiece, & Reaves, 2019) Another paper published in 2018found SDK credential misuse in 68 out of a sample of 100 popular iOS apps.(Wen, Li, Zhang, & Gu, 2018)

This week on NSHipster, we’re shamelessly invoking a meme from 2017 to disclose progressively more sophisticated countermeasures for keeping your secrets safe.

If your app is among the myriad others out there with an embedded Twitter access token, Stripe API key, AWS Key ID, or any other manner of credential, read on and expand your mind.


🧠 Normal Brain Hard-Code Secrets in Source Code

Imagine an app that communicates with a web application that requires an API key (a secret) to be attached to outbound requests for purposes of authentication. This integration could be managed entirely by a third-party framework, or it might be brokered with a URLSession you set up in AppDelegate. In either case, there’s a question of how and where to store the required API key.

Let’s consider the consequences of embedding the secret in code as a string literal:

enumSecrets{staticletapiKey="6a0f0731d84afa4082031e3a72354991"}

When you archive your app for distribution, all traces of this code appear to vanish. The act of compilation transforms human-readable text into machine-executable binary, leaving no trace of the secret… right?

Not quite.

Using a reverse-engineering tool like Radare2, that secret is plainly visible from inside the compiled executable. You can prove this to yourself with your own project by selecting “Generic iOS Device” in the active scheme in your own project, creating an archive (Product > Archive), and inspecting the resulting .xcarchive bundle:

$ r2 ~/Developer/Xcode/Archives/.xcarchive/Products/Applications/Swordfish.app/Swordfish[0x1000051fc]> iz[Strings]
        Num Paddr      Vaddr      Len Size Section  Type  String
        000 0x00005fa0 0x100005fa0  30  31 (3.__TEXT.__cstring) ascii _TtC9Swordfish14ViewController
        001 0x00005fc7 0x100005fc7  13  14 (3.__TEXT.__cstring) ascii @32@0:8@16@24
        002 0x00005fe0 0x100005fe0  36  37 (3.__TEXT.__cstring) ascii 6a0f0731d84afa4082031e3a72354991

While a typical user downloading your app from the App Store would never even think to try opening the .ipa on their device, there are plenty of individuals and organizations whomst’d’ve carved out a niche in the mobile app industry analyzing app payloads for analytics and security purposes. And even if the majority of these firms are above-board, there’s likely to be a few bots out there combing through apps just to pick out hard-coded credentials.

Much of that is just conjecture, but here’s what we know for sure:

If you hard-code secrets in source code, they live forever in source control — and forever’s a long time when it comes to keeping a secret. All it takes is one misconfiguration to your repo permissions or a data breach with your service provider for everything to be out in the open. And as one human working with many other humans, chances are good that someone, at some point, will eventually mess up.

Not to put too fine a point on it, but:

To quote Benjamin Franklin:

If you would keep your secret from an enemy, tell it not to a friend.

🧠 Big Brain Store Secrets in Xcode Configuration and Info.plist

In a previous article, we described how you could use .xcconfig files to externalize configuration from codeaccording to 12-Factor App best practices. When reified through Info.plist, these build settings can serve as makeshift.env files:

// Development.xcconfig
        API_KEY = 6a0f0731d84afa4082031e3a72354991
        // Release.xcconfig
        API_KEY = d9b3c5d63229688e4ddbeff6e1a04a49

So long as these files are kept out of source control, this approach solves the problem of leaking credentials in code. And at first glance, it would appear to remediate our concern about leaking to static analysis:

If we fire up our l33t hax0r tools and attenuate them to our executable as before (izz to list strings binary,~6a0f07 to filter using the first few characters of our secret), our search comes up empty:

$ r2 ~/Developer/Xcode/Archives/.xcarchive/Products/Applications/Swordfish.app/Swordfish[0x100005040]> izz~6a0f[0x100005040]>

But before you start celebrating, you might want to reacquaint yourself with the rest of the app payload:

$ tree /Swordfish.app├── Base.lproj
        │   ├── LaunchScreen.storyboardc│   │   ├── 01J-lp-oVM-view-Ze5-6b-2t3.nib│   │   ├── Info.plist
        │   │   └── UIViewController-01J-lp-oVM.nib│   └── Main.storyboardc
        │       ├── BYZ-38-t0r-view-8bC-Xf-vdC.nib│       ├── Info.plist
        │       └── UIViewController-BYZ-38-t0r.nib├── Info.plist
        ├── PkgInfo├── Swordfish
        ├── _CodeSignature│   └── CodeResources└── embedded.mobileprovision
        

That Info.plist file we used to store our secrets? Here it is, packaged right alongside our executable.

By some measures, this approach might be considered less secure than writing secrets in code, because the secret is accessible directly from the payload, without any fancy tooling.

$ plutil -p/Swordfish.app/Info.plist{  "API_KEY" =>"6a0f0731d84afa4082031e3a72354991"

There doesn’t seem to be a way to configure the environment when a user launches your app on their device. And as we saw in the previous section, using Info.plist as a clearinghouse for secrets qua build settings is also a no-go.

But maybe there’s another way we could capture our environment in code…

🧠 Cosmic Brain Obfuscate Secrets Using Code Generation

A while back, we wrote about GYB, a code generation tool used in the Swift standard library. Although that article focuses on eliminating boilerplate code, GYB’s metaprogramming capabilities go far beyond that.

For example, we can use GYB to pull environment variables into generated code:

$API_KEY=6a0f0731d84afa4082031e3a72354991 \
        gyb --line-directive''<<"EOF"%{ import os }%let apiKey = "${os.environ.get('API_KEY')}"EOF
        let apiKey = "6a0f0731d84afa4082031e3a72354991"

Generating (and not committing) Swift files from GYB while pulling secrets from environment variables solves the problem of leaking credentials in source code, but it fails to guard against common static analysis tools. However, we can use a combination of Swift and Python code (via GYB) to obfuscate secrets in a way that’s more difficult to reverse-engineer.

For example, here’s an (admittedly crude) solution that implements an XOR cipher using a salt that’s generated randomly each time:

// Secrets.swift.gyb%{importosdefchunks(seq,size):return(seq[i:(i+size)]foriinrange(0,len(seq),size))defencode(string,cipher):bytes=string.encode("UTF-8")return[ord(bytes[i])^cipher[i%len(cipher)]foriinrange(0,len(bytes))]}%enumSecrets{privatestaticletsalt:[UInt8]=[%{salt=[ord(byte)forbyteinos.urandom(64)]}%%forchunkinchunks(salt,8):${"".join(["0x%02x, "%byteforbyteinchunk])}%end]staticvarapiKey:String{letencoded:[UInt8]=[%forchunkinchunks(encode(os.environ.get('API_KEY'),salt),8):${"".join(["0x%02x, "%byteforbyteinchunk])}%end]returndecode(encoded,salt:cipher)}}

Secrets are pulled from the environment and encoded by a Python function before being included in the source code as [UInt8] array literals. Those encoded values are then run through an equivalent Swift function to retrieve the original value without exposing any secrets directly in the source.

The resulting code looks something like this:

// Secrets.swiftenumSecrets{privatestaticletsalt:[UInt8]=[0xa2,0x00,0xcf,,0x06,0x84,0x1c,]staticvarapiKey:String{letencoded:[UInt8]=[0x94,0x61,0xff,0x15,0x05,0x59,]returndecode(encoded,cipher:salt)}staticfuncdecode(_encoded:[UInt8],cipher:[UInt8])->String{String(decoding:encoded.enumerated().map{(offset,element)inelement^cipher[offset%cipher.count]},as:UTF8.self)}}Secrets.apiKey// "6a0f0731d84afa4082031e3a72354991"

While security through obscurity may be theoretically unsound,it can be an effective solution in practice.(Wang, Wu, Chen, & Wei, 2018)

As the old saying goes:

You don’t have to outrun a bear to be safe. You just have to outrun the guy next to you.

🧠 Galaxy Brain Don’t Store Secrets On-Device

No matter how much we obfuscate a secret on the client, it’s only a matter of time before the secret gets out. Given enough time and sufficient motivation, an attacker will be able to reverse-engineer whatever you throw their way.

The only true way to keep secrets in mobile apps is to store them on the server.

Following this tack, our imagined plot shifts from an Oceans 11-style heist movie to a high-stakes Behind Enemy Lines escort mission movie.(Your mission: Transfer a payload from the server and store it in theSecure Enclave without an attacker compromising it.)

If you don’t have a server that you can trust, there are a few Apple services that can serve as a transport mechanism:

Then again, once your secret reaches the Secure Enclave, it’ll be sent right back out with the next outbound request for which it’s used. It’s not enough to get it right once; security is a practice.

Or put another way:

A secret in Secure Enclave is safe, but that is not what secrets are for.

🧠 Universe Brain Client Secrecy is Impossible

There’s no way to secure secrets stored on the client. Once someone can run your software on their own device, it’s game over.

And maintaining a secure, closed communications channel between client and server incurs an immense amount of operational complexity — assuming it’s possible in the first place.

Perhaps Julian Assange said it best:

The only way to keep a secret is to never have one.


Rather than looking at client secret management as a problem to be solved, we should see it instead as an anti-pattern to be avoided.

What is an API_KEY other than an insecure, anonymous authentication mechanism, anyway? It’s a blank check that anyone can cash, a persistent liability the operational integrity of your business.

Any third-party SDK that’s configured with a client secret is insecure by design. If your app uses any SDKs that fits this description, you should see if it’s possible to move the integration to the server. Barring that, you should take time to understand the impact of a potential leak and consider whether you’re willing to accept that risk. If you deem the risk to be substantial, it wouldn’t be a bad idea to look into ways to obfuscate sensitive information to reduce the likelihood of exposure.


Restating our original question:“How do I store secrets securely on the client?”

Our answer:“Don’t (but if you must, obfuscation wouldn’t hurt).”


References
  1. Meli, M., McNiece, M. R., & Reaves, B. (2019). How Bad Can It Git? Characterizing Secret Leakage in Public GitHub Repositories. In Proceedings of the NDSS Symposium 2019. Retrieved from https://www.ndss-symposium.org/ndss-paper/how-bad-can-it-git-characterizing-secret-leakage-in-public-github-repositories/
  2. Wen, H., Li, J., Zhang, Y., & Gu, D. (2018). An Empirical Study of SDK Credential Misuse in iOS Apps. In 2018 25th Asia-Pacific Software Engineering Conference (APSEC) (pp. 258–267). https://doi.org/10.1109/APSEC.2018.00040
  3. Wang, P., Wu, D., Chen, Z., & Wei, T. (2018). Protecting Million-User iOS Apps with Obfuscation: Motivations, Pitfalls, and Experience. In 2018 IEEE/ACM 40th International Conference on Software Engineering: Software Engineering in Practice Track (ICSE-SEIP) (pp. 235–244).

KeyValuePairs

$
0
0

Cosmologies seek to create order by dividing existence into discrete, interdependent parts. Thinkers in every society throughout history have posited various arrangements — though Natural numbers being what they are, there are only so many ways to slice the ontological pie.

There are dichotomies like陰陽(yīnyáng)(): incontrovertible and self-evident (albeit reductive). There are trinities, which position man in relation to heaven and earth. One might divide everything into four,like the ancient Greeks with the elements of earth, water, air, and fire. Or you could carve things into five,like the Chinese withWood(),Fire(huǒ),Earth(),Metal(jīn), andWater(shuǐ). Still not satisfied? Perhaps the eight-part八卦(bāguà) will provide the answers that you seek:

Trigram
Nature(Heaven)(Lake / Marsh)(Fire)(Thunder)(Wind)(Water)(Mountain)(Ground)

Despite whatever galaxy brain opinion we may have about computer science, the pragmatic philosophy of day-to-day programming more closely aligns with a mundane cosmology; less imago universi, moreじゃんけんjon-ken(Rock-Paper-Scissors✊🤚✌️).

For a moment, ponder the mystical truths of fundamental Swift collection types:

Arrays are ordered collections of values.
Sets are unordered collections of unique values.
Dictionaries are unordered collections of key-value associations.The Book of Swift

Thus compared to the pantheon ofjava.util collections or std containers, Swift offers a coherent coalition of three. Yet, just as we no longer explain everyday phenomena strictly in terms ofhumors or æther, we must reject this formulation. Such a model is incomplete.

We could stretch our understanding of sets to incorporateOptionSet (as explained in a previous article), but we’d be remiss to try and shoehorn Range and ClosedRange into the same bucket as Array— and that’s to say nothing of the panoply of Swift Collection Protocols(an article in dire need of revision).

This week on NSHipster, we’ll take a look at KeyValuePairs, a small collection type that challenges our fundamental distinctions betweenArray, Set, and Dictionary. In the process, we’ll gain a new appreciation and a deeper understanding of the way things work in Swift.


KeyValuePairs is a structure in the Swift standard library that — surprise, surprise— represents a collection of key-value pairs.

structKeyValuePairs<Key,Value>:ExpressibleByDictionaryLiteral,RandomAccessCollection{typealiasElement=(key:Key,value:Value)typealiasIndex=InttypealiasIndices=Range<Int>typealiasSubSequence=Slice<KeyValuePairs>}

This truncated declaration highlights the defining features of KeyValuePairs:

  • Its ability to be expressed by a dictionary literal
  • Its capabilities as a random-access collection

Dictionary Literals

Literals allow us to represent values directly in source code, and Swift is rather unique among other languages by extending this functionality to our own custom types through protocols.

A dictionary literal represents a value as mapping of keys and values like so:

["key":"value"]

However, the term“dictionary literal” is a slight misnomer, since a sequence of key-value pairs — not a Dictionary— are passed to the ExpressibleByDictionaryLiteral protocol’s required initializer:

protocolExpressibleByDictionaryLiteral{associatedtypeKeyassociatedtypeValueinit(dictionaryLiteralelements:(Key,Value)...)}

This confusion was amplified by the existence of a DictionaryLiteral type, which was only recently renamed to KeyValuePairs in Swift 5. The name change served to both clarify its true nature and bolster use as a public API (and not some internal language construct).

You can create a KeyValuesPairs object with a dictionary literal (in fact, this is the only way to create one):

letpairs:KeyValuePairs<String,String>=["木":"wood","火","fire","土":"ground""金":"metal""水":"water"]

Random-Access Collections

KeyValuePairs conforms to RandomAccessCollection, which allows its contents to be retrieved by (in this case, Int) indices. In contrast to Array,KeyValuePairs doesn’t conform to RangeReplaceableCollection, so you can’t append elements of remove individual elements at indices or ranges. This narrowly constrains KeyValuePairs, such that it’s effectively immutable once initialized from a dictionary literal.

These functional limitations are the key to understanding its narrow application in the standard library.

KeyValuePairs in the Wild

Across the Swift standard library and Apple SDK,KeyValuePairs are found in just two places:

structMirror{init<Subject>(_subject:Subject,children:KeyValuePairs<String,Any>,displayStyle:DisplayStyle?=nil,ancestorRepresentation:AncestorRepresentation=.generated)}typealiasRGBA=UInt32typealiasRGBAComponents=(UInt8,UInt8,UInt8,UInt8)letcolor:RGBA=0xFFEFD5FFletmirror=Mirror(color,children:["name":"Papaya Whip","components":(0xFF,0xEF,0xD5,0xFF)asRGBAComponents],displayStyle:.struct)mirror.children.first(where:{(label,_)inlabel=="name"})?.value// "Papaya Whip"
@dynamicCallablestructKeywordCallable{funcdynamicallyCall(withKeywordArgumentsargs:KeyValuePairs<String,Int>)->Int{returnargs.count}}letobject=KeywordCallable()object(a:1,2)// desugars to `object.dynamicallyCall(withKeywordArguments: ["a": 1, "": 2])`

On both occasions,KeyValuePairs is employed as an alternative to [(Key, Value)] to enforce restraint by the caller. Without any other public initializers,KeyValuePairs can only be constructed from dictionary literals, and can’t be constructed dynamically.

Working with KeyValuePairs Values

If you want to do any kind of work with a KeyValuePairs, you’ll first want to convert it into a conventional Collection type — either Array or Dictionary.

Converting to Arrays

KeyValuePairs is a Sequence, by virtue of its conformance to RandomAccessCollection (and therefore Collection). When we pass it to the corresponding Array initializer, it becomes an array of its associated Element type ((Key, Value)).

letarrayOfPairs:[(Key,Value)]=Array(pairs)

Though, if you just want to iterate over each key-value pair, it’s conformance to Sequence means that you can pass it directly to a for-in loop:

for(key,value)inpairs{}

You can always create an Array from a KeyValuePairs object, but creating Dictionary is more complicated.

Converting to Dictionaries

There are four built-in types that conform to ExpressibleByDictionaryLiteral:

  • Dictionary
  • NSDictionary
  • NSMutableDictionary
  • KeyValuePairs

Each of the three dictionary types constitutes asurjective mapping, such that every value element has one or more corresponding keys.KeyValuePairs is the odd one out: it instead maintains an ordered list of tuples that allows for duplicate key associations.

Dictionary got a number of convenient initializers in Swift 4 thanks to SE-0165(thanks, Nate!), includinginit(uniqueKeysWithValues:),init(_:uniquingKeysWith:), andinit(grouping:by)

Consider the following example that constructs a KeyValuePairs object with a duplicate key:

letpairsWithDuplicateKey:KeyValuePairs<String,String>=["天":"Heaven","澤":"Lake","澤":"Marsh",]

Attempting to pass this to init(uniqueKeysWithValues:) results in a fatal error:

Dictionary<String,Int>(uniqueKeysWithValues:Array(pairsWithDuplicateKey))// Fatal error: Duplicate values for key: '澤'

Instead, you must either specify which value to map or map

Dictionary(Array(pairsWithDuplicateKey),uniquingKeysWith:{(first,_)infirst})// ["澤": "Lake", ]Dictionary(Array(pairsWithDuplicateKey),uniquingKeysWith:{(_,last)inlast})// ["澤": "Marsh", ]Dictionary(grouping:Array(pairsWithDuplicateKey),by:{(pair)inpair.value})// ["澤": ["Lake", "Marsh"], ]

Outside of its narrow application in the standard library,KeyValuePairs are unlikely to make an appearance in your own codebase. You’re almost always better off going with a simple [(Key, Value)] tuple array.

However, the very existence of Like all cosmological exceptions,KeyValuePairs is uncomfortable — at times, even unwelcome — but it serves to expand our understanding.

Much as today’s Standard Model more closely resembles the cacophony of azoo than themusica universalis ofcelestial spheres,KeyValuePairs challenges our tripartite view of Swift collection types. But like all cosmological exceptions — though uncomfortable or even unwelcome at times — it serves to expand our understanding.

That’s indeed its key value.

bless

$
0
0

This Thursday is Thanksgiving in the United States, a holiday in which we’ll gather together to cultivate and express gratitude against a backdrop of turkey, mashed potatoes, and origin-myths of European colonialism in the Americas.

As the year — and indeed the decade— draws to a close, we’re invited to reflect on the blessings in our lives: The individuals who fill them with joy, the circumstances that bring them comfort and security, the pursuits that endow them with meaning.

…of course, it’s also a time of ritualized, over-indulgent consumption. And for the PC gamers among us, that means shutting down our Macs, booting into our Windows partitions, and settling into our long-neglected back catalogs on Steam.


So while you wait for your AAA titles to download and your Windows software updates to apply(let’s assume you’re reading this on your iPhone), we invite you to count your blessings.


This week on NSHipster: a quick look at the bless command and the macOS boot process.

But first, a quick disclaimer:


The process of booting a computer is a small miracle. Starting with nothing — “tabula rasa”— a computer incrementally runs smaller, simpler programs to load larger, more capable programs into memory, until it finally has everything it needs to run the operating system itself.

In a previous era of computers, operating systems were initialized usingBIOS firmware, which came pre-installed in systemROM. When a machine powered up, that firmware would be the first thing that the computer encountered, and it’d proceed to load machine code from the boot sector.

Nowadays, Macs and most other computers boot according to theExtensible Firmware Interface (EFI), which introduces a few levels of indirection for greater flexibility. When a machine powers on under this regime, it loads a boot manager, which is responsible for loading configuration fromnon-volatile RAM (NVRAM), including the file system paths of the loaders and kernels for the operating system. This additional step allows computers to boot into different partitions on disk and even network volumes.

The process of making a volume bootable is called blessing, and the operative command to accomplish this has been, historically,bless.

To get an overview of the blessed volumes on your machine, run bless --info:

$sudo bless --info => Blessed System File is {Preboot}//System/Library/CoreServices/boot.efi => Blessed System Folder is {Preboot}//System/Library/CoreServicesThe blessed volume in this APFS container is "/".
        No blessed APFS snapshot for this volume.

Blessing a volume on the latest Mac hardware running the latest version of macOS is an involved process, which includes (among other things):

  • Copying boot.efi to the correct file system path on the volume
  • Writing information to the volume header to designate it as being bootable
  • Setting the efi-boot-device and efi-boot-device-data variables to designate the volume as the next startup disk

To illustrate how much complexity is baked into blessing a volume, let’s focus on the first of these tasks:

Copy boot.efi? Easy: cp boot.efi "/Volumes/Macintosh HD".
(It’s a UNIX system! I know this!)

Well, not quite.

On a Mac booted from an APFS disk,boot.efi is in a special preboot volume (/dev/disk1s2), at a path containing a generated UUID.HFS+ is rather simpler by comparison, since the boot loader has a hard-coded located at/System/Library/CoreServices/boot.efi. That is, unless, the disk is encrypted with FileVault full-disk encryption or is part of a RAID array that requires additional drivers. And all of this is to say nothing of Secure Boot and the Apple T2 Security Chip, which comes standard on the iMac Pro as well as Mac Mini, MacBook Air, and MacBook Pro computers since 2018.

But in case you remain undeterred, and really want to avoid the inconvenience of opening up System Preferences, here’s the invocation you seek:

$sudo bless -mount /Volumes/BOOTCAMP -setBoot--nextonlyCould not set boot device property: 0xe00002e2

However, that hasn’t worked since the advent of System Integrity Protection (SIP) in macOS 10.11.

A suggested workaround invokes systemsetup as a replacement for bless.

The systemsetup command is similar to System Preferences, in that it can be used to set machine-wide preferences, including: date and time settings, remote login and Apple Events and the behavior of the physical power button, as well as when a computer should go to sleep and what should happen after a power failure.

The systemsetup invocation to set the startup disk goes a little something like this:

$sudo systemsetup -setstartupdisk /Volume/BOOTCAMP/WindowsSetting startup disk not allowed while System Integrity Protection is enabled.

But that, too, doesn’t seem to work on recent versions of macOS without disabling SIP(and for the vast majority of folks, there’s never a good reason to do this).


So by now, Windows has finished updating itself (maybe), the game is 100% downloaded and ready to launch, and you’re left wondering “What’s the point of this article?”

To that, dear reader, I’d first say that there are far, far worse things than being pointless(or Point-Free, as it were). But if you’re still searching for a takeaway, here it is:

You probably won’t ever have an occasion to use the bless command(or systemsetup, for that matter).

With every design decision, software vendors perform a balancing act between security and convenience. For most people, the new security features in recent versions of macOS and new Apple hardware make them undeniably safer, usually without any perceivable downside. Yet we, as developers, especially those of us with a hacker mentality, may see the Mac’s trajectory towards a closed, iOS-like security model as a mixed blessing — a curse, even.

If that’s the case for you (and even if it isn’t), then please allow us to offer another helping of article-concluding takeaway:

Take time to be thankful for all that you have, and seek to be present for those around you this holiday season.

AVSpeechSynthesizer

$
0
0

Though we’re a long way off from Hal orHer, we shouldn’t forget about the billions of people out there for us to talk to.

Of the thousands of languages in existence, an individual is fortunate to gain a command of just a few within their lifetime. And yet, over several millennia of human co-existence, civilization has managed to make things work (more or less) through an ad-hoc network of interpreters, translators, scholars, and children raised in the mixed linguistic traditions of their parents. We’ve seen that mutual understanding fosters peace and that conversely, mutual unintelligibility destabilizes human relations.

It’s fitting that the development of computational linguistics should coincide with the emergence of the international community we have today. Working towards mutual understanding, intergovernmental organizations like the United Nations and European Union have produced a substantial corpus of parallel texts, which form the foundation of modern language translation technologies.

Computer-assisted communication between speakers of different languages consists of three tasks:transcribing the spoken words into text,translating the text into the target language, and synthesizing speech for the translated text.

This article focuses on how iOS handles the last of these: speech synthesis.


Introduced in iOS 7 and available in macOS 10.14 Mojave,AVSpeechSynthesizer produces speech from text.

To use it, create an AVSpeechUtterance object with the text to be spoken and pass it to the speakUtterance(_:) method:

importAVFoundationletstring="Hello, World!"letutterance=AVSpeechUtterance(string:string)letsynthesizer=AVSpeechSynthesizer()synthesizer.speakUtterance(utterance)

You can use the adjust the volume, pitch, and rate of speech by configuring the corresponding properties on the AVSpeechUtterance object.

When speaking, a synthesizer can be paused on the next word boundary, which makes for a less jarring user experience than stopping mid-vowel.

synthesizer.pauseSpeakingAtBoundary(.word)

Supported Languages

Mac OS 9 users will no doubt have fond memories of the old system voices — especially the novelty ones, like Bubbles, Cellos, Pipe Organ, and Bad News.

In the spirit of quality over quantity, each language is provided a voice for each major locale region. So instead of asking for “Fred” or “Markus”, AVSpeechSynthesisVoice asks for en-US or de-DE.

VoiceOver supports over 30 different languages. For an up-to-date list of what’s available, call AVSpeechSynthesisVoice class method speechVoices() or check this support article.

By default,AVSpeechSynthesizer will speak using a voice based on the user’s current language preferences. To avoid sounding like a stereotypical American in Paris, set an explicit language by selecting a AVSpeechSynthesisVoice.

letstring="Bonjour!"letutterance=AVSpeechUtterance(string:string)utterance.voice=AVSpeechSynthesisVoice(language:"fr")

Many APIs in foundation and other system frameworks use ISO 681 codes to identify languages.AVSpeechSynthesisVoice, however, takes anIETF Language Tag, as specified BCP 47 Document Series. If an utterance string and voice aren’t in the same language, speech synthesis fails.

Not all languages are preloaded on the device, and may have to be downloaded in the background before speech can be synthesized.

Customizing Pronunciation

A few years after it first debuted on iOS,AVUtterance added functionality to control the pronunciation of particular words, which is especially helpful for proper names.

To take advantage of it, construct an utterance using init(attributedString:) instead of init(string:). The initializer scans through the attributed string for any values associated with the AVSpeechSynthesisIPANotationAttribute, and adjusts pronunciation accordingly.

importAVFoundationlettext="It's pronounced 'tomato'"letmutableAttributedString=NSMutableAttributedString(string:text)letrange=NSString(string:text).range(of:"tomato")letpronunciationKey=NSAttributedString.Key(rawValue:AVSpeechSynthesisIPANotationAttribute)// en-US pronunciation is /tə.ˈme͡ɪ.do͡ʊ/mutableAttributedString.setAttributes([pronunciationKey:"tə.ˈme͡ɪ.do͡ʊ"],range:range)letutterance=AVSpeechUtterance(attributedString:mutableAttributedString)// en-GB pronunciation is /tə.ˈmɑ.to͡ʊ/... but too bad!utterance.voice=AVSpeechSynthesisVoice(language:"en-GB")letsynthesizer=AVSpeechSynthesizer()synthesizer.speak(utterance)

Beautiful. 🍅

Of course, this property is undocumented at the time of writing, so you wouldn’t know that the IPA you get from Wikipedia won’t work correctly unless you watched this session from WWDC 2018.

To get IPA notation that AVSpeechUtterance can understand, you can open the Settings app, navigate to Accessibility > VoiceOver > Speech > Pronunciations, and… say it yourself!

Speech Pronunciation Replacement

Hooking Into Speech Events

One of the coolest features of AVSpeechSynthesizer is how it lets developers hook into speech events. An object conforming to AVSpeechSynthesizerDelegate can be called when a speech synthesizer starts or finishes, pauses or continues, and as each range of the utterance is spoken.

For example, an app — in addition to synthesizing a voice utterance — could show that utterance in a label, and highlight the word currently being spoken:

varutteranceLabel:UILabel!// MARK: AVSpeechSynthesizerDelegateoverridefuncspeechSynthesizer(_synthesizer:AVSpeechSynthesizer,willSpeakRangeOfSpeechStringcharacterRange:NSRange,utterance:AVSpeechUtterance){self.utterranceLabel.attributedText=attributedString(from:utterance.speechString,highlighting:characterRange)}

AVSpeechSynthesizer Example

Check out this Playground for an example of live text-highlighting for all of the supported languages.


Anyone who travels to an unfamiliar place returns with a profound understanding of what it means to communicate. It’s totally different from how one is taught a language in High School: instead of genders and cases, it’s about emotions and patience and clinging onto every shred of understanding. One is astounded by the extent to which two humans can communicate with hand gestures and facial expressions. One is also humbled by how frustrating it can be when pantomiming breaks down.

In our modern age, we have the opportunity to go out in a world augmented by a collective computational infrastructure. Armed with AVSpeechSynthesizer and myriad other linguistic technologies on our devices, we’ve never been more capable of breaking down the forces that most divide our species.

If that isn’t universe-denting, then I don’t know what is.

Swift API Availability

$
0
0

Code exists in a world of infinite abundance. Whatever you can imagine is willed into being — so long as you know how to express your desires.

As developers, we know that code will eventually be compiled into software, and forced to compete in the real-world for allocation of scarce hardware resources. Though up until that point, we can luxuriate in the feeling of unbounded idealism… well, mostly. For software is not a pure science, and our job — in reality — is little more than shuttling data through broken pipes between leaky abstractions.

This week on NSHipster, we’re exploring a quintessential aspect of our unglamorous job: API availability. The good news is that Swift provides first-class constructs for dealing with these real-world constraints by way of @available and #available. However, there are a few nuances to these language features, of which many Swift developers are unaware. So be sure to read on to make sure that you’re clear on all the options available to you.


@available

In Swift, you use the @available attribute to annotate APIs with availability information, such as“this API is deprecated in macOS 10.15” or “this API requires Swift 5.1 or higher”. With this information, the compiler can ensure that any such APIs used by your app are available to all platforms supported by the current target.

The @available attribute can be applied to declarations, including top-level functions, constants, and variables, types like structures, classes, enumerations, and protocols, and type members — initializers, class deinitializers, methods, properties, and subscripts.

Platform Availability

When used to designate platform availability for an API, the @available attribute can take one or two forms:

  • A “shorthand specification” that lists minimum version requirements for multiple platforms
  • An extended specification that can communicate additional details about availability for a single platform

Shorthand Specification

@available(platformversion, platform version ..., *)
  • A platform;iOS,macCatalyst,macOS / OSX,tvOS, orwatchOS, or any of those with ApplicationExtension appended(e.g. macOSApplicationExtension).
  • A version number consisting of one, two, or three positive integers, separated by a period (.), to denote the major, minor, and patch version.
  • Zero or more versioned platforms in a comma-delimited (,) list.
  • An asterisk (*), denoting that the API is unavailable for all other platforms. An asterisk is always required for platform availability annotations to handle potential future platforms (such as the long-rumored iDishwasherOS).

For example, new, cross-platform APIs introduced at WWDC 2019 might be annotated as:

@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)

Shorthand specifications are a convenient way to annotate platform availability. But if you need to communicate more information, such as when or why an API was deprecated or what should be used as a replacement, you’ll want to opt for an extended specification instead.

Introduced, Deprecated, Obsoleted, and Unavailable

// With introduced, deprecated, and/or obsoleted@available(platform | *, introduced: version, deprecated: version, obsoleted: version, renamed: "...", message: "...")// With unavailable@available(platform | *, unavailable , renamed: "...", message: "...")
  • A platform, same as before, or an asterisk (*) for all platforms.
  • Either introduced, deprecated, and/or obsoleted
    • An introducedversion, denoting the first version in which the API is available
    • A deprecatedversion, denoting the first version when using the API generates a compiler warning
    • An obsoletedversion, denoting the first version when using the API generates a compiler error
  • …or unavailable, which causes the API to generate a compiler error when used
  • renamed with a keypath to another API; when provided, Xcode provides an automatic “fix-it”
  • A message string to be included in the compiler warning or error

Unlike shorthand specifications, this form allows for only one platform to be specified. So if you want to annotate availability for multiple platforms, you’ll need stack @available attributes. For example, here’s how the previous shorthand example can be expressed in multiple attributes:

@available(macOS, introduced: 10.15)@available(iOS, introduced: 13)@available(watchOS, introduced: 6)@available(tvOS, introduced: 13)

Apple SDK frameworks make extensive use of availability annotations to designate new and deprecated APIs with each release of iOS, macOS, and other platforms.

For example, iOS 13 introduces a newUICollectionViewCompositionalLayout class. If you jump to its declaration by holding command () and clicking on that symbol in Xcode, you’ll see that it’s annotated with @available:

@available(iOS 13.0, *)openclassUICollectionViewCompositionalLayout:UICollectionViewLayout{}

This @available attribute tells the compiler thatUICollectionViewCompositionalLayout can only be called on devices running iOS, version 13.0 or later(with caveats; see note below).

If your app targets iOS 13 only, then you can use UICollectionViewCompositionalLayout without any special consideration. If, however, your deployment target is set below iOS 13 in order to support previous versions of iOS (as is the case for many existing apps), then any use of UICollectionViewCompositionalLayout must be conditionalized.

More on that in a moment.

Swift Language Availability

Your code may depend on a new language feature of Swift, such as property wrappers or default enumeration case associated values— both new in Swift 5.1. If you want to support development of your app with previous versions of Xcode or for your Swift package to work for multiple Swift compiler toolchains, you can use the @available attribute to annotate declarations containing new language features.

When used to designate Swift language availability for an API, the @available attribute takes the following form:

@available(swift version)

Unlike platform availability, Swift language version annotations don’t require an asterisk (*); to the compiler, there’s one Swift language, with multiple versions.

#available

In Swift, you can predicate if, guard, and while statements with an availability condition,#available, to determine the availability of APIs at runtime. Unlike the @available attribute, an #available condition can’t be used for Swift language version checks.

The syntax of an #available expression resembles that of an @available attribute:

if | guard | while#available(platformversion, platform version ..., *)

Now that we know how APIs are annotated for availability, let’s look at how to annotate and conditionalize code based on our platform and/or Swift language version.


Working with Unavailable APIs

Similar to how, in Swift,thrown errors must be handled or propagated, use of potentially unavailable APIS mist be either annotated and conditionalize code.

When you attempt to call an API that is unavailable for at least one of your supported targets, Xcode will recommend the following options:

  • “Add if #available version check”
  • “Add @available attribute to enclosing declaration(suggested at each enclosing scope; for example, the current method as well as that method’s containing class)

Following our analogy to error handling, the first option is similar to prepending try to a function call, and the second option is akin to wrapping a statement within do/catch.

For example, within an app supporting iOS 12 and iOS 13, a class that subclasses UICollectionViewCompositionalLayout must have its declaration annotated with @available, and any references to that subclass would need to be conditionalized with #available:

@available(iOS 13.0, *)finalclassCustomCompositionalLayout:UICollectionViewCompositionalLayout{<#...>}funccreateLayout()->UICollectionViewLayout{if#available(iOS 13, *){returnCustomCompositionalLayout()}else{returnUICollectionViewFlowLayout()}}

Swift comprises many inter-dependent concepts, and crosscutting concerns like availability often result in significant complexity as various parts of the language interact. For instance, what happens if you create a subclass that overrides a property marked as unavailable by its superclass? Or what if you try to call a function that’s renamed on one platform, but replaced by an operator on another?

While it’d be tedious to enumerate every specific behavior here, these questions can and often do arise in the messy business of developing apps in the real world. If you find yourself wondering “what if” and tire of trial-and-error experimentation, you might instead try consulting the Swift language test suite to determine what’s expected behavior.

Alternatively, you can surmise how things work generally from Clang’s diagnostic text:

-Wavailability Diagnostic Text
warning:‘unavailable’ availability overrides all other availability information
warning:unknown platform  A  in availability macro
warning:feature cannot be 
introduced
deprecated
obsoleted
in  B  version  C  before it was
introduced
deprecated
obsoleted
in version  E; attribute ignored
warning:use same version number separators ‘_’ or ‘.’; as in ‘major[.minor[.subminor]]’
warning:availability does not match previous declaration
warning:
 
overridding
method
introduced after
deprecated before
obsoleted before
the protocol method it implements
overridden method
on  B
(C  vs.  D)
warning:
 
overridding
method cannot be unavailable on A when 
the protocol method it implements
its overridden method
 is available

Annotating Availability in Your Own APIs

Although you’ll frequently interact with @available as a consumer of Apple APIs, you’re much less likely to use them as an API producer.

Availability in Apps

Within the context of an app, it may be convenient to use @available deprecation warnings to communicate across a team that use of a view controller, convenience method, or what have you is no longer advisable.

@available(iOS, deprecated: 13, renamed: "NewAndImprovedViewController")classOldViewController:UIViewController{}classNewAndImprovedViewController:UIViewController{}

Use of unavailable or deprecated, however, are much less useful for apps; without any expectation to vend an API outside that context, you can simply remove an API outright.

Availability in Third-Party Frameworks

If you maintain a framework that depends on the Apple SDK in some way, you may need to annotate your APIs according to the availability of the underlying system calls. For example, a convenience wrapper aroundKeychain APIs would likely annotate the availability of platform-specific biometric features like Touch ID and Face ID.

However, if your APIs wrap the underlying system call in a way that doesn’t expose the implementation details, you may be able For example, an NLP library that previously delegated functionality toNSLinguisticTagger could instead useNatural Language framework when available (as determined by #available), without any user-visible API changes.

Availability in Swift Packages

If you’re writing Swift qua Swift in a platform-agnostic way and distributing that code as a Swift package, you may want to use @available to give a heads-up to consumers about APIs that are on the way out.

Unfortunately, there’s currently no way to designate deprecation in terms of the library version (the list of platforms are hard-coded by the compiler). While it’s a bit of a hack, you could communicate deprecation by specifying an obsolete / non-existent Swift language version like so:

@available(swift, deprecated: 0.0.1, message: "Deprecated in 1.2.0")funcgoing(going:Gone...){}

Working Around Deprecation Warnings

As some of us are keenly aware,it’s not currently possible to silence deprecation warnings in Swift. Whereas in Objective-C, you could suppress warnings with #pragma clang diagnostic push / ignored / pop, no such convenience is afforded to Swift.

If you’re among the l33t coders who have “hard mode” turned on (“Treat Warnings as Errors” a.k.a. SWIFT_TREAT_WARNINGS_AS_ERRORS), but find yourself stymied by deprecation warnings, here’s a cheat code you can use:

classCustomView{@available(iOS, introduced: 10, deprecated: 13, message: "😪")funcmethod(){}}CustomView().method()// "'method()' was deprecated in iOS 13: 😪"protocolIgnoringMethodDeprecation{funcmethod()}extensionCustomView:IgnoringMethodDeprecation{}(CustomView()asIgnoringMethodDeprecation).method()// No warning! 😁

As an occupation, programming epitomizes the post-scarcity ideal of a post-industrial world economy in the information age. But even so far removed from physical limitations, we remain inherently constrained by forces beyond our control. However, with careful and deliberate practice, we can learn to make use of everything available to us.

Objective-C Direct Methods

$
0
0

It’s hard to get excited when new features come to Objective-C. These days, any such improvements are in service of Swift interoperability rather than an investment in the language itself (see nullability and lightweight generics).

So it was surprising to learn aboutthis recently merged patch to Clang, which adds a new direct dispatch mechanism to Objective-C methods.

The genesis of this new language feature is unclear; the most we have to go on is an Apple-internal Radar number (2684889), which doesn’t tell us much beyond its relative age (sometime in the early ’00s, by our estimation). Fortunately, the feature landed with enough documentation and test coverage to get a good idea of how it works.(Kudos to implementor Pierre Habouzit, review manager John McCall, and the other LLVM contributors).

This week on NSHipster, we’re taking this occasion to review Objective-C method dispatching and try to understand the potential impact of this new language feature on future codebases.


To understand the significance of direct methods, you need to know a few things about the Objective-C runtime. But let’s start our discussion one step before that, to the origin of OOP itself:

Object-Oriented Programming

Alan Kay coined the term “object-oriented programming in the late 1960s. With the help of Adele Goldberg, Dan Ingalls, and his other colleagues at Xerox PARC, Kay put this idea into practice in the ’70s with the creation of the Smalltalk programming language.

In the 1980s, Brad Cox and Tom Love started work the first version of Objective-C, a language that sought to take the object-oriented paradigm of Smalltalk and implement it on solid fundamentals of C. Through a series of fortuitous events in the ’90s, the language would come to be the official language of NeXT, and later, Apple.

For those of us who started learning Objective-C in the iPhone era, the language was often seen as yet another proprietary Apple technology — one of a myriad, obscure byproducts of the company’s “Not invented here” (NIH) culture. However, Objective-C isn’t just “an object-oriented C”, it’s one of the original object-oriented languages, with as strong a claim to OOP credentials as any other.

Now, what does OOP mean? That’s a good question. ’90s era hype cycles have rendered the term almost meaningless. However, for our purposes today, let’s focus on something Alan Kay wrote in 1998:

I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is “messaging” […] > Alan Kay

Dynamic Dispatch and the Objective-C Runtime

In Objective-C, a program consists of a collection of objects that interact with each other by passing messages that, in turn, invoke methods, or functions. This act of message passing is denoted by square bracket syntax:

[someObjectaMethod:withAnArgument];

When Objective-C code is compiled, message sends are transformed into calls to a function calledobjc_msgSend (literally “send a message to some object with an argument”).

objc_msgSend(object,@selector(message),withAnArgument);
  • The first argument is the receiver (self for instance methods)
  • The second argument is _cmd: the selector, or name of the method
  • Any method parameters are passed as additional function arguments

objc_msgSend is responsible for determining which underlying implementation to call in response to this message, a process known as method dispatch.

In Objective-C, each class (Class) maintains a dispatch table to resolve messages sent at runtime. Each entry in the dispatch table is a method (Method) that keys a selector (SEL) to a corresponding implementation (IMP), which is a pointer to a C function. When an object receives a message, it consults the dispatch table of its class. If it can find an implementation for the selector, the associated function is called. Otherwise, the object consults the dispatch table of its superclass. This continues up the inheritance chain until a match is found or the root class (NSObject) deems the selector to be unrecognized.

If you think all of this indirection sounds like a lot of work… in a way, you’d be right!

If you have a hot path in your code, an expensive method that’s called frequently, you could imagine some benefit to avoiding all of this indirection. To that end, some developers have used C functions as a way around dynamic dispatch.

Direct Dispatch with a C Function

As we saw with objc_msgSend, any method invocation can be represented by an equivalent function by passing implicit self as the first argument.

For example, consider the following declaration of an Objective-C class with a conventional, dynamically-dispatched method.

@interfaceMyClass:NSObject-(void)dynamicMethod;@end

If a developer wanted to implement some functionality on MyClass without going through the whole message sending shebang, they could declare a static C function that took an instance of MyClass as an argument.

staticvoiddirectFunction(MyClass*__unsafe_unretainedobject);

Here’s how each of these approaches translates to the call site:

MyClass*object=[[[MyClass]alloc]init];// Dynamic Dispatch[objectdynamicMethod];// Direct DispatchdirectFunction(object);

Direct Methods

A direct method has the look and feel of a conventional method, but has the behavior of a C function. When a direct method is called, it directly calls its underlying implementation rather than going through objc_msgSend.

With this new LLVM patch, you now have a way to annotate Objective-C methods to avoid participation in dynamic dispatch selectively.

objc_direct, @property(direct), and objc_direct_members

To make an instance or class method direct, you can mark it with the objc_directClang attribute. Likewise, the methods for an Objective-C property can be made direct by declaring it with the direct property attribute.

@interfaceMyClass:NSObject@property(nonatomic)BOOLdynamicProperty;@property(nonatomic,direct)BOOLdirectProperty;-(void)dynamicMethod;-(void)directMethod__attribute__((objc_direct));@end

When an @interface for a category or class extension is annotated with the objc_direct_members attribute, all method and property declarations contained within it are considered to be direct, unless previously declared by that class.

__attribute__((objc_direct_members))@interfaceMyClass()@property(nonatomic)BOOLdirectExtensionProperty;-(void)directExtensionMethod;@end

Annotating an @implementation with objc_direct_members has a similar effect, causing non-previously declared members to be deemed direct, including any implicit methods resulting from property synthesis.

__attribute__((objc_direct_members))@implementationMyClass-(BOOL)directProperty{}-(void)dynamicMethod{}-(void)directMethod{}-(void)directExtensionMethod{}-(void)directImplementationMethod{}@end

Applying these annotations to our example from before, we can see how direct and dynamic methods are indistinguishable at the call site:

MyClass*object=[[[MyClass]alloc]init];// Dynamic Dispatch[objectdynamicMethod];// Direct Dispatch[objectdirectMethod];

Direct methods seem like a slam dunk feature for the performance-minded developers among us. But here’s the twist:

In most cases, making a method direct probably won’t have a noticeable performance advantage.

As it turns out,objc_msgSend is surprisingly fast. Thanks to aggressive caching, extensive low-level optimization, and intrinsic performance characteristics of modern processors,objc_msgSend has an extremely low overhead.

We’re long past the days when iPhone hardware could reasonably be described as a resource-constrained environment. So unless Apple is preparing for a new embedded platform (AR glasses, anyone?), the most reasonable explanation we have for Apple implementing Objective-C direct methods in 2019 stems from something other than performance.

Hidden Motives

When an Objective-C method is marked as direct, its implementation has hidden visibility. That is, direct methods can only be called within the same module(or to be pedantic,linkage unit). It won’t even show up in the Objective-C runtime.

Hidden visibility has two direct advantages:

  • Smaller binary size
  • No external invocation

Without external visibility or a way to invoke them dynamically from the Objective-C runtime, direct methods are effectively private methods.

While hidden visibility can be used by Apple to prevent swizzling and private API use, that doesn’t seem to be the primary motivation.

According to Pierre, who implemented this feature, the main benefit of this optimization is code size reduction. Reportedly, the weight of unused Objective-C metadata can account for 5 – 10% of the __text section in the compiled binary.


You could imagine that, from now until next year’s developer conference, a few engineers could go through each of the SDK frameworks, annotating private methods with objc_direct and private classes with objc_direct_members as a lightweight way to progressively tighten its SDK.

If that’s true, then perhaps it’s just as well that we’ve become skeptical of new Objective-C features. When they’re not in service of Swift, they’re in service of Apple. Despite its important place in the history of programming and Apple itself, it’s hard not to see Objective-C as just that — history.

#pragma

$
0
0

#pragma declarations are a mark of craftsmanship in Objective-C. Although originally used to make source code compatible across different compilers, Xcode-savvy programmers use #pragma declarations to very different ends.

Whereas other preprocessor directives allow you to define behavior when code is executed,#pragma is unique in that it gives you controlat the time code is being written— specifically, by organizing code under named headings and inhibiting warnings.

As we’ll see in this week’s article, good developer habits start with #pragma mark.

Organizing Your Code

Code organization is a matter of hygiene. How you structure your code is a reflection of you and your work. A lack of convention and internal consistency indicates either carelessness or incompetence — and worse, it makes a project more challenging to maintain over time.

We here at NSHipster believe that code should be clean enough to eat off of. That’s why we use #pragma mark to divide code into logical sections.

If your class overrides any inherited methods, organize them under common headings according to their superclass. This has the added benefit of describing the responsibilities of each ancestor in the class hierarchy. An NSInputStream subclass, for instance, might have a group marked NSInputStream, followed by NSStream, and then finally NSObject.

If your class adopts any protocols, it makes sense to group each of their respective methods using a #pragma mark header with the name of that protocol(bonus points for following the same order as the original declarations).

Finding it difficult to make sense of your app’s MVC architecture? (“Massive View Controller”, that is.)#pragma marks allow you to divide-and-conquer! With a little practice, you’ll be able to identify and organize around common concerns with ease. Some headings that tend to come up regularly include initializers, helper methods, @dynamic properties, Interface Builder outlets or actions, and handlers for notification or Key-Value Observing (KVO) selectors.

Putting all of these techniques together, the @implementation for a ViewController class that inherits from UITableViewController might organize Interface Builder actions together at the top, followed by overridden view controller life-cycle methods, and then methods for each adopted protocol, each under their respective heading.

@implementationViewController-(instancetype)init{}#pragma mark - IBAction-(IBAction)confirm:(id)sender{}-(IBAction)cancel:(id)sender{}#pragma mark - UIViewController-(void)viewDidLoad{}-(void)viewDidAppear:(BOOL)animated{}#pragma mark - UITableViewDataSource-(NSInteger)tableView:(UITableView*)tableViewnumberOfRowsInSection:(NSInteger)section{}#pragma mark - UITableViewDelegate-(void)tableView:(UITableView*)tableViewdidSelectRowAtIndexPath:(NSIndexPath*)indexPath{}@end

Not only do these sections make for easier reading in the code itself, but they also create visual cues to the Xcode source navigator and minimap.

#pragma headings in the Xcode source navigator

Inhibiting Warnings

Do you know what’s even more annoying than poorly-formatted code? Code that generates warnings — especially 3rd-party code. Is there anything more irksome than a vendor SDK that generates dozens of warnings each time you hit R? Heck, even a single warning is one too many for many developers.

Warnings are almost always warranted, but sometimes they’re unavoidable. In those rare circumstances where you are absolutely certain that a particular warning from the compiler or static analyzer is errant,#pragma comes to the rescue.

Let’s say you’ve deprecated a class:

@interfaceDeprecatedClass__attribute__((deprecated))@end

Now, deprecation is something to be celebrated. It’s a way to responsibly communicate future intent to API consumers in a way that provides enough time for them to migrate to an alternative solution. But you might not feel so appreciated if you have CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS enabled in your project. Rather than being praised for your consideration, you’d be admonished for implementing a deprecated class.

One way to silence the compiler would be to disable the warnings by setting -Wno-deprecated-implementations. However, doing this for the entire project would be too coarse an adjustment, and doing this for this file only would be too much work. A better option would be to use #pragma to inhibit the unhelpful warning around the problematic code.

Using #pragma clang diagnostic push/pop, you can tell the compiler to ignore certain warnings for a particular section of code:

#pragma clang diagnostic push
        #pragma clang diagnostic ignored "-Wdeprecated-implementations"@implementationDeprecatedClass@end#pragma clang diagnostic pop

Before you start copy-pasting this across your project in a rush to zero out your warnings, remember that, in the overwhelming majority of cases, Clang is going to be right. Fixing an analyzer warning is strongly preferred to ignoring it, so use #pragma clang diagnostic ignored as a method of last resort.


Though it skirts the line between comment and code,#pragma remains a vital tool in the modern app developer’s toolbox. Whether it’s for grouping methods or suppressing spurious warnings, judicious use of #pragma can go a long way to make projects easier to understand and maintain.

Like the thrift store 8-track player you turned into that lamp in the foyer,#pragma remains a curious vestige of the past: Once the secret language of compilers, now re-purposed to better-communicate intent to other programmers.How delightfully vintage!

@

$
0
0

Birdwatchers refer to it as (and I swear I’m not making this up)“Jizz”: the general characteristics that form an overall impression of a thing.

Walking through the forests of the Pacific Northwest, a birder would know a nighthawk from other little brown jobs from its distinct vocalization, or a grey-cheeked thrush by its white-dark-white underwing pattern. Looking up in the sky, there’d be no mistaking a Flying-V formation of migratory geese from the undulating murmuration of starlings. And while a twitcher would be forgiven for mistaking a coot for a duck at the water’s edge, their scaley, non-webbed feet are an obvious tell to an ornithophile.

The usefulness of jizz isn’t limited to amateur ornithology, either. We can distinguish varieties of programming languages based on their defining characteristics: Go with its tell-tale couplets of if err, Rust with its unpronounceable, consonant-laden keywords, pub, fn, and mut, Perl with its special characters that read like Q*bert swearing. Lisp’s profusion of parentheses is an old cliché at this point; our favorite telling is that one joke about the stolen last page of a Lisp program’s printed source code.

                )))
        ) )
        ))) ) ))
        )))))
        )))
        ))
        )))) ))
        )))) ))
        )))
        )

If we were to go code-watching for the elusive Objective-C species, what would we look for? Square brackets, ridiculously long method names, and @’s.

@, or “at” sign compiler directives, are as central to understanding Objective-C’s gestalt as its ancestry and underlying mechanisms. Those little cinnamon roll glyphs are the sugary glue that allows Objective-C to be such a powerful, expressive language, and yet still compile down to C. So varied and disparate are its uses that the only accurate way to describe what @ means on its own is “shorthand for something to do with Objective-C”. They cover a broad range in usefulness and obscurity, from staples like @interface and @implementation to ones you could go your whole career without spotting, like @defs and @compatibility_alias. But to anyone aspiring to be an NSHipster, knowledge of every @ directives is tantamount to a birder’s familiarity with the frenetic hovering of a hummingbird, the commanding top knot of a Mountain quail, or the eponymous “cuckoo” of Coccyzus americanus.

Interface & Implementation

@interface and @implementation are the first things you encounter when you start learning Objective-C:

// MyObject.h@interfaceMyObject@end// MyObject.m@implementationMyObject@end

What you don’t learn about until later on are categories and class extensions.

Categories allow you to extend the behavior of existing classes by adding new class or instance methods. As a convention, categories are defined in their own .{h,m} files:

// MyObject+CategoryName.h@interfaceMyObject(CategoryName)-(void)foo;-(BOOL)barWithBaz:(NSInteger)baz;@end// MyObject+CategoryName.m@implementationMyObject(CategoryName)-(void)foo{}-(BOOL)barWithBaz:(NSInteger)baz{returnYES;}@end

Categories are particularly useful for convenience methods on standard framework classes (just don’t go overboard with your utility functions).

Extensions look like categories but omit the category name. They’re typically declared before an @implementation to specify a private interface or override properties declared in the public interface:

// MyObject.m@interfaceMyObject()@property(readwrite,nonatomic,strong)NSString*name;-(void)somePrivateMethod;@end@implementationMyObject@end

Properties

Property directives are likewise, learned early on:

@property
Declares a class or instance property.
@synthesize
Automatically synthesizes getter / setter methods to an underlying instance or class variable for a declared property.
@dynamic
Instructs the compiler that you’ll provide your own implementation for property getter and/or setter methods.

Property Attributes

@property declarations comprise their own little sub-phylum of syntax, with attributes for specifying:

  • Accessor names (getter / setter)
  • Access types (readwrite / readonly)
  • Atomicity (atomic / nonatomic)
  • Nullability (nullable / nonnullable / null_resettable)
  • Ownership (weak / strong / copy / retain / unsafe_unretained)

And that’s not all — there’s also the class attribute, which lets you declare a class property using the same, familiar instance property syntax, as well as the forthcoming direct attribute, which will let you opt in to direct method dispatch.

Forward Class Declarations

Occasionally,@interface declarations will reference an external type in a property or as a parameter. Rather than adding an #import statement in the interface, you can use a forward class declaration in the header and import the necessary in the implementation.

@class
Creates a forward declaration, allowing a class to be referenced before its actual declaration.

Shorter compile times, less chance of cyclical references; you should get in the habit of doing this if you aren’t already.

Instance Variable Visibility

It’s a matter of general convention that classes provide state and mutating interfaces through properties and methods, rather than directly exposing ivars. Nonetheless, in cases where ivars are directly manipulated, there are the following visibility directives:

@public
Instance variable can be read and written to directly using the following notation:
object->_ivar=
@package
Instance variable is public, except outside of the framework in which it is specified (64-bit architectures only)
@protected
Instance variable is only accessible to its class and derived classes
@private
Instance variable is only accessible to its class
@interfacePerson:NSObject{@publicNSString*name;intage;@privateintsalary;}@end

Protocols

There’s a distinct point early in an Objective-C programmer’s evolution when they realize that they can define their own protocols.

The beauty of protocols is that they let you design contracts that can be adopted outside of a class hierarchy. It’s the egalitarian mantra at the heart of the American Dream: It doesn’t matter who you are or where you come from; anyone can achieve anything if they work hard enough.

@protocol@end defines a set of methods to be implemented by any conforming class, as if they were added to the interface of that class directly.

Architectural stability and expressiveness without the burden of coupling? Protocols are awesome.

Requirement Options

You can further tailor a protocol by specifying methods as required or optional. Optional methods are stubbed in the interface, so as to be auto-completed in Xcode, but do not generate a warning if the method isn’t implemented. Protocol methods are required by default.

The syntax for @required and @optional follows that of the visibility macros:

@protocolCustomControlDelegate-(void)control:(CustomControl*)controldidSucceedWithResult:(id)result;@optional-(void)control:(CustomControl*)controldidFailWithError:(NSError*)error;@end

Exception Handling

Objective-C communicates unexpected state primarily through NSError. Whereas other languages would use exception handling for this, Objective-C relegates exceptions to truly exceptional behavior.

@ directives are used for the traditional convention of try/catch/finally blocks:

@try{// attempt to execute the following statements[selfgetValue:&valueerror:&error];// if an exception is raised, or explicitly thrown...if(error){@throwexception;}}@catch(NSException*e){}@finally{// always executed after @try or @catch[selfcleanup];}

Literals

Literals are shorthand notation for specifying fixed values, and their availability in a language is directly correlated with programmer happiness. By that measure, Objective-C has long been a language of programmer misery.

Object Literals

For years, Objective-C only had literals for NSString values. But with the release of the Apple LLVM 4.0 compiler, there are now literals for NSNumber, NSArray, and NSDictionary.

@""
An NSString object initialized with the text inside the quotation marks.
@42 / @3.14 / @YES / @'Z'
An NSNumber object initialized with the adjacent value using the pertinent class constructor, such that @42[NSNumber numberWithInteger:42] and @YES[NSNumber numberWithBool:YES]. (You can use suffixes to further specify type, like @42U[NSNumber numberWithUnsignedInt:42U])
@[]
An NSArray object initialized with a comma-delimited list of objects as its contents. It uses the +arrayWithObjects:count: class constructor method, which is a more precise alternative to the more familiar +arrayWithObjects:.
@{}
An NSDictionary object initialized with key-value pairs as its contents using the format: @{@"someKey" : @"theValue"}.
@()
A boxed expression using the appropriate object literal for the enclosed value (for example, NSString for const char*, NSNumber for int, and so on). This is also the designated way to use number literals with enum values.

Objective-C Literals

Selectors and protocols can be passed as method parameters.@selector() and @protocol() serve as pseudo-literal directives that return a pointer to a particular selector (SEL) or protocol (Protocol *).

@selector()
Provides an SEL pointer to a selector with the specified name. Used in methods like -performSelector:withObject:.
@protocol()
Provides a Protocol * pointer to the protocol with the specified name. Used in methods like -conformsToProtocol:.

C Literals

Literals can also work the other way around, transforming Objective-C objects into C values. These directives in particular allow us to peek underneath the Objective-C veil to see what’s really going on.

Did you know that all Objective-C classes and objects are just glorified structs? Or that the entire identity of an object hinges on a single isa field in that struct?

For most of us, most of the time, this is an academic exercise. But for anyone venturing into low-level optimizations, this is simply the jumping-off point.

@encode()
Provides the type encoding of a type. This value can be used as the first argument in NSCoder -encodeValueOfObjCType:at:.
@defs()
Provides the layout of an Objective-C class. For example, struct { @defs(NSObject) } declares a struct with the same fields as an NSObject:

Optimizations

Some @ compiler directives provide shortcuts for common optimizations.

@autoreleasepool {}
If your code contains a tight loop that creates lots of temporary objects, you can use the @autoreleasepool directive to aggressively deallocate these short-lived, locally-scoped objects.@autoreleasepool replaces and improves upon the old NSAutoreleasePool, which was significantly slower and unavailable with ARC.
@synchronized(object) {}
Guarantees the safe execution of a particular block within a specified context (usually self). Locking in this way is expensive, however, so for classes aiming for a particular level of thread safety, a dedicated NSLock property or the use of low-level primitives like GCD are preferred.

Compatibility

When Apple introduces a new API, it’s typically available for the latest SDK only. If you want to start using these APIs in your app without dropping backward compatibility, you can create a compatibility alias.

For example, back when UICollectionView was first introduced in iOS 6, many developers incorporated a 3rd-party library calledPSTCollectionView, which uses @compatibility_alias to provide a backwards-compatible, drop-in replacement for UICollectionView:

#if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000@compatibility_aliasUICollectionViewControllerPSTCollectionViewController;@compatibility_aliasUICollectionViewPSTCollectionView;@compatibility_aliasUICollectionReusableViewPSTCollectionReusableView;@compatibility_aliasUICollectionViewCellPSTCollectionViewCell;@compatibility_aliasUICollectionViewLayoutPSTCollectionViewLayout;@compatibility_aliasUICollectionViewFlowLayoutPSTCollectionViewFlowLayout;@compatibility_aliasUICollectionViewLayoutAttributesPSTCollectionViewLayoutAttributes;@protocolUICollectionViewDataSource<PSTCollectionViewDataSource>@end@protocolUICollectionViewDelegate<PSTCollectionViewDelegate>@end#endif

You can use the same approach today to strategically adopt new APIs in your app, alongside the next and final @ compiler directive in this week’s article:

Availability

Achieving backwards or cross-platform compatibility in your app can often feel like a high-wire act. If you so much as glance towards an unavailable class or method, it could mean curtains for your app. That’s why the new features in Clang 5.0 came as such a relief. Now developers have a compiler-provide safety net to warn them whenever an unavailable API is referenced for one of your supported targets.

@available
Use in an if statement to have the compiler conditionally execute a code path based on the platform availability.

For example, if you wanted to use a fancyNewMethod in the latest version of macOS, but provide a fallback for older versions of macOS:

-(void)performCalculation{if(@available(macOS10.15,*)){[selffancyNewMethod];}else{[selfoldReliableMethod];}}

Much like the familiar call of a warbler or the tell-tale plumage of a peacock, the @ sigil plays a central role in establishing Objective-C’s unique identity. It’s a versatile, power-packed character that embodies the underlying design and mechanisms of the language. So be on the lookout for its many faces as you wander through codebases, new or familiar.


RawRepresentable

$
0
0

Programming is about typing. And programming languages are typically judged by how much they make you type — in both senses of the word.

Swift is beloved for being able to save us a few keystrokes without compromising safety or performance, whether it’s through implicit typing or automatic synthesis of protocols like Equatable andHashable. But the OG ergonomic feature of Swift is undoubtedly automatic synthesis of RawRepresentable conformance for enumerations with raw types. You know… the language feature that lets you do this:

enumGreeting:String{case hello = "hello"case goodbye // implicit raw value of "goodbye"}enumSortOrder:Int{case ascending = -1case same // implicit raw value of 0case descending  // implicit raw value of 1}

Though “enum + RawValue” has been carved into the oak tree of our hearts since first we laid eyes on that language with a fast bird, few of us have had occasion to consider what RawRepresentable means outside of autosynthesis. This week, we invite you to do a little extra typing and explore some untypical use cases for the RawRepresentable protocol.


In Swift, an enumeration can be declared withraw value syntax.

According to the documentation:

For any enumeration with a string, integer, or floating-point raw type, the Swift compiler automatically adds RawRepresentable conformance.

When developers first start working with Swift, they inevitably run into situations where raw value syntax doesn’t work:

  • Enumerations with raw values other than Int or String
  • Enumerations with associated values

Upon seeing those bright, red error sigils, many of us fall back to a more conventional enumeration, failing to realize that what we wanted to do wasn’t impossible, but rather just slightly beyond what the compiler can do for us.


RawRepresentable with C Raw Value Types

The primary motivation for raw value enumerations is to improve interoperability. Quoting again from the docs:

Using the raw value of a conforming type streamlines interoperation with Objective-C and legacy APIs.

This is true of Objective-C frameworks in the Apple SDK, which declare enumerations with NS_ENUM. But interoperability with other C libraries is often less seamless.

Consider the task of interfacing with libcmark, a library for working with Markdown according to theCommonMark spec. Among the imported data types is cmark_node_type, which has the following C declaration:

typedefenum{/* Error status */CMARK_NODE_NONE,/* Block */CMARK_NODE_DOCUMENT,CMARK_NODE_BLOCK_QUOTE,CMARK_NODE_HEADING,CMARK_NODE_THEMATIC_BREAK,CMARK_NODE_FIRST_BLOCK=CMARK_NODE_DOCUMENT,CMARK_NODE_LAST_BLOCK=CMARK_NODE_THEMATIC_BREAK,}cmark_node_type;

We can immediately see a few details that would need to be ironed out along the path of Swiftification — notably, 1) the sentinel NONE value, which would instead be represented by nil, and 2) the aliases for the first and last block values, which wouldn’t be encoded by distinct enumeration cases.

Attempting to declare a Swift enumeration with a raw value type of cmark_node_type results in a compiler error.

enumNodeType:cmark_node_type{}// Error

However, that doesn’t totally rule out cmark_node_type from being a RawValue type. Here’s what we need to make that happen:

enumNodeType:RawRepresentable{case documentcase blockQuoteinit?(rawValue:cmark_node_type){switchrawValue{case CMARK_NODE_DOCUMENT: self = .documentcase CMARK_NODE_BLOCK_QUOTE: self = .blockQuotedefault:returnnil}}varrawValue:cmark_node_type{switchself{case .document: return CMARK_NODE_DOCUMENTcase .blockQuote: return CMARK_NODE_BLOCK_QUOTE}}}

It’s a far cry from being able to say case document = CMARK_NODE_DOCUMENT, but this approach offers a reasonable solution that falls within the existing semantics of the Swift standard library.

That debunks the myth aboutInt and String being the only types that can be a raw value. What about that one about associated values?

RawRepresentable and Associated Values

In Swift, an enumeration case can have one or more associated values. Associated values are a convenient way to introduce some flexibility into the closed semantics of enumerations and all the benefits they confer.

As the old adage goes:

There are three numbers in computer science: 0, 1, and N.

enumNumber{case zerocase onecase n(Int)}

Because of the associated value on n, the compiler can’t automatically synthesize an Int raw value type. But that doesn’t mean we can’t roll up our sleeves and pick up the slack.

extensionNumber:RawRepresentable{init?(rawValue:Int){switchrawValue{case 0: self = .zerocase 1: self = .onecase let n: self = .n(n)}}varrawValue:Int{switchself{case .zero: return 0case .one: return 1case let .n(n): return n}}}Number(rawValue:1)// .one

Another myth busted!

Let’s continue this example to clear up a misconception we found in the documentation.

RawRepresentable as Raw Values for Another Enumeration

Consider the following from the RawRepresentable docs:

For any enumeration with a string, integer, or floating-point raw type, the Swift compiler automatically adds RawRepresentable conformance.

This is, strictly speaking, true. But it actually under-sells what the compiler can do. The actual requirements for raw values are as follows:

  • The raw value type must be Equatable
  • The raw value type must beExpressibleByIntegerLiteral,ExpressibleByFloatLiteral, orExpressibleByStringLiteral
  • The raw value for each enumeration case must be a literal (or unspecified, in which case the value is inferred)

Let’s see what happens if we satisfy that for our Number type from before.

extensionNumber:Equatable{}// conformance is automatically synthesizedextensionNumber:ExpressibleByIntegerLiteral{init(integerLiteralvalue:Int){self.init(rawValue:value)!}}-1asNumber// .n(-1)0asNumber// .zero1asNumber// .one2asNumber// .n(2)

If we declare a new enumeration, (literally “Number”) with a Number raw value…

enum:Number{case一 = 1
        case二 = 2
        case三 = 3
        }.// 二..rawValue// .n(2)..rawValue.rawValue// 2

Wait, that actually works? Neat!

What’s really interesting is that our contrived little enumeration type benefits from the same, small memory footprint that you get from using enumerations in more typical capacities:

MemoryLayout.size(ofValue:.)// 1 (bytes)MemoryLayout.size(ofValue:..rawValue)// 9 (bytes)MemoryLayout.size(ofValue:..rawValue.rawValue)// 8 (bytes)

If raw values aren’t limited to String or Int, as once believed, you may start to wonder:How far can we take this?

RawRepresentable with Metatype Raw Values

Probably the biggest selling point of enumerations in Swift is how they encode a closed set of values.

enumElement{case earth, water, air, fire}

Unfortunately, there’s no equivalent way to “close off” which types conform to a protocol.

publicprotocolElemental{}publicstructEarth:Elemental{}publicstructWater:Elemental{}publicstructAir:Elemental{}publicstructFire:Elemental{}

Without built-in support for type unions or an analog to the open access modifier for classes, there’s nothing that an API provider can do, for example, to prevent a consumer from doing the following:

structAether:Elemental{}

Any switch statement over a type-erased Elemental value using is checks will necessarily have a default case.

Until we have a first-class language feature for providing such guarantees, we can recruit enumerations and raw values for a reasonable approximation:

extensionElement:RawRepresentable{init?(rawValue:Elemental.Type){switchrawValue{case is Earth.Type:self=.earthcase is Water.Type:self=.watercase is Air.Type:self=.aircase is Fire.Type:self=.firedefault:returnnil}}varrawValue:Elemental.Type{switchself{case .earth: return Earth.selfcase .water: return Water.selfcase .air: return Air.selfcase .fire: return Fire.self}}}

Returning one last time to the docs, we’re reminded that:

With a RawRepresentable type, you can switch back and forth between a custom type and an associated RawValue type without losing the value of the original RawRepresentable type.

From the earliest days of the language, RawRepresentable has been relegated to the thankless task of C interoperability. But looking now with a fresh set of eyes, we can now see it for in all its injective glory.

So the next time you find yourself with an enumeration whose cases broker in discrete, defined counterparts, consider adopting RawRepresentable to formalize the connection.

Swift Development with Visual Studio Code

$
0
0

Visual Studio Code (VSCode) is a cross-platform text and source code editor from Microsoft. It’s one of the most exciting open source projects today, with regular updates from hundreds of contributors. VSCode was among the first tools to support Language Server Protocol (LSP), which has played a large part in providing a great developer experience, in a variety of languages and technologies.

With the previously announcednow shipping in Xcode, it’s a great time to see how this integration works for yourself.

This week, we’ll walk through the process of how to get started with Swift’s new Language Server Protocol support in Visual Studio Code on macOS. If you haven’t tried writing Swift outside Xcode, or are already a VSCode user and new to the language entirely, this article will tell you everything you need to know.


Step 0: Install Xcode

If you don’t already have Xcode installed on your machine, open the Terminal app and run the following command:

$ xcode-select --install

Running this command presents a system prompt.

Click the “Get Xcode” button and continue installation on the App Store.

Step 1: Install Visual Studio Code

Download Visual Studio Code and install it to your system Applications folder. Open the app andfollow the instructions for launching from the command line. You’ll need to have the code command accessible from $PATH in order to install the SourceKit-LSP extension later on.

Step 2: Install the Latest Swift Toolchain

Go to Swift.org and download the latest trunk development snapshot (at the time of writing, this was from November 16th, 2018). Once it’s finished downloading, run the package to install the Xcode toolchain. To enable it, open Xcode, select the “Xcode > Preferences…” menu item (,), navigate to Components and choose Swift Development Snapshot.

You can verify that everything is working as expected by running the sourcekit-lsp command:

$ xcrun sourcekit-lsp

This command launches a new language server process, but don’t worry if it doesn’t provide any feedback to STDOUT— that means it’s working as intended. Exit the process with an ETX signal (^C).

Step 3: Install Node and NPM

VSCode extensions are written in JavaScript / TypeScript. If you’re not already set up for JS development, you can download Node (a JavaScript run-time for outside the browser)
and npm (a package manager for Node) with Homebrew using the following commands or manually by following these instructions:

$ brew install node

To verify that you have a working installation, run the following command:

$ npm --version6.13.4

Step 4: Build and Install SourceKit-LSP Extension for Visual Studio Code

From the command line, clone the sourcekit-lsp repository and navigate to Editors/vscode in the resulting directory. Use npm to build the extension and then use the code command to install it:

$ git clone https://github.com/apple/sourcekit-lsp.git$cd sourcekit-lsp/Editors/vscode/$ npm run createDevPackage$ code --install-extension out/sourcekit-lsp-vscode-dev.vsix

Now launch (or relaunch) VSCode and open a Swift project, such as this one, and test out Language Server Protocol support for Swift.


So there you have it — the makings of a first-class Swift development experience outside of Xcode. For now, Swift support for Language Server Protocol is limited, but we couldn’t be more excited for the future of this project and what it means for Swift beyond the Apple ecosystem.

Language Server Protocol

$
0
0

In October 2018, Apple announced on the Swift.org forums that it was starting work to adopt the Language Server Protocol (LSP) for Swift and C languages.

At Apple we are making it a priority to support high-quality tooling for all Swift developers, including those working on non-Apple platforms. We want to collaborate with the open-source community and focus our efforts on building common infrastructure that can be shared by Xcode and other editors and platforms. To that end, [ … ] we’ve chosen to adopt LSP.

Argyrios Kyrtzidis, October 15th, 2018

This is arguably the most important decision Apple has made for Swift since releasing the language as open source in 2014. It’s a big deal for app developers, and it’s an even bigger deal for Swift developers on other platforms.

To understand why, this week’s article will take a look at what problem the Language Server Protocol solves, how it works, and what its long-term impacts may be.


Imagine a grid with each row representing a different programming language (Swift, JavaScript, Ruby, Python, etc.) and each column representing a different code editor (Xcode, Visual Studio, Vim, Atom, etc.), such that each cell represents the level of support that a particular editor has for a language.

Up until recently, what you’d find was a patchwork of compatibility across the various combinations. Some editors offered deep integration with a few languages and little to no support for anything else, whereas other editors aimed to be general-purpose with at least a modicum of support for many languages. (The term IDE is often used to describe the former.)

Case in point:You’d be stubborn not to use Xcode for app development and foolish to use it for anything else.

For an editor to have better support for a particular language, it needs to write integration code — either directly in the code base or via a plugin system. Due to implementation differences across languages and editors, improvements to, say, Ruby support in Vim wouldn’t translate into better support for Python, nor could they be applied to make Ruby work better in Atom. The end result: inconsistent support across technologies and a lot of wasted effort.

The situation we described is often referred to as an M × N problem, where the number of integrations is the product ofM editors and N languages. What the Language Server Protocol does is change this M × N problem into a M + N problem.

Rather than an editor having to implement support for each language, it only needs to support the LSP. And in doing so, it gets the same level of functionality for all languages that support the LSP.

Language Server Protocol provides a common set of functionality for supported languages, including:

  • Syntax Highlighting
  • Automatic Formatting
  • Autocomplete
  • Syntax
  • Tooltips
  • Inline Diagnostics
  • Jump to Definition
  • Find References in Project
  • Advanced Text and Symbol Search

Rather than reinventing the wheel for each new technology, tools and editors can invest in better usability and more advanced functionality.

How Language Server Protocol Works

If you’re an iOS developer, you may be most familiar with the terms server and protocol in the sense of communicating with web applications in JSON format via HTTP. This actually isn’t too far off from how the Language Server Protocol works.

In the case of LSP, the client refers to the editor — or more generally, the tool — and the server refers to an external program run locally in a separate process.

As for the protocol itself, LSP resembles a simplified version of HTTP:

  • Each message consists of a header part and a content part.
  • The header part has a required Content-Length field containing the size of the content part in bytes, and an optional Content-Type field (application/vscode-jsonrpc; charset=utf-8 by default)
  • The content part uses JSON-RPC to describe the structure of requests, responses, and notifications.

Whenever something happens in the tool, such as the user jumping to the definition of a symbol, the tool sends a request to the server. The server receives that request and then returns an appropriate response.

For example, imagine that a user opens the following Swift code in an Xcode-like editor that supported the Language Server Protocol:

classParent{}classChild:Parent{}

When the user -clicks the symbol Parent in the inheritance clause on line 2, the editor jumps to the definition of the Parent class on line 1.

Here’s how LSP enables this interaction behind the scenes:

First, when the user opens the Swift code, the editor launches its Swift language server in a separate process, if it isn’t running already, and performs any additional setup.

When the user executes the “jump to definition” command, the editor sends the following request to its Swift language server:

{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///Users/NSHipster/Example.swift"},"position":{"line":1,"character":13}}}

Upon receiving this request, the Swift language server uses a compiler tool likeSourceKit to identify the corresponding code entity and find the location of its declaration on the preceding line. The language server then responds with the following message:

{"jsonrpc":"2.0","id":1,"result":{"uri":"file:///Users/NSHipster/Example.swift","range":{"start":{"line":0,"character":6},"end":{"line":0,"character":12}}}}

Finally, the editor navigates to the file (which, in this case, is already open), moves the cursor to that range, and highlights the token.

The beauty of this approach is that the editor did all of this without knowing anything about the Swift programming language other than that .swift files are associated with Swift code. All the editor needs to do is talk to the language server and update the UI. And knowing how to do that, the editor can follow the same procedure to facilitate this interaction for code written in any language with a language server implementation.

Language Server Protocol Support in Clang / LLVM

If the M + N diagram from before looks familiar, it might be because it’s the same approach taken by LLVM.

At the core of LLVM is an intermediate representation (IR). Supported languages generate IR using a compiler frontend, and that IR can generate machine code for any platform supported by a compiler backend.

The LLVM compiler frontend for C languages is called Clang. It’s also used by Swift for inter-operability with Objective-C. In its recent 5.0.0 release, Clang added a new tool called Clangd, LLVM’s implementation for the Language Server Protocol.

In April 2018,Apple announced to the LLVM mailing list that it was switching the focus of its development efforts fromlibclang to Clangd as the primary way to create interactive tooling.

Now you might think, “So what?” Apple is among the most prominent supporters of the LLVM project — having, among other things, employed the project’s founder, Chris Lattner, for over a decade. Apple’s decision to switch from one obscure Clang tool to another would seem to be an implementation detail (so to speak).

What makes this announcement quite interesting is that Clangd appears to have been created entirely outside of Apple, with significant contributions from Google and other companies. This announcement signals a significant shift in the direction of tooling development going forward — something that would be confirmed 6 months later on the Swift.org forums.

Potential Consequences of Apple’s Support of Language Server Protocol

It’ll take some time to feel the full impact of these developments, but believe me: your patience will be rewarded. Here are just a few of what I believe will happen as a result of LSP in the coming months and years.

Swift Becomes More Appealing as a General-Purpose Programming Language

Although Swift is used primarily for app development, it was designed from the start to be a capable general-purpose programming language. BetweenSwift for TensorFlow,SwiftNIO, and other projects, we’re just starting to see the promise of what Swift can do beyond the App Store.

Among the biggest factors holding Swift back from mainstream adoption up to this point has been its reliance on Xcode.

It’s a lot to ask, say, a web developer or machine learning engineer to download Xcode just to try Swift when there are so many great alternatives with a much lower barrier to entry. Support for the Language Server Protocol should make it significantly easier for folks outside the Apple ecosystem to evaluate Swift with the same, familiar tools they use for everything else.

Xcode Gets Better

Adopting LSP isn’t just about making Swift work better in other editors; Xcode stands to benefit immensely, as well.

Consider this forum post from Project Lead for Swift at Apple, Ted Kremenek:

The LSP service [Argyrios] is describing will be functionally more powerful than SourceKit is today.

LSP is an opportunity for the Xcode team to take a fresh approach to Swift integration, and to capitalize on all of the improvements to the language and tooling in the four years since its 1.0 release.

Our first glimpse into this overhauled infrastructure comes by way ofIndexStoreDB: a powerful new API for querying code symbols in Swift projects from a Clang index.

Xcode (Eventually) Becomes More Capable

The benefit of LSP isn’t limited to Swift and Objective-C; from another post by Argyrios in that thread:

Getting Xcode to use our new LSP service should make it viable to use other LSP services as well, and it’s something that we are interested in, but we don’t have specific plans to announce at this moment.

The main focus for the current efforts are to improve the story for Swift. But once that’s done, it should be relatively straightforward to have those benefits cascade down to other languages with LSP support.


The architecture of software reflects the structure and values of the organizations that create it. The converse is true as well, to some extent.

By adopting the open Language Server Protocol standard for Xcode, Apple is making good on its commitment to the success of Swift on platforms outside the Apple ecosystem. And I think it’ll work: tooling (or lack thereof) is often the key decider in which technologies gain mindshare. But perhaps more importantly, I believe this decision demonstrates an increased willingness within (at least some small part of) the company for collaboration and transparency.

Static and Dynamic Callable Types in Swift

$
0
0

Last week, Apple released the first beta of Xcode 11.4, and it’s proving to be one of the most substantial updates in recent memory. XCTest got a huge boost, with numerous quality of life improvements, and Simulator, likewise, got a solid dose ofTLC. But it’s the changes to Swift that are getting the lion’s share of attention.

In Xcode 11.4, Swift compile times are down across the board, with many developers reporting improvements of 10 – 20% in their projects. And thanks to a new diagnostics architecture, error messages from the compiler are consistently more helpful. This is also the first version of Xcode to ship with the newsourcekit-lsp server, which serves to empower editors like VSCode to work with Swift in a more meaningful way.

Yet, despite all of these improvements (which are truly an incredible achievement by Apple’s Developer Tools team), much of the early feedback has focused on the most visible additions to Swift 5.2. And the response from the peanut galleries of Twitter, Hacker News, and Reddit has been — to put it charitably — “mixed”.


If like most of us, you aren’t tuned into the comings-and-goings of Swift Evolution, Xcode 11.4 was your first exposure to two new additions to the language:key path expressions as functions andcallable values of user-defined nominal types.

The first of these allows key paths to replace one-off closures used by functions like map:

// Swift >= 5.2"🧁🍭🍦".unicodeScalars.map(\.properties.name)// ["CUPCAKE", "LOLLIPOP", "SOFT ICE CREAM"]// Swift <5.2 equivalent"🧁🍭🍦".unicodeScalars.map{$0.properties.name}

The second allows instances of types with a method named callAsFunction to be called as if they were a function:

structSweetener{letadditives:Set<Character>init<S>(_sequence:S)whereS:Sequence,S.Element==Character{self.additives=Set(sequence)}funccallAsFunction(_message:String)->String{message.split(separator:" ").flatMap{[$0,"\(additives.randomElement()!)"]}.joined(separator:" ")+"😋"}}letdessertify=Sweetener("🧁🍭🍦")dessertify("Hello, world!")// "Hello, 🍭 world! 🍦😋"

Granted, both of those examples are terrible. And that’s kinda the problem.


Too often, coverage of “What’s New In Swift” amounts to little more than a regurgitation of Swift Evolution proposals, interspersed with poorly motivated (and often emoji-laden) examples. Such treatments provide a poor characterization of Swift language features, and — in the case of Swift 5.2 — serves to feed into the popular critique that these are frivolous additions — mere syntactic sugar.

This week, we hope to reach the ooey gooey center of the issue by providing some historical and theoretical context for understanding these new features.

Syntactic Sugar in Swift

If you’re salty about “key path as function” being too sugary, recall that the status quo isn’t without a sweet tooth. Consider our saccharine example from before:

"🧁🍭🍦".unicodeScalars.map{$0.properties.name}

That expression relies on at least four different syntactic concessions:

  1. Trailing closure syntax, which allows a final closure argument label of a function to be omitted
  2. Anonymous closure arguments, which allow arguments in closures to be used positionally ($0, $1, …) without binding to a named variable.
  3. Inferred parameter and return value types
  4. Implicit return from single-expression closures

If you wanted to cut sugar out of your diet completely, you’d best get Mavis Beacon on the line, because you’ll be doing a lot more typing.

"🧁🍭🍦".unicodeScalars.map(transform:{(unicodeScalar:Unicode.Scalar)->StringinreturnunicodeScalar.properties.name})

In fact, as we’ll see in the examples to come, Swift is a marshmallow world in the winter, syntactically speaking. From initializers and method calls to optionals and method chaining, nearly everything about Swift could be described as a cotton candy melody — it really just depends on where you draw the line between “language feature” and “syntactic sugar”.


To understand why, you have to understand how we got here in the first place, which requires a bit of history, math, and computer science. Get ready to eat your vegetables 🥦.

The λ-Calculus and Speculative Computer Science Fiction

All programming languages can be seen as various attempts to representthe λ-calculus. Everything you need to write code — variables, binding, application — it’s all in there, buried under a mass of Greek letters and mathematical notation.

Setting aside syntactic differences, each programming language can be understood by its combination of affordances for making programs easier to write and easier to read. Language features like objects, classes, modules, optionals, literals, and generics are all just abstractions built on top of the λ-calculus.

Any other deviation from pure mathematical formalism can be ascribed to real-world constraints, such asa typewriter from the 1870s,a punch card from the 1920s,a computer architecture from the 1940s, or a character encoding from the 1960s.

Among the earliest programming languages were Lisp, ALGOL*, and COBOL, from which nearly every other language derives.

(defunsquare(x)(*xx))(print(square4));; 16

Here you get a glimpse into three very different timelines; ours is the reality in which ALGOL’s syntax (option #2) “won out” over the alternatives. From ALGOL 60, you can draw a straight line from CPL in 1963, to BCPL in 1967 and C in 1972, followed by Objective-C in 1984 and Swift in 2014. That’s the lineage that informs what types are callable and how we call them.


Now, back to Swift…

Function Types in Swift

Functions are first-class objects in Swift, meaning that they can be assigned to variables, stored in properties, and passed as arguments or returned as values from other functions.

What distinguishes function types from other values is that they’re callable, meaning that you can invoke them to produce new values.

Closures

Swift’s fundamental function type is the closure, a self-contained unit of functionality.

letsquare:(Int)->Int={xinx*x}

As a function type, you can call a closure by passing the requisite number of arguments between opening and closing parentheses ()a la ALGOL.

square(4)// 16

Closures are so called because they close over and capture references to any variables from the context in which they’re defined. However, capturing semantics aren’t always desirable, which is why Swift provides dedicated syntax to a special kind of closure known as a function.

Functions

Functions defined at a top-level / global scope are named closures that don’t capture any values. In Swift, you declare them with the func keyword:

funcsquare(_x:Int)->Int{x*x}square(4)// 16

Compared to closures, functions have greater flexibility in how arguments are passed.

Function arguments can have named labels instead of a closure’s unlabeled, positional arguments — which goes a long way to clarify the effect of code at its call site:

funcdeposit(amount:Decimal,fromsource:Account,todestination:Account)throws{}trydeposit(amount:1000.00,from:checking,to:savings)

Functions can be generic, allowing them to be used for multiple types of arguments:

funcsquare<T:Numeric>(_x:T)->T{x*x}funcincrement<T:Numeric>(_x:T)->T{x+1}funccompose<T>(_f:@escaping(T)->T,_g:@escaping(T)->T)->(T)->T{{xing(f(x))}}compose(increment,square)(4asInt)// 25 ((4 + 1)²)compose(increment,square)(4.2asDouble)// 27.04 ((4.2 + 1)²)

Functions can also take variadic arguments, implicit closures, and default argument values (allowing for magic expression literals like #file and #line):

funcprint(items:Any...){}funcassert(_condition:@autoclosure()->Bool,_message:@autoclosure()->String=String(),file:StaticString=#file,line:UInt=#line){}

And yet, despite all of this flexibility for accepting arguments, most functions you’ll encounter operate on an implicitself argument. These functions are called methods.

Methods

A method is a function contained by a type. Methods automatically provide access to self, allowing them to effectively capture the instance on which they’re called as an implicit argument.

structQueue<Element>{privatevarelements:[Element]=[]mutatingfuncpush(_newElement:Element){self.elements.append(newElement)}mutatingfuncpop()->Element?{guard!self.elements.isEmptyelse{returnnil}returnself.elements.removeFirst()}}

Putting everything together, these syntactic affordances allow Swift code to be expressive, clear, and concise:

varqueue=Queue<Int>()queue.push(1)queue.push(2)queue.pop()// 1

Compared to more verbose languages like Objective-C, the experience of writing Swift is, well, pretty sweet. It’s hard to imagine any Swift developers objecting to what we have here as being “sugar-coated”.

But like a 16oz can of Surge, the sugar content of something is often surprising. Turns out, that example from before is far from innocent:

varqueue=Queue<Int>()// desugars to `Queue<Int>.init()`queue.push(1)// desugars to `Queue.push(&queue)(1)`

All this time, our so-called “direct” calls to methods and initializers were actually shorthand for function curryingpartially-applied functions.

With this in mind, let’s now take another look at callable types in Swift more generally.

{Type, Instance, Member} ⨯ {Static, Dynamic}

Since their introduction in Swift 4.2 and Swift 5, respectively, many developers have had a hard time keeping@dynamicMemberLookup and @dynamicCallable straight in their minds — made even more difficult by the introduction of callAsFunction in Swift 5.2.

If you’re also confused, we think the following table can help clear things up:

 StaticDynamic
TypeinitN/A
InstancecallAsFunction@dynamicCallable
Memberfunc@dynamicMemberLookup

Swift has always had static callable types and type members. What’s changed in new versions of Swift is that instances are now callable, and both instances and members can now be called dynamically.

Let’s see what that means in practice, starting with static callables.

Static Callable

structStatic{init(){}funccallAsFunction(){}staticfuncfunction(){}funcfunction(){}}

This type can be called statically in the following ways:

letinstance=Static()//  desugars to `Static.init()`Static.function()//  (no syntactic sugar!)instance.function()//  desugars to Static.function(instance)()instance()//  desugars to `Static.callAsFunction(instance)()`
Calling the Static type invokes an initializer
Calling function on the Static type invokes the corresponding static function member, passing Static as an implicit self argument.
Calling function on an instance of Static invokes the corresponding function member, passing the instance as an implicit self argument.
Calling an instance of Static invokes the callAsFunction() function member, passing the instance as an implicit self argument.

Dynamic Callable

@dynamicCallable@dynamicMemberLookupstructDynamic{funcdynamicallyCall(withArgumentsargs:[Int])->Void{()}funcdynamicallyCall(withKeywordArgumentsargs:KeyValuePairs<String,Int>)->Void{()}staticsubscript(dynamicMembermember:String)->(Int)->Void{{_in}}subscript(dynamicMembermember:String)->(Int)->Void{{_in}}}

This type can be called dynamically in a few different ways:

letinstance=Dynamic()// desugars to `Dynamic.init()`instance(1)//  desugars to `Dynamic.dynamicallyCall(instance)(withArguments: [1])`instance(a:1)//  desugars to `Dynamic.dynamicallyCall(instance)(withKeywordArguments: ["a": 1])`Dynamic.function(1)//  desugars to `Dynamic[dynamicMember: "function"](1)`instance.function(1)//  desugars to `instance[dynamicMember: "function"](1)`
Calling an instance of Dynamic invokes the dynamicallyCall(withArguments:) method, passing an array of arguments and Dynamic as an implicit self argument.
Calling an instance of Dynamic with at least one labeled argument invokes the dynamicallyCall(withKeywordArguments:) method, passing the arguments in a KeyValuePairs object and Dynamic as an implicit self argument.
Calling function on the Dynamic type invokes the static dynamicMember subscript, passing "function" as the key; here, we call the returned anonymous closure.
Calling function on an instance of Dynamic invokes the dynamicMember subscript, passing "function" as the key; here, we call the returned anonymous closure.

Dynamism by Declaration Attributes

@dynamicCallable and @dynamicMemberLookup are declaration attributes, which means that they can’t be applied to existing declarations through an extension.

So you can’t, for example, spice upInt with Ruby-ish natural language accessors:

@dynamicMemberLookup// ⚠︎ Error: '@dynamicMemberLookup' attribute cannot be applied to this declarationextensionInt{staticsubscript(dynamicMembermember:String)->Int?{letstring=member.replacingOccurrences(of:"_",with:"-")letformatter=NumberFormatter()formatter.numberStyle=.spellOutreturnformatter.number(from:string)?.intValue}}// ⚠︎ Error: Just to be super clear, this doesn't workInt.forty_two// 42 (hypothetically, if we could apply `@dynamicMemberLookup` in an extension)

Contrast this with callAsFunction, which can be added to any type in an extension.


Speaking of RubyPython

Swift ⨯ _______

Adding toour list of “What code is like”:

Code is like fan fiction.

Sometimes to ship software, you need to pair up and “ship” different technologies.

In building these features, the “powers that be” have ordained that Swift replace Python for Machine Learning. Taking for granted that an incremental approach is best, the way to make that happen is to allow Swift to interoperate with Python as seamlessly as it does with Objective-C. And since Swift 4.2, we’ve been getting pretty close.

importPythonletnumpy=Python.import("numpy")letzeros=numpy.ones([2,4])/* [[1, 1, 1, 1]
        [1, 1, 1, 1]] */

The Externalities of Dynamism

The promise of additive changes is that they don’t change anything if you don’t want them to. You can continue to write Swift code remaining totally ignorant of the features described in this article (most of us have so far). But let’s be clear: there are no cost-free abstractions.

Economics uses the term negative externalities to describe indirect costs incurred by a decision. Although you don’t pay for these features unless you use them, we all shoulder the burden of a more complex language that’s more difficult to teach, learn, document, and reason about.


A lot of us who have been with Swift from the beginning have grown weary of Swift Evolution. And for those on the outside looking in, it’s unfathomable that we’re wasting time on inconsequential “sugar” like this instead of features that will really move the needle, like async / await.

In isolation, each of these proposals is thoughtful and useful — genuinely. We’ve already had occasion to use a few of them. But it can be really hard to judge things on their own technical merits when they’re steeped in emotional baggage.

Everyone has their own sugar tolerance, and it’s often informed by what they’re accustomed to. Being cognizant of the drawbridge effect, I honestly can’t tell if I’m out of touch, or if it’s the children who are wrong

Xcode Build Configuration Files

$
0
0

Software development best practicesprescribe strict separation of configuration from code. Yet developers on Apple platforms often struggle to square these guidelines with Xcode’s project-heavy workflow.

Understanding what each project setting does and how they all interact with one another is a skill that can take years to hone. And the fact that much of this information is buried deep within the GUIs of Xcode does us no favors.

Navigate to the “Build Settings” tab of the project editor, and you’ll be greeted by hundreds of build settings spread across layers of projects, targets, and configurations — and that’s to say nothing of the other six tabs!

Xcode build settings

Fortunately, there’s a better way to manage all of this configuration that doesn’t involve clicking through a maze of tabs and disclosure arrows.

This week, we’ll show you how you can use text-based xcconfig files to externalize build settings from Xcode to make your projects more compact, comprehensible, and powerful.


Xcode build configuration files, more commonly known by their xcconfig file extension, allow build settings for your app to be declared and managed without Xcode. They’re plain text, which means they’re much friendlier to source control systems and can be modified with any editor.

Fundamentally, each configuration file consists of a sequence of key-value assignments with the following syntax:

BUILD_SETTING_NAME=value

For example, to specify the Swift language version for a project, you’d specify the SWIFT_VERSION build setting like so:

SWIFT_VERSION=5.0

At first glance,xcconfig files bear a striking resemblance to .env files, with their simple, newline-delimited syntax. But there’s more to Xcode build configuration files than meets the eye. Behold!

Retaining Existing Values

To append rather than replace existing definitions, use the $(inherited) variable like so:

BUILD_SETTING_NAME=$(inherited)additional value

You typically do this to build up lists of values, such as the paths in which the compiler searches for frameworks to find included header files (FRAMEWORK_SEARCH_PATHS):

FRAMEWORK_SEARCH_PATHS=$(inherited)$(PROJECT_DIR)

Xcode assigns inherited values in the following order (from lowest to highest precedence):

  • Platform Defaults
  • Xcode Project xcconfig File
  • Xcode Project File Build Settings
  • Target xcconfig File
  • Target Build Settings

Referencing Values

You can substitute values from other settings by their declaration name with the following syntax:

BUILD_SETTING_NAME=$(ANOTHER_BUILD_SETTING_NAME)

Substitutions can be used to define new variables according to existing values, or inline to build up new values dynamically.

OBJROOT=$(SYMROOT)CONFIGURATION_BUILD_DIR=$(BUILD_DIR)/$(CONFIGURATION)-$(PLATFORM_NAME)

Setting Fallback Values for Referenced Build Settings

In Xcode 11.4 and later, you can use the default evaluation operator to specify a fallback value to use if the referenced build setting evaluates as empty.

$(BUILD_SETTING_NAME:default=value)

Conditionalizing Build Settings

You can conditionalize build settings according to their SDK (sdk), architecture (arch), and / or configuration (config) according to the following syntax:

BUILD_SETTING_NAME[sdk=sdk] =value for specified sdkBUILD_SETTING_NAME[arch=architecture] =value for specified architectureBUILD_SETTING_NAME[config=configuration] =value for specified configuration

Given a choice between multiple definitions of the same build setting, the compiler resolves according to specificity.

BUILD_SETTING_NAME[sdk=sdk][arch=architecture] =value for specified sdk and architecturesBUILD_SETTING_NAME[sdk=*][arch=architecture] =value for all other sdks with specified architecture

For example, you might specify the following build setting to speed up local builds by only compiling for the active architecture:

ONLY_ACTIVE_ARCH[config=Debug][sdk=*][arch=*] =YES

Including Build Settings from Other Configuration Files

A build configuration file can include settings from other configuration files using the same #include syntax as the equivalent C directive on which this functionality is based:

#include "path/to/File.xcconfig"

As we’ll see later on in the article, you can take advantage of this to build up cascading lists of build settings in really powerful ways.

Creating Build Configuration Files

To create a build configuration file, select the “File > New File…” menu item (N), scroll down to the section labeled “Other”, and select the Configuration Settings File template. Next, save it somewhere in your project directory, making sure to add it to your desired targets

Xcode new configuration file

Once you’ve created an xcconfig file, you can assign it to one or more build configurations for its associated targets.

Xcode project configuration

Now that we’ve covered the basics of using Xcode build configuration files let’s look at a couple of examples of how you can use them to manage development, stage, and production environments.


Customizing App Name and Icon for Internal Builds

Developing an iOS app usually involves juggling various internal builds on your simulators and test devices (as well as the latest version from the App Store, to use as a reference).

You can make things easier on yourself with xcconfig files that assign each configuration a distinct name and app icon.

// Development.xcconfigPRODUCT_NAME=$(inherited)αASSETCATALOG_COMPILER_APPICON_NAME=AppIcon-Alpha//////////////////////////////////////////////////// Staging.xcconfigPRODUCT_NAME=$(inherited)βASSETCATALOG_COMPILER_APPICON_NAME=AppIcon-Beta

Managing Constants Across Different Environments

If your backend developers comport themselves according to the aforementioned12 Factor App philosophy, then they’ll have separate endpoints for development, stage, and production environments.

On iOS, perhaps the most common approach to managing these environments is to use conditional compilation statements with build settings like DEBUG.

importFoundation#if DEBUGletapiBaseURL=URL(string:"https://api.staging.example.com")!#elseletapiBaseURL=URL(string:"https://api.example.com")!#endif

This gets the job done, but runs afoul of the canon of code / configuration separation.

An alternative approach takes these environment-specific values and puts them where they belong — into xcconfig files.

// Development.xcconfigAPI_BASE_URL=api.staging.example.com//////////////////////////////////////////// Production.xcconfigAPI_BASE_URL=api.example.com

However, to pull these values programmatically, we’ll need to take one additional step:

Accessing Build Settings from Swift

Build settings defined by the Xcode project file, xcconfig files, and environment variables, are only available at build time. When you run the compiled app, none of that surrounding context is available.(And thank goodness for that!)

But wait a sec — don’t you remember seeing some of those build settings before in one of those other tabs? Info, was it?

As it so happens, that info tab is actually just a fancy presentation of the target’s Info.plist file. At build time, that Info.plist file is compiled according to the build settings provided and copied into the resulting app bundle. Therefore, by adding references to $(API_BASE_URL), you can access the values for those settings through the infoDictionary property of Foundation’s Bundle API.Neat!

Xcode Info.plist

Following this approach, we might do something like the following:

importFoundationenumConfiguration{enumError:Swift.Error{casemissingKey,invalidValue}staticfuncvalue<T>(forkey:String)throws->TwhereT:LosslessStringConvertible{guardletobject=Bundle.main.object(forInfoDictionaryKey:key)else{throwError.missingKey}switchobject{caseletvalueasT:returnvaluecaseletstringasString:guardletvalue=T(string)else{fallthrough}returnvaluedefault:throwError.invalidValue}}}enumAPI{staticvarbaseURL:URL{returntry!URL(string:"https://"+Configuration.value(for:"API_BASE_URL"))!}}

When viewed from the call site, we find that this approach harmonizes beautifully with our best practices — not a single hard-coded constant in sight!

leturl=URL(string:path,relativeTo:API.baseURL)!varrequest=URLRequest(url:url)request.httpMethod=method

Xcode projects are monolithic, fragile, and opaque. They’re a source of friction for collaboration among team members and generally a drag to work with.

Fortunately,xcconfig files go a long way to address these pain points. Moving configuration out of Xcode and into xcconfig files confers a multitude of benefits and offers a way to distance your project from the particulars of Xcode without leaving the Cupertino-approved “happy path”.

Swift Logging

$
0
0

In 2002, the United States Congress enacted the Sarbanes–Oxley Act, which introduced broad oversight to corporations in response to accounting scandals at companies likeEnron and MCI WorldCom around that time. This act,PCI and HIPAA, formed the regulatory backdrop for a new generation ofIT companies emerging from the dot-com bubble.

Around the same time, we saw the emergence of ephemeral, distributed infrastructure — what we now call “Cloud computing”— a paradigm that made systems more capable but also more complex.

To solve both the regulatory and logistical challenges of the 21st century, our field established best practices around application logging. And many of the same tools and standards are still in use today.


This week on NSHipster, we’re taking a look at SwiftLog: a community-driven, open-source standard for logging in Swift.

Developed by the Swift on Server community and endorsed by theSSWG (Swift Server Work Group), its benefit isn’t limited to use on the server. Indeed, any Swift code intended to be run from the command line would benefit from adopting SwiftLog. Read on to learn how.


As always, an example would be helpful in guiding our discussion. In the spirit of transparency and nostalgia, let’s imagine writing a Swift program that audits the finances of a ’00s Fortune 500 company.

importFoundationstructAuditor{funcwatch(_directory:URL)throws{}funccleanup(){}}do{letauditor=Auditor()defer{auditor.cleanup()}tryauditor.watch(directory:URL(string:"ftp:///reports")!,extensions:["xls","ods","qdf"])// poll for changes}catch{print("error: \(error)")}

An Auditor type polls for changes to a directory(an FTP server, because remember: it’s 2003). Each time a file is added, removed, or changed, its contents are audited for discrepancies. If any financial oddities are encountered, they’re logged using the print function. The same goes for issues connecting to the FTP, or any other problems the program might encounter — everything’s logged using print.

Simple enough. We can run it from the command line like so:

$swift run auditstarting up...
        ERROR: unable to reconnect to FTP#(try again after restarting PC under our desk)$swift run audit+ connected to FTP server
        ! accounting discrepancy in balance sheet 
        ** Quicken database corruption! **
        ^C
        shutting down...

Such a program might be technically compliant, but it leaves a lot of room for improvement:

  • For one, our output doesn’t have any timestamps associated with it. There’s no way to know whether a problem was detected an hour ago or last week.

  • Another problem is that our output lacks any coherent structure. At a glance, there’s no straightforward way to isolate program noise from real issues.

  • Finally, — and this is mostly due to an under-specified example— it’s unclear how this output is handled. Where is this output going? How is it collected, aggregated, and analyzed?


The good news is that all of these problems (and many others) can be solved by adopting a formal logging infrastructure in your project.


Adopting SwiftLog in Your Swift Program

Adding SwiftLog to an existing Swift package is straightforward. You can incorporate it incrementally without making any fundamental changes to your code and have it working in a matter of minutes.

Add swift-log as a Package Dependency

In your Package.swift manifest, add swift-log as a package dependency and add the Logging module to your target’s list of dependencies.

// swift-tools-version:5.1importPackageDescriptionletpackage=Package(name:"Auditor2000",products:[.executable(name:"audit",targets:["audit"])],dependencies:[.package(url:"https://github.com/apple/swift-log.git",from:"1.2.0"),],targets:[.target(name:"audit",dependencies:["Logging"])])

Create a Shared, Global Logger

Logger provides two initializers, the simplest taking a single label parameter:

letlogger=Logger(label:"com.NSHipster.Auditor2000")

In POSIX systems, programs operate on three, predefinedstreams:

File HandleDescriptionName
0stdinStandard Input
1stdoutStandard Output
2stderrStandard Error

By default,Logger uses the built-in StreamLogHandler type to write logged messages to standard output (stdout). We can override this behavior to instead write to standard error (stderr) by using the more complex initializer, which takes a factory parameter: a closure that takes a single String parameter (the label) and returns an object conforming to LogHandler.

letlogger=Logger(label:"com.NSHipster.Auditor2000",factory:StreamLogHandler.standardError)

Replacing Print Statements with Logging Statements

Declaring our logger as a top-level constant allows us to call it anywhere within our module. Let’s revisit our example and spruce it up with our new logger:

do{letauditor=Auditor()defer{logger.trace("Shutting down")auditor.cleanup()}logger.trace("Starting up")tryauditor.watch(directory:URL(string:"ftp:///reports")!,extensions:["xls","ods","qdf"])// poll for changes}catch{logger.critical("\(error)")}

The trace, debug, and critical methods log a message at their respective log level.SwiftLog defines seven levels, ranked in ascending order of severity from trace to critical:

LevelDescription
.traceAppropriate for messages that contain information only when debugging a program.
.debugAppropriate for messages that contain information normally of use only when debugging a program.
.infoAppropriate for informational messages.
.noticeAppropriate for conditions that are not error conditions, but that may require special handling.
.warningAppropriate for messages that are not error conditions, but more severe than .notice
.errorAppropriate for error conditions.
.criticalAppropriate for critical error conditions that usually require immediate attention.

If we re-run our audit example with our new logging framework in place, we can see the immediate benefit of clearly-labeled, distinct severity levels in log lines:

$swift run audit2020-03-26T09:40:10-0700 critical: Couldn't connect to ftp://#(try again after plugging in loose ethernet cord)$swift run audit2020-03-26T10:21:22-0700 warning: Discrepancy in balance sheet
        2020-03-26T10:21:22-0700 error: Quicken database corruption
        ^C

Beyond merely labeling messages,which — don’t get us wrong — is sufficient benefit on its own, log levels provide a configurable level of disclosure. Notice that the messages logged with the trace method don’t appear in the example output. That’s because Logger defaults to showing only messages logged as info level or higher.

You can configure that by setting the Logger’s logLevel property.

varlogger=Logger(label:"com.NSHipster.Auditor2000")logger.logLevel=.trace

After making this change, the example output would instead look something like this:

$swift run audit2020-03-25T09:40:00-0700 trace: Starting up
        2020-03-26T09:40:10-0700 critical: Couldn't connect to ftp://
        2020-03-25T09:40:11-0700 trace: Shutting down#(try again after plugging in loose ethernet cord)$swift run audit2020-03-25T09:41:00-0700 trace: Starting up
        2020-03-26T09:41:01-0700 debug: Connected to ftp:///reports
        2020-03-26T09:41:01-0700 debug: Watching file extensions ["xls", "ods", "qdf"]
        2020-03-26T10:21:22-0700 warning: Discrepancy in balance sheet
        2020-03-26T10:21:22-0700 error: Quicken database corruption
        ^C
        2020-03-26T10:30:00-0700 trace: Shutting down

Using Multiple Logging Handlers at Once

Thinking back to our objections in the original example, the only remaining concern is the what we actually do with these logs.

According to 12 Factor App principles:

XI. Logs

[…]

A twelve-factor app never concerns itself with routing or storage of its output stream. It should not attempt to write to or manage logfiles. Instead, each running process writes its event stream, unbuffered, to stdout.

Collecting, routing, indexing, and analyzing logs across a distributed system often requires a constellation of open-source libraries and commercial products. Fortunately, most of these components traffic in a shared currency ofsyslog messages — and thanks to this package by Ian Partridge, Swift can, as well.

That said, few engineers have managed to retrieve this information from the likes of Splunk and lived to tell the tale. For us mere mortals, we might preferthis package by Will Lisac, which sends log messages toSlack.

The good news is that we can use both at once, without changing how messages are logged at the call site by using another piece of the Logging module:MultiplexLogHandler.

importstructFoundation.ProcessInfoimportLoggingimportLoggingSyslogimportLoggingSlackLoggingSystem.bootstrap{labelinletwebhookURL=URL(string:ProcessInfo.processInfo.environment["SLACK_LOGGING_WEBHOOK_URL"]!)!varslackHandler=SlackLogHandler(label:label,webhookURL:webhookURL)slackHandler.logLevel=.criticalletsyslogHandler=SyslogLogHandler(label:label),returnMultiplexLogHandler([syslogHandler,slackHandler])}letlogger=Logger(label:"com.NSHipster.Auditor2000")

With all of this in place, our system will log everything in syslog format to standard out (stdout), where it can be collected and analyzed by some other system.


But the real strength of this approach to logging is that it can be extended to meet the specific needs of any environment. Instead of writing syslog to stdout or Slack messages, your system could send emails, open SalesForce tickets, or trigger a webhook to activate some IoT device.

Here’s how you can extend SwiftLog to fit your needs by writing a custom log handler:

Creating a Custom Log Handler

The LogHandler protocol specifies the requirements for types that can be registered as message handlers by Logger:

protocolLogHandler{subscript(metadataKey_:String)->Logger.Metadata.Value?{getset}varmetadata:Logger.Metadata{getset}varlogLevel:Logger.Level{getset}funclog(level:Logger.Level,message:Logger.Message,metadata:Logger.Metadata?,file:String,function:String,line:UInt)}

In the process of writing this article, I created custom handler that formats log messages for GitHub Actions so that they’re surfaced on GitHub’s UI like so:

If you’re interested in making your own logging handler, you can learn a lot by just browsing the code for this project. But I did want to call out a few points of interest here:

Conditional Boostrapping

When bootstrapping your logging system, you can define some logic for how things are configured. For logging formatters specific to a particular CI vendor, for example, you might check the environment to see if you’re running locally or on CI and adjust accordingly.

importLoggingimportLoggingGitHubActionsimportstructFoundation.ProcessInfoLoggingSystem.bootstrap{labelin// Are we running in a GitHub Actions workflow?ifProcessInfo.processInfo.environment["GITHUB_ACTIONS"]=="true"{returnGitHubActionsLogHandler.standardOutput(label:label)}else{returnStreamLogHandler.standardOutput(label:label)}}

Testing Custom Log Handlers

Testing turned out to be more of a challenge than originally anticipated. I could be missing something obvious, but there doesn’t seem to be a way to create assertions about text written to standard output. So here’s what I did instead:

First, create an internal initializer that takes a TextOutputStream parameter, and store it in a private property.

publicstructGitHubActionsLogHandler:LogHandler{privatevaroutputStream:TextOutputStreaminternalinit(outputStream:TextOutputStream){self.outputStream=outputStream}}

Then, in the test target, create a type that adopts TextOutputStream and collects logged messages to a stored property for later inspection. By using a @testable import of the module declaring GitHubActionsLogHandler, we can access that internal initializer from before, and pass an instance of MockTextOutputStream to intercept logged messages.

importLogging@testableimportLoggingGitHubActionsfinalclassMockTextOutputStream:TextOutputStream{publicprivate(set)varlines:[String]=[]publicinit(_body:(Logger)->Void){letlogger=Logger(label:#file){labelinGitHubActionsLogHandler(outputStream:self)}body(logger)}// MARK: - TextOutputStreamfuncwrite(_string:String){lines.append(string)}}

With these pieces in place, we can finally test that our handler works as expected:

functestLogging(){varlogLevel:Logger.Level?letexpectation=MockTextOutputStream{loggerinlogLevel=logger.handler.logLevellogger.trace("🥱")logger.error("😱")}XCTAssertGreaterThan(logLevel!,.trace)XCTAssertEqual(expectation.lines.count,1)// trace log is ignoredXCTAssertTrue(expectation.lines[0].hasPrefix("::error "))XCTAssertTrue(expectation.lines[0].hasSuffix("::😱"))}

Contact Tracing

$
0
0

An ounce of prevention is worth a pound of cure.

Early intervention is among the most effective strategies for treating illness. This is true not only for the human body, for society as a whole. That’s why public health officials use contact tracing as their first line of defense against the spread of infectious disease in a population.

We’re hearing a lot about contact tracing these days, but the technique has been used for decades. What’s changed is that thanks to the ubiquity of personal electronic devices, we can automate what was — up until now — a labor-intensive, manual process. Much like how “computer” used to be a job title held by humans, the role of “contact tracer” may soon be filled primarily by apps.

On Friday, Apple and Google announced a joint initiative to deploy contact tracing functionality to the billions of devices running iOS or Android in the coming months. As part of this announcement, the companies shared draft specifications for thecryptography,hardware, andsoftware involved in their proposed solution.

In this article, we’ll take a first look at these specifications — particularly Apple’s proposed ContactTracing framework — and use what we’ve learned to anticipate what this will all look like in practice.


What is contact tracing?

Contact tracing is a technique used by public health officials to identify people who are exposed to an infectious disease in order to slow the spread of that illness within a population.

When a patient is admitted to a hospital and diagnosed with a new, communicable disease, they’re interviewed by health workers to learn who they’ve interacted recently. Any contacts whose interactions with the patient are then evaluated, and if they’re diagnosed with the disease, the process repeats with their known, recent contacts.

Contact tracing disrupts the chain of transmission. It gives people the opportunity to isolate themselves before infecting others and to seek treatment before they present symptoms. It also allows decision-makers to make more informed recommendations and policy decisions about additional measures to take.

If you start early and act quickly, contact tracing gives you a fighting chance of containing an outbreak before it gets out of hand.

Unfortunately, we weren’t so lucky this time around.

With over a million confirmed cases of COVID-19 worldwide, many regions are well past the point where contact tracing is practical. But that’s not to say that it can’t play an essential role in the coming weeks and months.

“Only Apple (and Google) can do this.”

Since the outbreak, various governments and academics have proposed standards for contact tracing. But the most significant development so far came yesterday with Apple and Google’s announcement of a joint initiative.

According to theNHS, around 60% of adults in a population would need to participate in order for digital contact tracing to be effective. Researchers from the aforementioned institutions have noted that the limits imposed by iOS on 3rd-party apps make this level of participation unlikely.

On the one hand, it feels weird to congratulate Apple for stepping in to solve a problem it created in the first place. But we can all agree that Friday’s announcement is something to celebrate. It’s no exaggeration to say that this wouldn’t be possible without their help.

What are Apple and Google proposing as a solution?

At a high level, Apple and Google are proposing a common standard for how personal electronic devices (phones, tablets, watches) can automate the process of contact tracing.

Instead of health workers chasing down contacts on the phone — a process that can take hours, or even days — the proposed system could identify every recent contact and notify all of them within moments of a confirmed, positive diagnosis.

Apple’s CEO, Tim Cook, promises that “Contact tracing can help slow the spread of COVID-19 and can be done without compromising user privacy.”. The specifications accompanying Friday’s announcement show how that’s possible.

Let’s take them in turn, starting with cryptography (key derivation & rotation), followed byhardware (Bluetooth), andsoftware (app) components.

Cryptography

When you install an app and open it for the first time, the ContactTracing framework displays a dialog requesting permission to enable contact tracing on the device.

If the user accepts, the framework generates a 32-byte cryptographic random number to serve as the device’s Tracing Key. The Tracing Key is kept secret, never leaving the device.

Every 24 hours, the device takes the Tracing Key and the day number (0, 1, 2, …) and uses HKDF to derive a 16-byte Daily Tracing Key. These keys stay on the device, unless you consent to share them.

Every 15 minutes, the device takes the Daily Tracing Key and the number of 10-minute intervals since the beginning of the day (0 – 143), and uses HMAC to generate a new 16-byte Rolling Proximity Identifier. This identifier is broadcast from the device usingBluetooth LE.

If someone using a contact tracing app gets a positive diagnosis, the central health authority requests their Daily Tracing Keys for the period of time that they were contagious. If the patient consents, those keys are then added to the health authority’s database as Positive Diagnosis Keys. Those keys are shared with other devices to determine if they’ve had any contact over that time period.

Hardware

Bluetooth organizes communications between devices around the concept of services.

A service describes a set of characteristics for accomplishing a particular task. A device may communicate with multiple services in the course of its operation. Many service definitions are standardized so that devices that do the same kinds of things communicate in the same way.

For example, a wireless heart rate monitor that uses Bluetooth to communicate to your phone would have a profile containing two services: a primary Heart Rate service and a secondary Battery service.

Apple and Google’s Contact Tracing standard defines a new Contact Detection service.

When a contract tracing app is running (either in the foreground or background), it acts as a peripheral, advertising its support for the Contact Detection service to any other device within range. The Rolling Proximity Identifier generated every 15 minutes is sent in the advertising packet along with the 16-bit service UUID.

Here’s some code for doing this from an iOS device using the Core Bluetooth framework:

importCoreBluetooth// Contact Detection service UUIDletserviceUUID=CBUUID(string:"FD6F")// Rolling Proximity Identifierletidentifier:Data=// 16 bytesletperipheralManager=CBPeripheralManager()letadvertisementData:[String:Any]=[CBAdvertisementDataServiceUUIDsKey:[serviceUUID]CBAdvertisementDataServiceDataKey:identifier]peripheralManager.startAdvertising(advertisementData)

At the same time that the device broadcasts as a peripheral, it’s also scanning for other devices’ Rolling Proximity Identifiers. Again, here’s how you might do that on iOS using Core Bluetooth:

letdelegate:CBCentralManagerDelegate=letcentralManager=CBCentralManager(delegate:delegate,queue:.main)centralManager.scanForPeripherals(withServices:[serviceUUID],options:[:])extensionDelegateClass:CBCentralManagerDelegate{funccentralManager(_central:CBCentralManager,didDiscoverperipheral:CBPeripheral,advertisementData:[String:Any],rssiRSSI:NSNumber){letidentifier=advertisementData[CBAdvertisementDataServiceDataKey]as!Data}}

Bluetooth is an almost ideal technology for contact tracing. It’s on every consumer smart phone. It operates with low power requirement, which lets it run continuously without draining your battery. And it just so happens to have a transmission range that approximates the physical proximity required for the airborne transmission of infectious disease. This last quality is what allows contact tracing to be done without resorting to location data.

Software

Your device stores any Rolling Proximity Identifiers it discovers, and periodically checks them against a list of Positive Diagnosis Keys sent from the central health authority.

Each Positive Diagnosis Key corresponds to someone else’s Daily Tracing Key. we can derive all of the possible Rolling Proximity Identifiers that it could advertise over the course of that day (using the same HMAC algorithm that we used to derive our own Rolling Proximity Identifiers). If any matches were found among your device’s list of Rolling Proximity Identifiers, it means that you may have been in contact with an infected individual.

Suffice to say that digital contact tracing is really hard to get right. Given the importance of getting it right, both in terms of yielding accurate results and preserving privacy, Apple and Google are providing SDKs for app developers to use for iOS and Android, respectively.

All of the details we discussed about cryptography and Bluetooth are managed by the framework. The only thing we need to do as developers is communicate with the user — specifically, requesting their permission to start contact tracing and notifying them about a positive diagnosis.

“Nobody ever got fired for buying IBMusing Objective-C.”

In our time of crisis, what technology did Apple entrust with the fate of humanity?None other than Objective-C.

typedefvoid(^CTExposureDetectionFinishHandler)(CTExposureDetectionSummary*_NullableinSummary,NSError*_NullableinError);typedefvoid(^CTExposureDetectionContactHandler)(NSArray<CTContactInfo*>*_NullableinContacts,NSError*_NullableinError);@interfaceCTExposureDetectionSession:NSObject@propertydispatch_queue_tdispatchQueue;@property(readonly,nonatomic)NSIntegermaxKeyCount;-(void)activateWithCompletion:(nullableCTErrorHandler)inCompletion;-(void)invalidate;-(void)addPositiveDiagnosisKeys:(NSArray<CTDailyTracingKey*>*)inKeyscompletion:(nullableCTErrorHandler)inCompletion;-(void)finishedPositiveDiagnosisKeysWithCompletion:(nullableCTExposureDetectionFinishHandler)inFinishHandler;-(void)getContactInfoWithHandler:(nullableCTExposureDetectionContactHandler)inHandler;@end

Although we only have the interface right now, you can get a reasonable understanding of how everything works from the APIs and documentation.

The biggest challenge you’ll face using the ContactTracing framework API is dealing with all of its completion handlers. Most of the functionality is provided through asynchronous APIs; without a way to compose these operations, you can easily find yourself nested 4 or 5 closures deep, indented to the far side of your editor.

After some trial and error, I managed to come up with a reasonable solution following the delegate pattern. The end result should be familiar to anyone who’s ever used CLLocationManager:

letmanager=ContactTracingManager.sharedmanager.delegate=DelegateClass()manager.startContactTracing()classDelegateClass:NSObject,ContactTracingManagerDelegate{funccontactTacingManager(_manager:ContactTracingManager,didReceiveExposureDetectionSummarysummary:CTExposureDetectionSummary){ifsummary.matchedKeyCount>1{// ⚠️ Possible exposure!}}}

For something that released under such an unyielding deadline, mistakes are inevitable. All in all, I think the teams responsible for the ContactTracing framework did an admirable job, and I extend my most sincere respect and gratitude for their work.

Tracing a path back to normal life

Many of us have been sheltering in place for weeks, if not months. Until a vaccine is developed and made widely available, this is the most effective strategy we have for stopping the spread of the disease.

But experts are saying that a vaccine could be anywhere from 9 to 18 months away.“What will we do until then?”

At least here in the United States, we don’t yet have a national plan for getting back to normal, so it’s hard to say. What we do know is that it’s not going to be easy, and it’s not going to come all at once.

Once the rate of new infections stabilizes, our focus will become containing new outbreaks in communities. And to that end, technology-backed contact tracing can play a crucial role.

From a technical perspective, Apple and Google’s proposal gives us every reason to believe that we can do contact tracing without compromising privacy. However, the amount of faith you put into this solution depends on how much you trust these companies and our governments in the first place.

Personally, I remain cautiously optimistic. Apple’s commitment to privacy has long been one of its greatest assets, and it’s now more important than ever.


Cross-Pollination

$
0
0

April is the month when apple trees start to bloom up here in the Pacific Northwest. All across Oregon’s Willamette Valley, from Portland stretching south to Eugene, long-barren branches sprout white, 5-petaled blossoms tinged with pink. Any other year, our family would be taking weekend trips southwest to Sherwood or east towards Hood River to visit their orchards.

Like the Fuji and Gala varieties that predominate in this region, most apple cultivars are self-unfruitful— which is to say that they require cross-pollination to produce a good crop consistently.

When fertilized by the pollen of Fuji apple blossoms (or those of Braeburn, Honey Crisp, or McIntosh varieties), a Gala apple tree can yield 20 kilograms of fruit each season. Those Gala trees, in return, endow their pollen on the Fuji apple trees so that they too may blossom and bear one or two bushels of fruit, each.

The Dance of the Honey Bee

Appletree pollen is sticky. In contrast with the windborne pollen of Alder, Birch, and Ash trees (whose allergenic quality gave the Willamette its name, meaning “valley of sickness” in the indigenous Kalapuya dialect), appletrees rely on insects to transfer pollen — particularly the honey bee.

Honey bees eat the pollen of flowers and convert their nectar into honey. Some of the pollen sticks to their furry bodies, which is inadvertently spread as they move from plant to plant.

When a scout bee encounters a food source, she flies back to the hive and communicates the location of that food source to male worker bees by performing what’s called a waggle dance. Performed in darkness on the vertical honeycomb surface in the hive, she’s able to convey the precise location of new food sources to them by flying a coffee bean-shaped pattern oriented in the direction of the sun. It’s an incredible feat, made all the more remarkable by the fact that bees are not, individually, very intelligent. Bees have brains on the order of 1 million neurons, compared to the 100 billion neurons of a human brain.

If you move a food source closer and farther away from a hive, you can see how the dance changes to convey this new information. But move it just past some critical point, and the dance becomes something entirely different: instead of the waggle dance, the bee performs a round dance with a totally different cadence and flight path.

For many years, the dance language of the bumblebee eluded all those who studied it. That is until a mathematician named Barbara Shipman made the connection between a bee’s dance language and the six-dimensional geometry of flag manifolds, of all things. What was the unique insight that allowed her to see what others couldn’t? She grew up in a family of beekeepers and cultivated an interest in mathematics and biology that carried throughout her studies.

The leap from furry, buzzing insects to abstract geometry is inconceivable unless you’re accustomed to looking at the world in that particular way.

The Rose that Grows From the Dunghill

When Apple first announced the Swift programming language in 2014, it generated a flurry of excitement as we all tried to understand it. One of the most powerful tools at our disposal for understanding is analogy:

New Thing is like Familiar Thing crossed with Another Thing.

So in those early days, there was a lot of discussion within the community attempting to compare and contrast Swift withHaskell or Go or Python or Scheme or Dylan.

Last year, we saw something similar with at WWDC 2019. Anyone familiar with React or Elm immediately recognized their influence onSwiftUI and Combine (even if Apple hadn’t come out and acknowledged it explicitly).

For some, the connection between React and Elm with JavaScript is an inconvenient truth. I’ve seen numerous developers profess their disdain for the language in ways that echo the old rivalry between iOS and Android (or the even older rivalry between Mac and PC).

And yet, there are countless examples of good ideas from “them” being criticized and mocked until they’re incorporated into an Apple product:

All of which begs the question:

Why did we consider these good ideas heretical until Apple did it?

Us vs. Them

Another flavor of this arises from the dichotomy between “Native” and “Hybrid”.

Whenever a company writes some blog post about React Native, what inevitably follows is chorus of developers who either praise the decision as courageous (if switching away) or call it idiotic (if adopting it).

As developers, we tend to align ourselves with enlightenment ideals like objectivity. We say that we make decisions based in the indisputable reality of fact. We consider ourselves reasonable and our decisions well-reasoned.

But to what extent is this actually true? Do our thoughts lead us to our preferences, or do we use thoughts to rationalize them after the fact?


In the 1960s and 70s, the social psychologist Henri Tajfel and his colleagues ran a series of experiments that demonstrated how little it takes for people to engage in intergroup discrimination.

In one experiment, a group of boys were shown pictures with clusters of dots and instructed to guess how many there were as a test of their visual judgment. The researchers split the group between those who overestimated or underestimated the number. Except, they only pretended to do this — the boys were, in fact, randomly assigned to one of the two groups. They were then given the task of allocating a fixed amount of real money to other boys in the study.

The results surprised even the researchers:

Overwhelmingly, the boys chose outcomes where their assigned group (under- or over-estimators) received more money than their counterparts — even when that meant getting less overall.

Successful replication of these results in follow-up studies since then presents compelling evidence of this peculiarity in human nature. That a willingness to engage in “us vs. them” discrimination can arise from completely artificial distinctions, irrespective of any rationale of self-interest.


How else could you explain the intense tribalism around how we talk to computers?

The Dream of Purity

When a developer proudly declares something to be“Pure Swift” or “100% JavaScript free”, what are they really saying? What’s presented as an objective statement of fact often feels more like an oath of allegiance.

If you see the existence of competing technologies as a fight between good and evil, perhaps there are more important battles to fight. If you can’t evaluate solutions as a series of trade-offs, what chance do you have at accomplishing anything at all?

Yes, there are real differences between technologies and reasonable people disagree about which one is best-suited to solve a particular problem. But don’t mistake this for a moral contest.

Purity is an ideal; a vision of the condition which needs yet to be created, or such as needs to be diligently protected against the genuine or imagined odds. Without such a vision, neither the concept of purity makes sense, nor the distinction between purity and impurity can be sensibly drawn.

– Zygmunt Bauman


It’s of no practical consequence that the grounds on which Apple Park sits today were fruit orchards a hundred years ago. But it’s poetic. Long before it was “Silicon Valley”, the stretch of land between the San Andreas and Hayward faults was called “the Valley of Heart’s Delight” for all of its fruit trees and flowering plants.

Dwelling on this, you might reflect on how humans are like apple trees. That we need a variety of different influences to reach our potential. (Even self-starters benefit from a unique perspective).

You might then consider what we share in common with the bees that pollinate apple trees. Like them, our success comes not from our individual intelligence, but in our ability to share information.

Whether we’re like bees or like apples, we come away learning the same lesson: We can achieve remarkable results by working together.

WWDC 2020

$
0
0

Like everything else in 2020, this year’s WWDC had to be a little different if it was going to happen at all.

When Apple first announced that the conference would be fully remote, nobody knew what that would look like, exactly. What parts of the dubdub experience would be kept in this new format? What details would be lost in translation? Could they actually pull it off?

As it turns out, going fully remote wasn’t merely good enough — it was, in many ways, superior to the original thing. There’s a lot to like about the new format.

The videos are well-produced, and let each presenter’s personality really shine. Everybody looks and sounds great.

Sessions are tight and well-paced. Rather than stretching or cramming content into a fixed time slot, they’re as long as they need to be. And thanks to this more digestible format, we’re starting to see WWDC clips being shared around, which is new and refreshing.

To be honest, it’s hard to imagine ever going back to a physical conference.

However, as someone who had the privilege of attending WWDC in years past, there are things I’m going to miss (and some that I decidedly won’t).

🥰😫
Refrigerators stocked with Odwalla smoothiesTrying to download the latest Xcode beta over hotel internet
Lunchtime sessionsEating lunch at or around Moscone
WWDC track jackets saving the lives of first-time attendees from the cold of San Francisco summerBeing in San Francisco, generally
Eating burritos on the terrace of McEnery Convention Center during WWDC check-inBeing in San Jose, generally
Guessing who would be playing at Bash this yearHearing the same handful of songs on repeat before and after every session (this song in particular)
Watching Apple executives dance at Bash
Talking to people as you wait in line for the keynote on Monday morningWaking up late and having to settle for watching from the overflow room
Leaving at the end of the week with a mix of hope, fear, and inspiration *Being sick for the next week with dubdub flu

* I’d like to hold on this last point for a moment.


In the Before Times, many of us traveled far from home to attend WWDC. There was a physical and temporal delineation between life before, during, and after the conference. Flying out of SFO, SJC, or OAK, you escaped Apple’s “reality distortion field” and its surrounding echo chamber. You returned to your normal life.

This year? Not so much.

WWDC 2020 was just another week in this bizarre existence amidst this pandemic. Not only is there no escape from the “reality distortion field”, there isn’t even a “normal life” for us to leave or return to.

So here we are, filled with anxious excitement and fear; our corporeal forms replaced by Memoji floating in a black, digital void lit only by the screens of our soon-to-be-obsolete MacBooks Pro.


Excitement

I don’t have a real sense of how everything went over this year. There wasn’t any applause (or heckling) at this year’s keynote to gauge the temperature in the room. There were no parties to overhear shop talk and hot takes. There was no line for lunch to make small talk with a fellow attendee.

But if Twitter is anything to go on, my overall impression is that everyone is really excited.

Which is fine. I get it.

But it should come as no surprise that things announced at WWDC are exciting — that’s the whole point of having a developer conference in the first place. Apple has the best marketing in the world, and WWDC is Apple’s way of marketing to us.

Here’s the thing about excitement: It’s kryptonite to developers.

Excitement messes with our ability to focus on one thing, which is already a big struggle for a lot of us (myself included). When you’re excited, it’s almost impossible to get anything done.

There are plenty of voices in the community who are echoing this excitement. I can’t add anything to that discussion. And besides, that’s not really where my head’s at right now.

Trivial Pursuit

I briefly considered reviving the NSHipster Quiz for WWDC 2020, but it didn’t feel right. With everything going on in the world, Apple trivia just isn’t where my head or heart are right now.

To give you a sense of what I mean, here’s what I had for Round 3, whose theme was inspired by Richard Hamming:

Question 1.
What are the important problems of your field?
Question 2.
What important problems are you working on?
Question 3.
If what you are doing is not important, why are working on it?

Temperance

If you’re serious about solving a problem, you owe it to yourself to temper any excitement that distracts you from making real progress.

In last year’s write-up for WWDC 2019, I concluded with the following, as a counterpoint to the conference’s theme of #mindblown🤯:

Taking care of yourself — sleeping enough, eating right, exercising regularly — will do more to improve your productivity than any language or framework out there. Your ability to communicate and collaborate with others will always be a better predictor of success than your choice of technology stack. Your relationships with others are the most significant factors of success and happiness in life.

I stand by this advice, boring as it may be.

It’s been an exciting week, so take a moment to collect yourself. Go on a walk. Take a hike. (Be safe about it.) Do whatever you need to break free of the “reality distortion field”. Once you do, you’ll have the necessary distance to determine what new technologies you should pay attention to and what you can ignore for now.

We have a lot of work ahead of us.

As We May Code

$
0
0

Chris Lattner often describes LLVM as a process of lowering.

Swift Compiler Architecture Diagram

You start at the highest level of abstraction, source code written in a programming language like Swift or Objective-C. That code is parsed into an abstract syntax tree, (AST), which is progressively transformed into lower-level, intermediate representations until it finally becomes executable binary.

What if, instead of lowering source code down for the purpose of execution, we raised source code for the purpose of understanding?

You could say that we already do this to some degree withsyntax highlighting
(func f()funcf()),structured editing, anddocumentation generation. But how far could we take it?


In this article, I’d like to share an idea that I’ve been kicking around for a while. It’s something that’s come into greater focus with my recent work on swift-doc, but first started to form during tenure in Apple Developer Publications, back in 2015.

The idea is this:
What if we took the lessons of the semantic web and applied them to source code?

Specifically:

  • Representation: Software components should be represented by a common, language-agnostic data format.
  • Addressability: Packages, modules, and their constituent APIs should each have a unique URL identifier.
  • Decentralization: Information should be distributed across a federated network of data sources, which can cross-reference one another by URL.

I grew up with the Internet, and got to see it, first-hand, go from an obscure technology to the dominant cultural force. So much of what I see in software development today reminds me of what I remember about the web from 20 years ago. And if you’ll forgive the extended wind-up, I think there’s a lot we can learn by looking at that evolution.


Web 1.0 The Web of Documents

Tim Berners-Lee launched the World Wide Web from a NeXT workstation 27 years ago. His vision for a globally-distributed, decentralized network of inter-connected documents gave rise to the Internet as we know it today. But it was also part of an intellectual tradition dating back to the 1940s, which includes Vannevar Bush’s Memex, Ted Nelson’s Xanadu, and Doug Engelbart’s Mother of All Demos.

In those early days, the knowledge being shared was primarily academic. As the userbase grew over time, so too did the breadth and diversity of the information available. And, for a time, that’s what the Internet was: fan sites for Sunbeam toasters,recipes for Neapolitan-style pizza, andthe official website for the 1996 film Space Jam.

But the web of documents had limits.

If you wanted to shop for appliances, see the menu of a pizza shop, or get local showtimes for a movie, you might be able to do that on the early Internet. But you really had to work at it.

Back then, you’d start by going to a directory like Yahoo! or DMOZ, navigate to the relevant topic, and click around until you found a promising lead. Most of the time, you wouldn’t find what you were looking for; instead, you’d disconnect your modem to free up your landline and consult the yellow pages.

This started to change in the early ’00s.

Web 2.0 The Social Web

With Perl CGI and PHP, you could now easily generate web pages on-the-fly. This enabled eCommerce and the first commercial uses of the Internet.

After the ensuing dot-com bubble, you had technologies like Java applets and Flash bring a new level of interactivity to web sites. Eventually, folks figured out how to usean obscure API from Internet Explorer 5 to replicate this interactivity on normal webpages — a technique dubbed AJAX. Interacting with a page and seeing results live, without reloading a page? This was huge. Without that, social media might not have taken off as it did.

Anyway, the server-side APIs powering those AJAX interactions on the client, they were the secret sauce that let the Internet evolve into what it is today.

Remember mashups?

Thanks to all of these (often unsecured) AJAX endpoints, developers could synthesize information across multiple sources in ways that nobody had ever thought to do. You could get someone’s location from Fire Eagle, search for photos taken nearby on Flickr, and use MOO to print and deliver prints of them on-demand.

By the end of the decade, the rise of social networks and the early promise of mashups started to coalesce into the modern Internet.

Web 3.0 The Web of Data

The term “Web 3.0” didn’t catch on like its predecessor, but there’s a clear delineation between the technologies and culture of the web between the early and late ’00s.

It’s hard to overstate how much the iPhone’s launch in 2007 totally changed the trajectory of the Internet. But many other events played an important role in shaping the web as we know it today:

  • Google acquiring the company behind Freebase, giving it a knowledge graph to augment its website index.
  • Facebook launching Open Graph, which meant everything could now be “Liked” (and everyone could be targeted for advertisements).
  • Yahoo releasing SearchMonkey andBOSS, two ambitious (albeit flawed) attempts to carve out a niche from Google’s monopoly on search.
  • Wolfram launching Wolfram|Alpha, which far exceeded what many of us thought was possible for a question answering system.

The Internet always had a lot of information on it; the difference now is that the information is accessible to machines as well as humans.

Today, you can ask Google“Who was the first person to land on the moon?” and get an info box saying, “Commander Neil Armstrong”. You can post a link in Messages and see it represented bya rich visual summary instead of a plain text URL. You can ask Siri,“What is the airspeed velocity of an unladen swallow?” and hear back“I can’t get the answer to that on HomePod”About 25 miles per hour.

Think about what we take for granted about the Internet now, and try to imagine doing that on the web when it lookedlike this. It’s hard to think that any of this would be possible without the semantic web.


GitHub.com, Present Day The Spider and The Octocat

READMEs on GitHub.com today remind me of personal home pages on Geocities back in the Web 1.0 days.

Even with the standard coat of paint, you see an enormous degree of variance across projects and communities. Some are sparse; others are replete with adornment.

And yet, no matter what a project’s README looks like, onboarding onto a new tool or library entails, well reading.

GitHub offers some structured informational cues: language breakdown, license, some metadata about commit activity. You can search within the repo using text terms. And thanks to semantic / tree-sitter, you can even click through to find declarations in some languages.

But where’s a list of methods?Where are the platform requirements?
You have to read the README to find out! (Better hope it’s up-to-date 😭)

The modest capabilities of browsing and searching code today more closely resemble AltaVista circa 2000 than Google circa 2020. Theres so much more that we could be doing.


RDF Vocabularies The Owl and The Turtle

At the center of the semantic web is something calledRDF, the Resource Description Framework. It’s a collection of standards for representing and exchanging data. The atomic data entity in RDF is called a triple, which comprises:

  • a subject (“the sky”)
  • a predicate (“has the color”)
  • an object (“blue”)

You can organize triples according to avocabulary, or ontology, which defines rules about how things are described. RDF vocabularies are represented by the Web Ontology Language (OWL).

The ideas behind RDF are simple enough. Often, the hardest part is navigating its confusing, acronym-laden technology stack. The important thing to keep in mind is that information can be represented in several different ways without changing the meaning of that information.

Here’s a quick run-down:

RDF/XML
An XML representation format for RDF graphs.
JSON-LD
A JSON representation format for RDF graphs.
N-Triples
A plain text representation format for RDF graphs where each line encodes a subject–predicate–object triple.
Turtle
A human-friendly, plain text representation format for RDF graphs. A superset of N-Triples, and the syntax used in SPARQL queries.
SPARQL
A query language for RDF graphs.

Defining a Vocabulary

Let’s start to define a vocabulary for the Swift programming language. To start, we’ll define the concept of a Symbol along with two subclasses, Structure and Function. We’ll also define a name property that holds a token (a string) that applies to any Symbol. Finally, we’ll define a returns property that applies to a Function and holds a reference to another Symbol.

@prefix:<http://www.swift.org/#>.@prefixowl:<http://www.w3.org/2002/07/owl#>.@prefixrdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#>.@prefixrdfs:<http://www.w3.org/2000/01/rdf-schema#>.@prefixxsd:<http://www.w3.org/2001/XMLSchema#>.:Symbolrdf:typeowl:Class.:namerdf:typeowl:FunctionalProperty;rdfs:domain:Symbol;rdfs:rangexsd:token.:Structurerdfs:subClassOf:Symbol.:Functionrdfs:subClassOf:Symbol.:returnsrdf:typeowl:FunctionalProperty;rdfs:domain:Function;rdfs:range:Symbol.

Parsing Code Declarations

Now consider the following Swift code:

structWidget{}funcfoo()->Widget{}funcbar()->Widget{}

We can use SwiftSyntax to parse the code into an AST and SwiftSemantics to convert those AST nodes into a more convenient representation.

importSwiftSyntaximportSwiftSemanticsvarcollector=DeclarationCollector()lettree=trySyntaxParser.parse(source:source)collector.walk(tree)collector.functions.first?.name// "foo()"collector.functions.first?.returns// "Widget"

Combining this syntactic reading with information from compiler, we can express facts about the code in the form of RDF triples.

{"@context":{"name":{"@id":"http://www.swift.org/#name","@type":"http://www.w3.org/2001/XMLSchema#token"},"returns":"http://www.swift.org/#returns"},"symbols":[{"@id":"E83C6A28-1E68-406E-8162-D389A04DFB27","@type":"http://www.swift.org/#Structure","name":"Widget"},{"@id":"4EAE3E8C-FD96-4664-B7F7-D64D8B75ECEB","@type":"http://www.swift.org/#Function","name":"foo()"},{"@id":"2D1F49FE-86DE-4715-BD59-FA70392E41BE","@type":"http://www.swift.org/#Function","name":"bar()"}]}

Encoding our knowledge into a standard format lets anyone access that information — however they like. And because these facts are encoded within an ontology, they can be validated for coherence and consistency. It’s totally language agnostic.

Querying the Results

With an RDF graph of facts, we can query it using SPARQL. Or, we could load the information into a graph database like Neo4j or a relational database like PostgreSQL and perform the query in Cypher or SQL, respectively.

PREFIXswift:<http://www.swift.org/#>SELECT?function?nameWHERE{?functionaswift:Function;swift:returns?type;swift:name?name.?typeswift:name"Widget".}ORDERBY?function

Whichever route we take, we get the same results:

idname
4EAE3E8C-FD96-4664-B7F7-D64D8B75ECEBfoo()
2D1F49FE-86DE-4715-BD59-FA70392E41BEbar()

Answering Questions About Your Code

“What can you do with a knowledge graph?” That’s kind of like asking, “What can you do with Swift?” The answer — “Pretty much anything”— is as true as it is unhelpful.

Perhaps a better framing would be to consider the kinds of questions that a knowledge graph of code symbols can help answer:

  • Which methods in Foundation produce a Date value?
  • Which public types in my project don’t conform to Codable?
  • Which methods does Array inherit default implementations from RandomAccessCollection?
  • Which APIs have documentation that includes example code?
  • What are the most important APIs in MapKit?
  • Are there any unused APIs in my project?
  • What’s the oldest version of iOS that my app could target based on my current API usage?
  • What APIs were added to Alamofire between versions 4.0 and 4.2?
  • What APIs in our app are affected by a CVE issued for a 3rd-party dependency?

The possibilities get even more interesting as you layer additional contexts by linking Swift APIs to different domains and other programming languages:

  • How is this Swift API exposed in Objective-C?
  • Who are the developers maintaining the packages that are pulled in as external dependencies for this project?
  • What’s the closest functional equivalent to this Swift package that’s written in Rust?

Future Applications The Promise of What Lies Ahead

Any fact becomes important when it’s connected to another.

Umberto Eco, Foucault’s Pendulum

Operating on code symbolically is more powerful than treating it as text. Once you’ve experienced proper refactoring tools, you’ll never want to go back to global find-and-replace.

The leap from symbolic to semantic understanding of code promises to be just as powerful. What follows are a few examples of potential applications of the knowledge graph we’ve described.

Flexible Search Queries

GitHub’s advanced search provides an interface to filter results on variousfacets, but they’re limited to metadata about the projects. You can search for Swift code written by @kateinoigakukun in 2020, but you can’t, for example, filter for code compatible with Swift 5.1. You can search code for the string “record”, but you can’t disambiguate between type and function definitions (class Record vs. func record()).

As we showed earlier, the kinds of queries we can perform across a knowledge graph are fundamentally different from what’s possible with a conventional faceted, full-text search index.

For example, here’s a SPARQL query to find the urls of repositories created by @kateinoigakukun and updated this year that contain Swift functions named record:

PREFIXswift:<http://www.swift.org/#>skos:<http://www.w3.org/2004/02/skos/core/#>sdo:<http://schema.org/#>SELECT?urlWHERE{?functionaswift:Function;swift:name"record";skos:member?repository.?repositoryasdo:SoftwareSourceCode;sdo:contributor?contributor;sdo:url?url;sdo:dateModified?date.?contributorasdo:Person;sdo:username"kateinoigakukun".FILTER(?date>="2020-01-01")}ORDERBY?url

Linked Documentation

When faced withmissing or incomplete documentation, developers are left to search Google for blog posts, tutorials, conference videos, and sample code to fill in the gaps. Often, this means sifting through pages of irrelevant results — to say nothing of outdated and incorrect information.

A knowledge graph can improve search for documentation much the same as it can for code, but we can go even further. Similar to how academic papers contain citations, example code can be annotated to include references to the canonical APIs it interacts with. Strong connections between references and its source material make for easy retrieval later on.

Imagine if, when you option-click on an API in Xcode to get its documentation, you also saw a list of sample code and WWDC session videos? Or what if we could generate sample code automatically from test cases? Wouldn’t that be nice?

All of that information is out there, just waiting for us to connect the dots.

Automatic µDependencies

John D. Cook onceobserved, code reuse is more like an organ transplant than snapping LEGO blocks together. Fred Brooks similarly analogized software developers to surgeons inThe Mythical Man-Month.

But that’s not to say that things can’t get better — it’d be hard to argue that they haven’t.

Web applications were once described in similar, organic terms, but that came to an end with the advent ofcontainerization. Now you can orchestrate entire multi-cloud deployments automatically via declarative configuration files.

Before [CPAN], the state of the art for dependency management was copy-pasting chunks of codeyou found on a web page. But today, package managers are essential infrastructure for projects.


What if, instead of organizing code into self-contained, modular chunks ourselves, we let software do it for us? Call itFaaD (Functions as a Dependency).

Say you want an implementation ofk-means clustering. You might search around for “k-means” or “clustering” on GitHub and find a package named “SwiftyClusterAlgorithms” (😒), only to discover that it includes a bunch of functionality that you don’t need — and to add insult to injury, some of those extra bits happen to generate compiler warnings. Super annoying.

Today, there’s no automatic way to pick and choose what you need. (Swift import syntax (import func kMeans) is a lie) But there’s no inherent reason why the compiler couldn’t do this for you.

Or to go even further: If everything compiles down to web assembly, there’s no inherent requirement for that implementation of k-means — it could be written in Rust or JavaScript, and you’d be none the wiser.

At a certain point, you start to question the inherent necessity of software packaging as we know it today. Take it far enough, and you may wonder how much code we’ll write ourselves in the future.

Code Generation

A few months ago, Microsoft hosted its Build conference. And among the videos presented was an interview withSam Altman, CEO of OpenAI. A few minutes in, the interview cut to a video of Sam using a fine-tuned version ofGPT-2 towrite Python code from docstrings.

defis_palindrome(s):"""Check whether a string is a palindrome"""returns==s[::-1]# ← Generated by AI model from docstring!
        

And that’s using a model that treats code as text. Imagine how far you could go with a priori knowledge of programming languages! Unlike English, the rules of code are, well, codified. You can check to see if code compiles — and if it does compile, you can run it to see the results.

At this point, you should feel either very worried or very excited.
If you don’t, then you’re not paying attention.

Taking Ideas Seriously The Shoemaker’s Children

The use of FORTRAN, like the earlier symbolic programming, was very slow to be taken up by the professionals. And this is typical of almost all professional groups. Doctors clearly do not follow the advice they give to others, and they also have a high proportion of drug addicts. Lawyers often do not leave decent wills when they die. Almost all professionals are slow to use their own expertise for their own work. The situation is nicely summarized by the old saying,“The shoe maker’s children go without shoes”. Consider how in the future, when you are a great expert, you will avoid this typical error!

Richard W. Hamming, “The Art of Doing Science and Engineering”

Today, lawyers delegate many paralegal tasks like document discovery to computers and doctors routinely use machine learning models to help diagnose patients.

So why aren’t we — ostensibly the people writing software— doing more with AI in our day-to-day? Why are things like TabNine andKite so often seen as curiosities instead of game-changers?

If you take seriously the idea thatAI will fundamentally change the nature of many occupations in the coming decade, what reason do you have to believe that you’ll be immune from that because you work in software? Looking at the code you’ve been paid to write over the past few years, how much of that can you honestly say is truly novel?

We’re really not as clever as we think we are.


Postscript Reflection and Metaprogramming

Today marks 8 years since I started NSHipster.

You might’ve noticed that I don’t write here as much as I once did. And on the occasions that I do publish an article, it’s more likely to include obscure historical facts andcultural references than to the obscure APIs promised by this blog’s tagline.

A few weeks out now from WWDC, I should be writing aboutDCAppAttestService,SKTestSession, SwiftUI Namespace andUTType. But here we are, at the end of an article about the semantic web, of all things…


The truth is, I’ve come around to thinking that programming isn’t the most important thing for programmers to pay attention to right now.


Anyway, I’d like to take this opportunity to extend my sincere gratitude to everyone who reads the words I write. Thank you. It may be a while before I get back into a regular cadence, so apologies and in advance.

Until next time,May your code continue to compile and inspire.

RawRepresentable

$
0
0

Programming is about typing. And programming languages are typically judged by how much they make you type — in both senses of the word.

Swift is beloved for being able to save us a few keystrokes without compromising safety or performance, whether it’s through implicit typing or automatic synthesis of protocols like Equatable andHashable. But the OG ergonomic feature of Swift is undoubtedly automatic synthesis of RawRepresentable conformance for enumerations with raw types. You know… the language feature that lets you do this:

enumGreeting:String{casehello="hello"casegoodbye// implicit raw value of "goodbye"}enumSortOrder:Int{caseascending=-1casesame// implicit raw value of 0casedescending// implicit raw value of 1}

Though “enum + RawValue” has been carved into the oak tree of our hearts since first we laid eyes on that language with a fast bird, few of us have had occasion to consider what RawRepresentable means outside of autosynthesis. This week, we invite you to do a little extra typing and explore some untypical use cases for the RawRepresentable protocol.


In Swift, an enumeration can be declared withraw value syntax.

According to the documentation:

For any enumeration with a string, integer, or floating-point raw type, the Swift compiler automatically adds RawRepresentable conformance.

When developers first start working with Swift, they inevitably run into situations where raw value syntax doesn’t work:

  • Enumerations with raw values other than Int or String
  • Enumerations with associated values

Upon seeing those bright, red error sigils, many of us fall back to a more conventional enumeration, failing to realize that what we wanted to do wasn’t impossible, but rather just slightly beyond what the compiler can do for us.


RawRepresentable with C Raw Value Types

The primary motivation for raw value enumerations is to improve interoperability. Quoting again from the docs:

Using the raw value of a conforming type streamlines interoperation with Objective-C and legacy APIs.

This is true of Objective-C frameworks in the Apple SDK, which declare enumerations with NS_ENUM. But interoperability with other C libraries is often less seamless.

Consider the task of interfacing with libcmark, a library for working with Markdown according to theCommonMark spec. Among the imported data types is cmark_node_type, which has the following C declaration:

typedefenum{/* Error status */CMARK_NODE_NONE,/* Block */CMARK_NODE_DOCUMENT,CMARK_NODE_BLOCK_QUOTE,CMARK_NODE_HEADING,CMARK_NODE_THEMATIC_BREAK,CMARK_NODE_FIRST_BLOCK=CMARK_NODE_DOCUMENT,CMARK_NODE_LAST_BLOCK=CMARK_NODE_THEMATIC_BREAK,}cmark_node_type;

We can immediately see a few details that would need to be ironed out along the path of Swiftification — notably, 1) the sentinel NONE value, which would instead be represented by nil, and 2) the aliases for the first and last block values, which wouldn’t be encoded by distinct enumeration cases.

Attempting to declare a Swift enumeration with a raw value type of cmark_node_type results in a compiler error.

enumNodeType:cmark_node_type{}// Error

However, that doesn’t totally rule out cmark_node_type from being a RawValue type. Here’s what we need to make that happen:

enumNodeType:RawRepresentable{casedocumentcaseblockQuoteinit?(rawValue:cmark_node_type){switchrawValue{caseCMARK_NODE_DOCUMENT:self=.documentcaseCMARK_NODE_BLOCK_QUOTE:self=.blockQuotedefault:returnnil}}varrawValue:cmark_node_type{switchself{case.document:returnCMARK_NODE_DOCUMENTcase.blockQuote:returnCMARK_NODE_BLOCK_QUOTE}}}

It’s a far cry from being able to say case document = CMARK_NODE_DOCUMENT, but this approach offers a reasonable solution that falls within the existing semantics of the Swift standard library.

That debunks the myth aboutInt and String being the only types that can be a raw value. What about that one about associated values?

RawRepresentable and Associated Values

In Swift, an enumeration case can have one or more associated values. Associated values are a convenient way to introduce some flexibility into the closed semantics of enumerations and all the benefits they confer.

As the old adage goes:

There are three numbers in computer science: 0, 1, and N.

enumNumber{casezerocaseonecasen(Int)}

Because of the associated value on n, the compiler can’t automatically synthesize an Int raw value type. But that doesn’t mean we can’t roll up our sleeves and pick up the slack.

extensionNumber:RawRepresentable{init?(rawValue:Int){switchrawValue{case0:self=.zerocase1:self=.onecaseletn:self=.n(n)}}varrawValue:Int{switchself{case.zero:return0case.one:return1caselet.n(n):returnn}}}Number(rawValue:1)// .one

Another myth busted!

Let’s continue this example to clear up a misconception we found in the documentation.

RawRepresentable as Raw Values for Another Enumeration

Consider the following from the RawRepresentable docs:

For any enumeration with a string, integer, or floating-point raw type, the Swift compiler automatically adds RawRepresentable conformance.

This is, strictly speaking, true. But it actually under-sells what the compiler can do. The actual requirements for raw values are as follows:

  • The raw value type must be Equatable
  • The raw value type must beExpressibleByIntegerLiteral,ExpressibleByFloatLiteral, orExpressibleByStringLiteral
  • The raw value for each enumeration case must be a literal (or unspecified, in which case the value is inferred)

Let’s see what happens if we satisfy that for our Number type from before.

extensionNumber:Equatable{}// conformance is automatically synthesizedextensionNumber:ExpressibleByIntegerLiteral{init(integerLiteralvalue:Int){self.init(rawValue:value)!}}-1asNumber// .n(-1)0asNumber// .zero1asNumber// .one2asNumber// .n(2)

If we declare a new enumeration, (literally “Number”) with a Number raw value…

enum:Number{case=1case=2case=3}.// 二..rawValue// .n(2)..rawValue.rawValue// 2

Wait, that actually works? Neat!

What’s really interesting is that our contrived little enumeration type benefits from the same, small memory footprint that you get from using enumerations in more typical capacities:

MemoryLayout.size(ofValue:.)// 1 (bytes)MemoryLayout.size(ofValue:..rawValue)// 9 (bytes)MemoryLayout.size(ofValue:..rawValue.rawValue)// 8 (bytes)

If raw values aren’t limited to String or Int, as once believed, you may start to wonder:How far can we take this?

RawRepresentable with Metatype Raw Values

Probably the biggest selling point of enumerations in Swift is how they encode a closed set of values.

enumElement{caseearth,water,air,fire}

Unfortunately, there’s no equivalent way to “close off” which types conform to a protocol.

publicprotocolElemental{}publicstructEarth:Elemental{}publicstructWater:Elemental{}publicstructAir:Elemental{}publicstructFire:Elemental{}

Without built-in support for type unions or an analog to the open access modifier for classes, there’s nothing that an API provider can do, for example, to prevent a consumer from doing the following:

structAether:Elemental{}

Any switch statement over a type-erased Elemental value using is checks will necessarily have a default case.

Until we have a first-class language feature for providing such guarantees, we can recruit enumerations and raw values for a reasonable approximation:

extensionElement:RawRepresentable{init?(rawValue:Elemental.Type){switchrawValue{caseisEarth.Type:self=.earthcaseisWater.Type:self=.watercaseisAir.Type:self=.aircaseisFire.Type:self=.firedefault:returnnil}}varrawValue:Elemental.Type{switchself{case.earth:returnEarth.selfcase.water:returnWater.selfcase.air:returnAir.selfcase.fire:returnFire.self}}}

Returning one last time to the docs, we’re reminded that:

With a RawRepresentable type, you can switch back and forth between a custom type and an associated RawValue type without losing the value of the original RawRepresentable type.

From the earliest days of the language, RawRepresentable has been relegated to the thankless task of C interoperability. But looking now with a fresh set of eyes, we can now see it for in all its injective glory.

So the next time you find yourself with an enumeration whose cases broker in discrete, defined counterparts, consider adopting RawRepresentable to formalize the connection.

Static and Dynamic Callable Types in Swift

$
0
0

Last week, Apple released the first beta of Xcode 11.4, and it’s proving to be one of the most substantial updates in recent memory. XCTest got a huge boost, with numerous quality of life improvements, and Simulator, likewise, got a solid dose ofTLC. But it’s the changes to Swift that are getting the lion’s share of attention.

In Xcode 11.4, Swift compile times are down across the board, with many developers reporting improvements of 10 – 20% in their projects. And thanks to a new diagnostics architecture, error messages from the compiler are consistently more helpful. This is also the first version of Xcode to ship with the newsourcekit-lsp server, which serves to empower editors like VSCode to work with Swift in a more meaningful way.

Yet, despite all of these improvements (which are truly an incredible achievement by Apple’s Developer Tools team), much of the early feedback has focused on the most visible additions to Swift 5.2. And the response from the peanut galleries of Twitter, Hacker News, and Reddit has been — to put it charitably — “mixed”.


If like most of us, you aren’t tuned into the comings-and-goings of Swift Evolution, Xcode 11.4 was your first exposure to two new additions to the language:key path expressions as functions andcallable values of user-defined nominal types.

The first of these allows key paths to replace one-off closures used by functions like map:

// Swift >= 5.2"🧁🍭🍦".unicodeScalars.map(\.properties.name)// ["CUPCAKE", "LOLLIPOP", "SOFT ICE CREAM"]// Swift <5.2 equivalent"🧁🍭🍦".unicodeScalars.map{$0.properties.name}

The second allows instances of types with a method named callAsFunction to be called as if they were a function:

structSweetener{letadditives:Set<Character>init<S>(_sequence:S)whereS:Sequence,S.Element==Character{self.additives=Set(sequence)}funccallAsFunction(_message:String)->String{message.split(separator:" ").flatMap{[$0,"\(additives.randomElement()!)"]}.joined(separator:" ")+"😋"}}letdessertify=Sweetener("🧁🍭🍦")dessertify("Hello, world!")// "Hello, 🍭 world! 🍦😋"

Granted, both of those examples are terrible. And that’s kinda the problem.


Too often, coverage of “What’s New In Swift” amounts to little more than a regurgitation of Swift Evolution proposals, interspersed with poorly motivated (and often emoji-laden) examples. Such treatments provide a poor characterization of Swift language features, and — in the case of Swift 5.2 — serves to feed into the popular critique that these are frivolous additions — mere syntactic sugar.

This week, we hope to reach the ooey gooey center of the issue by providing some historical and theoretical context for understanding these new features.

Syntactic Sugar in Swift

If you’re salty about “key path as function” being too sugary, recall that the status quo isn’t without a sweet tooth. Consider our saccharine example from before:

"🧁🍭🍦".unicodeScalars.map{$0.properties.name}

That expression relies on at least four different syntactic concessions:

  1. Trailing closure syntax, which allows a final closure argument label of a function to be omitted
  2. Anonymous closure arguments, which allow arguments in closures to be used positionally ($0, $1, …) without binding to a named variable.
  3. Inferred parameter and return value types
  4. Implicit return from single-expression closures

If you wanted to cut sugar out of your diet completely, you’d best get Mavis Beacon on the line, because you’ll be doing a lot more typing.

"🧁🍭🍦".unicodeScalars.map(transform:{(unicodeScalar:Unicode.Scalar)->StringinreturnunicodeScalar.properties.name})

In fact, as we’ll see in the examples to come, Swift is a marshmallow world in the winter, syntactically speaking. From initializers and method calls to optionals and method chaining, nearly everything about Swift could be described as a cotton candy melody — it really just depends on where you draw the line between “language feature” and “syntactic sugar”.


To understand why, you have to understand how we got here in the first place, which requires a bit of history, math, and computer science. Get ready to eat your vegetables 🥦.

The λ-Calculus and Speculative Computer Science Fiction

All programming languages can be seen as various attempts to representthe λ-calculus. Everything you need to write code — variables, binding, application — it’s all in there, buried under a mass of Greek letters and mathematical notation.

Setting aside syntactic differences, each programming language can be understood by its combination of affordances for making programs easier to write and easier to read. Language features like objects, classes, modules, optionals, literals, and generics are all just abstractions built on top of the λ-calculus.

Any other deviation from pure mathematical formalism can be ascribed to real-world constraints, such asa typewriter from the 1870s,a punch card from the 1920s,a computer architecture from the 1940s, or a character encoding from the 1960s.

Among the earliest programming languages were Lisp, ALGOL*, and COBOL, from which nearly every other language derives.

(defunsquare(x)(*xx))(print(square4));; 16

Here you get a glimpse into three very different timelines; ours is the reality in which ALGOL’s syntax (option #2) “won out” over the alternatives. From ALGOL 60, you can draw a straight line from CPL in 1963, to BCPL in 1967 and C in 1972, followed by Objective-C in 1984 and Swift in 2014. That’s the lineage that informs what types are callable and how we call them.


Now, back to Swift…

Function Types in Swift

Functions are first-class objects in Swift, meaning that they can be assigned to variables, stored in properties, and passed as arguments or returned as values from other functions.

What distinguishes function types from other values is that they’re callable, meaning that you can invoke them to produce new values.

Closures

Swift’s fundamental function type is the closure, a self-contained unit of functionality.

letsquare:(Int)->Int={xinx*x}

As a function type, you can call a closure by passing the requisite number of arguments between opening and closing parentheses ()a la ALGOL.

square(4)// 16

Closures are so called because they close over and capture references to any variables from the context in which they’re defined. However, capturing semantics aren’t always desirable, which is why Swift provides dedicated syntax to a special kind of closure known as a function.

Functions

Functions defined at a top-level / global scope are named closures that don’t capture any values. In Swift, you declare them with the func keyword:

funcsquare(_x:Int)->Int{x*x}square(4)// 16

Compared to closures, functions have greater flexibility in how arguments are passed.

Function arguments can have named labels instead of a closure’s unlabeled, positional arguments — which goes a long way to clarify the effect of code at its call site:

funcdeposit(amount:Decimal,fromsource:Account,todestination:Account)throws{}trydeposit(amount:1000.00,from:checking,to:savings)

Functions can be generic, allowing them to be used for multiple types of arguments:

funcsquare<T:Numeric>(_x:T)->T{x*x}funcincrement<T:Numeric>(_x:T)->T{x+1}funccompose<T>(_f:@escaping(T)->T,_g:@escaping(T)->T)->(T)->T{{xing(f(x))}}compose(increment,square)(4asInt)// 25 ((4 + 1)²)compose(increment,square)(4.2asDouble)// 27.04 ((4.2 + 1)²)

Functions can also take variadic arguments, implicit closures, and default argument values (allowing for magic expression literals like #file and #line):

funcprint(items:Any...){}funcassert(_condition:@autoclosure()->Bool,_message:@autoclosure()->String=String(),file:StaticString=#file,line:UInt=#line){}

And yet, despite all of this flexibility for accepting arguments, most functions you’ll encounter operate on an implicitself argument. These functions are called methods.

Methods

A method is a function contained by a type. Methods automatically provide access to self, allowing them to effectively capture the instance on which they’re called as an implicit argument.

structQueue<Element>{privatevarelements:[Element]=[]mutatingfuncpush(_newElement:Element){self.elements.append(newElement)}mutatingfuncpop()->Element?{guard!self.elements.isEmptyelse{returnnil}returnself.elements.removeFirst()}}

Putting everything together, these syntactic affordances allow Swift code to be expressive, clear, and concise:

varqueue=Queue<Int>()queue.push(1)queue.push(2)queue.pop()// 1

Compared to more verbose languages like Objective-C, the experience of writing Swift is, well, pretty sweet. It’s hard to imagine any Swift developers objecting to what we have here as being “sugar-coated”.

But like a 16oz can of Surge, the sugar content of something is often surprising. Turns out, that example from before is far from innocent:

varqueue=Queue<Int>()// desugars to `Queue<Int>.init()`queue.push(1)// desugars to `Queue.push(&queue)(1)`

All this time, our so-called “direct” calls to methods and initializers were actually shorthand for function curryingpartially-applied functions.

With this in mind, let’s now take another look at callable types in Swift more generally.

{Type, Instance, Member} ⨯ {Static, Dynamic}

Since their introduction in Swift 4.2 and Swift 5, respectively, many developers have had a hard time keeping@dynamicMemberLookup and @dynamicCallable straight in their minds — made even more difficult by the introduction of callAsFunction in Swift 5.2.

If you’re also confused, we think the following table can help clear things up:

 StaticDynamic
TypeinitN/A
InstancecallAsFunction@dynamicCallable
Memberfunc@dynamicMemberLookup

Swift has always had static callable types and type members. What’s changed in new versions of Swift is that instances are now callable, and both instances and members can now be called dynamically.

Let’s see what that means in practice, starting with static callables.

Static Callable

structStatic{init(){}funccallAsFunction(){}staticfuncfunction(){}funcfunction(){}}

This type can be called statically in the following ways:

letinstance=Static()//  desugars to `Static.init()`Static.function()//  (no syntactic sugar!)instance.function()//  desugars to Static.function(instance)()instance()//  desugars to `Static.callAsFunction(instance)()`
Calling the Static type invokes an initializer
Calling function on the Static type invokes the corresponding static function member, passing Static as an implicit self argument.
Calling function on an instance of Static invokes the corresponding function member, passing the instance as an implicit self argument.
Calling an instance of Static invokes the callAsFunction() function member, passing the instance as an implicit self argument.

Dynamic Callable

@dynamicCallable@dynamicMemberLookupstructDynamic{funcdynamicallyCall(withArgumentsargs:[Int])->Void{()}funcdynamicallyCall(withKeywordArgumentsargs:KeyValuePairs<String,Int>)->Void{()}staticsubscript(dynamicMembermember:String)->(Int)->Void{{_in}}subscript(dynamicMembermember:String)->(Int)->Void{{_in}}}

This type can be called dynamically in a few different ways:

letinstance=Dynamic()// desugars to `Dynamic.init()`instance(1)//  desugars to `Dynamic.dynamicallyCall(instance)(withArguments: [1])`instance(a:1)//  desugars to `Dynamic.dynamicallyCall(instance)(withKeywordArguments: ["a": 1])`Dynamic.function(1)//  desugars to `Dynamic[dynamicMember: "function"](1)`instance.function(1)//  desugars to `instance[dynamicMember: "function"](1)`
Calling an instance of Dynamic invokes the dynamicallyCall(withArguments:) method, passing an array of arguments and Dynamic as an implicit self argument.
Calling an instance of Dynamic with at least one labeled argument invokes the dynamicallyCall(withKeywordArguments:) method, passing the arguments in a KeyValuePairs object and Dynamic as an implicit self argument.
Calling function on the Dynamic type invokes the static dynamicMember subscript, passing "function" as the key; here, we call the returned anonymous closure.
Calling function on an instance of Dynamic invokes the dynamicMember subscript, passing "function" as the key; here, we call the returned anonymous closure.

Dynamism by Declaration Attributes

@dynamicCallable and @dynamicMemberLookup are declaration attributes, which means that they can’t be applied to existing declarations through an extension.

So you can’t, for example, spice upInt with Ruby-ish natural language accessors:

@dynamicMemberLookup// ⚠︎ Error: '@dynamicMemberLookup' attribute cannot be applied to this declarationextensionInt{staticsubscript(dynamicMembermember:String)->Int?{letstring=member.replacingOccurrences(of:"_",with:"-")letformatter=NumberFormatter()formatter.numberStyle=.spellOutreturnformatter.number(from:string)?.intValue}}// ⚠︎ Error: Just to be super clear, this doesn't workInt.forty_two// 42 (hypothetically, if we could apply `@dynamicMemberLookup` in an extension)

Contrast this with callAsFunction, which can be added to any type in an extension.


There’s much more to talk about with @dynamicMemberLookup, @dynamicCallable, and callAsFunction, and we look forward to covering them all in more detail in future articles.


But speaking of RubyPython

Swift ⨯ _______

Adding toour list of “What code is like”:

Code is like fan fiction.

Sometimes to ship software, you need to pair up and “ship” different technologies.

In building these features, the “powers that be” have ordained that Swift replace Python for Machine Learning. Taking for granted that an incremental approach is best, the way to make that happen is to allow Swift to interoperate with Python as seamlessly as it does with Objective-C. And since Swift 4.2, we’ve been getting pretty close.

importPythonletnumpy=Python.import("numpy")letzeros=numpy.ones([2,4])/* [[1, 1, 1, 1]
        [1, 1, 1, 1]] */

The Externalities of Dynamism

The promise of additive changes is that they don’t change anything if you don’t want them to. You can continue to write Swift code remaining totally ignorant of the features described in this article (most of us have so far). But let’s be clear: there are no cost-free abstractions.

Economics uses the term negative externalities to describe indirect costs incurred by a decision. Although you don’t pay for these features unless you use them, we all shoulder the burden of a more complex language that’s more difficult to teach, learn, document, and reason about.


A lot of us who have been with Swift from the beginning have grown weary of Swift Evolution. And for those on the outside looking in, it’s unfathomable that we’re wasting time on inconsequential “sugar” like this instead of features that will really move the needle, like async / await.

In isolation, each of these proposals is thoughtful and useful — genuinely. We’ve already had occasion to use a few of them. But it can be really hard to judge things on their own technical merits when they’re steeped in emotional baggage.

Everyone has their own sugar tolerance, and it’s often informed by what they’re accustomed to. Being cognizant of the drawbridge effect, I honestly can’t tell if I’m out of touch, or if it’s the children who are wrong

Viewing all 71 articles
Browse latest View live