ipoalerts Logoipoalerts
Quick Start Examples

Swift Examples

Quick start examples for integrating with the API using Swift

This page provides practical Quick start examples for integrating with the API using Swift.

Basic Client

import Foundation

struct IPOAlertsClient {
    let apiKey: String
    let baseURL = "https://api.ipoalerts.in"
    
    init(apiKey: String) {
        self.apiKey = apiKey
    }
    
    func getIPOs(status: String? = nil, type: String? = nil, page: Int = 1, limit: Int = 10) async throws -> IPOResponse {
        var components = URLComponents(string: "\(baseURL)/ipos")!
        var queryItems: [URLQueryItem] = []
        
        if let status = status {
            queryItems.append(URLQueryItem(name: "status", value: status))
        }
        if let type = type {
            queryItems.append(URLQueryItem(name: "type", value: type))
        }
        queryItems.append(URLQueryItem(name: "page", value: String(page)))
        queryItems.append(URLQueryItem(name: "limit", value: String(limit)))
        
        components.queryItems = queryItems
        
        return try await makeRequest(url: components.url!)
    }
    
    func getIPO(identifier: String) async throws -> IPO {
        let url = URL(string: "\(baseURL)/ipos/\(identifier)")!
        let response: IPODetailResponse = try await makeRequest(url: url)
        return response.ipo
    }
    
    private func makeRequest<T: Codable>(url: URL) async throws -> T {
        var request = URLRequest(url: url)
        request.setValue(apiKey, forHTTPHeaderField: "x-api-key")
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode < 400 else {
            throw NSError(domain: "HTTPError", code: (response as? HTTPURLResponse)?.statusCode ?? 0)
        }
        
        return try JSONDecoder().decode(T.self, from: data)
    }
}

struct IPOResponse: Codable {
    let meta: Meta
    let ipos: [IPO]
}

struct Meta: Codable {
    let count: Int
    let countOnPage: Int
    let totalPages: Int
    let page: Int
    let limit: Int
}

struct IPO: Codable {
    let id: String
    let name: String
    let symbol: String
    let slug: String
    let type: String?
    let startDate: String?
    let endDate: String?
    let listingDate: String?
    let priceRange: String?
    let listingGain: String?
    let status: String?
    let issueSize: String?
    let minQty: Int?
    let minAmount: Int?
    let logo: String?
    let about: String?
    let strengths: [String]?
    let risks: [String]?
    let schedule: [ScheduleItem]?
}

struct ScheduleItem: Codable {
    let event: String
    let date: String
}

struct IPODetailResponse: Codable {
    let ipo: IPO
}

// Usage
Task {
    let client = IPOAlertsClient(apiKey: "YOUR_API_KEY")
    
    do {
        // Get upcoming IPOs
        let ipos = try await client.getIPOs(status: "upcoming", type: "EQ", page: 1, limit: 10)
        print("Found \(ipos.meta.count) upcoming equity IPOs")
        
        for ipo in ipos.ipos {
            print("\(ipo.name) (\(ipo.symbol)) - \(ipo.priceRange ?? "N/A")")
        }
        
        // Get specific IPO
        let ipoDetails = try await client.getIPO(identifier: "example-company-limited")
        print("IPO Details: \(ipoDetails.name)")
        
    } catch {
        print("Error: \(error)")
    }
}

Advanced Client with Caching and Rate Limiting

import Foundation

class AdvancedIPOAlertsClient {
    private let apiKey: String
    private let baseURL = "https://api.ipoalerts.in"
    private let cache = NSCache<NSString, CachedResponse>()
    private let rateLimiter = RateLimiter(requestsPerMinute: 6)
    
    init(apiKey: String) {
        self.apiKey = apiKey
        cache.countLimit = 100
        cache.totalCostLimit = 50 * 1024 * 1024 // 50MB
    }
    
    func getIPOs(status: String? = nil, type: String? = nil, page: Int = 1, limit: Int = 10) async throws -> IPOResponse {
        let cacheKey = "ipos_\(status ?? "nil")_\(type ?? "nil")_\(page)_\(limit)"
        
        if let cached = getCachedResponse(key: cacheKey) as? IPOResponse {
            print("Returning cached data")
            return cached
        }
        
        try await rateLimiter.wait()
        
        let result = try await makeRequest(
            endpoint: "/ipos",
            queryItems: [
                "status": status,
                "type": type,
                "page": String(page),
                "limit": String(limit)
            ].compactMapValues { $0 }
        ) as IPOResponse
        
        setCachedResponse(key: cacheKey, response: result)
        return result
    }
    
    func getIPO(identifier: String) async throws -> IPO {
        let cacheKey = "ipo_\(identifier)"
        
        if let cached = getCachedResponse(key: cacheKey) as? IPODetailResponse {
            print("Returning cached IPO data")
            return cached.ipo
        }
        
        try await rateLimiter.wait()
        
        let result = try await makeRequest(endpoint: "/ipos/\(identifier)") as IPODetailResponse
        setCachedResponse(key: cacheKey, response: result)
        return result.ipo
    }
    
    private func makeRequest<T: Codable>(endpoint: String, queryItems: [String: String] = [:]) async throws -> T {
        var components = URLComponents(string: "\(baseURL)\(endpoint)")!
        components.queryItems = queryItems.map { URLQueryItem(name: $0.key, value: $0.value) }
        
        guard let url = components.url else {
            throw NSError(domain: "InvalidURL", code: 0)
        }
        
        var request = URLRequest(url: url)
        request.setValue(apiKey, forHTTPHeaderField: "x-api-key")
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode < 400 else {
            throw NSError(domain: "HTTPError", code: (response as? HTTPURLResponse)?.statusCode ?? 0)
        }
        
        return try JSONDecoder().decode(T.self, from: data)
    }
    
    private func getCachedResponse(key: String) -> Any? {
        return cache.object(forKey: NSString(string: key))?.response
    }
    
    private func setCachedResponse(key: String, response: Any) {
        let cachedResponse = CachedResponse(response: response, timestamp: Date())
        cache.setObject(cachedResponse, forKey: NSString(string: key))
    }
    
    func clearCache() {
        cache.removeAllObjects()
    }
}

class CachedResponse {
    let response: Any
    let timestamp: Date
    
    init(response: Any, timestamp: Date) {
        self.response = response
        self.timestamp = timestamp
    }
}

class RateLimiter {
    private let requestsPerMinute: Int
    private var requestTimes: [Date] = []
    private let queue = DispatchQueue(label: "rateLimiter")
    
    init(requestsPerMinute: Int) {
        self.requestsPerMinute = requestsPerMinute
    }
    
    func wait() async throws {
        try await withCheckedThrowingContinuation { continuation in
            queue.async {
                let now = Date()
                self.requestTimes.removeAll { now.timeIntervalSince($0) > 60 }
                
                if self.requestTimes.count >= self.requestsPerMinute {
                    let sleepTime = 60 - now.timeIntervalSince(self.requestTimes.first!)
                    if sleepTime > 0 {
                        DispatchQueue.global().asyncAfter(deadline: .now() + sleepTime) {
                            self.requestTimes.append(Date())
                            continuation.resume()
                        }
                        return
                    }
                }
                
                self.requestTimes.append(now)
                continuation.resume()
            }
        }
    }
}

// Usage
Task {
    let client = AdvancedIPOAlertsClient(apiKey: "YOUR_API_KEY")
    
    do {
        // First request - will be cached
        let ipos1 = try await client.getIPOs(status: "upcoming", type: "EQ")
        
        // Second request - will return cached data
        let ipos2 = try await client.getIPOs(status: "upcoming", type: "EQ")
        
        print("Found \(ipos1.meta.count) upcoming equity IPOs")
        print("Cached result: \(ipos2.meta.count) upcoming equity IPOs")
        
    } catch {
        print("Error: \(error)")
    }
}

SwiftUI Integration

import SwiftUI
import Combine

class IPOViewModel: ObservableObject {
    @Published var ipos: [IPO] = []
    @Published var loading = false
    @Published var error: String?
    
    private let client = IPOAlertsClient(apiKey: "YOUR_API_KEY")
    private var cancellables = Set<AnyCancellable>()
    
    func loadIPOs(status: String? = nil, type: String? = nil) {
        loading = true
        error = nil
        
        Task {
            do {
                let response = try await client.getIPOs(status: status, type: type)
                await MainActor.run {
                    self.ipos = response.ipos
                    self.loading = false
                }
            } catch {
                await MainActor.run {
                    self.error = error.localizedDescription
                    self.loading = false
                }
            }
        }
    }
}

struct IPOListView: View {
    @StateObject private var viewModel = IPOViewModel()
    @State private var selectedStatus = "upcoming"
    @State private var selectedType = "EQ"
    
    var body: some View {
        NavigationView {
            VStack {
                // Filter controls
                HStack {
                    Picker("Status", selection: $selectedStatus) {
                        Text("Upcoming").tag("upcoming")
                        Text("Open").tag("open")
                        Text("Closed").tag("closed")
                        Text("Listed").tag("listed")
                    }
                    .pickerStyle(SegmentedPickerStyle())
                    
                    Picker("Type", selection: $selectedType) {
                        Text("Equity").tag("EQ")
                        Text("SME").tag("SME")
                        Text("Debt").tag("DEBT")
                    }
                    .pickerStyle(SegmentedPickerStyle())
                }
                .padding()
                
                // IPO List
                if viewModel.loading {
                    ProgressView("Loading IPOs...")
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                } else if let error = viewModel.error {
                    VStack {
                        Text("Error: \(error)")
                            .foregroundColor(.red)
                        Button("Retry") {
                            viewModel.loadIPOs(status: selectedStatus, type: selectedType)
                        }
                    }
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                } else {
                    List(viewModel.ipos) { ipo in
                        NavigationLink(destination: IPODetailView(ipo: ipo)) {
                            IPORowView(ipo: ipo)
                        }
                    }
                }
            }
            .navigationTitle("IPOs")
            .onAppear {
                viewModel.loadIPOs(status: selectedStatus, type: selectedType)
            }
            .onChange(of: selectedStatus) { _ in
                viewModel.loadIPOs(status: selectedStatus, type: selectedType)
            }
            .onChange(of: selectedType) { _ in
                viewModel.loadIPOs(status: selectedStatus, type: selectedType)
            }
        }
    }
}

struct IPORowView: View {
    let ipo: IPO
    
    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            HStack {
                Text(ipo.name)
                    .font(.headline)
                Spacer()
                Text(ipo.symbol)
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
            
            HStack {
                Text("Price: \(ipo.priceRange ?? "N/A")")
                    .font(.caption)
                Spacer()
                Text("Status: \(ipo.status ?? "N/A")")
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
            
            if let startDate = ipo.startDate {
                Text("Start: \(startDate)")
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
        }
        .padding(.vertical, 2)
    }
}

struct IPODetailView: View {
    let ipo: IPO
    
    var body: some View {
        ScrollView {
            VStack(alignment: .leading, spacing: 16) {
                // Basic Info
                VStack(alignment: .leading, spacing: 8) {
                    Text(ipo.name)
                        .font(.title)
                        .fontWeight(.bold)
                    
                    Text(ipo.symbol)
                        .font(.title2)
                        .foregroundColor(.secondary)
                    
                    if let priceRange = ipo.priceRange {
                        Text("Price Range: \(priceRange)")
                            .font(.headline)
                    }
                }
                
                // Dates
                VStack(alignment: .leading, spacing: 4) {
                    Text("Important Dates")
                        .font(.headline)
                    
                    if let startDate = ipo.startDate {
                        Text("Start Date: \(startDate)")
                    }
                    if let endDate = ipo.endDate {
                        Text("End Date: \(endDate)")
                    }
                    if let listingDate = ipo.listingDate {
                        Text("Listing Date: \(listingDate)")
                    }
                }
                
                // About
                if let about = ipo.about {
                    VStack(alignment: .leading, spacing: 4) {
                        Text("About")
                            .font(.headline)
                        Text(about)
                            .font(.body)
                    }
                }
                
                // Strengths
                if let strengths = ipo.strengths, !strengths.isEmpty {
                    VStack(alignment: .leading, spacing: 4) {
                        Text("Strengths")
                            .font(.headline)
                        ForEach(strengths, id: \.self) { strength in
                            Text("• \(strength)")
                                .font(.body)
                        }
                    }
                }
                
                // Risks
                if let risks = ipo.risks, !risks.isEmpty {
                    VStack(alignment: .leading, spacing: 4) {
                        Text("Risks")
                            .font(.headline)
                        ForEach(risks, id: \.self) { risk in
                            Text("• \(risk)")
                                .font(.body)
                        }
                    }
                }
            }
            .padding()
        }
        .navigationTitle(ipo.name)
        .navigationBarTitleDisplayMode(.inline)
    }
}

// Make IPO identifiable
extension IPO: Identifiable {
    var id: String { self.id }
}

// App entry point
@main
struct IPOAlertsApp: App {
    var body: some Scene {
        WindowGroup {
            IPOListView()
        }
    }
}