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()
}
}
}