paint-brush
How to Implement Real-Time Display of ChatGPT Response Generation in an iOS Appby@debug45
255 reads

How to Implement Real-Time Display of ChatGPT Response Generation in an iOS App

by Sergei MoskvinNovember 8th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

If you try ChatGPT on the official OpenAI website, you do not have to wait for the neural network answer to be completely generated. Instead, you see beautiful word-by-word appearing of the answer dynamically in real-time. Even when it has not yet been fully received, you may already find some information you need or understand that your question is poorly formulated.
featured image - How to Implement Real-Time Display of ChatGPT Response Generation in an iOS App
Sergei Moskvin HackerNoon profile picture

If you try ChatGPT on the official OpenAI website, you may notice that you do not have to wait for the neural network answer to be completely generated. Instead, you see beautiful word-by-word appearing of the answer dynamically in real-time. Even when it has not yet been fully received, you may already find some information you need or understand that your question is poorly formulated, and it is better to change the request and ask it again.


This behavior is much more user-friendly but somewhat more technically complicated. So, if you want to implement it in your iOS app, you have to write a little non-obvious code.


First, let’s write a regular network request, which simply waits for the server’s full response and only then returns it. In order to keep the article short enough, I omit nuances of forming a typical request and working with OpenAI API. I also do not pay sufficient attention to handling standard and typical network and server errors.


let urlSession = URLSession(configuration: .default)

let request: URLRequest! // A little mock code

urlSession.dataTask(with: request) { data, response, error in
    guard let data, let string = String(data: data, encoding: .utf8) else {
        print(error) // Handle network errors and basic parsing problems
        return
    }
    
    print(string) // A response to our request in JSON format
}


OK, we can send a request and receive a response, but only when it is completely generated. Now, let’s try to fix this nuance. In the beginning, we should add to the body of our URLRequest the stream parameter and set it to the true value (see OpenAI API documentation for details). Then remove the completionHandler closure and add a delegate for our URLSession.


let urlSession = URLSession(
    configuration: .default,
    delegate: self,
    delegateQueue: nil
)

urlSession.dataTask(with: request)


After that, we have to implement the necessary methods of the delegate. But be careful — the type of the delegate parameter in the URLSession initializer is URLSessionDelegate. It does not contain all the methods we need, so instead, we should implement its subprotocol URLSessionDataDelegate.


OK, let’s start with the first necessary delegate method. Our URLSession object will call it whenever it receives a chunk of a ChatGPT response.


func urlSession(
    _ session: URLSession,
    dataTask: URLSessionDataTask,
    didReceive data: Data
) {
    guard let response = dataTask.response as? HTTPURLResponse else {
        print("Some network error has occured. Internet connection, timeout, etc.")
        return
    }
    
    guard (200 ..< 300).contains(response.statusCode) else {
        print("Some HTTP error has occured, the code is \(response.statusCode)")
        return
    }
    
    guard let string = String(data: data, encoding: .utf8) else {
        print("Some parsing error has occured")
        return
    }
    
    // A chunk of the response to our request, in JSON format with some meta
    print(string)
}


Great, we have the working code and are ready to get ChatGPT response chunks!


But how to fully process the completion of a network request? Especially if the request is ended with an error. For example, if it times out or cannot reach the OpenAI server at all. Actually, we just need one more method in our URLSession delegate.


func urlSession(
    _ session: URLSession,
    task: URLSessionTask,
    didCompleteWithError error: Error?
) {
    if let error {
        print(error)
        return
    }
    
    guard let response = task.response as? HTTPURLResponse else {
        print("Some network error has occured")
        return
    }
    
    guard (200 ..< 300).contains(response.statusCode) else {
        print("Some HTTP error has occured, the code is \(response.statusCode)")
        return
    }
    
    print("The request is completed successfully")
}


That is all. Now, you just need to complete these drafts to a fully-designed iOS app — parse incoming JSON strings to your own models, correctly handle possible errors, and implement some user interface. I am sure you will succeed!


Thank you for your attention.