Advanced Networking with URLSession

Sep 15 2022 · Swift 5.6, iOS 15, Xcode 13.4.1

Part 1: Upload Data, Background Downloads & WebSockets

03. Upload Data

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 02. Install Vapor Next episode: 04. Background Downloads

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Notes: 03. Upload Data

URLSession - Apple Developer

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

So far you’ve download data via URLSession tasks and its asynchronous transfer methods. But what if you need to upload data to a server instead?

cd ~/desktop
vapor new UploadServer
cd UploadServer
vapor xcode -y
struct MusicItemRating: Content {
  let id: String
  let artistName: String
  let trackName: String
  let rating: Int
}
app.post("upload") { req -> HTTPResponseStatus in
  let item = try req.content.decode(MusicItemRating.self)
        
  print("ID: \(item.id)")
  print("Artist Name: \(item.artistName)")
  print("Track Name: \(item.trackName)")
  print("Rating: \(item.rating)")
        
  return .ok
}
import SwiftUI
class RatingUploader: ObservableObject {
  private let session: URLSession
  private let sessionConfiguration: URLSessionConfiguration
  
  init() {
    self.sessionConfiguration = URLSessionConfiguration.default
    self.session = URLSession(configuration: sessionConfiguration)
  }
  
  func submit(rating: Int, for musicItem: MusicItem) async throws {
    
  }
}
guard let uploadURL = URL(string: "http://localhost:8080/upload") else {
  throw RatingUploadError.failedToCreateUploadURL
}
enum RatingUploadError: Error {
  case failedToCreateUploadURL
}
guard let uploadData = json.data(using: .utf8) else {
  throw RatingUploadError.failedToCreateUploadData
}
case failedToCreateUploadData
var request = URLRequest(url: uploadURL)
request.httpMethod = "Post"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let json = """
{
  "id": \(musicItem.id),
  "artistName": \(musicItem.artistName),
  "trackName": \(musicItem.trackName),
  "rating": \(rating)
}
"""
case invalidResponse
let (_, response) = try await session.upload(for: request, from: uploadData)
guard let httpResponse = response as? HTTPURLResponse,
      httpResponse.statusCode == 200
else {
  throw RatingUploadError.invalidResponse
}
@ObservedObject private var uploader: RatingUploader = RatingUploader()

@MainActor @State private var showRatingSubmitFailedAlert: Bool = false
private func submitRatingTapped() async {
  do {
    try await uploader.submit(rating: ratingView.rating, for: musicItem)

    ratingSubmitted = true
  } catch {
    showRatingSubmitFailedAlert = true
  }
}
.alert("Failed to submit your rating", isPresented: $showRatingSubmitFailedAlert) {
  Button(role: .cancel, action: {
    showRatingSubmitFailedAlert = false
  }, label: {
    Text("Dismiss")
  })
}
.alert("Rating submitted successfully", isPresented: $ratingSubmitted) {
  Button(role: .cancel, action: {
    ratingSubmitted = false
  }, label: {
    Text("Dismiss")
  })
}
VStack(spacing: 16) {
  ratingView
    .disabled(ratingSubmitted)
            
  Button {
    Task {
      await submitRatingTapped()
    }
  } label: {
    Text("Submit")
  }
  .disabled(ratingSubmitted)
}
let json = “””
{
  “id”: \(musicItem.id),
  “artistName”: “\(musicItem.artistName)”,
  “trackName”: “\(musicItem.trackName)”,
  “rating”: \(rating)
}
“””