The Author Online Book Forums are Moving

The Author Online Book Forums will soon redirect to Manning's liveBook and liveVideo. All book forum content will migrate to liveBook's discussion forum and all video forum content will migrate to liveVideo. Log in to liveBook or liveVideo with your Manning credentials to join the discussion!

Thank you for your engagement in the AoF over the years! We look forward to offering you a more enhanced forum experience.

I just wanted to say thank you for the great book. I've enjoyed working through the BookCase App.

Thank you, Craig, for responding to my question and comments! I've finished Chapter 15 and am going to stop there to build on my own.



I am working on Chapter 15, section 7 "Testing Your App". I've cloned the "Chapter15.3.Tested" branch of the Bookcase app from github. When I run the "testSortTitle" test it fails. It fails because the BooksManager class calls the loadBooks() function on instantiation. So, the array is not empty. The first item is "Animal Farm", which the unit test is not expecting. It expects "Gulliver's Travels".

I got around this by creating a subclass of the BooksManager class in the unit test. It overrides the "retrieveBooks()" function to return an empty array.

Is this an appropriate approach to this type of problem? Is there another way to approach it?

Here is what I did:

import XCTest
@testable import Bookcase

class MockBooksManager: BooksManager {
  override func retrieveBooks() -> [Book]? {
    return []

class BooksManagerTests: XCTestCase {

    var booksManager:MockBooksManager!
    var bookDaVinci:Book!
    var bookGulliver:Book!
    var bookOdyssey:Book!

    override func setUp() {
        bookDaVinci = Book(title: "The Da Vinci Code", author: "Dan Brown", rating: 5, isbn: "", notes: "")
        bookGulliver = Book(title: "Gulliver's Travels", author: "Jonathan Swift", rating: 5, isbn: "", notes: "")
        bookOdyssey = Book(title: "The Odyssey", author: "Homer", rating: 5, isbn: "", notes: "")
        booksManager = MockBooksManager()
 // rest of unit test code snipped


In section 14.11 "Downloading data from a web service", Step 1 code states:

let thumbnailURL = volumeInfo["imageLinks"]?["thumbnail"].string{

But it should be:

let thumbnailURL = item["volumeInfo"]["imageLinks"]["thumbnail"].string {

Note: "volumeInfo" needs to be obtained from the "item" dictionary.
Hi Craig,

I figured it out. In the github repo (branch Chapter13.7.AVAudioPlayer) the metadataOutput function is:

func metadataOutput(captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {

But in the book it is (note the addition of the _):

func metadataOutput(_ captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {

The book code works! I point it at an ISBN and it captures the data and fills in the ISBN field.



I'm having trouble getting the barcode reader to detect a barcode. I've installed the app on to my phone (SE iOS 11.4). The app launches and I'm able to bring up the "Detect barcode" scene. However, I am not able to detect a barcode. I've tried detecting a barcode in both portrait and landscape mode. I've tried different books. When I get the phone close to the barcode I'm not able to focus the camera, so the barcode is bleary and not detected. From further away the barcode is clear but is not detected. I am using the code from Chapter13.7.AVAudioPlayer

Is there a trick to get the phone to read the barcode?


In SQLite section in Chapter 11 the code to update the booksFile property includes the following line:

if let bundleFilePath = Bundle().resourceURL?.appendingPathComponent("Books").appendingPathExtension("db") {

However, this returns a nil value, which results in an empty Books.db file.

It should be:
if let bundleFilePath = Bundle.main.resourceURL?.appendingPathComponent("Books").appendingPathExtension("db") {

A bit more debugging tonight lead me to why I had the bad books.db file.

The code in the book for setting the booksFile property uses the following:


which returns a nil value.

The code in the repository uses:
 Bundle.main.resourceURL?.appendingPathComponent("Books").appendingPathExtension("db") { 

which works.

I'll add this to the errata thread.
Oh, wait I know what happened. I deleted the file before I did the break points.

Here's what happened:

- My version wasn't working (no books table as well)
- I pulled the version from github
- It had the same problem too (no books table). But the code checks to see if the Books.db file exists. Since it did (my broken one), it did not copy the Books.db file over.
- I deleted the file, so when I reran it, the Books.db file was copied over and now it works.
- When I go back into my version of the code it works too, as it's now using the file that was copied over in the step above.

So, I must have created a bad Books.db file at some point, and both versions of the app were using that (since the file existed). But now it works.

Sorry for the rambling thread.
Hmm, this is weird. I set a break point at the line:
if !FileManager().fileExists(atPath: filePath.path) {

and then stepped through code. I inspected the values of 'bundleFilePath' and 'filePath' at the line:
try FileManager().copyItem(at: bundleFilePath, to: filePath)

I opened the bundleFilePath file and it had the books table. I stepped past the line with no error and looked at the filePath Books.db file and it had the books table in it. I let the program continue running and it works! No errors. And if I rerun it works.

I'm not sure what happened and why it works now.
Not sure if this helps, but here is the output from doing a
ls -la
on the path:

ls -la /Users/monica/Library/Developer/CoreSimulator/Devices/5B886E05-1300-43FE-9F76-82CD5102BDF0/data/Containers/Data/Application/29E60DC9-8B98-40F8-AB0A-EE85A7A12C75/Library/Application\ Support/Books.db
-rw-r--r--@ 1 monica access_bpf 0 15 May 21:19 /Users/monica/Library/Developer/CoreSimulator/Devices/5B886E05-1300-43FE-9F76-82CD5102BDF0/data/Containers/Data/Application/29E60DC9-8B98-40F8-AB0A-EE85A7A12C75/Library/Application Support/Books.db

Sorry I am having more troubles with the SQLite section. I checkout out the Chapter11.6.SToreDataSQL branch When I run the app I get the following error:

failed: no such table: Books

The path given to the file is:
/Users/monica/Library/Developer/CoreSimulator/Devices/5B886E05-1300-43FE-9F76-82CD5102BDF0/data/Containers/Data/Application/29E60DC9-8B98-40F8-AB0A-EE85A7A12C75/Library/Application Support/Books.db

If I open that database in the db browser, I see that it is empty. There is no books table.

However, if I open the "Books.db" file that is checked into the repository, I do see the books table, with one entry.

I tried "Erase all content and settings" on the simulator. I also checked in the "Build Phases" section that the Books.db was included in the "Copy bundled resources" section.

Any ideas what else to try?

Thank you,

Awesome! Thank you!
Hello Craig,

I am in the SQLite section of Chapter 11 and I am getting a compiler error stating:

Initializer for conditional binding must have Optional type, not 'FMDatabase'

on the line
guard let db = FMDatabase(path: booksFile.path) else {

in the getOpenDB() function.

I am wondering if something has changed in the latest version of fmdb which causes the code to no longer compile. Pulling the Chapter11.6.StoreDataSQL branch it compiles fine, but it has older versions of the fmdb files.

The FMDB readme on github has the following example for creating a db connection in Swift:

let database = FMDatabase(url: fileURL)

guard else {
    print("Unable to open database")

I've modified my project to use that code (but with the path rather the url argument) and it compiles.


Hi Craig,

Yes, it works now! I had to do the "Erase All Content And Settings..." in the simulator, and then it worked.

Thank you!

Hello Craig,

Sorry, but I am getting an "index out of range" error now in the XML.swift file, when I run the app. It occurs in the Chapter11.4.StoreDataXML branch as well. I've tried restarting XCode and also double checked that the 'booksFile' file is empty (it wasn't containing old data from the PList section).


Hi Craig,

Yes, it works now! Thank you for fixing it. smilie

Chapter 11.2.3 - Archiving Objects. In the section "Adopting Codable protocol" the first step's code example is:

class Book: Codable {

But up to this point Book has always been a struct. And the github code has it as a struct as well:

struct Book: Codable {

I am working on Chapter 11 and am in the XML section. I've cloned the Swift-XML.git repository and then dragged the XML.swift file into my BookCase Project Navigator.

When I enter in the xml computed property in the Book object I get an error stating "XMLNode' is ambiguous for type lookup in this context"

Looking at the XML.swift file I dragged into the my project it contains similar errors too:

'XMLNode' is ambiguous for type lookup in this context
Invalid redeclaration of 'XML'.

What am I missing?

Here is a screenshot of the errors I see when looking at the XML.swift file:


Thank you!


There is a minor typo in listing 8.3.4

The code in the book states:
var keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey]
                as? NSValue)?.cgRectValue()

But, there should be no parentheses at the end of cgRectValue
var keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey]
                as? NSValue)?.cgRectValue

And point 4 in that section is different than what is in the git repository. The book code has the following which does not compile:
guard let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double       
     curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt
            else { return }

The git repository has the following:
guard let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double
    else { return }
guard let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt
    else { return }

In section 8.3.5 the book declares the firstResponder as a UIResponder? but it should be UIView? as in the git repository.

Book code:
import UIKit
extension UIView {
  var firstResponder: UIResponder? {
   //...more code

Git code:
import UIKit
extension UIView {
    var firstResponder:UIView? {
    //..more code

Minor typo in section 8.1. The checkpoint says to use the BookCase branch of


But the branch is (no hyphen):


In section 7.1.2 "Size classes in Interface Builder" it wasn't clear in point 2 that I needed to click in the Document Outline or Canvas area to see the "use auto layout" and "use trait variations" options. I had only clicked on the Main.storyboard file in the project navigator area, which does not show those options in the file inspector. Once I clicked in the Document Outline area, then the options appeared in the File Inspector menu.

Anyway, it took me a few minutes to figure out why I wasn't seeing what is displayed in figure 7.6. I'm not sure if it's an errata item, but figured I would note it.


It works now. Thank you Craig!


I cloned the Chapter 3 repository. When I open it in playgrounds I get the following error in a popup window:

Couldn't load settings from contents.xcplayground

I was able to clone the Chapter 2 and the Cheatsheet repositories and open them in playgrounds.

Any ideas on how to resolve this?


Hi Craig,

It is working now. Thank you!


I tried running the completed app in section 1.6.3. I got the following error message:

dyld: Library not loaded: @rpath/UIImageColors.framework/UIImageColors
  Referenced from: /Users/monica/Library/Developer/CoreSimulator/Devices/B3A8E0A1-6B41-4B35-B6C9-C19A97AF964C/data/Containers/Bundle/Application/894CFD03-633D-4CAD-930F-3A700343EEBF/
  Reason: image not found

Any ideas on how to resolve this?



In the intro of chapter 5, it states "In addition, using internal iteration, the Streams API can decide to run your code in parallel". Does this mean even if a sequential stream is used the operations could still be executed in parallel? If so, what happens if there is bad code that uses mutable state? Will the Streams API recognize that and run the code sequentially? Or would it run it in parallel with potentially a race condition occurring?

Thank you,