twinsunllc/inventiv-critic-ios
A Swift SDK for collecting actionable customer feedback via [Inventiv Critic](https://critictracking.com). Built with modern Swift concurrency (async/await), strict Sendable conformance, and zero external dependencies.
Installation
Swift Package Manager (Recommended)
Add the package to your Package.swift dependencies:
dependencies: [
.package(url: "https://github.com/twinsunllc/inventiv-critic-ios.git", from: "1.0.0")
]Then add "Critic" to the target's dependencies:
.target(
name: "YourApp",
dependencies: [
.product(name: "Critic", package: "inventiv-critic-ios")
]
)Or in Xcode: File > Add Package Dependencies... and enter:
https://github.com/twinsunllc/inventiv-critic-iosQuick Start
1. Initialize the SDK
Call initialize early in your app lifecycle — typically in your AppDelegate or App struct:
import Critic
@main
struct MyApp: App {
init() {
Task {
try await Critic.shared.initialize(apiToken: "YOUR_API_TOKEN")
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}Or in a UIKit AppDelegate:
import Critic
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Task {
try await Critic.shared.initialize(apiToken: "YOUR_API_TOKEN")
}
return true
}
}Find your API token in the Critic Web Portal under your product's details.
2. Submit Feedback
Submit a bug report programmatically:
let input = BugReportInput(
description: "App crashes when tapping the profile button",
metadata: ["screen": "profile", "user_tier": "premium"],
stepsToReproduce: "1. Open app\n2. Tap Profile\n3. App crashes",
userIdentifier: "user@example.com"
)
do {
let report = try await Critic.shared.submitReport(input)
print("Report submitted: \(report.id)")
} catch {
print("Failed to submit: \(error)")
}3. Submit with Attachments
Include screenshots or log files with your report:
let screenshotData = try Data(contentsOf: screenshotURL)
let report = try await Critic.shared.submitReport(
BugReportInput(description: "UI layout broken on iPad"),
attachments: [
(filename: "screenshot.png", mimeType: "image/png", data: screenshotData)
]
)Built-in UI
SwiftUI Feedback View
Present the built-in feedback form in SwiftUI:
import Critic
struct ContentView: View {
@State private var showFeedback = false
var body: some View {
Button("Send Feedback") {
showFeedback = true
}
.sheet(isPresented: $showFeedback) {
FeedbackView(
onSubmit: { report in
showFeedback = false
print("Submitted: \(report.id)")
},
onCancel: {
showFeedback = false
},
userIdentifier: "user@example.com"
)
}
}
}UIKit Feedback View Controller
Use FeedbackViewController in UIKit apps:
import Critic
let feedbackVC = FeedbackViewController()
feedbackVC.userIdentifier = "user@example.com"
feedbackVC.onSubmit = { report in
print("Submitted: \(report.id)")
}
feedbackVC.onCancel = {
print("Cancelled")
}
feedbackVC.modalPresentationStyle = .formSheet
present(feedbackVC, animated: true)Shake to Send Feedback
Enable shake-to-report using ShakeDetectingWindow:
import Critic
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
let shakeDetector = ShakeDetector()
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else { return }
let window = ShakeDetectingWindow(windowScene: windowScene)
window.rootViewController = UINavigationController(rootViewController: YourRootVC())
self.window = window
window.makeKeyAndVisible()
shakeDetector.install(on: window)
}
}Error Handling
All API methods throw CriticError on failure:
do {
let report = try await Critic.shared.submitReport(input)
} catch CriticError.unauthorized {
print("Invalid API token")
} catch CriticError.forbidden {
print("Access forbidden")
} catch CriticError.notFound {
print("Resource not found")
} catch CriticError.validationFailed(let message) {
print("Validation error: \(message)")
} catch CriticError.badRequest(let message) {
print("Bad request: \(message)")
} catch CriticError.notInitialized {
print("SDK not initialized — call Critic.shared.initialize() first")
} catch CriticError.networkError(let message) {
print("Network error: \(message)")
} catch {
print("Unexpected error: \(error)")
}Custom Base URL
Point the SDK to a self-hosted Critic instance:
try await Critic.shared.initialize(
apiToken: "YOUR_API_TOKEN",
baseURL: URL(string: "https://your-critic-instance.example.com")
)Requirements
- iOS 16.0+
- Swift 6.0+
- Xcode 16.0+
Contributing
- Clone this repository.
- Open
Package.swiftin Xcode. - Run tests with
Cmd+Uorswift test.
License
This library is released under the MIT License.
Package Metadata
Repository: twinsunllc/inventiv-critic-ios
Default branch: main
README: README.md