From af9251e2281c0fb7d9b1ec0d7ed327f551f00dfe Mon Sep 17 00:00:00 2001 From: rajpootathar Date: Mon, 30 Jun 2025 11:13:24 +0500 Subject: [PATCH 1/7] integrated encrypted links functionality and updated the README.md file --- README.md | 22 ++++++++++++ SwiftUIProject/ShortIOApp/ContentView.swift | 38 +++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/README.md b/README.md index 7afd8d0..61cdca5 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,28 @@ Task { } ``` +### ๐Ÿ” Secure Short Links (Encrypted) + +If you want to encrypt the original URL, the SDK provides a `createSecure` function that uses AES-GCM encryption. + +#### ๐Ÿ”ง Example + +```swift +Task { + do { + let result = try shortLinkSDK.createSecure(originalURL: "https://{your_domain}") + print("result", result.securedOriginalURL, result.securedShortUrl) + } catch { + print("Failed to create secure URL: \(error)") + } +} +``` +#### ๐Ÿงพ Output Format + +- **`securedOriginalURL:`** An encrypted URL like `shortsecure://?` + +- **`securedShortUrl:`** A Base64-encoded decryption key to be appended as a fragment (e.g. `#`) + ## ๐ŸŒ Handling Universal Links ### SwiftUI Implementation diff --git a/SwiftUIProject/ShortIOApp/ContentView.swift b/SwiftUIProject/ShortIOApp/ContentView.swift index c5fade0..905f5ec 100644 --- a/SwiftUIProject/ShortIOApp/ContentView.swift +++ b/SwiftUIProject/ShortIOApp/ContentView.swift @@ -6,6 +6,7 @@ struct ContentView: View { @State private var shortURL: String? @State private var errorMessage: String? @State private var isLoading: Bool = false + @State private var secureShortURL: String? private let shortLinkSDK = ShortIOSDK() var body: some View { @@ -31,6 +32,17 @@ struct ContentView: View { .cornerRadius(10) } .padding(.horizontal) + + Button(action: createEncryptedLink) { + Text("Create Encrypted Short Link") + .font(.headline) + .padding() + .frame(maxWidth: .infinity) + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(10) + } + .padding(.horizontal) } if let shortURL = shortURL { @@ -48,6 +60,21 @@ struct ContentView: View { } } + if let secureShortURL = secureShortURL { + Text("Secure Short URL: \(secureShortURL)") + .font(.subheadline) + .foregroundColor(.green) + .padding() + .contextMenu { + Button(action: { + UIPasteboard.general.string = secureShortURL + }) { + Text("Copy to Clipboard") + Image(systemName: "doc.on.doc") + } + } + } + if let errorMessage = errorMessage { Text("Error: \(errorMessage)") .font(.subheadline) @@ -90,6 +117,17 @@ struct ContentView: View { isLoading = false } } + + private func createEncryptedLink() { + Task { + do { + let result = try shortLinkSDK.createSecure(originalURL: "https://{your_domain}") + print("result", result.securedOriginalURL, result.securedShortUrl) + } catch { + print("Failed to create secure URL: \(error)") + } + } + } } #Preview { From 94b451f95dd7c1733f76c5fdbc1a176c000dfc76 Mon Sep 17 00:00:00 2001 From: rajpootathar Date: Mon, 30 Jun 2025 11:54:18 +0500 Subject: [PATCH 2/7] optimized code --- SwiftUIProject/ShortIOApp/ContentView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/SwiftUIProject/ShortIOApp/ContentView.swift b/SwiftUIProject/ShortIOApp/ContentView.swift index 905f5ec..117afa3 100644 --- a/SwiftUIProject/ShortIOApp/ContentView.swift +++ b/SwiftUIProject/ShortIOApp/ContentView.swift @@ -122,6 +122,7 @@ struct ContentView: View { Task { do { let result = try shortLinkSDK.createSecure(originalURL: "https://{your_domain}") + secureShortURL = result.securedShortUrl print("result", result.securedOriginalURL, result.securedShortUrl) } catch { print("Failed to create secure URL: \(error)") From 0960f6db6a60e9c511d851d2700d6a050cda4f01 Mon Sep 17 00:00:00 2001 From: rajpootathar Date: Mon, 30 Jun 2025 18:35:37 +0500 Subject: [PATCH 3/7] integrated encrypted links functionality into stroyboard project --- .../ShortIOApp/ViewController.swift | 193 ++++++++++++++---- 1 file changed, 149 insertions(+), 44 deletions(-) diff --git a/StoryboardProject/ShortIOApp/ViewController.swift b/StoryboardProject/ShortIOApp/ViewController.swift index 2e0365f..36394a0 100644 --- a/StoryboardProject/ShortIOApp/ViewController.swift +++ b/StoryboardProject/ShortIOApp/ViewController.swift @@ -13,7 +13,7 @@ class ViewController: UIViewController { return label }() - private let createButton: UIButton = { + private let createShortLinkButton: UIButton = { let button = UIButton(type: .system) button.setTitle("Create Short Link", for: .normal) button.titleLabel?.font = .systemFont(ofSize: 18, weight: .semibold) @@ -24,9 +24,21 @@ class ViewController: UIViewController { return button }() - private let activityIndicator = UIActivityIndicatorView(style: .medium) + private let createSecureShortLinkButton: UIButton = { + let button = UIButton(type: .system) + button.setTitle("Create Secure Short Link", for: .normal) + button.titleLabel?.font = .systemFont(ofSize: 18, weight: .semibold) + button.backgroundColor = .systemBlue + button.tintColor = .white + button.layer.cornerRadius = 10 + button.addTarget(self, action: #selector(createSecureShortLink), for: .touchUpInside) + return button + }() - private let loadingLabel: UILabel = { + private let shortLinkActivityIndicator = UIActivityIndicatorView(style: .medium) + private let secureLinkActivityIndicator = UIActivityIndicatorView(style: .medium) + + private let loadingShortLinkLabel: UILabel = { let label = UILabel() label.text = "Creating Short Link..." label.textAlignment = .center @@ -35,8 +47,26 @@ class ViewController: UIViewController { label.isHidden = true return label }() + + private let loadingSecuredShortLinkLabel: UILabel = { + let label = UILabel() + label.text = "Creating Secured Short Link..." + label.textAlignment = .center + label.textColor = .gray + label.font = .systemFont(ofSize: 14) + label.isHidden = true + return label + }() + + private let resultShortLinkLabel: UILabel = { + let label = UILabel() + label.textAlignment = .center + label.numberOfLines = 0 + label.textColor = .systemGreen + return label + }() - private let resultLabel: UILabel = { + private let resultSecureShortLinkLabel: UILabel = { let label = UILabel() label.textAlignment = .center label.numberOfLines = 0 @@ -44,7 +74,7 @@ class ViewController: UIViewController { return label }() - private let copyButton: UIButton = { + private let copyShortLinkButton: UIButton = { let button = UIButton(type: .system) button.setTitle("Copy Short Link", for: .normal) button.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium) @@ -52,11 +82,33 @@ class ViewController: UIViewController { button.tintColor = .white button.layer.cornerRadius = 8 button.isHidden = true - button.addTarget(self, action: #selector(copyToClipboard), for: .touchUpInside) + button.tag = 1 + button.addTarget(self, action: #selector(copyToClipboard(_:)), for: .touchUpInside) return button }() - private let errorLabel: UILabel = { + private let copySecureShortLinkButton: UIButton = { + let button = UIButton(type: .system) + button.setTitle("Copy Secure Short Link", for: .normal) + button.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium) + button.backgroundColor = .systemGray + button.tintColor = .white + button.layer.cornerRadius = 8 + button.isHidden = true + button.tag = 2 + button.addTarget(self, action: #selector(copyToClipboard(_:)), for: .touchUpInside) + return button + }() + + private let errorShortLinkLabel: UILabel = { + let label = UILabel() + label.textAlignment = .center + label.numberOfLines = 0 + label.textColor = .systemRed + return label + }() + + private let errorSecureShortLinkLabel: UILabel = { let label = UILabel() label.textAlignment = .center label.numberOfLines = 0 @@ -71,21 +123,33 @@ class ViewController: UIViewController { } private func layoutUI() { - let loaderStack = UIStackView(arrangedSubviews: [activityIndicator, loadingLabel]) - loaderStack.axis = .vertical - loaderStack.alignment = .center - loaderStack.spacing = 8 + let shortLinkLoaderStack = UIStackView(arrangedSubviews: [shortLinkActivityIndicator, loadingShortLinkLabel]) + shortLinkLoaderStack.axis = .vertical + shortLinkLoaderStack.alignment = .center + shortLinkLoaderStack.spacing = 8 + + let secureLinkLoaderStack = UIStackView(arrangedSubviews: [secureLinkActivityIndicator, loadingSecuredShortLinkLabel]) + secureLinkLoaderStack.axis = .vertical + secureLinkLoaderStack.alignment = .center + secureLinkLoaderStack.spacing = 8 let stackView = UIStackView(arrangedSubviews: [ titleLabel, - createButton, - loaderStack, - resultLabel, - copyButton, - errorLabel + + createShortLinkButton, + shortLinkLoaderStack, + resultShortLinkLabel, + copyShortLinkButton, + errorShortLinkLabel, + + createSecureShortLinkButton, + secureLinkLoaderStack, + resultSecureShortLinkLabel, + copySecureShortLinkButton, + errorSecureShortLinkLabel ]) stackView.axis = .vertical - stackView.spacing = 20 + stackView.spacing = 10 stackView.alignment = .center stackView.translatesAutoresizingMaskIntoConstraints = false @@ -95,61 +159,102 @@ class ViewController: UIViewController { stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor), - createButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), - copyButton.widthAnchor.constraint(equalTo: stackView.widthAnchor) + + createShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), + copyShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), + createSecureShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), + copySecureShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor) ]) } - @objc private func copyToClipboard() { - guard let text = resultLabel.text?.replacingOccurrences(of: "Short URL: ", with: "") else { return } - UIPasteboard.general.string = text - let alert = UIAlertController(title: "Copied", message: "Short URL copied to clipboard.", preferredStyle: .alert) + @objc private func copyToClipboard(_ sender: UIButton) { + let text: String? + let message: String + + switch sender.tag { + case 1: + text = resultShortLinkLabel.text?.replacingOccurrences(of: "Short URL: ", with: "") + message = "Short URL copied to clipboard." + case 2: + text = resultSecureShortLinkLabel.text?.replacingOccurrences(of: "Secured Short URL: ", with: "") + message = "Secured Short URL copied to clipboard." + default: + return + } + + guard let copiedText = text, !copiedText.isEmpty else { return } + + UIPasteboard.general.string = copiedText + + let alert = UIAlertController(title: "Copied", message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default)) present(alert, animated: true) } @objc private func createShortLink() { - resultLabel.text = nil - errorLabel.text = nil - copyButton.isHidden = true - activityIndicator.startAnimating() - loadingLabel.isHidden = false - createButton.isEnabled = false + resultShortLinkLabel.text = nil + errorShortLinkLabel.text = nil + copyShortLinkButton.isHidden = true + shortLinkActivityIndicator.startAnimating() + loadingShortLinkLabel.isHidden = false + createShortLinkButton.isEnabled = false let parameters: ShortIOParameters do { parameters = try ShortIOParameters( - domain: "your_domain", - originalURL: "your_original_url" + domain: "demodeeplinkapp.short.gy", + originalURL: "https://demodeeplinkapp.short.gy/yei3jh" ) } catch { - activityIndicator.stopAnimating() - loadingLabel.isHidden = true - createButton.isEnabled = true - errorLabel.text = "Invalid input: \(error.localizedDescription)" +// finishLoading() + shortLinkActivityIndicator.stopAnimating() + createShortLinkButton.isEnabled = true + errorShortLinkLabel.text = "Invalid input: \(error.localizedDescription)" return } - let apiKey = "your_api_key" + let apiKey = "pk_VPfQI2HDiStIVUB0" Task { @MainActor in do { let result = try await shortLinkSDK.createShortLink(parameters: parameters, apiKey: apiKey) switch result { case .success(let response): - resultLabel.text = "Short URL: \(response.shortURL)" - copyButton.isHidden = false + resultShortLinkLabel.text = "Short URL: \(response.shortURL)" + copyShortLinkButton.isHidden = false case .failure(let errorResponse): - errorLabel.text = "Error: \(errorResponse.message)" + errorShortLinkLabel.text = "Error: \(errorResponse.message)" } } catch { - errorLabel.text = "Error: \(error.localizedDescription)" + errorShortLinkLabel.text = "Error: \(error.localizedDescription)" } - activityIndicator.stopAnimating() - loadingLabel.isHidden = true - createButton.isEnabled = true +// finishLoading() + shortLinkActivityIndicator.stopAnimating() + createShortLinkButton.isEnabled = true + loadingShortLinkLabel.isHidden = true } } -} + @objc private func createSecureShortLink() { + resultSecureShortLinkLabel.text = nil + errorSecureShortLinkLabel.text = nil + copySecureShortLinkButton.isHidden = true + secureLinkActivityIndicator.startAnimating() + loadingSecuredShortLinkLabel.isHidden = false + createSecureShortLinkButton.isEnabled = false + + Task { @MainActor in + do { + let result = try await shortLinkSDK.createSecure(originalURL: "https://{your_domain}") + resultSecureShortLinkLabel.text = "Secured Short URL: \(result.securedShortUrl)" + copySecureShortLinkButton.isHidden = false + } catch { + errorSecureShortLinkLabel.text = "Error: \(error.localizedDescription)" + } + secureLinkActivityIndicator.stopAnimating() + createSecureShortLinkButton.isEnabled = true + loadingSecuredShortLinkLabel.isHidden = true + } + } +} \ No newline at end of file From 0cde58d44b2ae1e21e11344828f04dd4e77d892f Mon Sep 17 00:00:00 2001 From: rajpootathar Date: Wed, 9 Jul 2025 17:34:29 +0500 Subject: [PATCH 4/7] integrated conversion tracking functionality and updated the README.md file --- README.md | 17 +++++++- .../ShortIOApp/SceneDelegate.swift | 4 +- .../ShortIOApp/ViewController.swift | 39 +++++++++++++++---- SwiftUIProject/ShortIOApp/ContentView.swift | 26 ++++++++++++- SwiftUIProject/ShortIOApp/ShortIOApp.swift | 4 +- 5 files changed, 75 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 61cdca5..d4ad950 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ let sdk = ShortIOSDK() let parameters = ShortIOParameters( domain: "your_domain", - originalURL: "https://yourdomain.com" + originalURL: "https://{your_domain}" ) let apiKey = "your_api_key" @@ -120,6 +120,21 @@ Task { - **`securedShortUrl:`** A Base64-encoded decryption key to be appended as a fragment (e.g. `#`) +## ๐Ÿ”„ Conversion Tracking + +Track conversions for your short links to measure campaign effectiveness. The SDK provides a simple method to record conversions. + +```swift +Task { + do { + let result = try await shortLinkSDK.trackConversion(originalURL: "https://{your_domain}", clid: "your_clid", conversionId: "your_conversionID") + print("result", result) + } catch { + print("Failed to track conversion: \(error)") + } +} +``` + ## ๐ŸŒ Handling Universal Links ### SwiftUI Implementation diff --git a/StoryboardProject/ShortIOApp/SceneDelegate.swift b/StoryboardProject/ShortIOApp/SceneDelegate.swift index 42734ab..22596c3 100644 --- a/StoryboardProject/ShortIOApp/SceneDelegate.swift +++ b/StoryboardProject/ShortIOApp/SceneDelegate.swift @@ -20,8 +20,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { print("Invalid universal link or URL components") return } - sdk.handleOpen(incomingURL) { result in - print("Host: \(result?.host), Path: \(result?.path)") + sdk.handleOpen(incomingURL) { result, error in + print("Host: \(result?.host), Path: \(result?.path)", "QueryParams: \(result?.queryItems)") } } diff --git a/StoryboardProject/ShortIOApp/ViewController.swift b/StoryboardProject/ShortIOApp/ViewController.swift index 36394a0..d737cd4 100644 --- a/StoryboardProject/ShortIOApp/ViewController.swift +++ b/StoryboardProject/ShortIOApp/ViewController.swift @@ -35,6 +35,17 @@ class ViewController: UIViewController { return button }() + private let conversionTracking: UIButton = { + let button = UIButton(type: .system) + button.setTitle("Conversion Tracking", for: .normal) + button.titleLabel?.font = .systemFont(ofSize: 18, weight: .semibold) + button.backgroundColor = .systemBlue + button.tintColor = .white + button.layer.cornerRadius = 10 + button.addTarget(self, action: #selector(trackingConversion), for: .touchUpInside) + return button + }() + private let shortLinkActivityIndicator = UIActivityIndicatorView(style: .medium) private let secureLinkActivityIndicator = UIActivityIndicatorView(style: .medium) @@ -146,7 +157,9 @@ class ViewController: UIViewController { secureLinkLoaderStack, resultSecureShortLinkLabel, copySecureShortLinkButton, - errorSecureShortLinkLabel + errorSecureShortLinkLabel, + + conversionTracking ]) stackView.axis = .vertical stackView.spacing = 10 @@ -163,7 +176,8 @@ class ViewController: UIViewController { createShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), copyShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), createSecureShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), - copySecureShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor) + copySecureShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), + conversionTracking.widthAnchor.constraint(equalTo: stackView.widthAnchor), ]) } @@ -203,18 +217,17 @@ class ViewController: UIViewController { let parameters: ShortIOParameters do { parameters = try ShortIOParameters( - domain: "demodeeplinkapp.short.gy", - originalURL: "https://demodeeplinkapp.short.gy/yei3jh" + domain: "your_domain", + originalURL: "{https://{your_domain}" ) } catch { -// finishLoading() shortLinkActivityIndicator.stopAnimating() createShortLinkButton.isEnabled = true errorShortLinkLabel.text = "Invalid input: \(error.localizedDescription)" return } - let apiKey = "pk_VPfQI2HDiStIVUB0" + let apiKey = "your_api_key" Task { @MainActor in do { @@ -229,7 +242,6 @@ class ViewController: UIViewController { } catch { errorShortLinkLabel.text = "Error: \(error.localizedDescription)" } -// finishLoading() shortLinkActivityIndicator.stopAnimating() createShortLinkButton.isEnabled = true loadingShortLinkLabel.isHidden = true @@ -257,4 +269,15 @@ class ViewController: UIViewController { loadingSecuredShortLinkLabel.isHidden = true } } -} \ No newline at end of file + + @objc private func trackingConversion() { + Task { + do { + let result = try await shortLinkSDK.trackConversion(originalURL: "https://{your_domain}", clid: "your_clid", conversionId: "your_coversionID") + print("result", result) + } catch { + print("Failed to track conversion: \(error)") + } + } + } +} diff --git a/SwiftUIProject/ShortIOApp/ContentView.swift b/SwiftUIProject/ShortIOApp/ContentView.swift index 117afa3..76d89c3 100644 --- a/SwiftUIProject/ShortIOApp/ContentView.swift +++ b/SwiftUIProject/ShortIOApp/ContentView.swift @@ -43,6 +43,17 @@ struct ContentView: View { .cornerRadius(10) } .padding(.horizontal) + + Button(action: conversionTracking) { + Text("Conversion Tracking") + .font(.headline) + .padding() + .frame(maxWidth: .infinity) + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(10) + } + .padding(.horizontal) } if let shortURL = shortURL { @@ -92,7 +103,7 @@ struct ContentView: View { let parameters = ShortIOParameters( domain: "your_domain", - originalURL: "https://{your_domain}" + originalURL:"https://{your_domain}" ) let apiKey = "your_api_key" @@ -122,13 +133,24 @@ struct ContentView: View { Task { do { let result = try shortLinkSDK.createSecure(originalURL: "https://{your_domain}") - secureShortURL = result.securedShortUrl + secureShortURL = result.securedOriginalURL print("result", result.securedOriginalURL, result.securedShortUrl) } catch { print("Failed to create secure URL: \(error)") } } } + + private func conversionTracking() { + Task { + do { + let result = try await shortLinkSDK.trackConversion(originalURL: "https://{your_domain}", clid: "your_clid", conversionId: "your_coversionID") + print("result", result) + } catch { + print("Failed to track conversion: \(error)") + } + } + } } #Preview { diff --git a/SwiftUIProject/ShortIOApp/ShortIOApp.swift b/SwiftUIProject/ShortIOApp/ShortIOApp.swift index 950f8dd..2d17a96 100644 --- a/SwiftUIProject/ShortIOApp/ShortIOApp.swift +++ b/SwiftUIProject/ShortIOApp/ShortIOApp.swift @@ -10,8 +10,8 @@ struct ShortIOApp: App { WindowGroup { ContentView() .onOpenURL { url in - sdk.handleOpen(url) { result in - print("Host: \(result?.host), Path: \(result?.path)") + sdk.handleOpen(url) { result, error in + print("Host: \(result?.host), Path: \(result?.path)", "QueryParams: \(result?.queryItems)") } } } From 2127a5f71c20b36f8601dc7b0fe2f0f73db0515d Mon Sep 17 00:00:00 2001 From: rajpootathar Date: Fri, 8 Aug 2025 20:05:55 +0500 Subject: [PATCH 5/7] implemented destination URL handling from SDK in example app --- README.md | 15 ----------- .../ShortIOApp/SceneDelegate.swift | 18 +++++++++++-- .../ShortIOApp/ViewController.swift | 25 ------------------- SwiftUIProject/ShortIOApp/ContentView.swift | 21 ---------------- SwiftUIProject/ShortIOApp/ShortIOApp.swift | 17 +++++++++++-- 5 files changed, 31 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index d4ad950..c29dc5d 100644 --- a/README.md +++ b/README.md @@ -120,21 +120,6 @@ Task { - **`securedShortUrl:`** A Base64-encoded decryption key to be appended as a fragment (e.g. `#`) -## ๐Ÿ”„ Conversion Tracking - -Track conversions for your short links to measure campaign effectiveness. The SDK provides a simple method to record conversions. - -```swift -Task { - do { - let result = try await shortLinkSDK.trackConversion(originalURL: "https://{your_domain}", clid: "your_clid", conversionId: "your_conversionID") - print("result", result) - } catch { - print("Failed to track conversion: \(error)") - } -} -``` - ## ๐ŸŒ Handling Universal Links ### SwiftUI Implementation diff --git a/StoryboardProject/ShortIOApp/SceneDelegate.swift b/StoryboardProject/ShortIOApp/SceneDelegate.swift index 22596c3..a7c00ab 100644 --- a/StoryboardProject/ShortIOApp/SceneDelegate.swift +++ b/StoryboardProject/ShortIOApp/SceneDelegate.swift @@ -20,8 +20,22 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { print("Invalid universal link or URL components") return } - sdk.handleOpen(incomingURL) { result, error in - print("Host: \(result?.host), Path: \(result?.path)", "QueryParams: \(result?.queryItems)") + Task{ + await sdk.handleOpen(incomingURL) { result,destinationUrl, error in + if let error = error { + print("Error: \(error)") + return + } + + if let components = result { + print("Host: \(components.host ?? "nil")") + print("Path: \(components.path)") + print("QueryParams: \(result?.queryItems)") + print("DestinationUrl: \(destinationUrl ?? "nil")") + } else { + print("No components returned") + } + } } } diff --git a/StoryboardProject/ShortIOApp/ViewController.swift b/StoryboardProject/ShortIOApp/ViewController.swift index d737cd4..8605ed9 100644 --- a/StoryboardProject/ShortIOApp/ViewController.swift +++ b/StoryboardProject/ShortIOApp/ViewController.swift @@ -35,17 +35,6 @@ class ViewController: UIViewController { return button }() - private let conversionTracking: UIButton = { - let button = UIButton(type: .system) - button.setTitle("Conversion Tracking", for: .normal) - button.titleLabel?.font = .systemFont(ofSize: 18, weight: .semibold) - button.backgroundColor = .systemBlue - button.tintColor = .white - button.layer.cornerRadius = 10 - button.addTarget(self, action: #selector(trackingConversion), for: .touchUpInside) - return button - }() - private let shortLinkActivityIndicator = UIActivityIndicatorView(style: .medium) private let secureLinkActivityIndicator = UIActivityIndicatorView(style: .medium) @@ -158,8 +147,6 @@ class ViewController: UIViewController { resultSecureShortLinkLabel, copySecureShortLinkButton, errorSecureShortLinkLabel, - - conversionTracking ]) stackView.axis = .vertical stackView.spacing = 10 @@ -177,7 +164,6 @@ class ViewController: UIViewController { copyShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), createSecureShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), copySecureShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), - conversionTracking.widthAnchor.constraint(equalTo: stackView.widthAnchor), ]) } @@ -269,15 +255,4 @@ class ViewController: UIViewController { loadingSecuredShortLinkLabel.isHidden = true } } - - @objc private func trackingConversion() { - Task { - do { - let result = try await shortLinkSDK.trackConversion(originalURL: "https://{your_domain}", clid: "your_clid", conversionId: "your_coversionID") - print("result", result) - } catch { - print("Failed to track conversion: \(error)") - } - } - } } diff --git a/SwiftUIProject/ShortIOApp/ContentView.swift b/SwiftUIProject/ShortIOApp/ContentView.swift index 76d89c3..e58c01a 100644 --- a/SwiftUIProject/ShortIOApp/ContentView.swift +++ b/SwiftUIProject/ShortIOApp/ContentView.swift @@ -44,16 +44,6 @@ struct ContentView: View { } .padding(.horizontal) - Button(action: conversionTracking) { - Text("Conversion Tracking") - .font(.headline) - .padding() - .frame(maxWidth: .infinity) - .background(Color.blue) - .foregroundColor(.white) - .cornerRadius(10) - } - .padding(.horizontal) } if let shortURL = shortURL { @@ -140,17 +130,6 @@ struct ContentView: View { } } } - - private func conversionTracking() { - Task { - do { - let result = try await shortLinkSDK.trackConversion(originalURL: "https://{your_domain}", clid: "your_clid", conversionId: "your_coversionID") - print("result", result) - } catch { - print("Failed to track conversion: \(error)") - } - } - } } #Preview { diff --git a/SwiftUIProject/ShortIOApp/ShortIOApp.swift b/SwiftUIProject/ShortIOApp/ShortIOApp.swift index 2d17a96..0cfb751 100644 --- a/SwiftUIProject/ShortIOApp/ShortIOApp.swift +++ b/SwiftUIProject/ShortIOApp/ShortIOApp.swift @@ -10,8 +10,21 @@ struct ShortIOApp: App { WindowGroup { ContentView() .onOpenURL { url in - sdk.handleOpen(url) { result, error in - print("Host: \(result?.host), Path: \(result?.path)", "QueryParams: \(result?.queryItems)") + Task{ + await sdk.handleOpen(url) { result,destinationUrl, error in + if let error = error { + print("Error: \(error)") + return + } + + if let components = result { + print("Host: \(components.host ?? "nil")") + print("Path: \(components.path)") + print("DestinationUrl: \(destinationUrl ?? "nil")") + } else { + print("No components returned") + } + } } } } From 02044a2e0321e358281165a324aff0b611b68bfc Mon Sep 17 00:00:00 2001 From: rajpootathar Date: Wed, 13 Aug 2025 20:08:02 +0500 Subject: [PATCH 6/7] integrated appDelegate file to initialize the sdk and integrated the tracking conversion functionality and optimized the code --- SwiftUIProject/ShortIOApp/AppDelegate.swift | 19 ++++++++ SwiftUIProject/ShortIOApp/ContentView.swift | 54 ++++++++++++++------- SwiftUIProject/ShortIOApp/ShortIOApp.swift | 28 +++++------ 3 files changed, 67 insertions(+), 34 deletions(-) create mode 100644 SwiftUIProject/ShortIOApp/AppDelegate.swift diff --git a/SwiftUIProject/ShortIOApp/AppDelegate.swift b/SwiftUIProject/ShortIOApp/AppDelegate.swift new file mode 100644 index 0000000..4dc26cf --- /dev/null +++ b/SwiftUIProject/ShortIOApp/AppDelegate.swift @@ -0,0 +1,19 @@ +import Foundation +import SwiftUI +import ShortIOSDK + + +class AppDelegate: NSObject, UIApplicationDelegate { + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { + + let sdk = ShortIOSDK.shared + + sdk.initialize(apiKey: "your-api-key-here", domain: "your-domain-here") + + // Override point for customization after application launch. + return true + } + +} + diff --git a/SwiftUIProject/ShortIOApp/ContentView.swift b/SwiftUIProject/ShortIOApp/ContentView.swift index e58c01a..90968ea 100644 --- a/SwiftUIProject/ShortIOApp/ContentView.swift +++ b/SwiftUIProject/ShortIOApp/ContentView.swift @@ -7,18 +7,18 @@ struct ContentView: View { @State private var errorMessage: String? @State private var isLoading: Bool = false @State private var secureShortURL: String? - private let shortLinkSDK = ShortIOSDK() - + private let shortLinkSDK = ShortIOSDK.shared // Ensure ShortLinkSDK is accessible + var body: some View { VStack(spacing: 20) { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) - + Text("Short Link Generator") .font(.title) .fontWeight(.bold) - + if isLoading { ProgressView("Creating short link...") } else { @@ -44,8 +44,18 @@ struct ContentView: View { } .padding(.horizontal) + Button(action: conversionTracking) { + Text("Create conversion Tracking") + .font(.headline) + .padding() + .frame(maxWidth: .infinity) + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(10) + } + .padding(.horizontal) } - + if let shortURL = shortURL { Text("Short URL: \(shortURL)") .font(.subheadline) @@ -60,7 +70,7 @@ struct ContentView: View { } } } - + if let secureShortURL = secureShortURL { Text("Secure Short URL: \(secureShortURL)") .font(.subheadline) @@ -75,7 +85,7 @@ struct ContentView: View { } } } - + if let errorMessage = errorMessage { Text("Error: \(errorMessage)") .font(.subheadline) @@ -85,23 +95,20 @@ struct ContentView: View { } .padding() } - + private func createShortLink() { isLoading = true shortURL = nil errorMessage = nil - + let parameters = ShortIOParameters( - domain: "your_domain", - originalURL:"https://{your_domain}" + originalURL:"your-original-url-here" ) - let apiKey = "your_api_key" - - Task { @MainActor in + + Task { do { let result = try await shortLinkSDK.createShortLink( - parameters: parameters, - apiKey: apiKey + parameters: parameters ) switch result { case .success(let response): @@ -118,11 +125,11 @@ struct ContentView: View { isLoading = false } } - + private func createEncryptedLink() { Task { do { - let result = try shortLinkSDK.createSecure(originalURL: "https://{your_domain}") + let result = try shortLinkSDK.createSecure(originalURL: "your_original_url") secureShortURL = result.securedOriginalURL print("result", result.securedOriginalURL, result.securedShortUrl) } catch { @@ -130,6 +137,17 @@ struct ContentView: View { } } } + + private func conversionTracking() { + Task { + do { + let result = try await shortLinkSDK.trackConversion(clid: "your_clid", domain: "your_domain", conversionId: "your_conversion_id") + print("result", result) + } catch { + print("Failed to track conversion: \(error)") + } + } + } } #Preview { diff --git a/SwiftUIProject/ShortIOApp/ShortIOApp.swift b/SwiftUIProject/ShortIOApp/ShortIOApp.swift index 0cfb751..d287bab 100644 --- a/SwiftUIProject/ShortIOApp/ShortIOApp.swift +++ b/SwiftUIProject/ShortIOApp/ShortIOApp.swift @@ -3,27 +3,23 @@ import ShortIOSDK @main struct ShortIOApp: App { - - var sdk = ShortIOSDK() + + @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate + + var sdk = ShortIOSDK.shared var body: some Scene { WindowGroup { ContentView() .onOpenURL { url in - Task{ - await sdk.handleOpen(url) { result,destinationUrl, error in - if let error = error { - print("Error: \(error)") - return - } - - if let components = result { - print("Host: \(components.host ?? "nil")") - print("Path: \(components.path)") - print("DestinationUrl: \(destinationUrl ?? "nil")") - } else { - print("No components returned") - } + sdk.handleOpen(url) { result in + switch result { + case .success(let result): + // Handle successful URL processing + print("result", result, "Host: \(result.host), Path: \(result.path)", "QueryParams: \(result.queryItems)") + case .failure(let error): + // Handle error with proper error type + print("Error: \(error.localizedDescription)") } } } From df812a2854b3495452d7363e17651d8653d26bb1 Mon Sep 17 00:00:00 2001 From: rajpootathar Date: Fri, 15 Aug 2025 16:14:49 +0500 Subject: [PATCH 7/7] updated README.md file and integrated conversion tracking functionality --- README.md | 92 +++++++++++++++---- .../ShortIOApp/AppDelegate.swift | 6 ++ .../ShortIOApp/SceneDelegate.swift | 27 ++---- .../ShortIOApp/ViewController.swift | 32 ++++++- 4 files changed, 119 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index c29dc5d..a1add03 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,17 @@ This project helps developers understand how to: -- Set up and use `ShortIOSDK` - Generate short URLs with customizable parameters -- Integrate and handle Universal Links in SwiftUI and UIKit +- Handle deep links via Universal Links +- Track conversions +- Use secure (encrypted) short links ## ๐Ÿ“ฆ Requirements - iOS 13.0+ - Xcode 13.0+ - Swift 5+ -- A valid [Short.io](https://short.io/) account +- A valid `enterprises` [Short.io](https://short.io/) account ## ๐Ÿš€ Getting Started @@ -35,24 +36,37 @@ Open `ShortIOApp.xcodeproj` or `ShortIOApp.xcworkspace` in Xcode, depending on t ## ๐Ÿ›  Setup Instructions -### ๐Ÿ”‘ 1. Add Your API Key +### Initialize the SDK -Open the appropriate file: +Before using any functionality, you must initialize the SDK using your API key and domain in `AppDelegate` as part of application(launchOptions) for a UIKit app, or the @main initialization logic for a SwiftUI app. -- **SwiftUI:** `ContentView.swift` -- **UIKit:** `ViewController.swift` -Replace the placeholder with your **Short.io Public API Key:** -```bash -let apiKey = "your_api_key" + +```swift +... +import ShortIOSDK +... + +class AppDelegate: UIResponder, UIApplicationDelegate { + ... + func application(...) { + ... + let sdk = ShortIOSDK.shared + + sdk.initialize(apiKey: "your_apiKey_here", domain: "your_domain_here") + ... + } + ... +} ``` +**Note:** Both `apiKey` and `domain` are the required parameters. + ๐Ÿ”— **Need help finding your API key?** Follow this guide in the [ShortIOSDK README](https://github.com/Short-io/ios-sdk?tab=readme-ov-file#step-1-get-public-api-key-from-shortio). - ### ๐ŸŒ 2. Set Short Link Parameters In the same file (`ContentView.swift` or `ViewController.swift`), provide your **Short.io domain** and the **original URL** you want to shorten: @@ -74,7 +88,7 @@ The app demonstrates: Using your domain and original URL, you can generate a short link like this: ```swift -let sdk = ShortIOSDK() +let sdk = ShortIOSDK.shared let parameters = ShortIOParameters( domain: "your_domain", @@ -98,6 +112,8 @@ Task { } ``` +**โš ๏ธ Note**: Both `apiKey` and `domain` parameters is deprecated. Use the instance's configured API key instead. Call initialize(apiKey:domain:) before using this method + ### ๐Ÿ” Secure Short Links (Encrypted) If you want to encrypt the original URL, the SDK provides a `createSecure` function that uses AES-GCM encryption. @@ -105,9 +121,11 @@ If you want to encrypt the original URL, the SDK provides a `createSecure` funct #### ๐Ÿ”ง Example ```swift +let sdk = ShortIOSDK.shared + Task { do { - let result = try shortLinkSDK.createSecure(originalURL: "https://{your_domain}") + let result = try sdk.createSecure(originalURL: "your_originalURL_here") print("result", result.securedOriginalURL, result.securedShortUrl) } catch { print("Failed to create secure URL: \(error)") @@ -120,6 +138,32 @@ Task { - **`securedShortUrl:`** A Base64-encoded decryption key to be appended as a fragment (e.g. `#`) +### ๐Ÿ”„ Conversion Tracking + +Track conversions for your short links to measure campaign effectiveness. The SDK provides a simple method to record conversions. + +```swift +import ShortIOSDK + +let sdk = ShortIOSDK.shared + +Task { + do { + let result = try await sdk.trackConversion( + domain: "your_domain", // โš ๏ธ Deprecated (optional): + clid: "your_clid", // โš ๏ธ Deprecated (optional): + conversionId: "your_conversionID" (optional) + ) + print("result", result) + } catch { + print("Failed to track conversion: \(error)") + } +} +``` + +**โš ๏ธ Note:** All three parameters โ€” `domain`, `clid`, and `conversionId` โ€” are optional. +- `domain` and `clid` are deprecated and may be removed in future versions. + ## ๐ŸŒ Handling Universal Links ### SwiftUI Implementation @@ -128,9 +172,18 @@ Use the `.onOpenURL` modifier to process incoming links: ```swift .onOpenURL { url in - sdk.handleOpen(url) { result in - print("Navigated to path: \(result?.path ?? "")") + print("url", url) + sdk.handleOpen(url) { result in + switch result { + case .success(let result): + // Handle successful URL processing + print("result", result, "Host: \(result.host), Path: \(result.path)", "QueryParams: \(result.queryItems)") + case .failure(let error): + // Handle error with proper error type + print("Error: \(error.localizedDescription)") + } } + } ``` @@ -146,7 +199,14 @@ func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { return } sdk.handleOpen(incomingURL) { result in - print("Host: \(result?.host), Path: \(result?.path)") + switch result { + case .success(let result): + // Handle successful URL processing + print("result", result, "Host: \(result.host), Path: \(result.path)", "QueryParams: \(result.queryItems)") + case .failure(let error): + // Handle error with proper error type + print("Error: \(error.localizedDescription)") + } } } ``` diff --git a/StoryboardProject/ShortIOApp/AppDelegate.swift b/StoryboardProject/ShortIOApp/AppDelegate.swift index f7f60fd..44bc04e 100644 --- a/StoryboardProject/ShortIOApp/AppDelegate.swift +++ b/StoryboardProject/ShortIOApp/AppDelegate.swift @@ -1,4 +1,5 @@ import UIKit +import ShortIOSDK @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -6,6 +7,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + let sdk = ShortIOSDK.shared + + sdk.initialize(apiKey: "your_api_key_here", domain: "your_domain_here") + // Override point for customization after application launch. return true } diff --git a/StoryboardProject/ShortIOApp/SceneDelegate.swift b/StoryboardProject/ShortIOApp/SceneDelegate.swift index a7c00ab..5774087 100644 --- a/StoryboardProject/ShortIOApp/SceneDelegate.swift +++ b/StoryboardProject/ShortIOApp/SceneDelegate.swift @@ -4,8 +4,8 @@ import ShortIOSDK class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - private let sdk = ShortIOSDK() - + private let sdk = ShortIOSDK.shared + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. @@ -20,21 +20,14 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { print("Invalid universal link or URL components") return } - Task{ - await sdk.handleOpen(incomingURL) { result,destinationUrl, error in - if let error = error { - print("Error: \(error)") - return - } - - if let components = result { - print("Host: \(components.host ?? "nil")") - print("Path: \(components.path)") - print("QueryParams: \(result?.queryItems)") - print("DestinationUrl: \(destinationUrl ?? "nil")") - } else { - print("No components returned") - } + sdk.handleOpen(incomingURL) { result in + switch result { + case .success(let result): + // Handle successful URL processing + print("result", result, "Host: \(result.host), Path: \(result.path)", "QueryParams: \(result.queryItems)") + case .failure(let error): + // Handle error with proper error type + print("Error: \(error.localizedDescription)") } } } diff --git a/StoryboardProject/ShortIOApp/ViewController.swift b/StoryboardProject/ShortIOApp/ViewController.swift index 8605ed9..4a38ac2 100644 --- a/StoryboardProject/ShortIOApp/ViewController.swift +++ b/StoryboardProject/ShortIOApp/ViewController.swift @@ -3,7 +3,7 @@ import ShortIOSDK class ViewController: UIViewController { - private let shortLinkSDK = ShortIOSDK() + private let shortLinkSDK = ShortIOSDK.shared private let titleLabel: UILabel = { let label = UILabel() @@ -35,6 +35,17 @@ class ViewController: UIViewController { return button }() + private let conversionTrackingButton: UIButton = { + let button = UIButton(type: .system) + button.setTitle("Conversion Tracking", for: .normal) + button.titleLabel?.font = .systemFont(ofSize: 18, weight: .semibold) + button.backgroundColor = .systemBlue + button.tintColor = .white + button.layer.cornerRadius = 10 + button.addTarget(self, action: #selector(conversionTracking), for: .touchUpInside) + return button + }() + private let shortLinkActivityIndicator = UIActivityIndicatorView(style: .medium) private let secureLinkActivityIndicator = UIActivityIndicatorView(style: .medium) @@ -147,6 +158,8 @@ class ViewController: UIViewController { resultSecureShortLinkLabel, copySecureShortLinkButton, errorSecureShortLinkLabel, + + conversionTrackingButton ]) stackView.axis = .vertical stackView.spacing = 10 @@ -164,6 +177,7 @@ class ViewController: UIViewController { copyShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), createSecureShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), copySecureShortLinkButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), + conversionTrackingButton.widthAnchor.constraint(equalTo: stackView.widthAnchor), ]) } @@ -203,7 +217,6 @@ class ViewController: UIViewController { let parameters: ShortIOParameters do { parameters = try ShortIOParameters( - domain: "your_domain", originalURL: "{https://{your_domain}" ) } catch { @@ -213,11 +226,9 @@ class ViewController: UIViewController { return } - let apiKey = "your_api_key" - Task { @MainActor in do { - let result = try await shortLinkSDK.createShortLink(parameters: parameters, apiKey: apiKey) + let result = try await shortLinkSDK.createShortLink(parameters: parameters) switch result { case .success(let response): resultShortLinkLabel.text = "Short URL: \(response.shortURL)" @@ -255,4 +266,15 @@ class ViewController: UIViewController { loadingSecuredShortLinkLabel.isHidden = true } } + + @objc private func conversionTracking() { + Task { + do { + let result = try await shortLinkSDK.trackConversion(clid: "your_clid", domain: "your_domain", conversionId: "your_conversion_id") + print("result", result) + } catch { + print("Failed to track conversion: \(error)") + } + } + } }