Skip to content

Instantly share code, notes, and snippets.

@codewithsanthoshofficial
Last active February 12, 2026 06:24
Show Gist options
  • Select an option

  • Save codewithsanthoshofficial/fe0cb9699b92afe89a00c812086ee2d9 to your computer and use it in GitHub Desktop.

Select an option

Save codewithsanthoshofficial/fe0cb9699b92afe89a00c812086ee2d9 to your computer and use it in GitHub Desktop.
SwiftUI_SampleAPICall_List_stackoverflow
import Foundation
struct APIClient {
func fetchQuestions() async throws -> [QuestionsItemData] {
var components = URLComponents(string: "https://api.stackexchange.com/2.3/questions")!
components.queryItems = [
.init(name: "order", value: "desc"),
.init(name: "sort", value: "votes"),
.init(name: "site", value: "stackoverflow")
]
let (data, response) = try await URLSession.shared.data(from: components.url!)
guard let httpResponse = response as? HTTPURLResponse,
200...299 ~= httpResponse.statusCode else {
throw NetworkError.serverError
}
return try JSONDecoder().decode(QuestionResponse.self, from: data).items
}
}
===============================
import Foundation
extension DateFormatter {
static let cached: DateFormatter = {
let f = DateFormatter()
f.locale = Locale(identifier: "en_US")
f.dateStyle = .medium
return f
}()
}
===========
import Foundation
extension Int {
func shorted() -> String {
let hundred = 100
let thousandNum = 1000
let tenThousandNum = 10000
let oneLakh = 100000
let oneMillionNum = 1000000
let tenMillionNum = 10000000
if self >= thousandNum && self < tenThousandNum {
return String(format: "%.1fK", Double(self/hundred)/10).replacingOccurrences(of: ".0", with: "")
}
if self >= tenThousandNum && self < oneMillionNum {
return "\(self/thousandNum)k"
}
if self >= oneMillionNum && self < tenMillionNum {
return String(format: "%.1fM", Double(self/oneLakh)/10).replacingOccurrences(of: ".0", with: "")
}
if self >= tenMillionNum {
return "\(self/oneMillionNum)M"
}
return String(self)
}
}
===================
import Foundation
let formatter: DateFormatter = {
let f = DateFormatter()
f.locale = Locale(identifier: "en_US")
f.dateStyle = .medium
return f
}()
extension Double {
func getDateStringFromUTC() -> String {
let date = Date(timeIntervalSince1970: self)
return formatter.string(from: date)
}
}
==========
import SwiftUI
struct ColorTheme {
static let appColor = Color(.green)
}
import SwiftUI
struct QuestionView: View {
@StateObject private var viewModel = QuestionViewModel()
var body: some View {
NavigationStack {
Group {
if viewModel.isLoading {
ProgressView()
} else {
List(viewModel.questions) { question in
QuestionsListView(questionInfo: question)
}
}
}
.navigationTitle("Questions")
.alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) {
Button("OK", role: .cancel) {}
} message: {
Text(viewModel.errorMessage ?? "")
}
}
}
}
============================
import SwiftUI
struct QuestionsListView: View {
let questionInfo: QuestionsItemData
var body: some View {
VStack(alignment: .leading, spacing: 8) {
HStack(alignment: .top) {
AsyncImage(url: questionInfo.owner?.profileImageURL) { image in
image.resizable().clipShape(Circle())
} placeholder: {
Circle().foregroundStyle(.gray)
}
.frame(width: 60, height: 60)
VStack(alignment: .leading, spacing: 6) {
HStack {
Text(questionInfo.owner?.displayName ?? "Unknown")
.bold()
Spacer()
Text(questionInfo.creationDateValue)
.font(.caption)
}
Text(questionInfo.title ?? "")
.font(.subheadline)
HStack {
Label(questionInfo.viewAnswerCount, systemImage: "text.bubble")
Spacer()
Label(questionInfo.viewScore, systemImage: "chart.bar")
Spacer()
Label(questionInfo.viewCountValue, systemImage: "eye")
}
.font(.caption)
}
}
}
.padding(.vertical, 6)
}
}
//#Preview {
// QuestionsListView(questionInfo: QuestionsItemData)
//}
============================
/*
import SwiftUI
struct QuestionsListView: View {
var body: some View {
VStack {
HStack (alignment:.top) {
AsyncImage(url: URL(string: "")) { getImage in
getImage
.resizable()
.scaledToFit()
.clipShape(Circle())
} placeholder : {
Circle()
.foregroundStyle(ColorTheme.appColor)
}
.frame(width: 70, height: 70)
VStack(alignment: .leading, spacing: 5) {
HStack {
Text("Santhosh")
.fontWeight(.bold)
Text("•")
.fontWeight(.regular)
Text("501K")
.fontWeight(.bold)
Spacer()
Text("01 01 1991")
.fontWeight(.bold)
}
Text("Description")
.fontWeight(.light)
HStack {
Image(systemName: "text.bubble")
Text("11")
Spacer()
Image(systemName: "chart.bar.xaxis")
Text("21K")
Spacer()
Image(systemName: "eye")
Text("19M")
}
.fontWeight(.thin)
.padding(.top)
}
}
Divider()
}
.padding(.leading, 12)
.padding(.trailing, 12)
}
}
#Preview {
QuestionsListView()
}
*/
import Foundation
final class RequestMethod {
enum Method: String {
case GET
case POST
case PUT
case DELETE
case PATCH
}
static func request(method: Method, url: URL,headers:[String:String]?, body: Data?) -> URLRequest {
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = method.rawValue
urlRequest.setValue("application/json", forHTTPHeaderField: "Accept")
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
headers?.forEach {
urlRequest.setValue($0.value, forHTTPHeaderField: $0.key)
}
urlRequest.timeoutInterval = 60
if let body = body, method != .GET {
urlRequest.httpBody = body
}
return urlRequest
}
}
class NetworkManager {
static let sharedService = NetworkManager()
}
extension NetworkManager {
func proceedForNetworkCall(requestType: RequestMethod.Method = .GET,
url: String) async throws -> Data {
guard let request = URL(string: url) else { throw NetworkError.invalidURL }
let (data, response) = try await URLSession.shared.data(from: request)
guard let response = response as? HTTPURLResponse else { throw NetworkError.conversionFailedToHTTPURLResponse }
try response.statusCodeChecker()
return data
}
}
extension NetworkManager {
func request<T>(url: String, httpMethod: RequestMethod.Method = .GET,
body: Data?, headers: [String: String]?,
expectingReturnType: T.Type) async throws -> T where T: Codable {
guard let url = URL(string: url) else { throw NetworkError.invalidURL }
let urlRequest = RequestMethod.request(method: httpMethod, url: url, headers: [:], body: body)
//request.addHeader(from: headers)
return try await self.responseHeader(URLSession.shared.data(for: urlRequest))
}
func responseHeader<T: Codable>(_ dataWithRrsponse: (data: Data, response: URLResponse)) async throws -> T {
guard let response = dataWithRrsponse.response as? HTTPURLResponse else { throw NetworkError.conversionFailedToHTTPURLResponse }
try response.statusCodeChecker()
let dataResponse = try JSONDecoder().decode(T.self, from: dataWithRrsponse.data)
// print(dataResponse)
return dataResponse
}
}
==============
import Foundation
enum APIURL {
static let BASE_URL = "https://api.stackexchange.com/2.3/questions"
case getAllQuestions
case getAnsweredBy
var apiString: String {
switch self {
case .getAllQuestions:
return APIURL.BASE_URL + "?order=*:orderType:*&sort=*:sortType:*&site=stackoverflow"
case .getAnsweredBy:
return "/answers?order=desc&sort=votes&site=stackoverflow"
}
}
}
========
import Foundation
enum NetworkError: Error, LocalizedError {
case invalidURL
case serverError
case invalidData
// case unkown(Error)
case conversionFailedToHTTPURLResponse
var errorDescription: String? {
switch self {
case .invalidURL:
return "Malformed URL sent to session"
case .serverError:
return "There was an error with the server. Please try again later"
case .invalidData:
return "The data couldn’t be read because it isn’t in the correct format"
// case .unkown(let error):
// return error.localizedDescription
case .conversionFailedToHTTPURLResponse:
return "Type casting failed"
}
}
}
extension HTTPURLResponse {
func statusCodeChecker() throws {
switch self.statusCode {
case 200...299:
return
case 400:
throw NetworkError.serverError
default:
throw NetworkError.serverError
}
}
}
import SwiftUI
@main
struct StackOverFlowSampleApp: App {
var body: some Scene {
WindowGroup {
TabBarView()
}
}
}
import SwiftUI
struct TabBarView: View {
var body: some View {
TabView() {
QuestionView()
.tabItem {
Image(systemName:"questionmark.app.dashed")
}
SearchView()
.tabItem {
Image(systemName: "magnifyingglass")
}
UsersView()
.tabItem {
Image(systemName: "person.3")
.environment(\.symbolVariants, .none)
}
}
}
}
#Preview {
TabBarView()
}
import Foundation
@MainActor
final class QuestionViewModel: ObservableObject {
@Published private(set) var questions: [QuestionsItemData] = []
@Published var errorMessage: String?
@Published var isLoading = false
private let apiClient: APIClient
init(apiClient: APIClient = APIClient()) {
self.apiClient = apiClient
fetchQuestions()
}
func fetchQuestions() {
isLoading = true
Task {
do {
questions = try await apiClient.fetchQuestions()
} catch {
errorMessage = error.localizedDescription
}
isLoading = false
}
}
}
============================
import Foundation
struct QuestionResponse: Codable {
let items: [QuestionsItemData]
}
struct QuestionsItemData: Codable, Identifiable {
let id: Int
let owner: QuestionOwner?
let viewCount: Int?
let answerCount: Int?
let score: Int?
let creationDate: Int?
let title: String?
enum CodingKeys: String, CodingKey {
case id = "question_id"
case owner
case viewCount = "view_count"
case answerCount = "answer_count"
case score
case creationDate = "creation_date"
case title
}
var viewCountValue: String { viewCount?.shorted() ?? "0" }
var viewAnswerCount: String { answerCount?.shorted() ?? "0" }
var viewScore: String { score?.shorted() ?? "0" }
var creationDateValue: String {
guard let creationDate else { return "-" }
return DateFormatter.cached.string(
from: Date(timeIntervalSince1970: TimeInterval(creationDate))
)
}
}
struct QuestionOwner: Codable {
let reputation: Int?
let profileImage: String?
let displayName: String?
enum CodingKeys: String, CodingKey {
case reputation
case profileImage = "profile_image"
case displayName = "display_name"
}
var profileImageURL: URL? {
guard let profileImage else { return nil }
return URL(string: profileImage)
}
var reputationValue: String {
reputation?.shorted() ?? "0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment