Major updates: - Added 35+ new skills from awesome-opencode-skills and antigravity repos - Merged SEO skills into seo-master - Merged architecture skills into architecture - Merged security skills into security-auditor and security-coder - Merged testing skills into testing-master and testing-patterns - Merged pentesting skills into pentesting - Renamed website-creator to thai-frontend-dev - Replaced skill-creator with github version - Removed Chutes references (use MiniMax API instead) - Added install-openclaw-skills.sh for cross-platform installation - Updated .env.example with MiniMax API credentials
9.5 KiB
9.5 KiB
System Integration
iOS system integration guide covering permissions, location, sharing, app lifecycle, and haptic feedback.
Permission Requests
Request permissions contextually, not at launch:
import AVFoundation
@objc func openCamera() {
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
DispatchQueue.main.async {
if granted {
self?.showCameraInterface()
} else {
self?.showPermissionDeniedAlert()
}
}
}
}
Photo Library
import Photos
func requestPhotoAccess() {
PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in
DispatchQueue.main.async {
switch status {
case .authorized, .limited:
self.showPhotoPicker()
case .denied, .restricted:
self.showSettingsAlert()
default:
break
}
}
}
}
Microphone
func requestMicrophoneAccess() {
AVAudioSession.sharedInstance().requestRecordPermission { granted in
DispatchQueue.main.async {
if granted {
self.startRecording()
}
}
}
}
Notifications
import UserNotifications
func requestNotificationPermission() {
UNUserNotificationCenter.current().requestAuthorization(
options: [.alert, .badge, .sound]
) { granted, error in
DispatchQueue.main.async {
if granted {
self.registerForRemoteNotifications()
}
}
}
}
Location Button
For one-time location access without persistent permission:
import CoreLocationUI
class StoreFinderVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let locationBtn = CLLocationButton()
locationBtn.icon = .arrowFilled
locationBtn.label = .currentLocation
locationBtn.cornerRadius = 20
locationBtn.addTarget(self, action: #selector(findNearby), for: .touchUpInside)
view.addSubview(locationBtn)
locationBtn.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-24)
}
}
}
Core Location
import CoreLocation
class LocationManager: NSObject, CLLocationManagerDelegate {
private let manager = CLLocationManager()
func requestLocation() {
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch manager.authorizationStatus {
case .authorizedWhenInUse, .authorizedAlways:
manager.requestLocation()
case .denied:
showLocationDeniedAlert()
default:
break
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
handleLocation(location)
}
}
Share Sheet
@objc func shareContent() {
let items: [Any] = [contentURL, contentImage].compactMap { $0 }
let activityVC = UIActivityViewController(activityItems: items, applicationActivities: nil)
if let popover = activityVC.popoverPresentationController {
popover.sourceView = shareButton
popover.sourceRect = shareButton.bounds
}
present(activityVC, animated: true)
}
Custom Share Items
class ShareItem: NSObject, UIActivityItemSource {
let title: String
let url: URL
init(title: String, url: URL) {
self.title = title
self.url = url
}
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return url
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
return url
}
func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String {
return title
}
}
Excluding Activities
let activityVC = UIActivityViewController(activityItems: items, applicationActivities: nil)
activityVC.excludedActivityTypes = [
.addToReadingList,
.assignToContact,
.print
]
App Lifecycle
class PlayerViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self, selector: #selector(onBackground),
name: UIApplication.didEnterBackgroundNotification, object: nil
)
NotificationCenter.default.addObserver(
self, selector: #selector(onForeground),
name: UIApplication.willEnterForegroundNotification, object: nil
)
NotificationCenter.default.addObserver(
self, selector: #selector(onTerminate),
name: UIApplication.willTerminateNotification, object: nil
)
}
@objc private func onBackground() {
saveState()
pausePlayback()
}
@objc private func onForeground() {
restoreState()
resumePlayback()
}
@objc private func onTerminate() {
saveState()
}
}
Scene Lifecycle (iOS 13+)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func sceneDidBecomeActive(_ scene: UIScene) {
// Resume tasks
}
func sceneWillResignActive(_ scene: UIScene) {
// Pause tasks
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Save state
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Prepare UI
}
}
State Preservation
class ViewController: UIViewController {
override func encodeRestorableState(with coder: NSCoder) {
super.encodeRestorableState(with: coder)
coder.encode(currentItemID, forKey: "currentItemID")
}
override func decodeRestorableState(with coder: NSCoder) {
super.decodeRestorableState(with: coder)
if let itemID = coder.decodeObject(forKey: "currentItemID") as? String {
loadItem(itemID)
}
}
}
Haptic Feedback
func onTaskComplete() {
UINotificationFeedbackGenerator().notificationOccurred(.success)
}
func onError() {
UINotificationFeedbackGenerator().notificationOccurred(.error)
}
func onWarning() {
UINotificationFeedbackGenerator().notificationOccurred(.warning)
}
func onSelection() {
UISelectionFeedbackGenerator().selectionChanged()
}
func onImpact() {
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
}
Impact Styles
| Style | Usage |
|---|---|
.light |
Subtle feedback, small UI changes |
.medium |
Standard feedback, button presses |
.heavy |
Strong feedback, significant actions |
.soft |
Gentle feedback, background changes |
.rigid |
Sharp feedback, collisions |
Prepared Feedback
For time-critical haptics, prepare the generator in advance:
class DraggableView: UIView {
private let impactGenerator = UIImpactFeedbackGenerator(style: .medium)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
impactGenerator.prepare()
}
func didSnapToPosition() {
impactGenerator.impactOccurred()
}
}
Deep Linking
URL Schemes
// In AppDelegate or SceneDelegate
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return false
}
switch components.host {
case "item":
if let itemID = components.queryItems?.first(where: { $0.name == "id" })?.value {
navigateToItem(itemID)
return true
}
default:
break
}
return false
}
Universal Links
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else {
return false
}
return handleUniversalLink(url)
}
Background Tasks
import BackgroundTasks
func registerBackgroundTasks() {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.app.refresh",
using: nil
) { task in
self.handleAppRefresh(task: task as! BGAppRefreshTask)
}
}
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.app.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
do {
try BGTaskScheduler.shared.submit(request)
} catch {
print("Could not schedule app refresh: \(error)")
}
}
func handleAppRefresh(task: BGAppRefreshTask) {
scheduleAppRefresh()
let operation = RefreshOperation()
task.expirationHandler = {
operation.cancel()
}
operation.completionBlock = {
task.setTaskCompleted(success: !operation.isCancelled)
}
OperationQueue.main.addOperation(operation)
}
UIKit, Core Location, and Apple are trademarks of Apple Inc.