Files
Fluxup_PAP/node_modules/expo-router/ios/ExpoHeadAppDelegateSubscriber.swift
2026-03-10 16:18:05 +00:00

95 lines
3.2 KiB
Swift

// Copyright 2023-present 650 Industries. All rights reserved.
import ExpoModulesCore
import CoreSpotlight
/// Represents the Info.plist.
public struct InfoPlist {
public init() {}
/// Returns the custom URL schemes registered by the app ('CFBundleURLSchemes' array).
public static func bundleURLSchemes() -> [String] {
guard let path = Bundle.main.path(forResource: "Info", ofType: "plist") else {
log.error("Can't find path to Info.plist in the main bundle.")
return []
}
guard
// swiftlint:disable:next legacy_objc_type
let infoDict = NSDictionary(contentsOfFile: path) as? [String: AnyObject],
let anyDictionary = (infoDict["CFBundleURLTypes"] as? [[String: Any]])?.first,
let urlSchemes = anyDictionary["CFBundleURLSchemes"] as? [String]
else {
log.error("Can't find path to CFBundleURLSchemes in the Info.plist.")
return []
}
return urlSchemes
}
}
func encoded(_ value: String) -> String {
return value.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? value
}
func sendFakeDeepLinkEventToReactNative(obj: Any, url: String) {
NotificationCenter.default.post(
// swiftlint:disable:next legacy_objc_type
name: NSNotification.Name(rawValue: "RCTOpenURLNotification"),
object: obj,
userInfo: ["url": url])
}
func userInfoToQueryString(_ userInfo: [String: NSSecureCoding]?) -> String {
guard let userInfo = userInfo else {
return ""
}
var queryString = ""
for (key, value) in userInfo {
if let value = value as? String {
if key != "href" {
queryString += "&\(encoded(key))=\(encoded(value))"
}
}
}
return queryString
}
func prefixDeepLink(fragment: String) -> String {
// This can happen when an NSUserActivity href is used to activate the app.
if fragment.starts(with: "/") {
let schemes = InfoPlist.bundleURLSchemes()
return "\(schemes[0]):/\(fragment)"
}
return fragment
}
public class ExpoHeadAppDelegateSubscriber: ExpoAppDelegateSubscriber {
public func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
launchedActivity = userActivity
if let wellKnownHref = userActivity.userInfo?["href"] as? String {
// From a stored NSUserActivity, e.g. Quick Note or Siri Reminder
// From other native device to app
sendFakeDeepLinkEventToReactNative(obj: self, url: prefixDeepLink(fragment: wellKnownHref))
} else if userActivity.activityType == CSQueryContinuationActionType {
// From Spotlight search
if let query = userActivity.userInfo?[CSSearchQueryString] as? String {
let schemes = InfoPlist.bundleURLSchemes()
let encodedQuery = query.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? query
// swiftlint:disable:next todo
// TODO(EvanBacon): Allow user to define the scheme using structured data or something.
// opensearch = Chrome. spotlight = custom thing we're using to identify iOS
let url = "\(schemes[0])://search?q=\(encodedQuery)&ref=spotlight"
// https://github.com/search?q=
sendFakeDeepLinkEventToReactNative(obj: self, url: url)
}
}
return false
}
}