Why there is a working animation with ScrollView + ForEach of items removal, but there is none with List?
ScrollView + ForEach:
struct ContentView: View {
@State var items: [String] = Array(1...5).map(\.description)
var body: some View {
ScrollView(.vertical) {
ForEach(items, id: \.self) { item in
Text(String(item))
.frame(maxWidth: .infinity, minHeight: 50)
.background(.gray)
.onTapGesture {
withAnimation(.linear(duration: 0.1)) {
items = items.filter { $0 != item }
}
}
}
}
}
}
List:
struct ContentView: View {
@State var items: [String] = Array(1...5).map(\.description)
var body: some View {
List(items, id: \.self) { item in
Text(String(item))
.frame(maxWidth: .infinity, minHeight: 50)
.background(.gray)
.onTapGesture {
withAnimation(.linear(duration: 0.1)) {
items = items.filter { $0 != item }
}
}
}
}
}```
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I want to migrate from a Safari App Extension to a Safari Web Extension, but don't know how to get rid of the message, telling users that my extension can access their passwords. Here is a message which I see:
I was thinking that this might be because all Safari Web Extension get this type of access, but I have a Safari Web Extension which does not require such level of access:
Here is the manifest:
{
"manifest_version": 2,
"default_locale": "en",
"name": "__MSG_extension_name__",
"description": "__MSG_extension_description__",
"version": "1.1",
"icons": {
"48": "images/icon-48.png"
},
"background": {
"scripts": [
"background.js"
],
"persistent": true
},
"browser_action": {
"default_popup": "popup.html",
"default_icon": {
"16": "images/toolbar-icon-16.png"
}
},
"permissions": [
"nativeMessaging", "tabs"
]
}
and here is the Info.plist file:
Here is the entire code of the extension:
https://github.com/kopyl/web-extension-simplified
Consider the code from my previous question: https://developer.apple.com/forums/thread/776592
How can I change the background color of a focused item?
I just made a simple AppKit app, but don't know how to remove borders of rows when they're swiped.
SwiftUI's list does not have this problem though.
Attaching gif demo and code:
import SwiftUI
struct NSTableViewWrapper: NSViewRepresentable {
@State var data: [String]
class Coordinator: NSObject, NSTableViewDataSource, NSTableViewDelegate {
var parent: NSTableViewWrapper
weak var tableView: NSTableView?
init(parent: NSTableViewWrapper) {
self.parent = parent
}
func numberOfRows(in tableView: NSTableView) -> Int {
self.tableView = tableView
return parent.data.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("Cell"), owner: nil) as? NSTextField
?? NSTextField(labelWithString: "")
cell.identifier = NSUserInterfaceItemIdentifier("Cell")
cell.stringValue = parent.data[row]
cell.isBordered = false
return cell
}
func tableView(_ tableView: NSTableView, rowActionsForRow row: Int, edge: NSTableView.RowActionEdge) -> [NSTableViewRowAction] {
guard edge == .trailing else { return [] }
let deleteAction = NSTableViewRowAction(style: .destructive, title: "Delete") { action, index in
self.deleteRow(at: index, in: tableView)
}
return [deleteAction]
}
private func deleteRow(at index: Int, in tableView: NSTableView) {
guard index < parent.data.count else { return }
NSAnimationContext.runAnimationGroup({ context in
context.duration = 0.3
tableView.removeRows(at: IndexSet(integer: index), withAnimation: .slideUp)
}, completionHandler: {
DispatchQueue.main.async {
self.parent.data.remove(at: index)
tableView.reloadData()
}
})
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
func makeNSView(context: Context) -> NSScrollView {
let scrollView = NSScrollView()
let tableView = NSTableView()
let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Column"))
column.width = 200
tableView.addTableColumn(column)
tableView.delegate = context.coordinator
tableView.dataSource = context.coordinator
tableView.backgroundColor = .clear
tableView.headerView = nil
tableView.rowHeight = 50
tableView.style = .inset
scrollView.documentView = tableView
scrollView.hasVerticalScroller = true
scrollView.additionalSafeAreaInsets = .init(top: 0, left: 0, bottom: 6, right: 0)
return scrollView
}
func updateNSView(_ nsView: NSScrollView, context: Context) {
(nsView.documentView as? NSTableView)?.reloadData()
}
}
struct ContentView: View {
@State private var itemsString = Array(0..<40).map(\.description)
var body: some View {
NSTableViewWrapper(data: itemsString)
}
}
func createAppWindow() {
let window = NSWindow(
contentRect: .zero,
styleMask: [.titled],
backing: .buffered,
defer: false
)
window.title = "NSTableView from AppKit"
window.contentViewController = NSHostingController(rootView: ContentView())
window.setContentSize(NSSize(width: 759, height: 300))
window.center()
window.makeKeyAndOrderFront(nil)
}
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
createAppWindow()
}
}
let delegate = AppDelegate()
NSApplication.shared.delegate = delegate
NSApplication.shared.run()
I develop a tab manager extension: https://apps.apple.com/ua/app/tab-finder-for-safari/id6741719894
It's written purely in Swift. All Safari interactions are done solely inside a SFSafariExtensionHandler .
But now i'm considering adding some features from Google Chrome's Extension API like window switching.
Is it possible to add a background.js worker to my existing Safari App Extension to have access to the beginRequest method override inside SFSafariExtensionHandler?
Without converting my extension from Safari App Extension to Safari Web Extenion?
I'm building a macOS Google Chrome extension.
I need to be able to send messages from the Chrome extension to the macOS app
What's the set up flow?
I've heard about native messaging, but I struggle to implement it.
I've heard about XPC, but not sure JS can send messages to a macOS XPC service.
Here is a simple main.swift file of a macOS app:
import SwiftUI
struct ContentView: View {
@State private var selectedItem = 0
@FocusState private var isListFocused: Bool
var body: some View {
List(0..<40, id: \.self, selection: $selectedItem) { index in
Text("\(index)")
.padding()
.focusable()
}
.focused($isListFocused)
.onAppear {
isListFocused = true
}
}
}
func createAppWindow() {
let window = NSWindow(
contentRect: .zero,
styleMask: [.titled],
backing: .buffered,
defer: false
)
window.contentViewController = NSHostingController(rootView: ContentView())
window.setContentSize(NSSize(width: 759, height: 300))
window.center()
window.makeKeyAndOrderFront(nil)
}
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
createAppWindow()
}
}
let delegate = AppDelegate()
NSApplication.shared.delegate = delegate
NSApplication.shared.run()
Try to move the focus with a keyboard slowly as shown on the GIF attached and you'll see that the focus items don't sit in a List's view.
It's so bad seeing this light mode...
Can I ask Apple to give me an access to their frontend, so I can contribute to their repo with a dark mode feature?
Not asking Apple to make the dark theme, but rather asking them let me do it.
Hi. I'm a developer of Tab Finder (https://apps.apple.com/us/app/tab-finder/id6741719894)
My problem is that every time i switch from my first window to a second window, the tabs in the validateToolbarItem() are INcorrect on a first call, but when I switch back from the second window to my main window, the tabs are CORRECT even on a first call.
To demonstrate it, i recorded a video: https://youtu.be/RwskzrSJ8u0
To run the same sample extension from the video, you can get the code from this GitHub repo: https://github.com/kopyl/test-tabs-change
Its only purpose is to log URLs of an active page of all tabs.
The SafariExtensionHandler's code of the sample app is very simple:
import SafariServices
func printOpenTabsHost(in window: SFSafariWindow) async {
let tabs = await window.allTabs()
log("Logging tabs for a new window: \(window.hashValue)")
for tab in tabs {
let page = await tab.activePage()
let properties = await page?.properties()
let url = properties?.url
log(url?.absoluteString ?? "No URL")
}
}
class SafariExtensionViewController: SFSafariExtensionViewController {
static let shared = SafariExtensionViewController()
}
class SafariExtensionHandler: SFSafariExtensionHandler {
override func validateToolbarItem(in window: SFSafariWindow, validationHandler: @escaping ((Bool, String) -> Void)) {
Task {
await printOpenTabsHost(in: window)
}
validationHandler(true, "")
}
override func popoverViewController() -> SFSafariExtensionViewController {
return SafariExtensionViewController.shared
}
}
Could you please tell if i'm missing something and how to see the actual tabs inside the overridden validateToolbarItem call of the SafariExtensionHandler (or in any other way, I'm okay with any implementation as long as it works).
Topic:
Safari & Web
SubTopic:
General
Tags:
Extensions
Safari Services
Safari and Web
Safari Extensions
Consider this simple miniature of my iOS Share Extension:
import SwiftUI
import Photos
class ShareViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let itemProviders = (extensionContext?.inputItems.first as? NSExtensionItem)?.attachments {
let hostingView = UIHostingController(rootView: ShareView(extensionContext: extensionContext, itemProviders: itemProviders))
hostingView.view.frame = view.frame
view.addSubview(hostingView.view)
}
}
}
struct ShareView: View {
var extensionContext: NSExtensionContext?
var itemProviders: [NSItemProvider]
var body: some View {
VStack{}
.task{
await extractItems()
}
}
func extractItems() async {
guard let itemProvider = itemProviders.first else { return }
guard itemProvider.hasItemConformingToTypeIdentifier(UTType.url.identifier) else { return }
do {
guard let url = try await itemProvider.loadItem(forTypeIdentifier: UTType.url.identifier) as? URL else { return }
try await downloadAndSaveMedia(reelURL: url.absoluteString)
extensionContext?.completeRequest(returningItems: [])
}
catch {}
}
}
On the line 34
guard let url = try await itemProvider.loadItem
...
I get these warnings:
Passing argument of non-sendable type '[AnyHashable : Any]?' outside of main actor-isolated context may introduce data races; this is an error in the Swift 6 language mode
1.1. Generic enum 'Optional' does not conform to the 'Sendable' protocol (Swift.Optional)
Passing argument of non-sendable type 'NSItemProvider' outside of main actor-isolated context may introduce data races; this is an error in the Swift 6 language mode
2.2. Class 'NSItemProvider' does not conform to the 'Sendable' protocol (Foundation.NSItemProvider)
How to fix them in Xcode 16?
Please provide a solution which works, and not the one which might (meaning you run the same code in Xcode, add your solution and see no warnings).
I tried
Decorating everything with @MainActors
Using @MainActor in the .task
@preconcurrency import
Decorating everything with @preconcurrency
Playing around with nonisolated
Consider this code:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
EmptyView()
}
}
}
Which looks like this:
How can I prevent the sidebar from being resized by a mouse and from being hidden?
P.S. Can consider using AppKit if it can help.
How can I put one list item at the top and another one at the bottom, retaining the NavigationView functionality?
Here is a simple app:
struct ContentView: View {
var body: some View {
NavigationView {
Sidebar()
}
}
}
struct Sidebar: View {
@State private var isActive = true
var body: some View {
List {
NavigationLink(isActive: $isActive) {
HomeView()
} label: {
Text("Home")
}
NavigationLink {
SettingsView()
} label: {
Text("Settings")
}
}
}
}
struct HomeView: View {
var body: some View {
VStack {}
.navigationTitle("Home")
}
}
struct SettingsView: View {
var body: some View {
VStack {}
.navigationTitle("Settings")
}
}
Which looks like this:
My initial though was to put a Spacer() between each NavigationLink, but it results in an unexpected view:
What i can do:
Place an empty VStack between each NavigationLink with a hard-coded height like this:
VStack {}.frame(height: 275)
Which looks like this:
But it wouldn't work if a user would want to increase the height of a window.
I could disable window resizing which is kind of fine, but not the most optimal.
Another obvious option was to replace the List with a VStack, but with this approach the styling of the NavigationLink gets broken and it does not get highlighted when I click on it.
It looks like this:
P.S. I know that NavigationView is deprecated, but i want to support macOS 12.0.
I can't upload my macOS app to app store connect.
Each time i try to upload, i see this message:
Provisioning profile failed qualification
Profile doesn't support App Groups.
An empty app without an app group uploads fine, but if i add an app group to it, it does not upload.
Topic:
Code Signing
SubTopic:
Certificates, Identifiers & Profiles
Tags:
Entitlements
Notarization
Signing Certificates
Code Signing
I have a Safari App Extension which allows users to switch between last open tabs with a shortcut option+tab in the same way it's possible to switch between last open apps with command+tab.
Here is how i do it:
I inject a content script on all websites which has the only thing – key listener for option+tab presses.
When a user presses option+tab, that keyboard listener detects it and sends a message to the Safari Handler.
Then Safari Handler sends a message to the containing app and it shows a panel with last open tabs.
This approach has a problem: it shows a message to a user in settings: "Can read sensitive info from web pages, including passwords..."
Which is bad, because in reality i don't read passwords.
If i remove SFSafariContentScript key in the Safari App Extension target's Info.plist, then this message about reading sensitive data disappears, but then i loose the ability to open the tabs panel.
How can I open my app window with a shortcut without frightening a user?
It's possible to listen to global key presses, but that would require a user to grant the app permissions of Accessibility (Privacy & Security) in macOS system settings, which also sounds shady.
I know an app which does not require an Accessibility permission: https://apps.apple.com/ua/app/tabback-lite/id6469582909 and at the same time it does not tell a user about reading sensitive data in the extension settings.
Here is my app: https://apps.apple.com/ua/app/tab-finder/id6741719894 It's open-source: https://github.com/kopyl/safari-tab-switcher
Sometimes the Safari App Extension i'm developing does not show up in my Safari Extensions unless i change mu bundle identifier to some unique name.
Even if I delete all other apps i've built with the same (and different) bundle ID, the extension is still does not show up.
The only solution for me is to always change the bundle ID.
For example:
Now it is "kopyl.tab-finder-10"
If i change it to "kopyl.tab-finder-11", the extension does show up in the Safari extensions settings page again.
Is there any other way to fix it?