Loading...

Follow Ole Begemann's Blog on Feedspot

Continue with Google
Continue with Facebook
or

Valid

The fourth edition of our book Advanced Swift is available now.

If you bought a previous edition of the e-book, this is a free update for you.

Advanced Swift is the book for people who have read The Swift Programming Language and/or have been using Swift for some time and now want to dig deeper and really understand how everything works. It’s a book about Swift the language, not about iOS or macOS programming (though we do use Apple’s frameworks in some examples).

What changed

In addition to updating everything for Swift 5, we’ve added a new Enums chapter that is all about enums and how to use them effectively. Plus, we’ve completely rewritten the chapters on Structs and Classes, Generics, and Protocols. The chapters on Strings, Collection Protocols, and Error Handling also have substantial revisions and/or new content.

We moved the Collection Protocols chapter further back in the book, resulting in a smoother learning curve for readers.

Last but not least, Florian Kugler joined us as a co-author.

Video companion series

New this year is a bundle of 4.5 hours of companion videos that illustrate many techniques we present in the book in a more hands-on way and with longer examples. In five separate episodes, Chris and Florian demonstrate C Interoperability, String Parsing, Collection Protocols, Functions, and Encoding & Decoding Graphs. Using live-coding, pair programming, and a conversational style, the videos invite you to think through the concepts with us, as we apply them. If you’ve ever watched a Swift Talk episode, you’ll have a good idea of the style of the videos.

I think the videos are a great complement to the book because each medium has its own strengths. Text is great for conceptual discussions that require tons of detail. But nobody wants to read multiple pages of sample code where the concepts are applied to a practical example, and this is what the videos excel at.

Free updates since 2016.

The video bundle is a separate purchase. Since this is the third year of free, major updates to the e-book, we’d really appreciate your continued support by buying the video upgrade. Existing customers can purchase the videos at a discount by using this link.

How to buy

You can buy the e-book and videos at objc.io. The e-book download includes PDF, ePub, and Mobi (Kindle) files, all DRM-free. This version also includes the full text of the book as Xcode playgrounds (one playground per chapter).

If you prefer a printed book, you can order the paperback from Amazon (amazon.de, amazon.co.uk).

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

In episode 1 of the new Swift Community Podcast, Chris Lattner, Garric Nahapetian, and John Sundell spoke about the origins of Swift and the current state of the Swift community, among other things.

This is a transcript (edited for readability) of the parts I found most interesting. You’ll see I mainly quoted Chris Lattner because I think his account of how Swift was created is the most relevant to preserve for posterity. That’s not to say that what John and Garric said was any less interesting. You should really listen to the entire episode — it won’t take much longer than reading this post, anyway.

The Swift Community Podcast is also worth checking out on its own. It’s very much intended as a project where anybody can contribute in various forms (the three go into more detail in episode 1). In this spirit, I worked on creating the transcript in the open, and even received help from the community in the form of code and in editing the machine-generated transcript. Thank you to everyone who helped!

You can find the complete transcript on GitHub (in WebVTT format). The entire podcast is licensed under CC-BY 4.0.

On the origins of Swift

(Starting at 16:59)

Chris Lattner: I have to go back in time to WWDC 2010. We just launched C++ support in Clang and that was a huge, huge, huge amount of effort for tons of people. And I was feeling both very happy about that, but also a little bit burnt out because it was a lot of really nitty-gritty work. And you can’t implement C++ without thinking, “gosh, there should be a better thing out there!”

And so I got into a number of conversations with a guy named Bertrand Serlet. Bertrand was the head of the software team at the time, and Bertrand is an exceptional engineer. He’s an amazing human. He’s also a bit of a language geek. And he had been pushing to make Objective-C better. He always wanted more out of Objective-C. And he and I got into a number of one-on-one whiteboard sessions.

At the time, Swift was called ‘Shiny’.

Bertrand ran all of software at Apple, so he had no time. But he always encouraged me to drop by at the end of the day and try to see if he’s available. And he’d stay late, and we would geek out on the whiteboard. We talked about a ton of different things: the goals [of a new language], weird details like the type system, and we eventually turned it into a pitch deck. So I built the pitch deck for him and came up with this idea of building a new language. At the time it was codenamed “Shiny”, as though “you’re building a shiny new thing”. I was also a fan of the Firefly TV show.

John Sundell: Was the file extension .shiny?

Chris Lattner: Yes, in fact, it was. You know, at the time, it was super tiny. It was really just Bertrand and I talking about it. Another really awesome engineer named Dave Zarzycki got involved in some of that early conceptual discussion.

One of the things that came out of it really early on was that we started talking about memory management. At the time, we were both convinced that there had to be a good way to solve or improve memory management and we needed to get to memory safety. So you have to have automatic memory management.

The goal of having automatic memory management was the genesis of Swift internal design discussions turning into Objective-C features.

One of the key things that came out of that was ARC and the idea that we could have the compiler own and drive this instead of having a runtime. Objective-C had this libauto garbage collector at the time, but it had a number of problems. That was kind of the genesis of Swift internal design discussions turning into Objective-C features. And many of the things that came out, including ARC and modules and even literals and things like that, really came from the behind the scenes development of Swift.

John Sundell: So there was a bunch of features that you had in mind for Shiny and what would later become Swift. But you said, “we don’t want to wait until we have a brand-new language completely implemented. Let’s go ahead and take those features that we really want and just add those to Objective-C.”

As part of the ideation process behind building a new language, you always have to ask the question, ‘why not make the existing thing better?’

Chris Lattner: If you think about it from a different perspective, Swift may seem obvious now, but at the time it was not obvious at all to anybody, and even me. Bertrand was, and is, really great because he’s always super encouraging. And he always pushed through the doubts. Bertrand’s a bit of a scientist. He just wants to find truth in many ways. So yeah, there’s tons of uncertainty, but at the same time, there’s lots of good ideas. And so he and others were pushing, as part of the ideation behind building a new language, you always have to ask the question, “why not make the existing thing better?” And the answer was, “clearly, we should make the existing thing better”. And so ARC and all the other things that came out of that happened.

But in the case of Swift [the tentpole feature] became memory safety. And you can’t retrofit memory safety into Objective-C without removing the C. And if you remove the C you just lose too much and it becomes not Objective-C anymore.

Garric Nahapetian: Right. So was it somewhat like a Trojan horse to add those Swift features to Objective-C, which then made it easier to convince people about Swift later, as you’d already done the Objective-C work?

Chris Lattner: [There were] interesting internal dynamics. I guess we were very focused on making Objective-C and the platform better. In terms of the Swift development, it was a way of de-risking in a sense, because if you say, “we’re gonna roll out everything all at once”, and none of it is tested, then it’s hugely risky. But if you roll out “minor” things, like a whole new memory management system, separately and then you iterate, debug, and develop that with the community, then it takes a certain amount of risk away. But I would say that both the external and internal community at Apple was kind of saying, “why are you prioritizing this? It feels like we’re kind of on a random walk. Why are you doing this and not this other thing?” And so there was an interesting dynamic with that.

On growing the initial team

(Starting at 22:49)

Chris Lattner: Apple has a really strong engineering team. There was a ton of people at the time, all writing Objective-C, that are highly opinionated about things, but also have a huge depth and background in building frameworks, apps and all these kinds of things. And so there were a lot of ideas on how to make Objective-C better. There were big white papers written by luminaries that had been working since the NeXT day on this stuff. And so there was a tremendous internal community that drove that.

At the time, Bertrand and Dave and I talked about ideas, and I started coding a prototype of the compiler. But obviously, I could not build everything myself. And so what ended up happening is, around April 2011 we discussed Swift with management and then got agreement to pull in a few more people. This is when folks like Ted Kremenek, Doug Gregor, and John McCall, and a bunch of other exceptional engineers over time, got pulled in. And looking back on that, it was really interesting because this was the first time that other language and design experts were taking a critical look at this. And they had a lot of really harsh feedback. It wasn’t intended as harsh, but they were right. It [the language] was terrible at the time.

And [having] the ability to have one of the world’s experts on generics pulled into this, and the team that had built the Clang compiler and had been working together for years on many different, really interesting projects, being able to draw on that engineering talent was critical to making everything happen. And these are just a few of the people that helped drive and build that. But it was a big deal.

John Sundell: What was the state of the language at that point? Like, what did the syntax look like? What part of the compiler infrastructure was there? Was it all very much in the prototype phase or had you gotten a little bit further along at that point?

Chris Lattner: It was very much in the prototype phase. This is all public because the revision history is all public. There’s a changelog that goes not all the way back, but quite a ways back.

Before Doug got involved, there was no generics system. We wanted the generics system, but I don’t have the expertise to design it myself. And Doug is the guy. John, I remember very early on, took over making actually generate code instead of just being a parser. That was a major piece that he took on.

There were tons of missing pieces, but there are also pieces that go all the way back. I think var and func go all the way back to the beginning. Some of the basic syntax ideas are very, very similar [to what you see in Swift today].

When building a new thing, often the ideas are ahead of the documentation and the documentation is ahead of the code. It was very much like that. And the ideas are so way ahead of the code right now, incidentally.

So it was very much a prototype. But a lot of the ideas [were already there]. When building a new thing, often the ideas are ahead of the documentation and sometimes the documentation is ahead of the code. It was very much like that. And the ideas are so way ahead of the code right now, incidentally.

On Craig Federighi

(Starting at 26:10)

Chris Lattner: Another part of the community that was really important was a guy named Craig Federighi. Craig is well known in the Apple community. He became involved in the project some time in early 2011. That was right around when Bertrand retired from Apple and Craig took over his job.

Now, Craig is a really, really interesting person. He is super charismatic, both on stage, but also in person and in one-on-ones. But the thing that I think a lot of people don’t understand is that Craig is just ridiculously smart. And he is super deep on so many topics. And I didn’t expect this, but he knows a lot about languages. In former roles, he’s worked with Groovy and lots of other kinds of languages and things that I had not come in contact with. And he’s not just the high-level guy who cares about strategy. He also cares about a ton of things, like closure syntax, keywords, all that kind of stuff.

And Craig really was the hard driver and the advocate for making it real and making it relevant to Objective-C and caring about Objective-C development and caring about the APIs and caring about what the APIs imported into Swift look like, and all that. And Craig, in addition to giving great feedback, just kept, and keeps, an exceptionally high bar on the team and the project. He really helped shape a lot of what Swift is today.

Garric Nahapetian: It’s pretty cool because he was the one that first announced it on stage at WWDC 2014.

Chris Lattner: Yes.

John Sundell: And then he brought you out, right? That was the classic line: the “Objective-C without the C”.

I have mixed feelings about the ‘Objective-C without the C’ tagline, because that’s really not what it’s about.

Chris Lattner: Which honestly I have mixed feelings about, because that’s really not what it’s about.

John Sundell: It’s a good tagline.

Chris Lattner: It was the right thing to say to the community at the time.

[…]

From the beginning of the project, my goal was to build a full-stack system.

Chris Lattner: The reason it is conflicting to me is that from the beginning of the project, my goal was to build a full-stack system. It was to look at all the existing systems out there, see what’s good or bad about each of them, and then cherry-pick the best ideas from systems wherever they come from. And the goal was really to build something that you could write firmware in or that you could do scripting in, that you could write mobile apps or server apps or low-level systems code, and have it be great at all of those, not just some terrible compromise.

So that positioning was absolutely the right thing to do [at the time]. But hopefully, Swift will grow over time in kinds of what it is able to do.

On the importance of documentation

(Starting at 30:32)

Chris Lattner: [There is a] last group I want to talk about. Because, you look at what Swift is and sure, it’s a compiler, it’s a language spec, it’s a set of APIs, it’s an IDE. But the thing that makes it real and the thing that reached so many people is the work done by the Developer Publication Group. These are the tech writers at Apple that wrote things such as the Swift Programming Language book. And the success and the rapid adoption of Swift is in huge part directly related to such high-quality, good documentation and a book being available on day one. And it’s continued to be maintained today, and it’s amazing.

We pulled the tech writers right into the design meetings.

And part of that was that we pulled the tech writers right into the design meetings. Folks like Tim Isted, Brian Lanyon, and Alex Martini spent a tremendous amount of time in weekly meetings where we were arguing about, “do we use dot versus colon?” Or, “do we use this keyword versus that keyword?” Or, “should we change func to def?” But also about the depths of the type system and how the codegen algorithms should work. And how do we get good performance? And how do strings work? And all the different pieces that go into it.

If you can include the explaining-it-to-people part into the design process, you get something that’s so much better.

I’ve seen so many systems where you build a thing and then you try to explain it afterwards. And when you go to explain it and it’s really awkward and you’re like, “wow, I have to explain the workarounds for this thing”. If you can close that feedback loop and include the documentation, include the explaining-it-to-people part into the design process, you get something that’s so much better.

Developing Swift is a team effort

(Starting at 34:58)

Chris Lattner: I mean, this is the kind of thing where a lot of people say, “Chris invented Swift”. And well, there’s a truth to that, I have pushed it over many years in many different ways and kind of shepherded the project. But it misses the fact that there’s hundreds of people involved in so many critical ways. Building the debugger, building the IDE, building Playgrounds, building educational content within it, building all this stuff, building the community aspect of it.

There’s so many different pieces of this that far transcend me. It very much is a community of people both inside Apple, but also outside Apple, that are all pushing together and building things and contributing in their own ways. And that is why Swift is where it is. And that is why I see it growing.

Garric Nahapetian: And that’s one of the reasons why we want to have this podcast, to bring some of those people to light.

On not writing Swift code as a compiler developer

(Starting at 42:15)

Chris Lattner: One of the things that’s fascinating is that the two of you have written far more Swift code than I have. So I know a lot about the insides and the how and why and how it fits together, but you guys have the actual experience of building and using it in production.

John Sundell: Yeah, that’s funny. There’s this bunch of people working on Swift, working on the compiler, and they’re all writing C++ most of the time. Just a quick aside, what’s that like? You have this cool language that you’ve designed, and everyone else gets to use it, but you still have to use C++.

Chris Lattner: [laughing] It kills me. It’s terrible. It is the universe’s trick on me, to force me to keep writing C++.

On community feedback

(Starting at 43:11)

Chris Lattner: One of the major reasons that Swift is good right now is that there’s a big community of people out there that were blogging about it, right? And that feedback really shaped Swift 1 and 2. That was the signal, all those complaints, people saying “this doesn’t make sense”, “I’m having this problem with this, that and the other thing”. That really shaped and prioritized and drove a lot of what got built.

Early community feedback really shaped Swift 1 and 2.

Swift 1 accidentally, well not accidentally, intentionally shipped without error handling. It also shipped without protocol extensions, things that we absolutely wanted and just didn’t fit with the schedule. So we knew we had to build those things, but a lot of what happened in that first year or two was directly driven by the community. And when Swift became open source, Swift Evolution is a hugely great thing. It’s maybe not ideal in terms of optimizing for human time spent, but it’s an essential part of what makes Swift really exceptional. And I think that is total credit to the community of people that spend their time there and that help shape and drive it.

John Sundell: One thing that comes to mind is how not only articles and content, but also open source, it feels like, has fed a lot into Swift itself. For example, with things like Codable, people were creating a thousand different JSON mapping libraries. I was one of them. I built Unbox because it was one of those things where in Objective-C you didn’t think a lot about it most of the time. You just said, here’s a dictionary, let me access this key and I’m just gonna assume that’s always a string. But once you sat down and wrote that same code in Swift, you realized you needed to build a big pyramid of if lets. So I can only imagine that seeing those things that people tried to solve in different ways must have also fed back into the design process.

Chris Lattner: Yeah, absolutely. And the design of Codable came from a framework team at Apple that were outside of the core Swift group. They were really passionate about this, and they came up with it and drove it and advocated for it.

It’s maybe not understood how much the community shapes Swift.

And community can mean so many different things, right? Look at Result. Why is Result now coming into Swift 5? Well, it’s because so many people built it over and over and over again. And while the core team really doesn’t want to have a Result type because it’s kind of a failing of the language, and when we get concurrency it won’t be as necessary. But the community says loud and clear, “look, we need it. It doesn’t matter if it’s not ideal in the long term, we need it.” So the community really does shape things. And it’s maybe not understood how much the community does have that effect.

On initial expectations

(Starting at 46:07)

John Sundell: Looking at the current state of Swift and what it’s become since you put it out there, has it matched your expectations, if you had any? How does it match the ideas that you had in the beginning now that it’s been out for a couple of years?

When Swift 1 launched, there was the question, can we capture the Objective-C community? Can we capture the iOS ecosystem, or are we going to fragment it?

Chris Lattner: Well, I guess expectations change over time. Back in 2010–2011, I had no expectation that it would ever turn into anything. And I admit that it was very much a fun side project. It was a nights and weekends thing originally. Have a day job, have a side project that’s intellectually interesting and challenging. As it became more real and it launched into Swift 1, there was a question of, can we capture the Objective-C community? Can we really capture the iOS ecosystem, or are we..

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
Ole Begemann's Blog by Ole Begemann - 7M ago

Listed in the order I read them. The list includes some fiction, some non-fiction. Few tech books. Some are in English, some in German. Some on serious topics, some light entertainmen, one children’s book, and one photo book.

Colson Whitehead, The Underground Railroad (2016)

Dörte Hansen, Altes Land (2015)

Astrid Lindgren, Die Brüder Löwenherz (1973)

Seymour Papert, Mindstorms (1980)

Tim Krabbé, Das Rennen (1978)

Jan Konefke, Eine nie vergessene Geschichte (2008)

Neil deGrasse Tyson, Astrophysics for People in a Hurry (2017)

Jenny Erpenbeck, Aller Tage Abend (2012)

Haruki Murakami, 1Q84 Teil 1 und 2 (2009)

Haruki Murakami, 1Q84 Teil 3 (2010)

Margarete Stokowski, Untenrum frei (2016)

Sebastião Salgado, Africa (2007)

Hans Rosling, Factfulness (2018)

Garry Kasparov, Winter is Coming (2015)

David Auerbach, Bitwise: A Life in Code (2018)

Murat Kurnaz, Fünf Jahre meines Lebens (2007)

Martin Kleppmann, Designing Data-Intensive Applications (2017)

Lisa Brennan-Jobs, Small Fry (2018)

Lukas Rietzschel, Mit der Faust in die Welt schlagen (2018)

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
Ole Begemann's Blog by Ole Begemann - 8M ago

Small Fry, the memoir of Lisa Brennan-Jobs, is one of the best books I’ve read this year. Lisa tells the heartbreaking story of growing up in Silicon Valley, her loyalties split (and often torn) between her mother and her rich and famous father, who had disavowed paternity and did not support the family.

The central theme of the book is Lisa’s complicated relationship with her parents. Steve was absent for the first six or seven years of Lisa’s life, but the two later became close. Lisa admires, even idolizes, him for his success and wealth. It’s a stark contrast to her mother’s unstable life, which is characterized by frequent moves, changing jobs and boyfriends, and never having enough money.

Throughout her adolescence, Lisa is conflicted how to divide her loyalty between her parents, and they don’t make it easy for her. When Lisa is about thirteen, she moves in with Steve and his wife Laurene. She wants to get away from the constant fights with her mother, but she also hopes to win Steve’s affection and approval, to get him to fully accept her as his daughter. Steve doesn’t make this easy for Lisa. He only invites her into his house under the condition that she makes a permanent decision and doesn’t see her mother at all for six months. Lisa agrees, but she never gets over the guilt:

I would leave my mother. I felt giddy and guilty and numb. Maybe this was the origin of the guilt that seized me later and left me hardly able to walk sometimes, after I had moved in with them: having stolen her youth and energy, having driven her to a state of perpetual anxiety, without support or resources, now that I was flourishing in school and beloved by my teachers, I cast her out and picked him, the one who’d left. I chose the pretty place when she was the one who’d read me books of old stories with admonishments not to believe in the trick of facades.

“I’m proud of you,” he said.

Forcing your daughter to choose between you and her mother, and then telling her how proud you are when she chooses you. Nice parenting.

Lisa recounts many stories in the book where Steve was unjustifiably cruel to her. One example: Lisa often felt lonely in the large house, with her bedroom being far away from the others’. Here’s how Steve Jobs reacted when she asked him to say goodnight to her:

“Hey, would you guys come say goodnight to me sometimes?” I asked my father, standing in the kitchen. I’d built up the courage, after talking with Mona [Simpson, Lisa’s aunt, Steve’s sister].

“What?” he asked.

“Just a couple nights a week,” I said. “Because I’m lonely.”

“Nope, sorry,” he said, without pausing to think. …

A few days later I asked Laurene, separately.

“Sure,” she said.

I was flooded with gratitude and relief, the same feeling as when she pulled me into photographs, the same feeling that made me shower her with rose petals and lantana blossoms when she walked through the gate after work, so grateful it made me shiver as if from cold.

That night, she came down first, sat on my bed, and stretched out her legs. …

My father came down and sat beside Laurene on my bed. The joy and relief of this event made it hard to relax, like trying to breathe in a high wind. …

After that, they didn’t come down again. I asked one more time, my father said no, and I stopped mentioning it.

The book is full of WTF moments like this. Steve’s meanness left me speechless at times. He could be incredibly cruel to those closest to him, maybe even crueler than to his coworkers. He often let them feel his contempt for their actions or their pure existence.

Lisa never manages to win her father’s unconditional love — at least that’s how she feels. Steve regularly gives her not so subtle hints that he doesn’t see her as a full member of his family, for example when he asks her to step out of family photos with his and Laurene’s other children. Lisa’s feelings fluctuate between a sense of being owed his affection because he abandoned her as a baby and a desperate yearning for approval, to be accepted into his life.

When she returns from a student exchange in Japan and gives her mother a kimono she bought her as a gift, her jealous mother probes her what gifts she bought for Steve and Laurene:

“Did you get them better gifts that you got for me?”

“No, I got you different gifts—nothing better or worse.”

“But you spent more on them,” she said. How did she know? I should have bought her the best gifts because she had less money and couldn’t buy them for herself. …

“I’m your mother and you should be more honoring toward me.” …

How to explain to her that I’d bought them the more expensive gifts because I worried they didn’t care for me and I wanted them to like me, to love me, even? With them together, the feeling I was loved and belonged was tenuous, superficial, my place in their family not essential or fixed. They did not ask me questions about myself, or seem interested in me the way my mother was, and this made me hunger to impress them. My mother already loved me. Even when she screamed at me, I knew it. I wasn’t so sure about them.

It’s a profoundly sad story, and beautifully written. Highly recommended.

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

A common pattern in functional programming languages is to split a list into its head (the first element) and tail (all remaining elements). For example, in Haskell, the pattern x:xs matches against a non-empty list and binds the list’s head to the variable x and the tail to xs.

Swift is not a functional language. It neither has a built-in List type, nor a special pattern matching syntax for collections.1

Collections

Nonetheless, splitting a Sequence or Collection into head and tail can occasionally be useful. For collections, this is straightforward:

extension Collection {
    var headAndTail: (head: Element, tail: SubSequence)? {
        guard let head = first else { return nil }
        return (head, dropFirst())
    }
}

if let (firstLetter, remainder) = "Hello".headAndTail {
    // firstLetter: Character == "H"
    // remainder: Substring == "ello"
}
Sequences

For sequences it’s trickier because they are allowed to be single-pass: some sequences can only be iterated over once as iteration consumes their elements. Think of a network stream as an example. Once you read a byte from the buffer, the operating system throws it away. You can’t tell it to start over.

One possible solution is to create an iterator to read in the first element, and then wrap the current iterator state in a new AnySequence instance:

extension Sequence {
    var headAndTail: (head: Element, tail: AnySequence<Element>)? {
        var iterator = makeIterator()
        guard let head = iterator.next() else { return nil }
        let tail = AnySequence { iterator }
        return (head, tail)
    }
}

This code works, but it’s not a nice generic solution, especially for types that also conform to Collection. Wrapping the tail in an AnySequence is a big performance killer, and you can’t use the affordances of a collection’s proper SubSequence type.

You’d be better off writing two extensions, one for Collection and one for Sequence, in order to preserve the SubSequence type for collections. (And as we’ll see, this is also the preferred solution from Swift 5 onward, but I’ll get to that.)

Preserving the SubSequence type

I couldn’t figure out a generic solution for Sequence that kept the SubSequence type for the tail intact and worked correctly with single-pass sequences. I thank Dennis Vennink for coming up with a solution and sharing it with me. Here’s his code (slightly modified by me for style):

extension Sequence {
    var headAndTail: (head: Element, tail: SubSequence)? {
        var first: Element? = nil
        let tail = drop(while: { element in
            if first == nil {
                first = element
                return true
            } else {
                return false
            }
        })
        guard let head = first else {
            return nil
        }
        return (head, tail)
    }
}

Dennis’s trick is to call Sequence.drop(while:), which preserves the SubSequence type for the tail, and then “catch” the first element inside the drop(while:) predicate closure using a captured local variable. Nicely done!

Swift 5

The code above targets Swift 4.2. It will break in Swift 5 because sequencdes will no longer have an associated SubSequence type, only collections (Swift Evolution proposal SE-0234).2

This change has many advantages, but it means we’re no longer able to write generic algorithms that make use of SubSequence and work on Sequence and Collection at the same time.

Instead, we can add the straightforward solution to Collection:

extension Collection {
    var headAndTail: (head: Element, tail: SubSequence)? {
        guard let head = first else { return nil }
        return (head, dropFirst())
    }
}

If we find that we need the same functionality for Sequence as well, we should add a separate extension that uses the new DropWhileSequence type as the return type for the tail:

extension Sequence {
    var headAndTail: (head: Element, tail: DropWhileSequence<Self>)? {
        var first: Element? = nil
        let tail = drop(while: { element in
            if first == nil {
                first = element
                return true
            } else {
                return false
            }
        })
        guard let head = first else {
            return nil
        }
        return (head, tail)
    }
}

(The implementation is the same as above. Only the return type has changed.)

  1. Introducing a pattern matching construct for collections has been mentioned as a possible feature on the forums several times. With it, you’d be able to write something like this to destructure an ordered collection into head and tail:

    let numbers = 1...10
    let [head, tail...] = numbers
    // head == 1
    // tail == 2...10
    

    This would be very handy in switch statements. ↩︎

  2. It’s unfortunate that we’re now stuck with the misleading name SubSequence. Renaming Collection.SubSequence to the more appropriate Slice was considered too source-breaking↩︎

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

I recently read Roy Fielding’s 2000 PhD thesis, Architectural Styles and the Design of Network-based Software Architectures, in which he introduced and described REST. Here’s what I learned.

REST is almost as old as the web. I first heard of REST around 2005 while working with Rails. As mentioned, Fielding’s dissertation is from 2000, but he began developing the ideas that later became REST as early as 1994.

REST didn’t come out of nowhere. Roy Fielding wasn’t some random PhD student who sat in his ivory tower and came up with a bright idea. He was deeply involved in the web’s early development and standardization. Starting in 1994, Fielding began working at and for the World Wide Web Consortium and co-authored the HTTP 1.0 specification. In the second half of the 1990s, Fielding was the main author behind the HTTP 1.1 and URI specs. He also co-founded the Apache web server project.

REST is the web’s architecture. REST isn’t specifically about web services (i.e. machine-readable APIs that return JSON or XML). In fact, Fielding doesn’t really mention web APIs in his dissertation.

Rather, REST is first and foremost a description of the web’s architecture. The entire web is supposed to be RESTful. Specifying the web (as defined in the HTTP 1.1 spec) is the original purpose for which REST was developed.

Since 1994, the REST architectural style has been used to guide the design and development of the architecture for the modern Web. This work was done in conjunction with my authoring of the Internet standards for the Hypertext Transfer Protocol (HTTP) and Uniform Resource Identifiers (URI), the two specifications that define the generic interface used by all component interactions on the Web.
— Roy Fielding, Architectural Styles and the Design of Network-based Software Architectures, p. 107.

REST is defined through constraints. Any communication architecture that wants to call itself RESTful must abide by these constraints. You can read the full list on the Wikipedia page. Here, I’ll just list the ones I find most important.

Statelessness. Each request must contain all of the information necessary for the server to understand the request, and cannot take advantage of any stored context on the server. Session state is kept entirely on the client. Statelessness makes a service more reliable and easier to scale.

Fielding notes that cookies violate REST. To him, the presence of cookies is an unfortunate mismatch between the ideals of REST and the reality of HTTP:

An example of where an inappropriate extension has been made to the protocol [HTTP] to support features that contradict the desired properties of the generic interface is the introduction of site-wide state information in the form of HTTP cookies. Cookie interaction fails to match REST’s model of application state, often resulting in confusion for the typical browser application.
— ibid., p. 130.

Caching. Requests and responses must include information about their cacheability. If a response is marked as cacheable, the client (or any node sitting between server and client) is allowed to reuse the data for later requests.

Fielding’s focus in the dissertation is markedly different from how developers discuss REST today. He spends a lot of time discussing web characteristics like cacheability, scalability, and the transparency of messages to intermediaries (proxies), whereas the finer points of POST vs. PUT vs. PATCH play no role in the thesis. In fact, he doesn’t mention the different HTTP methods at all.

Identification of resources. Resources are the key abstraction of REST. This is in constrast to earlier specifications of the web, which used the term document for an individual “unit of content”. Resources are a more generic concept than documents. For example, having the URI to a document implies that the document exists, while a resource identifier can be valid before the resource exists (e.g. a client could pass a URI of a non-existent resource to create it).

A resource is a conceptual mapping to a set of concrete entities. For example, “today’s weather” is a valid resource, even though the concrete piece of information it maps to changes every day. Each resource has a unique identifier (usually a URI).

Manipulation of resources through representations. Since resources can be abstract concepts, a resource itself is never directly manipulated or sent over the network. Instead, server and client exchange representations of resources. The server can (and should) offer multiple representations (e.g. JSON and XML) of the same resource. Clients tell the server which representation formats they understand.

REST components communicate by transferring a representation of a resource in a format matching one of an evolving set of standard data types, selected dynamically based on the capabilities or desires of the recipient and the nature of the resource. Whether the representation is in the same format as the raw source, or is derived from the source, remains hidden behind the interface.
— ibid., p. 87.

Self-descriptive messages. Messages include enough information to describe how their payload is to be processed (e.g. media type information must be part of each message). Also, each message completely identifies the resource it concerns (this wasn’t the case in the early days of HTTP when the HTTP header didn’t contain the hostname because it was assumed that there was a 1:1 mapping between IP addresses and hostnames).

Hypermedia as the engine of application state. The central idea behing HATEOAS is that RESTful servers and clients shouldn’t rely on a hardcoded interface (that they agreed upon through a separate channel). Instead, the server is supposed to send the set of URIs representing possible state transitions with each response, from which the client can select the one it wants to transition to. This is exactly how web browsers work:

The model application is therefore an engine that moves from one state to the next by examining and choosing from among the alternative state transitions in the current set of representations. Not surprisingly, this exactly matches the user interface of a hypermedia browser.
— ibid., p. 103.

For web services where two machines talk to each other without a human controlling the interaction, I have a harder time imagining how this is supposed to work. How can you develop a client for a specific API without hardcoding some knowledge about the expected resource types? Fielding doesn’t elaborate on this in his thesis.

However, he later clarified in a 2008 blog post that APIs must be hypertext-driven to legitimately call themselves RESTful:

A REST API should be entered with no prior knowledge beyond the initial URI and set of standardized media types. From that point on, all application state transitions must be driven by client selection of server-provided choices that are present in the received representations or implied by the user’s manipulation of those representations. The transitions may be determined (or limited by) the client’s knowledge of media types and resource communication mechanisms, both of which may be improved on-the-fly (e.g., code-on-demand).

I’m still not sure how anybody can develop e.g. a great mobile app under these constraints that provides a specialized user interface for one particular service. If you don’t have any out-of-band knowledge about the service you’re interacting with, you’re basically reimplementing a web browser.

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
Ole Begemann's Blog by Ole Begemann - 10M ago

Apple’s Photos.framework, which powers the iOS Photos app and gives developers access to the device’s photo library, uses Core Data under the hood.

You can confirm this in at least two places:

  1. Write an app that accesses the photo library and launch it with these command line arguments: -com.apple.CoreData.SQLDebug 1. As you access the photo library, you’ll see Core Data logging debug information to the Console.

  2. If you look at a class dump of the Photos framework, you’ll see references to NSManagedObjectID and other Core Data types in the main classes, e.g. PHObject has an _objectID: NSManagedObjectID ivar.

Finding PhotoKit’s Core Data model

In my quest to understand the Photos framework better (especially its performance characteristics), I wanted to inspect its data model. I found a file named PhotoLibraryServices.framework/photos.momd/photos-10.0.mom deep in the bowels of the Xcode 10.0 app bundle:1

/Applications/Xcode-10.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/PhotoLibraryServices.framework/photos.momd/photos-10.0.mom
Opening compiled Core Data models in Xcode

A .mom file is a compiled Core Data model. Xcode can’t open this directly, but it can import one into another Core Data model. Follow these steps to view the model in Xcode:

  1. Create a new empty project. Xcode 10 doesn’t like to display Core Data model bundles outside of a project.
  2. Create a new empty “Core Data Data Model” file inside the project. This will create an .xcdatamodeld bundle.
  3. Open the new data model and select Editor > Import…. Select the .mom file you want to import.

Unfortunately, the compiled model doesn’t store the layout information for Xcode’s graphical model editor, so you’ll have to manually drag the entities into a pleasing layout. This took me a few hours.2

The formatted Photos model

This is photos-10.0.mom as bundled with Xcode 10.0 (iOS 12.0):

The Core Data data model of the PhotoLibraryServices.framework in iOS 12.0. Click the image for a high-resolution version.

Not everything can be seen in this image. Download the full model and open it in Xcode to inspect attribute properties etc.

Note that this isn’t necessarily the entire data model for photos on iOS. There are more Core Data models in PrivateFrameworks/PhotoAnalysis.framework which I ignored.

  1. You can use this terminal command to find other Core Data models inside the simulator runtimes bundled with Xcode:

    find /Applications/Xcode-10.app -name '*.mom'
    

    ↩︎

  2. Pro tip: you can use the arrow keys (and ⇧ + arrow keys) to position things precisely. Pro pro tip: don’t hit ⌘Z to undo a move operation. The graphical edits don’t register as undoable operations, so chances are Xcode will undo the initial import, which means you’ll lose all unsaved work. ↩︎

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Suppose we have a string that ends in a specific pattern, like this one:

let str = "abc,def,…[many fields]…,1234"

We want to extract the last field, i.e. the substring after the last comma, i.e. "1234".

Splitting the entire string

One solution might be to split the string at every comma and take the last element of the resulting array:

if let lastField = str.split(separator: ",").last {
    // lastField is "1234" ✅
}

But this is quite wasteful if the string is potentially very long.

Finding the index of the last comma

It would be more efficient to find the index of last comma in the string and then take a substring from there to the end. It makes sense to start the search from the back, not only for performance reasons (assume the last field is known to be short), but also because the first portion of the string may contain commas we’re not interested in.

lastIndex(of:)

Solving this task is trivial in Swift 4.2, thanks to the new last​Index​(of:) method, introduced with Swift Evolution proposal SE-0204:

if let lastCommaIndex = str.lastIndex(of: ",") {
    let lastField = str[lastCommaIndex...].dropFirst()
    // → "1234" ✅
}

Note the call to dropFirst. The substring str[lastCommaIndex...] includes the comma.

Manual implementation

But let’s assume for a minute that lastIndex(of:) isn’t available to us. We could write a loop to accomplish the task, manually traversing through the string (from the back) until we find a match.

Let’s wrap the algorithm in an extension on String to make it reusable:

extension String {
    func lastIndex1(of char: Character) -> String.Index? {
        var cursor = endIndex
        while cursor > startIndex {
            formIndex(before: &cursor)
            if self[cursor] == char {
                return cursor
            }
        }
        return nil
    }
}

This code is essentially the same as the standard library’s implementation for last​Index​(of:), if not quite as generic (the standard library version is implemented on Bidirectional​Collection). And it does the job:

if let lastCommaIndex = str.lastIndex1(of: ",") {
    let lastField = str[lastCommaIndex...].dropFirst()
    // → "1234" ✅
}
By composing reversed and firstIndex(of:)

This is all well and good, but there’s another way to solve this task, and it’s what I want to talk about in this article — not because it’s a particularly practical example, but because I believe it can teach a lot about the design of the Swift standard library.

We could also implement our own version of last​Index​(of:) by composing other Collection APIs as follows:

extension String {
    func lastIndex2(of char: Character) -> String.Index? {
        guard let reversedIndex = str
            .reversed()
            .firstIndex(of: char) 
        else {
            return nil
        }
        return index(before: reversedIndex.base)
    }
}

It may not be immediately apparent why this works, so let’s go through it step by step. The guard statement reverses the string before looking up the index of the search character:

guard let reversedIndex = str
    .reversed()
    .firstIndex(of: char)

Reversing the string makes intuitive sense because we want to search it from the back, but it raises two questions:

  1. Isn’t reversing the whole string expensive?

  2. What can we do with an index into the reversed string? Is there a way to turn it back into an index for the original string?

1. Isn’t reversing the whole string expensive?

Answer: no, because reversed is implemented lazily. The reversed method doesn’t perform any work. All it does is wrap its input in a Reversed​Collection value:

extension BidirectionalCollection {
    func reversed2() -> ReversedCollection2<Self> {
        return ReversedCollection2(base: self)
    }
}

(The code I show here and in the following is my own reimplementation of ReversedCollection. I named it Reversed​Collection2 to avoid conflicts it from the standard library type, but both implementations are more or less the same.)

At its most basic, Reversed​Collection is a simple generic wrapper for any Bidirectional​Collection:

struct ReversedCollection2<Base: BidirectionalCollection> {
    var base: Base
}

Even if the input string is many megabytes long, calling reversed() on it is effectively free because String uses copy-on-write. The actual string data isn’t copied until one of the copies is mutated.

Conforming to Collection

Next, the code calls first​Index​(of:)1 on the Reversed​Collection. first​Index​(of:) is part of the Collection protocol, so Reversed​Collection must conform to Collection. Let’s walk through a reimplementation of this conformance.

Index type

Every Collection must have an index type. For Reversed​Collection, we can use the same idea for our index that we used for the collection itself: the index type should be a simple wrapper around the base collection’s index:

extension ReversedCollection2 {
    struct Index {
        var base: Base.Index
    }
}

Collection indices must be Comparable, so we have to add that conformance as well. Since we know that the base value is also Comparable, we can forward to it in our implementation. But note that we have to invert the logic since our index type must model the reversed relationship: the greatest base index represents the smallest reversed index, and vice versa:

extension ReversedCollection2.Index: Comparable {
    static func <(lhs: ReversedCollection2.Index, rhs: ReversedCollection2.Index) -> Bool {
        // Inverted logic compared to base
        return lhs.base > rhs.base
    }
}
Collection implementation

Having laid the groundwork, we can add the Collection conformance. The idea is to base our implementation on the base collection, remembering that we have to invert everything: the reversed collection’s startIndex is the base’s endIndex (and vice versa), index(after:) should call base​.index​(before:), and so on.

extension ReversedCollection2: Collection {
    typealias Element = Base.Element

    var startIndex: Index {
        return Index(base: base.endIndex)
    }

    var endIndex: Index {
        return Index(base: base.startIndex)
    }

    func index(after idx: Index) -> Index {
        return Index(base: base.index(before: idx.base))
    }

    subscript(position: Index) -> Element {
        return base[base.index(before: position.base)]
    }
}

By the way, our reliance on base​.index​(before:) is the reason why Reversed​Collection’s generic parameter must be constrained to Bidirectional​Collection. A plain Collection has no index(before:) method and thus cannot be traversed in reverse.

There’s one more thing to consider: a collection’s endIndex signifies the “one past the end” position, not the position of the last element. By using base​.endIndex as the reversed collection’s startIndex, we have effectively shifted all reversed indices one element to the right. This is why the subscript implementation accesses the element at base.index(before: position.base) rather than position.base.

It’s wrappers all the way down

Going back to the snippet we were discussing in our lastIndex2 implementation, the expression:

str.reversed().firstIndex(of: char)
// type: Reversed​Collection​<String>​.Index?

returns an optional Reversed​Collection​<String>​.Index, which as we’ve seen is a wrapper around the corresponding index in the unreversed string.

2. Is there a way to turn it back into an index for the original string?

Answer: yes, because the standard library implements Reversed​Collection pretty much in the same way I have shown here, only with a few more bells and whistles. Read the code here.

And luckily, Reversed​Collection​.Index​.base is public2, which allows us to get back from the reversed index an index in the underlying base collection. Just as above, we have to remember to shift the index back, as the documentation reminds us:

To find the position that corresponds with this index in the original, underlying collection, use that collection’s index(before:) method with the base property.

Which is why the last line of our lastIndex2 implementation returns the index before the match:

return index(before: reversedIndex.base)
Conclusion

Arguably, the above is a pointless exercise without much practical relevance. However, I believe that understanding how and why this works can teach you a lot about the design of collection types and protocol hierarchy in the standard library. The Sequence and Collection APIs are designed to be composable, and to provide useful operations for implementing your own algorithms. Dave Abrahams’s WWDC 2018 talk Embracing Algorithms gives some great examples how to do this. It’s my favorite WWDC 2018 session.

Reversed​Collection isn’t the only lazy sequence/collection wrapper in the standard library. enumerated, joined, and zip all work in exactly the same way.

And by using lazy you can apply the same general pattern of returning a wrapper value that defers the actual work to map, filter, and other normally eager operations — the difference is that these wrappers store not only the base collection, but also the transformation function you pass in.

Why are some operation always lazy and others eager by default? The standard library’s policy is to make things lazy by default if the laziness is unambiguously free. If, on the other hand, there’s a significant downside to the lazy implementation, provide the lazy version under the lazy property. Ben Cohen:

For example, lazy.map performs the mapping every time you iterate, which could be very costly on multiple iteration, and also captures and stores the mapping closure, which can be risky if it is stateful.

  1. firstIndex(of:) was called index(of:) before Swift 4.2↩︎

  2. ReversedCollection​._base is public, too, though hidden from code completion. ↩︎

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
Ole Begemann's Blog by Ole Begemann - 11M ago

I’ve been using a new 2018 MacBook Pro as my primary work machine for a few weeks now. I bought the top configration: 2.9 GHz i9 CPU, 32 GB RAM, Radeon Pro 560X GPU.

My previous computer was a Late 2013 MacBook Pro with a 2.6 GHz i7 CPU, 16 GB RAM, and no discrete GPU. It served me very well for almost five years, except for a fried SSD after 46 months, which I replaced with a third-party OWC Aura SSD.

I’m glad the glowing Apple logo in the lid is gone. Getting rid of it in 2015 was long overdue. The reflective logo in the modern enclosures is still too much for me — I’m not going to be a walking advertisement for Apple. I’m still looking for a nice sticker to cover it.
Impressions

This is my first MacBook since the 2016 redesign with USB-C and the touch bar, so many of my observations may be old news to you.

Keyboard

I really like the feel of the keyboard. Typing feels very precise. It may be my favorite keyboard — laptop or desktop — I’ve ever used for any significant amount of time. I also understand why some people don’t like the keyboard at all. With its low travel and “clicky” feel, it’s a contentious design.

Obviously, I can’t say anything about the keyboard’s long-term reliability. If Apple hasn’t fixed the problems of the previous generation keyboards, I’ll have to change my verdict. Reliability is much more important than a small improvement in comfort.

Touch bar

The touch bar and I haven’t become friends yet I can see its usefulness for some tasks, and I don’t mind the tap-hold-pan gesture for changing the volume or display brightness as much as I expected. But any extra convenience is far outweighed for me by accidental (and sometimes destructive) touches. Some examples:

  • Closing a dialog or cancelling an action by unconsciously resting my finger on the Escape key before having made a decision whether or not I want to press the key.

  • Deleting a note in Notes.app.

  • Accidentally stopping or restarting a debugging session in Xcode while typing in the debugger console.

  • Unintentional back/forward navigation in Safari and Xcode.

I keep hitting the Run or Stop button accidentally while writing code (where it’s not a big problem) or during debugging (where it quits the current debugging session). Xcode has a Customize Touch Bar feature, but it’s very limited: you can’t remove the four leftmost buttons.

Often, these accidental touches are so light that they don’t even register in my fingertip — I can only deduce what happened when I notice something inexplicably happening on screen.

Unintentional touches will probably become less frequent as I get used to the touch bar. However, I still don’t think Apple should have shipped it in this state. The touch bar needs haptic feedback and some amount of force sensitivity in order to ignore light accidental touches. And Apple, while you’re at it, please add back a normal Escape key.

If you’re a Mac developer, consider carefully which controls to place on the touch bar. If you ask me, destructive actions have no place there as long as it’s so easy to invoke them accidentally.

USB-C

I miss MagSafe and the LED charging indicator. Overall, I’m a fan of the new USB-C world, though. Being able to charge from both sides is very convenient, as is having a single USB-C cable for an external monitor and power.

I bought a relatively cheap (40 €) hub that provides USB-A ports, HDMI, Ethernet, and an SD card reader. It has excellent build quality (aluminum!) and has been working well thus far, although it gets quite warm in use.

I bought this Inateck USB-C hub. It has 4× USB-A 3.0, 1× USB-C port (only for power delivery, no data), Gigabit Ethernet, HDMI (up to 4K at 30 Hz), and an SD and MicroSD reader.
Battery life

I expected worse from the battery, so I’m positively surprised. It doesn’t come close to the 10 hours advertised by Apple when working in Xcode, but I do get around 5–6 hours of real work use, which is way more than the 3–4 hours I got with my old machine (perhaps unsurprisingly, considering its battery is almost 5 years old, though still in good condition).

I started using Turbo Boost Switcher Pro to disable the CPU’s turbo boost feature on battery power after Marco Arment wrote about it, which stretches the battery for another hour or two.

I haven’t done any formal testing, but the battery drains extremely slowly under light use. I’d fully expect the battery to last more than 10 hours for very low CPU and GPU workloads.

Minimizing fan noise

Disabling turbo boost works really well for stretching out the battery, but it does have another advantage: it can stop the fans from spinning up.

Turbo Boost Switcher Pro

Do you know the situation when Dropbox or Spotlight reindex something in the background and the fans spin up for minutes or even hours? They still do on my 2018 MacBook Pro, it’s quite annoying. In my experience thus far, the MacBook’s enclosure can dissipate enough heat to allow a single i9 CPU core running at 100 % without spinning up the fans, but only when turbo boost is disabled (assuming the GPU is mostly idle). With turbo boost enabled, it has to spin up the fans when a core goes to 100 %.

So now, I often disable turbo boost at times when the fan noise annoys me, even when the machine is plugged in. I’d still like a laptop that can drive the CPU without the fans spinning up audibly, but until then being able to choose between silence and maximum power seems to be the next best thing.

Performance benchmarks

I didn’t try very hard to create completely equal environments on both machines for these benchmarks, so don’t put too much faith into these numbers. They should be quite similar, though, because I didn’t set up the new computer from scratch. Generally, the 2018 MacBook had a few more processes running in the background (such as Dropbox or iStat Menus), but they were mostly idle.

Compiling Swift from source

I built a release version of the Swift compiler, using build-script -x -R.

2018 MBP 2013 MBP Δ
39 min 58 min 33 % faster

After the 2018 MacBook Pro thermal throttling issue and the subsequent fix Apple released, I was curious to see how the computer behaved under heavy CPU load for an extended period of time, so I monitored the Swift build with Intel’s Power Gadget software. I observed very consistent results:

4+ GHz when a single core is maxed out

Build stages that aren’t parallelized tend to max out a single CPU core. During these stages, the CPU frequency stays consistently above 4 GHz. The highest frequency I saw was about 4.4 GHz, which is still shy of the advertised “up to 4.8 GHz” turbo boost speed. The following screenshot shows the start of the build:

Typical CPU utilization when a single core is maxed out. The CPU frequency hovers slighly above 4 GHz.

Observe how the Utilization curve is pegged at ~16 % (= one core @ 100 %, the other five are idle). The Frequency curve hovers slightly above 4 GHz for the entire time. The CPU draws approximately 30 W (Power / IA) in this state, with peaks going up to 40 W. The temperature is stable at slightly below 100 ℃.

2.9 GHz continuously when all cores are maxed out

Parallelized build stages (such as compiling) max out all six cores. CPU frequency drops to 2.9 GHz, which is exactly in line with expectations. The CPU draws 35–40 W in this state. CPU temperature remains unchanged. In the following screenshot, you can see the sudden jump in the Utilization curve from 16 % (one core) to 100 % and the corresponding drop in frequency from 4+ GHz to 2.9 GHz:

Typical CPU utilization when all six cores are maxed out. The CPU frequency stays constant at 2.9 GHz.
No erratic thermal throttling

This pattern remained remarkably consistent throughout the full 39-minute run. As soon as the core utilization dropped, the frequency shot up, and vice versa. There was no sign of erratic thermal throttling, although I guess the fact that the max turbo boost frequency never reached the advertised 4.8 GHz could be a sign that the MacBook Pro enclosure can’t dissipate enough heat to take full advantage of the i9.

Here are a few more screenshots taken at various stages during the build. You can observe the pattern in all of them:

Full six-core utilization for an extended period. The CPU frequency doesn’t drop below 2.9 GHz. As soon as utilization drops to a single core, the CPU frequency shoots up and the CPU draws less power. The CPU temperature remains more or less the same throughout. This screenshot shows that the turbo boost frequency went up to ~4.4 GHz. It’s at 4.33 GHz at the time the screenshot was taken.

Finally, note that the discrete GPU was essentially idle during these tests. It was active because I had an external monitor connected, but it had nothing to do except drive the macOS UI.

Xcode build times

A clean build of one of my current projects (mixed Swift and Objective-C), run from the command line using xcodebuild (because the times are easier to record this way):

2018 MBP 2013 MBP Δ
115 s 137 s 16 % faster

Running the project’s test suite on the iOS simulator with xcodebuild test. This is a mix of unit tests and a few UI tests:

2018 MBP 2013 MBP Δ
127 s 135 s 6 % faster
Unzipping Xcode

Extracting Xcode-beta.xip using Archive Utility (tested with Xcode 10 beta 6):

2018 MBP 2013 MBP Δ
6:02 min 6:39 min 9 % faster
Static website build

A clean build of this site using Middleman.

2018 MBP 2013 MBP Δ
8.3 s 10.3 s 19 % faster
Conclusion

Performance-wise, I’m a little disappointed. I expected a bigger jump from my old machine, especially because the SSD in the 2018 MacBook Pro should be much, much faster than my old third-party SSD.

And I do notice fewer and shorter lags while working, which I partly ascribe to the SSD. For example, Xcode is faster at relaunching with tons of open windows/tabs after a crash, and you don’t have to wait as long for inline compiler diagnostics to appear. But maybe this is all in my imagination.

Ignoring performance for a minute, I like the new MacBook Pro better than I expected. Not because of the touch bar, but because of USB-C and the great (in my opinion) keyboard (as long as it doesn’t break).

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Swift provides the Custom​String​Convertible and Custom​Debug​String​Convertible protocols to allow types to provide custom textual descriptions of themselves.

If you read the documentation for these protocols, you’ll notice that accessing a value’s description or debugDescription directly is discouraged. Instead, you’re supposed to use the corresponding String initializers, String(describing:) and String(reflecting:).

Every value is string-convertible

The reason for this rule is that every value in Swift is convertible to a string, regardless of a type’s conformance to one or both of these protocols. The protocols only exist to allow customization of a type’s textual representation. In particular, don’t ever use these protocols as types or generic constraints.

That said, there’s certainly nothing wrong with calling description on a concrete value in a local context. For example, there are at least three equivalent ways to convert a list of integers to strings:

(1...5).map { $0.description }        // → ["1", "2", "3", "4", "5"]
(1...5).map { "\($0)" }               // same
(1...5).map(String.init(describing:)) // same

Which one you prefer is largely a matter of taste.

Don’t use CustomStringConvertible as a constraint

However, you should absolutely not use the following pattern, where a function argument is constrained to Custom[Debug]StringConvertible, either with a generic constraint or a plain type specification:

func doSomething1<T>(with x: T) where T: CustomStringConvertible {
    // ...
    // Call x.description
}

func doSomething2(with x: CustomStringConvertible) {
    // ...
    // Call x.description
}

Both of these functions compile, but they needlessly constrain the set of accepted inputs — for example, they don’t accept types that only conform to Custom​Debug​String​Convertible, even though that’s a perfectly valid way to provide a textual representation.

Instead, the function should accept any type, because anything is printable:

func doSomething3<T>(with x: T) {
    // ...
    // Call String(describing: x)
}

Note that you can’t call description in the function body anymore, but you can call String(describing:).

How String(describing:) works

When you call String(describing:) on a value, the standard library follows a set of rules to find a suitable textual representation for the argument. Let’s take a quick look how this works.

  1. String(describing:) creates a new empty string and passes the argument and the empty string to a function named _print_unlocked. This is the implementation::

    extension String {
      public init<Subject>(describing instance: Subject) {
        self.init()
        _print_unlocked(instance, &self)
      } 
    }
    
  2. _print_unlocked looks like this:

    internal func _print_unlocked<T, TargetStream : TextOutputStream>(
      _ value: T, _ target: inout TargetStream
    ) {
      if _isOptional(type(of: value)) {
        let debugPrintable = value as! CustomDebugStringConvertible
        debugPrintable.debugDescription.write(to: &target)
        return
      }
      if case let streamableObject as TextOutputStreamable = value {
        streamableObject.write(to: &target)
        return
      }
       
      if case let printableObject as CustomStringConvertible = value {
        printableObject.description.write(to: &target)
        return
      }
       
      if case let debugPrintableObject as CustomDebugStringConvertible = value {
        debugPrintableObject.debugDescription.write(to: &target)
        return
      }
       
      let mirror = Mirror(reflecting: value)
      _adHocPrint_unlocked(value, mirror, &target, isDebugPrint: false)
    }
    

    The function first checks if the value to be printed is an Optional, and if so, prints the optional’s debugDescription. The force-cast to Custom​Debug​String​Convertible is safe here because the standard library knows that Optional conforms. Printing optionals with their debug description is preferred because optionals are not suitable for display to the user anyway.

  3. If we’re not dealing with an optional, _print_unlocked then tests the value consecutively for conformance to Text​Output​Streamable, Custom​String​Convertible, and Custom​Debug​String​Convertible, in that order. It uses the first match to generate the description.

    This is how a type that only conforms to Custom​Debug​String​Convertible can also be printed with String(describing:).

  4. If no matching conformance was found, the final fallback is a function named _adHocPrint_unlocked, which uses the value’s Mirror representation to print its components.

String(reflecting:) essentially works the same way, only with the protocol conformances checked in a different order.

LosslessStringConvertible

I should also mention a third string conversion protocol: Lossless​String​Convertible. This protocol refines Custom​String​Convertible and adds a semantic constraint:

The description property of a conforming type must be a value-preserving representation of the original value.

The string representation of a value that conforms to Lossless​String​Convertible preserves all the information that’s needed to recreate the value. It’s a guarantee that you can use the string representation e.g. to serialize the value without loss of information.

All trivial types in the standard library (Bool, the integer and floating-point types, Unicode.Scalar) as well as Character, String, and Substring conform to Lossless​String​Convertible.

An exception to the rule

When you rely on LosslessStringConvertible semantics, you should absolutely access the description property directly, despite the above advice to the contrary. As we have seen, the alternative String(describing:) prefers Text​Output​Streamable’s representation over description if it’s available, and you can’t be 100 % certain that representation is identical to the value’s description, however unlikely any differences are.

Read for later

Articles marked as Favorite are saved for later viewing.
close
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Separate tags by commas
To access this feature, please upgrade your account.
Start your free month
Free Preview