Post

Replies

Boosts

Views

Activity

Can an Action Extension use SwiftUI for its UI?
I'm trying to update an app of mine to have a more modern look, and the last part of it is the Action Extension in Safari. My info.plist file has the correct NSExtension details to use a storyboard, but storyboards look so old and I'd like to use a nicer SwiftUI-based look. Is this even possible? This is the relevant bit from the Info.plist: <dict> <key>NSExtensionAttributes</key> <dict> <key>NSExtensionActivationRule</key> <dict> <key>NSExtensionActivationSupportsWebPageWithMaxCount</key> <integer>1</integer> </dict> <key>NSExtensionJavaScriptPreprocessingFile</key> <string>GetURL</string> </dict> <key>NSExtensionPointIdentifier</key> <string>com.apple.ui-services</string> <key>NSExtensionActionWantsFullScreenPresentation</key> <false/> <key>NSExtensionMainStoryboard</key> <string>MainInterface</string> </dict> I see I can use NSExtensionPrincipalClass instead of NSExtensionMainStoryboard but then I get stuck. If I remove this: <key>NSExtensionMainStoryboard</key> <string>MainInterface</string> and replace it with this: <key>NSExtensionPrincipalClass</key> <string>$(PRODUCT_MODULE_NAME).ActionViewController</string> I get this error when I run the extension: Rejecting view controller creation request due to invalid extension storyboard or principal class: Error Domain=NSCocoaErrorDomain Code=967223 "(null)" UserInfo={Invalid Configuration=Either NSExtensionMainStoryboard or NSExtensionPrincipalClass must be specified in the extension's Info.plist file but not both.} According to that error the two keys are mutually-exclusive, which is fine as I'm using just one of them, so why do I get this error? Is it something to do with the actual code in ActionViewController? I have this, and nothing here ever runs: class ActionViewController: UIViewController { var theUrl: String = "" @objc override func viewDidLoad() { super.viewDidLoad() if let inputItem = extensionContext!.inputItems.first as? NSExtensionItem { if let itemProvider = inputItem.attachments?.first { itemProvider.loadItem(forTypeIdentifier: UTType.propertyList.identifier as String) { [unowned self] (dict, error) in let itemDictionary = dict as! NSDictionary let javaScriptValues = itemDictionary[NSExtensionJavaScriptPreprocessingResultsKey] as! NSDictionary self.theUrl = javaScriptValues["URL"] as! String // Build the SwiftUI view, wrap it in a UIHostingController then send to the main thread to update the UI let contentView = ActionExtensionView(theUrl: self.theUrl, clickedCancel: self.cancel, clickedDone: self.done) let childView = UIHostingController(rootView: contentView) self.view.addSubview(childView.view) // Set the place where your view will be displayed let constraints = [ childView.view.topAnchor.constraint(equalTo: view.topAnchor), childView.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), childView.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), childView.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), childView.view.widthAnchor.constraint(equalTo: view.widthAnchor), childView.view.heightAnchor.constraint(equalTo: view.heightAnchor) ] childView.view.translatesAutoresizingMaskIntoConstraints = false view.addConstraints(constraints) DispatchQueue.main.async { self.present(childView, animated: true) } } } } } Apple really don't make it easy to develop for their platforms, do they?
1
0
495
Feb ’25
Content blockers: ignore-previous-rules not working?
In my content blocker I have a bunch of rules that block some content in Safari, but I want my users to be able to whiltelist a website so the blocker rules don't apply on that site. I have something like this: { "action": { "type":"css-display-none", "selector":"a[href*='Bobbins']" }, "trigger": { "url-filter":".*" } }, { "trigger": { "url-filter": ".*", "if-domain": ["*mydomain.com"] }, "action": { "type": "ignore-previous-rules" } } I think that should block any a link where the href includes Bobbins but not if the site is mydomain.com. However, that simply doesn't work. It doesn't matter what I put in the array of domains to whitelist, it just doesn't apply it. In every case, the a link is blocked. How do you actually whitelist a website in a Safari content blocker?
Topic: Safari & Web SubTopic: General Tags:
1
0
434
Feb ’25
How to handle long press on a Text() in iOS26
In iOS 18 the following code works to set a state variable when you hold your finger on the Text() field (well, the ScrollView()), but it doesn't work in iOS 26: @State private var pressed: Bool = false ... ScrollView { VStack { Text("Some text goes here") }.frame(maxWidth: .infinity) } .onTapGesture {} // This is required to allow the long press gesture to be recognised .gesture( DragGesture(minimumDistance: 0) .onChanged({ _ in pressed = true }) .onEnded({ _ in pressed = false }) ) .background(pressed ? .black.opacity(0.4) : .clear) I've tried changing this to: var dragGesture: some Gesture { DragGesture(minimumDistance: 0) .onChanged({ _ in self.pressed = true }) .onEnded({ _ in self.pressed = false }) } ... ScrollView { VStack { Text("Some text goes here") }.frame(maxWidth: .infinity) } .gesture(dragGesture) .background(pressed ? .black.opacity(0.4) : .clear) And this: var longPress: some Gesture { LongPressGesture(minimumDuration: 0.25) .onChanged({ _ in self.pressed = true }) .onEnded({ _ in self.pressed = false }) } ... ScrollView { VStack { Text("Some text goes here") }.frame(maxWidth: .infinity) } .gesture(longPress) .background(pressed ? .black.opacity(0.4) : .clear) Neither works. Any ideas? Thanks.
1
0
85
Sep ’25
Can't get timers to work properly in widgets
Hi all. I have a timer working in a view on the Watch app, but I just can't get them working in widgets. Can you use timers in Home Screen & Lock Screen widgets? I can't find anything that says you can't... Take this code: struct ScratchPadView: View { @State var backgroundGradient: LinearGradient = gradientOne let gradientTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() var body: some View { let date: Date = getDateFromString("20220828 10:00") ZStack { backgroundGradient .onReceive(gradientTimer) { _ in self.backgroundGradient = (date >= Date()) ? gradientOne : gradientTwo } } } } All this is supposed to do is change the gradient in the background from gradientOne to gradientTwo when the current date is after the date supplied. Doesn't work. The date can come and go and the gradient never changes from what it was initially set up to use: gradientOne. Like I say, this works fine in a Watch app View, but just doesn't work in a widget. Is the only alternative to provide a timeline entry for that date so the widget refreshes and notices that the date has now passed?
0
0
817
Aug ’22
Text.init(date, style: .timer) is 1 second out when it counts back up
Let's say you have a Text.init(Date().advanced(by: 5), style: .timer). It's set to countdown to a date 5 seconds from now (just for simplicity's sake). It goes like this: Countdown starts, and shows 0:05. After 1 second it shows 0:04. After 2 seconds it shows 0:03. After 3 seconds it shows 0:02. After 4 seconds it shows 0:01. After 5 seconds it shows 0:00. Countdown done. After 6 seconds it should show 0:01, right? It doesn't; it shows 0:00. After 7 seconds it shows 0:01, even though only 6 seconds have passed. It's almost as though the timer is being restarted once it hits zero, but that isn't right. There aren't "two seconds at 0:00" (that's not how time works...). Anyone seeing this, or is just me?
0
0
736
Aug ’22
Content blocking a specific class?
I have a content blocker working fine, but have noticed some websites are putting a class in the main body tag, and removing it when the user taps to accept/decline cookies. For example: <body class="homepage ContentPage language-en modal-open no-overflow"> ... <div id="cookieNotice">...</div> My content blocker can remove the cookieNotice div completely using: div[id="cookieNotice"], but the page doesn't scroll because the body tag includes modal-open no-overflow. Can a content blocker remove values/classes from a tag? If so, what would the XPath look like? Thanks.
0
0
612
Apr ’23
How to calculate differences between dates for a widget timer
I have a countdown/up timer in a widget, and I want to format the timer to something more readable than the default that Text.init(myDate, style: .timer) provides. The default outputs a timer for just the hours in the date range. So, for example, a timer of 1 week, 5 hours, 12 minutes and 45 seconds will appear as 173:12:45 (which is 7 * 24 + 5 = 173) - not very user-friendly. An ideal output would be 1 week 05:12:45. Is there any way of doing that? I've tried a number of different ways using TimeInterval, DateInterval, and modding (%) values - like modding the hours count by 168 to get a number of weeks - but they're pretty much useless when there are never a set number of days or weeks in a year due to leap years. It would be great if you would provide actual code examples rather than saying to use a certain API, as I could very easily go down a rabbit hole like I have with TimeInterval and DateInterval. Thanks!
0
0
772
Jan ’24
Convert ObjC+CoreData to Swift/SwiftUI+SwiftData?
I have an iOS app that's written in Objective-C, uses Core Data, and has a number of SwiftUI targets (Widgets, Watch app). I'd like to convert the main app to SwiftUI and keep access to the data in the Core Data stack, but move to SwiftData immediately. Since I'm doing a lot of rewriting, it makes sense to leap ahead rather than have to rewrite it in a year or two. Effort isn't an issue; I'm a tenacious SOB ;) But I have no idea how to do this, and can't find any examples on the net of this particular scenario. All I can find is how to start using SwiftData instead of Core Data in an app that's already written in Swift. So, how do I go about the migration without losing data? I guess I'll need to add a new Swift/SwiftUI target for the main app, but then how do I migrate the Core Data store over? In ObjC there's a lot of messing with stacks and the actual location of the model in the filesystem, but I doubt this is necessary in the new way of doing things? Any help would be appreciated. Thanks!
0
0
773
Feb ’24