This episode is freely available thanks to the support of our subscribers

Subscribers get exclusive access to new and all previous subscriber-only episodes, video downloads, and 30% discount for team members. Become a Subscriber

In this episode, we answer some of the questions we've received over the past few weeks. We cover networking, table views, stack views, our App class, and testing.

0:06 This episode is a bit different because it's a Q&A episode where we answer questions that have been sent in to us. We'd like to know if people like these Q&A episodes, so if you have feedback, please send us an email.

Storing Authentication Information

0:48 Let's start with the first question: Where do you store authentication information when making a request? This is a question about the Networking episode, where we presented a networking layer with a Resource struct. In many cases, we have to use an authentication token for almost every request, as most endpoints are authenticated. Therefore, it would make sense to put the token in the Webservice class:

final class Webservice {
    var authenticationToken: String?

    // ...
}

1:50 Then we'd modify the request in the load method to include the token as a header field.

If it's important for your business logic to make a clear separation between authenticated and non-authenticated requests, then you could put the token in the Resource instead.

HTTP Headers

2:16 The next question: Where would you add HTTP headers — specifically, headers such as Content-Type, which are also connected to how you parse the resource? A header like Content-Type would be a perfect candidate to put into the Resource struct, as it's specific to the endpoint. We could, for example, add a dictionary called headers:

struct Resource<A> {
    let url: URL
    let method: HttpMethod<Data>
    let parse: (Data) -> A?
    let headers: [String: String]
}

Alternatively, we could add a separate property for the header:

struct Resource<A> {
    let url: URL
    let method: HttpMethod<Data>
    let parse: (Data) -> A?
    let contentType: String?
}

2:55 Having a headers dictionary is more generic, so it's also a bit more flexible. We could modify our initializer for JSON resources to automatically set the Content-Type or the Accept-Type. Likewise, if we want to accept XML, we would need a different parsing function, and we can support this by adding another initializer to Resource so that the parsing function and Content-Type automatically match up.

3:28 We could even combine the two questions: in our load method in the WebService, we could add both headers that are Resource specific and headers that are set for all resources.

Multiple Cell Types

3:47 The next question: How would you extend the approach in the Generic Table View Controllers episode to handle different cell types within the same table view?

4:14 At first, we thought it'd be easy by playing around with protocols a bit, but when we actually tried figuring it out, it wasn't so easy. Our conclusion was that it's possible, but none of our solutions felt as nice as the generic table view controller for a single cell type. For example, one simple solution had a big drawback: we needed to move a force-cast from our table view controller (the library) to the configure callback (the client of the library). Currently, within tableView(cellForRowAt:), we cast our cell to the right type so the configure callback gets called with the right cell type.

5:23 No matter how we solve this problem, we always end up with a compromise. We haven't found a nice solution, so if anyone discovers something, let us know.

Adding UITextView Support to ContentElement

5:39 Another question: In the Stack Views with Enums episode, if you want to add support for a text view, how do you deal with all the callbacks? A text view has multiple callbacks. If we only had a single callback, we could use something similar to the .button case, but what about when there are multiple callbacks?

6:18 First, the ContentElement approach is something we built for convenience, so it might not be the best tool for this problem.

6:26 Second, we could also add a custom case to the enum. This way, we can use any view within the stack view and we'd pass in a fully configured UIView subclass. This allows us to have more complicated views that we use together with the convenience cases:

enum ContentElement {
    case label(String)
    case button(String, () -> ())
    case image(UIImage)
    case custom(UIView)
}

6:57 Third, we could also add a dedicated case for the text view. To keep all the delegate callbacks in check, we could first start with spelling them out individually:

enum ContentElement {
    case label(String)
    case button(String, () -> ())
    case image(UIImage)
    case textView(String, didChange: (String) -> (), didBeginEditing: () -> ())
}

This get unwieldy quickly. Instead, we could have a single callback that groups these events together in an enum, with one case per event:

enum TextViewEvent {
  case didChange(String)
  case didBeginEditing
  case openURL(URL)
}

enum ContentElement {
    case label(String)
    case button(String, () -> ())
    case image(UIImage)
    case textView(String, (TextViewEvent) -> ())
}

By the way, we could also take this approach in other situations where the number of callbacks gets out of hand — for example, in a view controller such as the one described in our Connecting View Controllers episode.

Custom UIStoryboardSegue Subclasses

8:50 The next question: Is the App class in the Connecting View Controllers episode such a good idea? It will get complicated as we add more features. An alternative approach might be creating subclasses of UIStoryboardSegue, just like in Andy Matuschak's talk, Refactor the Mega Controller. Rather than having all the logic in an App class, he moved a lot of logic to segues. That way, it wasn't in the view controller.

9:30 There are two parts to this question. Firstly, we'll have to decide if we want to use storyboards at all. In our case, we decided not to use storyboards, and then we ended up with an App class that could become very big. That's definitely a problem, but it should be easy to split it up into multiple classes.

10:00 The second part is that we've never tried having a storyboard with custom segue subclasses. However, it seems like a sensible approach when using storyboards. There's no one right way. Our solution was definitely more experimental and strange, but it worked well for our use case.

Storyboards, Nibs, or Code?

10:26 There's a related question that we get a lot: Should I use storyboards, or nibs, or just code? There's no way we can answer this. We've used all these approaches in past projects, and all of them worked well. Just choose whatever fits you and your project. With all three approaches, you'll have to be careful to keep your code maintainable by continually refactoring your code.

Testing

11:08 There are also questions about testing: What should I test? How much should I test? What's the best way to do this? Again, there's no right answer. We've used different approaches ourselves. Once, for a Rails client project, we used BDD and this worked really well. Once we handed over the project, the next person could just start working without us having to spend a lot of time transferring the code base.

11:59 We also have some episodes coming up where we develop things in a TDD way because it's a perfect fit. In those particular instances, this made development much faster and easier. There are other parts of the same app that we only tested manually. In a lot of projects, we only tested a specific critical part or almost nothing. It depends on your project. Again, there's no one right way to test. It depends on who you're working with, what your project does, how important it is, and so on.

12:45 It's important that you figure out for yourself what helps you and what doesn't help you, along with when testing helps you. It's not so important what we do or how we come to our decisions, because everyone needs to figure it out for themselves personally. Different people have different capabilities and different ways of working.

13:13 When you think about testing, it's good to keep in mind the ways in which it can help you. For example, testing can help you write the code correctly as a verification, but it can also help you design your code. More specifically, it can help you define the API of your code. Finally, tests can serve as documentation. This is also useful when working with multiple people.