Skip to content
4 changes: 3 additions & 1 deletion vibetype/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@
<true/>
</dict>
<key>NSCameraUsageDescription</key>
<string>Capture Video by user request</string>
<string>Allow camera access to quickly scan event QR codes and check in</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Track current location by user request</string>
<key>NSUserTrackingUsageDescription</key>
<string>This app would like to track you across apps and websites to provide personalized experiences and analytics</string>
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The tracking usage description should be more privacy-focused and less intrusive. Apple's App Store guidelines recommend being transparent about tracking purposes. Consider revising to: "We use this to personalize your experience and show relevant content" or similar, avoiding language like "track you across apps and websites" which may concern users.

Suggested change
<string>This app would like to track you across apps and websites to provide personalized experiences and analytics</string>
<string>We use this information to personalize your experience and show relevant content.</string>

Copilot uses AI. Check for mistakes.
<!-- <key>NSMicrophoneUsageDescription</key>
<string>Capture Audio by user request</string> -->
<key>UIApplicationSceneManifest</key>
Expand Down
101 changes: 74 additions & 27 deletions vibetype/ViewController.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import UIKit
import WebKit
import AppTrackingTransparency

var webView: WKWebView! = nil

Expand Down Expand Up @@ -36,9 +37,7 @@ class ViewController: UIViewController, WKNavigationDelegate, UIDocumentInteract
initWebView()
initToolbarView()
loadRootUrl()

NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification , object: nil)

}

override func viewDidLayoutSubviews() {
Expand Down Expand Up @@ -121,25 +120,36 @@ class ViewController: UIViewController, WKNavigationDelegate, UIDocumentInteract
webviewView.addSubview(toolbarView)
}

@objc func loadRootUrl() {
@objc func loadRootUrl() {
vibetype.webView.load(URLRequest(url: SceneDelegate.universalLinkToLaunch ?? SceneDelegate.shortcutLinkToLaunch ?? rootUrl))
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!){
htmlIsLoaded = true

self.setProgress(1.0, true)
self.animateConnectionProblem(false)

DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
vibetype.webView.isHidden = false
self.loadingView.isHidden = true

self.setProgress(0.0, false)
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
htmlIsLoaded = true
self.setProgress(1.0, true)
self.animateConnectionProblem(false)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
vibetype.webView.isHidden = false
self.loadingView.isHidden = true
self.setProgress(0.0, false)
self.overrideUIStyle()
self.handleTrackingPermission()
}
}

self.overrideUIStyle()
}
// Enable this block for local simulator testing to bypass SSL certificate validation.
// (Do NOT use this in production.)
/*
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
} else {
completionHandler(.performDefaultHandling, nil)
}
}
*/


func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
htmlIsLoaded = false;
Expand Down Expand Up @@ -228,21 +238,58 @@ extension UIColor {
}

extension ViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "print" {
func returnTrackingPermissionResult(isAuthorized: Bool) {
let result = isAuthorized ? "authorized" : "denied"
dispatchEventToWebView(name: "tracking-permission-request", data: result)
}
Comment on lines +241 to +244
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The returnTrackingPermissionResult function is defined but never called. The handleTrackingPermission function directly calls dispatchEventToWebView instead of using this helper method, making it redundant code.

Suggested change
func returnTrackingPermissionResult(isAuthorized: Bool) {
let result = isAuthorized ? "authorized" : "denied"
dispatchEventToWebView(name: "tracking-permission-request", data: result)
}

Copilot uses AI. Check for mistakes.

func returnTrackingPermissionState(state: String) {
dispatchEventToWebView(name: "tracking-permission-state", data: state)
}

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
switch message.name {
case "print":
printView(webView: vibetype.webView)
}
if message.name == "push-subscribe" {
case "push-subscribe":
handleSubscribeTouch(message: message)
}
if message.name == "push-permission-request" {
case "push-permission-request":
handlePushPermission()
}
if message.name == "push-permission-state" {
case "push-permission-state":
handlePushState()
}
if message.name == "push-token" {
case "push-token":
handleFCMToken()
case "tracking-permission-request":
handleTrackingPermission()
case "tracking-permission-state":
handleTrackingState()
default:
break
}
}

func handleTrackingPermission() {
ATTrackingManager.requestTrackingAuthorization { [weak self] status in
let isAuthorized = status == .authorized
self?.dispatchEventToWebView(
name: "tracking-permission-result",
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Event name mismatch: handleTrackingPermission() dispatches an event named "tracking-permission-result" (line 275), but the registered message handler is "tracking-permission-request" (WebView.swift line 19). This inconsistency will cause the WebView to not receive the tracking permission response correctly.

Suggested change
name: "tracking-permission-result",
name: "tracking-permission-request",

Copilot uses AI. Check for mistakes.
data: isAuthorized ? "authorized" : "denied"
)
}
}
}

func dispatchEventToWebView(name: String, data: String) {
DispatchQueue.main.async {
vibetype.webView.evaluateJavaScript("""
window.dispatchEvent(new CustomEvent('\(name)', { detail: '\(data)' }));
""", completionHandler: nil)
}
}

func handleTrackingState() {
let status = ATTrackingManager.trackingAuthorizationStatus
let isAuthorized = status == .authorized
let state = isAuthorized ? "authorized" : "denied"
returnTrackingPermissionState(state: state)
}
}
Comment on lines +281 to 295
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dispatchEventToWebView function is incorrectly placed outside the ViewController extension scope. It should be indented to be a member of the extension. Currently it's defined at the file level which makes it not accessible as an instance method.

Suggested change
func dispatchEventToWebView(name: String, data: String) {
DispatchQueue.main.async {
vibetype.webView.evaluateJavaScript("""
window.dispatchEvent(new CustomEvent('\(name)', { detail: '\(data)' }));
""", completionHandler: nil)
}
}
func handleTrackingState() {
let status = ATTrackingManager.trackingAuthorizationStatus
let isAuthorized = status == .authorized
let state = isAuthorized ? "authorized" : "denied"
returnTrackingPermissionState(state: state)
}
}
func dispatchEventToWebView(name: String, data: String) {
DispatchQueue.main.async {
vibetype.webView.evaluateJavaScript("""
window.dispatchEvent(new CustomEvent('\(name)', { detail: '\(data)' }));
""", completionHandler: nil)
}
}
func handleTrackingState() {
let status = ATTrackingManager.trackingAuthorizationStatus
let isAuthorized = status == .authorized
let state = isAuthorized ? "authorized" : "denied"
returnTrackingPermissionState(state: state)
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +289 to 295
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The handleTrackingState function is incorrectly placed outside the ViewController extension scope. It should be indented to be a member of the extension. Currently it's defined at the file level which makes it not accessible as an instance method and unable to call returnTrackingPermissionState.

Suggested change
func handleTrackingState() {
let status = ATTrackingManager.trackingAuthorizationStatus
let isAuthorized = status == .authorized
let state = isAuthorized ? "authorized" : "denied"
returnTrackingPermissionState(state: state)
}
}
func handleTrackingState() {
let status = ATTrackingManager.trackingAuthorizationStatus
let isAuthorized = status == .authorized
let state = isAuthorized ? "authorized" : "denied"
returnTrackingPermissionState(state: state)
}
}

Copilot uses AI. Check for mistakes.
4 changes: 3 additions & 1 deletion vibetype/WebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import UIKit
@preconcurrency import WebKit
import AuthenticationServices
import SafariServices

import AppTrackingTransparency

func createWebView(container: UIView, WKSMH: WKScriptMessageHandler, WKND: WKNavigationDelegate, NSO: NSObject, VC: ViewController) -> WKWebView{

Expand All @@ -16,6 +16,8 @@ func createWebView(container: UIView, WKSMH: WKScriptMessageHandler, WKND: WKNav
userContentController.add(WKSMH, name: "push-permission-request")
userContentController.add(WKSMH, name: "push-permission-state")
userContentController.add(WKSMH, name: "push-token")
userContentController.add(WKSMH, name: "tracking-permission-request")
userContentController.add(WKSMH, name: "tracking-permission-state")

config.userContentController = userContentController

Expand Down