Skip to content

Instantly share code, notes, and snippets.

@simone-coelho
Created January 15, 2025 21:20
Show Gist options
  • Save simone-coelho/33c7594c8ddd0b99a69b3704911fcb21 to your computer and use it in GitHub Desktop.
Save simone-coelho/33c7594c8ddd0b99a69b3704911fcb21 to your computer and use it in GitHub Desktop.
import UIKit
// Import the Optimizely iOS SDK for Feature Experimentation
// import Optimizely
class CarouselViewController: UIViewController {
// MARK: - Properties
// Replace this with the real OptimizelyClient initialization
let optimizelyClient = OptimizelyClient(sdkKey: "YOUR_SDK_KEY")
// Example: A simple model for each slide in the carousel
struct CarouselSlide {
let backgroundImageUrl: String
let marketingMessage: String
let buttonText: String
let buttonColor: String
let buttonClickUrl: String
let backgroundClickUrl: String
let buttonWidth: Int
let buttonHeight: Int
}
var slides: [CarouselSlide] = []
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// Start the Optimizely client to fetch the datafile
optimizelyClient.start { [weak self] result in
switch result {
case .failure(let error):
print("Error starting Optimizely Client: \(error)")
// Fallback logic could go here
case .success:
// Successfully started the client, now decide which variation this user sees
self?.configureCarousel()
}
}
}
// MARK: - Carousel Configuration
func configureCarousel() {
// The flag key as defined in your Optimizely project
let flagKey = "carousel_flag"
// This is a unique user ID. In production, use something that identifies the user
let userId = "user_12345"
// Decide which variation the user sees.
// Note: The actual method or function name may vary depending on the SDK version
let decision = optimizelyClient.decide(key: flagKey, userId: userId)
// Check if the flag is enabled; if not, use fallback or default logic
guard decision.enabled == true else {
print("Flag '\(flagKey)' is not enabled. Using default configuration.")
return
}
// Retrieve the variable values from the decision object.
// Depending on the SDK version, you might access them differently:
// e.g. decision.variables["variable_key"]?.value, or helper functions.
// 1. Integer variable for numberOfSlides
let numberOfSlides = decision.variables["numberOfSlides"]?.value as? Int ?? 0
// Initialize an array to store slides
var tempSlides: [CarouselSlide] = []
// Loop over numberOfSlides to build our slides
for _ in 0..<numberOfSlides {
// 2. String variable for the text caption for the button
let buttonText = decision.variables["buttonText"]?.value as? String ?? "Default Button"
// 3. String variable for the color value for the button
let buttonColor = decision.variables["buttonColor"]?.value as? String ?? "#000000" // Default color black
// 4. String variable for the path or url of the background image
let backgroundImageUrl = decision.variables["backgroundImageUrl"]?.value as? String ?? ""
// 5. String variable for path or url to launch when the button is clicked
let buttonClickUrl = decision.variables["buttonClickUrl"]?.value as? String ?? ""
// 6. String variable for path or url to launch when the background image is clicked
let backgroundClickUrl = decision.variables["backgroundClickUrl"]?.value as? String ?? ""
// 7. String variable for the marketing message overlayed on the carousel
let marketingMessage = decision.variables["marketingMessage"]?.value as? String ?? "Welcome!"
// 8. Integer variable for button width
let buttonWidth = decision.variables["buttonWidth"]?.value as? Int ?? 100
// 9. Integer variable for button height
let buttonHeight = decision.variables["buttonHeight"]?.value as? Int ?? 40
// Create our slide model
let slide = CarouselSlide(backgroundImageUrl: backgroundImageUrl,
marketingMessage: marketingMessage,
buttonText: buttonText,
buttonColor: buttonColor,
buttonClickUrl: buttonClickUrl,
backgroundClickUrl: backgroundClickUrl,
buttonWidth: buttonWidth,
buttonHeight: buttonHeight)
tempSlides.append(slide)
}
// Assign the slides array to our class property
self.slides = tempSlides
// Build the UI for the carousel
DispatchQueue.main.async {
self.setupCarouselUI()
}
}
// MARK: - Setup Carousel UI
func setupCarouselUI() {
// This function is responsible for laying out the UI elements of the carousel.
// For simplicity, let's assume we just build a vertical stack of "slides" (though
// a real carousel might use a UIScrollView, UICollectionView, or a custom component).
let scrollView = UIScrollView(frame: self.view.bounds)
scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(scrollView)
var yOffset: CGFloat = 0
for (index, slide) in slides.enumerated() {
// Container for the slide
let slideView = UIView(frame: CGRect(x: 0, y: yOffset, width: self.view.frame.width, height: 300))
slideView.backgroundColor = .white
// Background image (in a real app, you’d load asynchronously)
if let bgUrl = URL(string: slide.backgroundImageUrl),
let data = try? Data(contentsOf: bgUrl),
let image = UIImage(data: data) {
let backgroundImageView = UIImageView(image: image)
backgroundImageView.contentMode = .scaleAspectFill
backgroundImageView.frame = slideView.bounds
backgroundImageView.isUserInteractionEnabled = true
// Background click gesture
let bgTap = UITapGestureRecognizer(target: self, action: #selector(backgroundTapped(_:)))
backgroundImageView.addGestureRecognizer(bgTap)
backgroundImageView.tag = index // store the slide index
slideView.addSubview(backgroundImageView)
// Overlaid marketing message
let messageLabel = UILabel(frame: CGRect(x: 20,
y: slideView.bounds.height/2 - 20,
width: slideView.bounds.width - 40,
height: 40))
messageLabel.text = slide.marketingMessage
messageLabel.textColor = .white
messageLabel.textAlignment = .center
messageLabel.font = UIFont.boldSystemFont(ofSize: 18)
backgroundImageView.addSubview(messageLabel)
}
// Button
let button = UIButton(type: .system)
button.setTitle(slide.buttonText, for: .normal)
// Convert color string to UIColor if your app supports hex parsing
// For simplicity, assume a function hexStringToUIColor exists
button.backgroundColor = hexStringToUIColor(hex: slide.buttonColor)
// Use the buttonWidth and buttonHeight from the flag variables
button.frame = CGRect(x: 20,
y: slideView.bounds.height - CGFloat(slide.buttonHeight) - 20,
width: CGFloat(slide.buttonWidth),
height: CGFloat(slide.buttonHeight))
button.setTitleColor(.white, for: .normal)
button.layer.cornerRadius = 5
// Button click action
button.tag = index // store the slide index
button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
slideView.addSubview(button)
scrollView.addSubview(slideView)
yOffset += slideView.frame.height
}
scrollView.contentSize = CGSize(width: self.view.frame.width, height: yOffset)
}
// MARK: - Actions
@objc func buttonTapped(_ sender: UIButton) {
let slide = slides[sender.tag]
// Launch the buttonClickUrl when the button is tapped
if let url = URL(string: slide.buttonClickUrl) {
// In a real app, consider SFSafariViewController or an in-app browser
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
@objc func backgroundTapped(_ sender: UITapGestureRecognizer) {
guard let bgView = sender.view else { return }
let slide = slides[bgView.tag]
// Launch the backgroundClickUrl
if let url = URL(string: slide.backgroundClickUrl) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
// MARK: - Helper
func hexStringToUIColor(hex: String) -> UIColor {
// Very basic hex to UIColor converter
var cString = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
// Remove '#' if present
if cString.hasPrefix("#") {
cString.removeFirst()
}
// If not 6 or 8 characters, return gray
if cString.count != 6 && cString.count != 8 {
return UIColor.gray
}
// If 8 characters, assume ARGB
if cString.count == 8 {
let aString = String(cString.prefix(2))
cString.removeFirst(2)
var a: UInt64 = 0
Scanner(string: aString).scanHexInt64(&a)
// For brevity, ignoring alpha logic
return UIColor.gray
}
var rgbValue: UInt64 = 0
Scanner(string: cString).scanHexInt64(&rgbValue)
return UIColor(
red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
alpha: 1.0
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment