API Basic To Advance in 9 Steps. Become Pro.
API Basic To Advance in 9 Steps. Code Like Pro.
1. Basic API Call
A simple GET request using URLSession
.
import Foundation
func basicGetRequest() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1") else { return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data, error == nil else {
print("Error:", error ?? "Unknown error")
return
}
if let httpResponse = response as? HTTPURLResponse {
print("Status Code: \(httpResponse.statusCode)")
}
if let result = try? JSONSerialization.jsonObject(with: data, options: []) {
print("Result:", result)
}
}
task.resume()
}
2. Handling JSON with Decodable
Use Codable
for type-safe JSON parsing.
import Foundation
struct Todo: Codable {
let userId: Int
let id: Int
let title: String
let completed: Bool
}
func fetchTodo() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1") else { return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data, error == nil else {
print("Error:", error ?? "Unknown error")
return
}
do {
let todo = try JSONDecoder().decode(Todo.self, from: data)
print("Todo:", todo)
} catch let decodingError {
print("Decoding Error:", decodingError)
}
}
task.resume()
}
3. POST Request with JSON Body
Send a POST request with a JSON body.
import Foundation
struct User: Codable {
let name: String
let email: String
}
func createUser() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let newUser = User(name: "John Doe", email: "john.doe@example.com")
do {
request.httpBody = try JSONEncoder().encode(newUser)
} catch let encodingError {
print("Encoding Error:", encodingError)
return
}
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error == nil else {
print("Error:", error ?? "Unknown error")
return
}
if let httpResponse = response as? HTTPURLResponse {
print("Status Code: \(httpResponse.statusCode)")
}
if let result = try? JSONSerialization.jsonObject(with: data, options: []) {
print("Result:", result)
}
}
task.resume()
}
4. Handling Errors and Status Codes
Use a custom error enum and handle different status codes.
import Foundation
enum NetworkError: Error {
case badURL
case requestFailed
case unknown
}
func fetchData() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1") else {
print(NetworkError.badURL)
return
}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data, error == nil else {
print(NetworkError.requestFailed)
return
}
if let httpResponse = response as? HTTPURLResponse {
switch httpResponse.statusCode {
case 200:
print("Success:", httpResponse.statusCode)
case 400...499:
print("Client Error:", httpResponse.statusCode)
case 500...599:
print("Server Error:", httpResponse.statusCode)
default:
print("Unexpected Status Code:", httpResponse.statusCode)
}
}
if let result = try? JSONSerialization.jsonObject(with: data, options: []) {
print("Result:", result)
}
}
task.resume()
}
5. Using URLComponents
to Build URLs with Query Parameters
For building complex URLs with query parameters.
import Foundation
func fetchTodos(userId: Int) {
var urlComponents = URLComponents(string: "https://jsonplaceholder.typicode.com/todos")!
urlComponents.queryItems = [URLQueryItem(name: "userId", value: "\(userId)")]
guard let url = urlComponents.url else { return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data, error == nil else {
print("Error:", error ?? "Unknown error")
return
}
if let result = try? JSONSerialization.jsonObject(with: data, options: []) {
print("Result:", result)
}
}
task.resume()
}
6. Advanced: API Call with Combine
Using the Combine framework for reactive programming.
import Foundationimport Combine
struct Post: Codable {
let id: Int
let title: String
let body: String
}
func fetchPost() -> AnyPublisher<Post, Error> {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1") else {
fatalError("Invalid URL")
}
return URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: Post.self, decoder: JSONDecoder())
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
var cancellable: AnyCancellable?
cancellable = fetchPost().sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Finished")
case .failure(let error):
print("Error:", error)
}
}, receiveValue: { post in
print("Post:", post)
})
7. Advanced: API Call with Async/Await (Swift 5.5+)
Leverage Swift's async/await feature for more readable and concise asynchronous code.
import Foundation
@available(iOS 15.0, *)
func fetchPostAsync() async {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1") else { return }
do {
let (data, response) = try await URLSession.shared.data(for: URLRequest(url: url))
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
let post = try JSONDecoder().decode(Post.self, from: data)
print("Post:", post)
}
} catch {
print("Error:", error)
}
}
@available(iOS 15.0, *)
func exampleUsage() {
Task {
await fetchPostAsync()
}
}
8. Advanced: Handling Multipart Form Data
Upload files using multipart form data.
swiftimport Foundation
func uploadFile(fileUrl: URL, to url: URL) {
var request = URLRequest(url: url)
request.httpMethod = "POST"
let boundary = UUID().uuidString
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let data = createMultipartData(fileUrl: fileUrl, boundary: boundary)
let task = URLSession.shared.uploadTask(with: request, from: data) { (data, response, error) in
guard let data = data, error == nil else {
print("Error:", error ?? "Unknown error")
return
}
if let result = try? JSONSerialization.jsonObject(with: data, options: []) {
print("Result:", result)
}
}
task.resume()
}
func createMultipartData(fileUrl: URL, boundary: String) -> Data {
var data = Data()
let fileName = fileUrl.lastPathComponent
let mimeType = "application/octet-stream" // Adjust based on your file type
data.append("--\(boundary)\r\n".data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"file\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!)
data.append("Content-Type: \(mimeType)\r\n\r\n".data(using: .utf8)!)
if let fileData = try? Data(contentsOf: fileUrl) {
data.append(fileData)
}
data.append("\r\n".data(using: .utf8)!)
data.append("--\(boundary)--\r\n".data(using: .utf8)!)
return data
}
9. Advanced: Handling API Calls with Dependency Injection
Using dependency injection for more testable and decoupled code.
import Foundation
protocol NetworkService {
func fetchData(from url: URL, completion: @escaping (Result<Data, Error>) -> Void)
}
class APIService: NetworkService {
func fetchData(from url: URL, completion: @escaping (Result<Data, Error>) -> Void) {
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.failure(NetworkError.unknown))
return
}
completion(.success(data))
}
task.resume()
}
}
class ViewModel {
private let networkService: NetworkService
init(networkService: NetworkService) {
self.networkService = networkService
}
func loadData(from url: URL) {
networkService.fetchData(from: url) { result in
switch result {
case .success(let data):
print("Data received: \(data)")
case .failure(let error):
print("Error: \(error)")
}
}
}
}
// Usage
let viewModel = ViewModel(networkService: APIService())
viewModel.loadData(from: URL(string: "https://jsonplaceholder.typicode.com/todos/1")!)
Comments
Post a Comment