Skip to main content

iOS (UIKit)

This sample shows a UIKit app that uses callback-based APIs (no Swift Concurrency) to interact with AFCore.

Replace placeholders like Your-API-Key and Your-Base-Url with your real values.

iOS (UIKit)

AppDelegate.swift

import UIKit
import AFCore

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
do {
let config = AFCoreConfig.Builder()
.authKey(value: "[Your_API_Key]")
.baseUrl(value: "[Your_Base_Url]")
.swAuthKey(value: "[Your_SW_API_Key]")
.swBaseUrl(value: "[Your_SW_Base_Url")
.enableLogging(value: true)
.logLevel(value: AFLogLevel.debug)
.build()

try AFCore.shared.initialize(config: config)
} catch {
print("AFCore init error: \(error)")
}
return true
}

// UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
}

SceneDelegate.swift

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?

func scene(_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
let nav = UINavigationController(rootViewController: LoginViewController())
window.rootViewController = nav
window.makeKeyAndVisible()
self.window = window
}
}

LoginViewController.swift

import UIKit
import AFCore

final class LoginViewController: UIViewController {

private let usernameField = UITextField()
private let passwordField = UITextField()
private let statusLabel = UILabel()
private let loginButton = UIButton(type: .system)
private let forgotButton = UIButton(type: .system)

override func viewDidLoad() {
super.viewDidLoad()
title = "AFCore Login"
view.backgroundColor = .systemBackground
layout()
}

private func layout() {
usernameField.placeholder = "Username"
usernameField.keyboardType = .default
usernameField.autocapitalizationType = .none
usernameField.borderStyle = .roundedRect

passwordField.placeholder = "Password"
passwordField.isSecureTextEntry = true
passwordField.borderStyle = .roundedRect

statusLabel.numberOfLines = 0
statusLabel.textAlignment = .center
statusLabel.textColor = .secondaryLabel

loginButton.setTitle("Login", for: .normal)
loginButton.addTarget(self, action: #selector(loginTapped), for: .touchUpInside)

forgotButton.setTitle("Forgot Password?", for: .normal)
forgotButton.addTarget(self, action: #selector(forgotTapped), for: .touchUpInside)

let stack = UIStackView(arrangedSubviews: [
usernameField, passwordField, loginButton, forgotButton, statusLabel
])
stack.axis = .vertical
stack.spacing = 12

stack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stack)

NSLayoutConstraint.activate([
stack.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
stack.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
stack.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}

@objc private func forgotTapped() {
if let url = URL(string: "https://api.activefitplus.com/recoverpassword") {
UIApplication.shared.open(url)
}
}

@objc private func loginTapped() {
setLoading(true)
AFCore.shared.authentication().login(username: usernameField.text ?? "",
password: passwordField.text ?? "") { tokens, error in
DispatchQueue.main.async {
self.setLoading(false)
if let error = error {
self.statusLabel.text = "Login failed: \(error.localizedDescription)"
} else if tokens != nil {
self.statusLabel.text = "Login success"
self.navigationController?.pushViewController(HomeViewController(), animated: true)
} else {
self.statusLabel.text = "Login failed"
}
}
}
}

private func setLoading(_ loading: Bool) {
loginButton.isEnabled = !loading
statusLabel.text = loading ? "Signing in..." : ""
}
}

HomeViewController.swift

import UIKit
import AFCore

final class HomeViewController: UIViewController {

private let textView = UITextView()

override func viewDidLoad() {
super.viewDidLoad()
title = "AFCore Sample"
view.backgroundColor = .systemBackground
setupUI()
}

private func setupUI() {
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Logout",
style: .plain,
target: self,
action: #selector(logout))

let healthBtn = makeButton("Connect Apple Health") { [weak self] in self?.connectHealth() }
let profileBtn = makeButton("Get Profile") { [weak self] in self?.getProfile() }
let actsBtn = makeButton("Get Activities (month)") { [weak self] in self?.getActivities() }
let payBtn = makeButton("Get Payments") { [weak self] in self?.getPayments() }
let startGeoBtn = makeButton("Start Geofencing") { [weak self] in self?.startGeofencing() }
let stopGeoBtn = makeButton("Stop Geofencing") { [weak self] in self?.stopGeofencing() }
let permWhenInUseBtn = makeButton("Request Location (When In Use)") { [weak self] in self?.reqPermission(.fineLocation) }
let permAlwaysBtn = makeButton("Request Location (Always)") { [weak self] in self?.reqPermission(.backgroundLocation) }
let permBleBtn = makeButton("Request Bluetooth LE") { [weak self] in self?.reqPermission(.bluetoothLe) }

let stack = UIStackView(arrangedSubviews: [
healthBtn, profileBtn, actsBtn, payBtn, startGeoBtn, stopGeoBtn, permWhenInUseBtn, permAlwaysBtn, permBleBtn, textView
])
stack.axis = .vertical
stack.spacing = 10
stack.translatesAutoresizingMaskIntoConstraints = false

textView.isEditable = false
textView.backgroundColor = .secondarySystemBackground
textView.heightAnchor.constraint(equalToConstant: 160).isActive = true

view.addSubview(stack)
NSLayoutConstraint.activate([
stack.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
stack.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
stack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 12),
])
}

private func makeButton(_ title: String, action: @escaping () -> Void) -> UIButton {
let b = UIButton(type: .system)
b.setTitle(title, for: .normal)
b.addAction(UIAction { _ in action() }, for: .touchUpInside)
b.heightAnchor.constraint(equalToConstant: 44).isActive = true
return b
}

// MARK: - Actions (CALLBACK VARIANTS)

private func connectHealth() {
let deviceId = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString
AFCore.shared.smartWalking().connectAppleHealthKit(deviceId: deviceId) { success, error in
self.log("HealthKit: \(success ? "connected" : "failed") \(error?.localizedDescription ?? "")")
}
}

private func reqPermission(_ p: Permission) {
AFPermissions.shared.requestPermission(permission: p) { granted, error in
self.log("Permission \(p): \(granted ? "granted" : "denied") \(error?.localizedDescription ?? "")")
}
}

private func startGeofencing() {
AFCore.shared.facilities().startMonitoring { _, error in
self.log(error == nil ? "Geofencing started" : "Start geo error: \(error!.localizedDescription)")
}
}

private func stopGeofencing() {
AFCore.shared.facilities().stopMonitoring { _, error in
self.log(error == nil ? "Geofencing stopped" : "Stop geo error: \(error!.localizedDescription)")
}
}

private func getActivities() {
let m = 7, y = 2025
AFCore.shared.activities().get(month: Int32(m), year: Int32(y)) { resp, error in
if let r = resp {
self.log("Activities: \(r.activities.count)")
} else {
self.log("Activities error: \(error?.localizedDescription ?? "unknown")")
}
}
}

private func getPayments() {
AFCore.shared.payments().get { list, error in
self.log(list != nil ? "Payments: \(list!.count)" : "Payments error: \(error?.localizedDescription ?? "unknown")")
}
}

private func getProfile() {
AFCore.shared.profile().get { prof, error in
if let p = prof {
self.log("Profile: \(p.firstName)")
} else {
self.log("Profile error: \(error?.localizedDescription ?? "unknown")")
}
}
}

@objc private func logout() {
AFCore.shared.authentication().logout { _, error in
if let e = error {
self.log("Logout error: \(e.localizedDescription)")
} else {
self.navigationController?.popToRootViewController(animated: true)
}
}
}

private func log(_ s: String) {
DispatchQueue.main.async {
self.textView.text = "[\(Date())] \(s)\n" + (self.textView.text ?? "")
}
}
}