Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions swift-sdk/Internal/HealthMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,12 @@ class HealthMonitor {
let currentDate = dateProvider.currentDate
let apiCallRequest = apiCallRequest.addingCreatedAt(currentDate)
if let urlRequest = apiCallRequest.convertToURLRequest(sentAt: currentDate) {
ITBInfo("Attempting to send failed-to-schedule request directly for path: '\(apiCallRequest.getPath())'")
_ = RequestSender.sendRequest(urlRequest, usingSession: networkSession)
} else {
let endpoint = apiCallRequest.endpoint
let path = apiCallRequest.getPath()
ITBError("Failed to convert to URL request in health monitor - endpoint: '\(endpoint)', path: '\(path)'")
}
onError()
}
Expand Down
25 changes: 24 additions & 1 deletion swift-sdk/Internal/InternalIterableAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,30 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {

private static func setApiEndpoint(apiEndPointOverride: String?, config: IterableConfig) -> String {
let apiEndPoint = config.dataRegion
return apiEndPointOverride ?? apiEndPoint
let endpoint = apiEndPointOverride ?? apiEndPoint

// Sanitize and validate endpoint
let sanitized = endpoint.trimmingCharacters(in: .whitespacesAndNewlines)

// Validate endpoint is a valid URL
if let url = URL(string: sanitized) {
if url.scheme == nil || url.host == nil {
ITBError("Invalid API endpoint - missing scheme or host: '\(sanitized)'")
}
} else {
ITBError("Invalid API endpoint - cannot create URL from: '\(sanitized)'")
}

// Check for common issues
if sanitized != endpoint {
ITBError("API endpoint contained whitespace, trimmed from '\(endpoint)' to '\(sanitized)'")
}

if sanitized.isEmpty {
ITBError("API endpoint is empty after sanitization")
}

return sanitized
}

init(apiKey: String,
Expand Down
40 changes: 36 additions & 4 deletions swift-sdk/Internal/IterableAPICallTaskProcessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ struct IterableAPICallTaskProcessor: IterableTaskProcessor {
let iterableRequest = decodedIterableRequest.addingCreatedAt(task.scheduledAt)

guard let urlRequest = iterableRequest.convertToURLRequest(sentAt: dateProvider.currentDate, processorType: .offline) else {
return IterableTaskError.createErroredFuture(reason: "could not convert to url request")
let endpoint = decodedIterableRequest.endpoint
let path = decodedIterableRequest.getPath()
let errorMessage = "Could not convert to URL request - endpoint: '\(endpoint)', path: '\(path)'"
ITBError(errorMessage)
return IterableTaskError.createErroredFuture(reason: errorMessage)
}

let result = Fulfill<IterableTaskResult, IterableTaskError>()
Expand All @@ -47,10 +51,38 @@ struct IterableAPICallTaskProcessor: IterableTaskProcessor {
private let dateProvider: DateProviderProtocol

private static func isNetworkUnavailable(sendRequestError: SendRequestError) -> Bool {
// Check for NSURLError codes that indicate network issues
if let nsError = sendRequestError.originalError as? NSError {
if nsError.domain == NSURLErrorDomain {
let networkErrorCodes: Set<Int> = [
NSURLErrorNotConnectedToInternet, // -1009
NSURLErrorNetworkConnectionLost, // -1005
NSURLErrorTimedOut, // -1001
NSURLErrorCannotConnectToHost, // -1004
NSURLErrorDNSLookupFailed, // -1006
NSURLErrorDataNotAllowed, // -1020 (cellular data disabled)
NSURLErrorInternationalRoamingOff // -1018
]
if networkErrorCodes.contains(nsError.code) {
ITBInfo("Network error detected: code=\(nsError.code), description=\(nsError.localizedDescription)")
return true
}
}
}

// Fallback to string check for other network-related errors
if let originalError = sendRequestError.originalError {
return originalError.localizedDescription.lowercased().contains("offline")
} else {
return false
let description = originalError.localizedDescription.lowercased()
let isNetworkError = description.contains("offline") ||
description.contains("network") ||
description.contains("internet") ||
description.contains("connection")
if isNetworkError {
ITBInfo("Network error detected via description: \(originalError.localizedDescription)")
}
return isNetworkError
}

return false
}
}
9 changes: 9 additions & 0 deletions swift-sdk/Internal/IterableRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ extension IterableRequest: Codable {
}
}

func getPath() -> String {
switch self {
case .get(let request):
return request.path
case .post(let request):
return request.path
}
}

private static let requestTypeGet = "get"
private static let requestTypePost = "post"
}
Expand Down
3 changes: 3 additions & 0 deletions swift-sdk/Internal/IterableRequestUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ struct IterableRequestUtil {
headers: [String: String]? = nil,
args: [String: String]? = nil) -> URLRequest? {
guard let url = getUrlComponents(forApiEndPoint: apiEndPoint, path: path, args: args)?.url else {
ITBError("Failed to create GET request URL")
return nil
}

Expand Down Expand Up @@ -53,6 +54,7 @@ struct IterableRequestUtil {
args: [String: String]? = nil,
body: Data? = nil) -> URLRequest? {
guard let url = getUrlComponents(forApiEndPoint: apiEndPoint, path: path, args: args)?.url else {
ITBError("Failed to create POST request URL")
return nil
}

Expand Down Expand Up @@ -88,6 +90,7 @@ struct IterableRequestUtil {
let endPointCombined = pathCombine(path1: apiEndPoint, path2: path)

guard var components = URLComponents(string: "\(endPointCombined)") else {
ITBError("Failed to create URLComponents - apiEndPoint: '\(apiEndPoint)', path: '\(path)', combined: '\(endPointCombined)'")
return nil
}

Expand Down
15 changes: 12 additions & 3 deletions swift-sdk/Internal/api-client/ApiClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,32 @@ class ApiClient {

func send(iterableRequest: IterableRequest) -> Pending<SendRequestValue, SendRequestError> {
guard let urlRequest = convertToURLRequest(iterableRequest: iterableRequest) else {
return SendRequestError.createErroredFuture()
let path = iterableRequest.getPath()
let errorMessage = "Failed to create URL request for endpoint: '\(endpoint)', path: '\(path)'"
ITBError(errorMessage)
return SendRequestError.createErroredFuture(reason: errorMessage)
}

return RequestSender.sendRequest(urlRequest, usingSession: networkSession)
}

func sendWithoutCreatedAt(iterableRequest: IterableRequest) -> Pending<SendRequestValue, SendRequestError> {
guard let urlRequest = convertToURLRequestWithoutCreatedAt(iterableRequest: iterableRequest) else {
return SendRequestError.createErroredFuture()
let path = iterableRequest.getPath()
let errorMessage = "Failed to create URL request for endpoint: '\(endpoint)', path: '\(path)'"
ITBError(errorMessage)
return SendRequestError.createErroredFuture(reason: errorMessage)
}

return RequestSender.sendRequest(urlRequest, usingSession: networkSession)
}

func send<T>(iterableRequest: IterableRequest) -> Pending<T, SendRequestError> where T: Decodable {
guard let urlRequest = convertToURLRequest(iterableRequest: iterableRequest) else {
return SendRequestError.createErroredFuture()
let path = iterableRequest.getPath()
let errorMessage = "Failed to create URL request for endpoint: '\(endpoint)', path: '\(path)'"
ITBError(errorMessage)
return SendRequestError.createErroredFuture(reason: errorMessage)
}

return RequestSender.sendRequest(urlRequest, usingSession: networkSession)
Expand Down
Loading