The Secret to Easy API Calls: Interceptors in SwiftUI, Compose, and Flutter

Mukund JogiMukund Jogi
4 min read

Imagine building an app that needs to fetch data from a server. You'd write code to make API calls, parse the data, and handle any errors. This can be a lot of repetitive code, especially if you're using the same API calls in different parts of your app.

Enter network call interceptors! They're like little helpers that sit between your app and the server, handling the tedious tasks of making API calls and processing data. This makes your code cleaner, more reusable, and easier to maintain.

Let's see how this works in SwiftUI, Compose, and Flutter:

1. SwiftUI

In SwiftUI, we can use URLSession to make API calls. We can create an interceptor that handles the request and response:

// Interceptor class
class APIInterceptor {
  static func intercept(url: URL, completion: @escaping (Data?, Error?) -> Void) {
    URLSession.shared.dataTask(with: url) { data, response, error in
      if let error = error {
        completion(nil, error)
        return
      }

      if let data = data {
        completion(data, nil) 
      }
    }.resume()
  }
}

Now, you can easily use this interceptor in your SwiftUI views:

struct MyView: View {
  @State private var data: [String] = []

  var body: some View {
    List(data, id: \.self) { item in
      Text(item)
    }
    .onAppear {
      APIInterceptor.intercept(url: URL(string: "https://api.example.com/data")!) { data, error in
        if let error = error {
          print("Error fetching data: \(error)")
        } else if let data = data {
          // Parse the data to an array of strings
          let decoder = JSONDecoder()
          if let decodedData = try? decoder.decode([String].self, from: data) {
            self.data = decodedData
          }
        }
      }
    }
  }
}

2. Compose

Compose uses Kotlin coroutines for asynchronous operations. Here's how you can create an interceptor in Compose:

object APIInterceptor {
  suspend fun <T> intercept(url: String, parser: (String) -> T): T {
    try {
      val response = HttpClient.get(url)
      val data = response.body<String>()
      return parser(data) // Parse the data to the desired type
    } catch (e: Exception) {
      throw e
    }
  }
}

You can then call the interceptor from a Compose Composable function:

@Composable
fun MyScreen() {
  val data = remember { mutableStateListOf<String>() } 

  LaunchedEffect(Unit) {
    try {
      val fetchedData = APIInterceptor.intercept("https://api.example.com/data") {
        // Parse the JSON string to an array of strings
        Gson().fromJson(it, Array<String>::class.java).toList()
      }
      data.addAll(fetchedData)
    } catch (e: Exception) {
      Log.e("MyScreen", "Error fetching data: ${e.message}")
    }
  }

  // Display the data using Compose components
  Column {
    data.forEach { item ->
      Text(item)
    }
  }
}

3. Flutter

Flutter uses the http package for API calls. Here's an interceptor example:

class APIInterceptor {
  static Future<dynamic> intercept(String url) async {
    try {
      final response = await http.get(Uri.parse(url));
      if (response.statusCode == 200) {
        // Parse the response body to the desired type
        return jsonDecode(response.body); 
      } else {
        throw Exception("API request failed with status code ${response.statusCode}");
      }
    } catch (e) {
      throw e;
    }
  }
}

Then, in your Flutter widget:

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  List<String> data = [];

  @override
  void initState() {
    super.initState();
    fetchData();
  }

  Future<void> fetchData() async {
    try {
      final fetchedData = await APIInterceptor.intercept("https://api.example.com/data");
      setState(() {
        data = fetchedData.cast<String>().toList();
      });
    } catch (e) {
      print("Error fetching data: ${e.toString()}");
    }
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: data.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(data[index]),
        );
      },
    );
  }
}

Benefits of Interceptors

  • Clean Code: Less repetitive code, easier to read and understand.

  • Reusability: The same interceptor can be used for multiple API calls.

  • Error Handling: Centralized error handling for all API calls.

  • Flexibility: You can add features like logging, authentication, and data caching to the interceptor.

Handling Errors

Interceptors make error handling easier. In the examples above, we catch errors and either print them to the console or throw exceptions that can be handled by the calling code. You can customize this error handling based on your app's requirements.

Parsing Data

The key is to use a parser to convert the data from the server into the format you need. In the examples, we've used JSON decoding to parse the data into arrays of strings. You can use other parsers depending on the data format.

Key Takeaway

Using network call interceptors in your declarative UI frameworks can significantly improve the organization and efficiency of your API interactions. By taking care of the repetitive tasks of making requests and handling responses, interceptors allow you to focus on the core logic of your app.

๐Ÿ’™๐Ÿ’™ Let me know if you see any areas where I can make this explanation even better! I'm always up for suggestions and improvements. ๐Ÿ’™๐Ÿ’™

Clap-clap-clap... clap-clap-clap... Keep clapping until you hit 10! ๐ŸŽ‰

Want to take your Flutter knowledge to the next level? Check out my recent series on Flutter interview questions for all levels. Get ready to impress in your next interview!

Let's keep sharing, learning, and practicing! ๐Ÿค˜๐Ÿป

0
Subscribe to my newsletter

Read articles from Mukund Jogi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mukund Jogi
Mukund Jogi

๐ŸŒ Iโ€™m from: Ahmedabad, Gujarat, India ๐ŸŽ“ Designation: Project Manager (at 7Span) ๐Ÿ’ป Tech Stack: Flutter, Android , iOS ๐Ÿ“ง Let's connect: โ€ฃ LinkedIn: https://linkedin.com/in/mukund-a-jogi/ โ€ฃ Twitter: https://twitter.com/mukundjogi โ€ฃ Medium: https://medium.com/@mukundjogi