Errata for Real-World iOS by Tutorials 1st Edition

Creating this topic to catch any typos and bugs in the 1st Edition of Real-World iOS by Tutorials.

In Section 3.5 “Layering on top of the brain” [online version], the definition for RequestManager includes the following:

  func perform<T: Decodable>(
    _ request: RequestProtocol) async throws -> T {
      // 3
      let data = try await apiManager.perform(data, authToken: "")

That should be try await apiManager.perform(request, authToken: "")


API used in the book, Petfinder, seems to be useful for US devs only, not usefull for the rest of the world. Please suggest the alternative API or some walk-around.

@horb - I’m in the UK. I used a random US zip code for the registration. You’re right though, this sort of thing shouldn’t happen.

@jellodiil - Chapter 4. I’m lost. The starter project has a lot of new code in AnimalEntity+CoreData that isn’t mentioned even once. It conforms the Animal struct (in an extension) to the UUIDIdentifiable protocol. Why isn’t this explained? There are 13 entities in the Core Data model and most of them aren’t mentioned even once in the whole book, they just get in the way of trying to work out the app’s structure.

Why is the AnimalEntity treated differently to all of the other entities? In “Making a Concrete Implementation” that small extension to Breed explains nothing about what is happening regarding the Animal struct, how it’s linked to anything or even what kind of structure we’re supposed to be working towards. Maybe my expectations are just wrong. I wanted to use the book to apply POP to my own app, but the great holes in the explanations make me think this isn’t the book for that.

I really think this could have been structured better with less unnecessary bloat and better and fuller detail and design explanations.

Thanks @magnas
Registration for the key and testing the app in the current state is not developer friendly.
I hope somebody from RW team is watching this topic and the proper solution will be delivered soon.

@hococoder Could you help here, please :slight_smile:

Hi @horb, hi @magnas

Thank you for your feedback. It’s really important to us to hear back from you and improve our content.

Petfinder’s API is a very simple and straight forward web API. It was chosen to help you learn core concepts of app develoment that are taught throughout the book. It was also chosen because it’s very easy to get a key and open to anyone that wants to develop an app with it.

While the API only shows pets in the US, it still presents a real-world scenario you may face as a developer; working on an app that does not work in your region. I live in Brazil, and I had to work on apps that not only didn’t work in my state (and are location-based) but also didn’t work in my country.

This is a situation that happens and Apple has tools that help you overcome this. Location data is only “required” after chapter 12 and you can either hard code a US location or use Xcode’s tools to simulate your location. The main point of chapter 12 was teaching core concepts to readers about user privacy and how to handle this kind of data in their apps.

As for the registration, you can simply use a random US zip code like @magnas suggested. This won’t affect the request itself. :]

@jellodiil - thanks for trying but my questions seem to have been missed/ignored. Never mind, I think the book is so poorly constructed that I’ve requested a refund.

Hi @magnas - my apologies for the delay in getting back to you (been a crazy few days)! Thanks to @renan.dias for answering the question about the PetFinder API - we hoped that wasn’t going to be a huge obstacle, and it was also something that the Real World Android book used as well. I’m definitely going to take it as an action item to consider more world-friendly APIs to use in the future. As Renan said though - what we’re focused on here is more the content, and not exactly the location where you are or where you are searching from.

As for the Chapter 4 code - yes, there was a lot new there! We couldn’t cover everything in this book - as much as we wanted to, so we had to make some design decisions. We also expected - as stated in the front of the book - that you would have some experience in Core Data. This book is intended as a broad and not deep dive into making an iOS app. But I’m happy to try to explain some of the choices here!

The use of UUIDIdentifable for the structs was to ensure that we had a unique ID associated with each object we wanted to persist (that is mentioned right below the declaration of that protocol in the text). The chapter sets up mechanisms to switch between structs and core data classes, and this ensures we can search on a unique identifier when we do so.

On the 13 entities - as mentioned before, this was done assuming you had previous Core Data knowledge, and these entities correspond to the models used in the previous chapters. As mentioned in the text, we need to represent those types as Core Data entities to store them, so that’s what these are for.

As for the Breed entity - again, this comes down to having SO much that we wanted to discuss, so we had to pick some examples out instead of walking you through each and every type (most of them were in the starter project ready to go, but the Breed was the walked-through example). Since Breed (and the other types in the starter project) now extends CoreDataPersistable, those inherited methods, along with the keyMap provides you a very simple way to declare a type that can be easily persisted to Core Data without having a lot of boilerplate code in the way - that was our goal here.

I want to echo something Renan said earlier - we really do appreciate your feedback! We try to write the book the reaches the most people, and unfortunately sometimes we are off the mark. Like I said earlier, we are collecting feedback and noting them for future works. I do apologize again for the delay in getting back to you.

@hococoder - Thanks for that. I have a lot of experience with Core Data and writing apps with many entities but when you drape the protocol oriented treatment on top and haven’t explained why some of those entities are treated differently, or why the most important object, Animal, isn’t described, or how relationships are handled, or why the code is structured in the way it is, it becomes just another do this, then do this, now do this and tada, look at what you’ve got. The explanations are too thin and the code base and structure is complicated by unnecessary objects. Not to worry, good luck with the book.

In Section 7.2 Benefits of modularization , under the title Build Time, there is a small typo:

But for you to guarantee this, you have **two** know the different kinds of frameworks

I too think chapter 4 grossly missed any design explanations. I don’t know about others, but I am trying to learn the entire setup by not starting with the “starter” and starting from chapter 4 a huge chunk is missing.

Hi! @black_coffee thanks for your feedback, we really do appreciate it! I am more than happy to help answer questions you have about the missing parts leading into Chapter 4. I’m also taking everyone’s feedback towards a better version of this chapter in the future.

In Chapter 3, during the challenge exercise for the tests, testRequestToken() and testCachedToken() kept failing until I found where the differences were. In sample files “AccessTokenManager” has the following ‘update()’ function that needs to be called on ‘init’ and ‘isTokenValid()’ but it wasn’t mentioned in the chapter tutorial:

  func update() {
    accessToken = getToken()
    expiresAt = getExpirationDate()

I think there is an error in the starter project for Chapter 4. The Animal+CoreData.swift file defines a number of properties. The one for size is

 var size: Size {
    get {
        guard let sizeValue = coatValue, let size = Size(rawValue: sizeValue) else { return Size.unknown }
        return size
    set {
        self.sizeValue = newValue.rawValue

The line:

guard let sizeValue = coatValue, let size = Size(rawValue: sizeValue) else { return Size.unknown }

should be:

guard let sizeValue = sizeValue, let size = Size(rawValue: sizeValue) else { return Size.unknown }
1 Like

Yes you seem to be correct! We will push an update in. Thanks!

Another small thing I noticed was for the AccessTokenManager.swift file, the final project for Chapter 3 includes a function:

func update() {
    accessToken = getToken()
    expiresAt = getExpirationDate()

which is called in both init(userDefaults:) and isTokenValid().

However, in the Chapter 4 starter and final projects this function is not used, and accessToken and expiresAt are not updated in init(userDefaults:) - only in isTokenValid().

I think I found this when adding unit tests in the Challenge section of Chapter 3 - one of the tests was failing.

I think we had someone else report this in a different thread but thanks for the report!

Hi guys,

I believe there’s a little typo in Chapter 3 in the explanation following the code block in which we add createURLRequest to the extension. Step 8 should refer to rather than