Improve tvOS settings UI styling and navigation

- Add TVOSPlainToggleStyle for cleaner toggle appearance on tvOS
- Remove focus overlays from settings navigation links and buttons
- Apply plain button and list styles across all settings screens
- Implement custom system controls picker for tvOS to avoid focus overlay
- Update SettingsPickerModifier with platform-specific styling

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Arkadiusz Fal
2025-11-09 15:36:41 +01:00
parent 495dcec874
commit e0ca48fd44
13 changed files with 166 additions and 9 deletions

View File

@@ -43,7 +43,7 @@ struct HomeView: View {
AccentButton(imageSystemName: "ellipsis") { AccentButton(imageSystemName: "ellipsis") {
NavigationModel.shared.presentingOpenVideos = true NavigationModel.shared.presentingOpenVideos = true
} }
.frame(maxWidth: 40) .frame(maxWidth: 40)
} }
} }
#endif #endif
@@ -74,6 +74,7 @@ struct HomeView: View {
#if os(tvOS) #if os(tvOS)
.font(.caption) .font(.caption)
.imageScale(.small) .imageScale(.small)
.foregroundColor(.primary)
#endif #endif
#endif #endif
} }

View File

@@ -31,7 +31,9 @@ struct AdvancedSettings: View {
List { List {
advancedSettings advancedSettings
} }
#if os(iOS) #if os(tvOS)
.listStyle(.plain)
#elseif os(iOS)
.sheet(isPresented: $presentingShareSheet) { .sheet(isPresented: $presentingShareSheet) {
ShareSheet(activityItems: filesToShare) ShareSheet(activityItems: filesToShare)
.id("logs-\(filesToShare.count)") .id("logs-\(filesToShare.count)")
@@ -41,6 +43,8 @@ struct AdvancedSettings: View {
#endif #endif
} }
#if os(tvOS) #if os(tvOS)
.buttonStyle(.plain)
.toggleStyle(TVOSPlainToggleStyle())
.frame(maxWidth: 1000) .frame(maxWidth: 1000)
#endif #endif
.navigationTitle("Advanced") .navigationTitle("Advanced")

View File

@@ -52,10 +52,14 @@ struct BrowsingSettings: View {
} }
#if os(iOS) #if os(iOS)
.listStyle(.insetGrouped) .listStyle(.insetGrouped)
#elseif os(tvOS)
.listStyle(.plain)
#endif #endif
#endif #endif
} }
#if os(tvOS) #if os(tvOS)
.buttonStyle(.plain)
.toggleStyle(TVOSPlainToggleStyle())
.frame(maxWidth: 1200) .frame(maxWidth: 1200)
#else #else
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
@@ -111,6 +115,9 @@ struct BrowsingSettings: View {
NavigationLink(destination: LazyView(HomeSettings())) { NavigationLink(destination: LazyView(HomeSettings())) {
Text("Home Settings") Text("Home Settings")
} }
#if os(tvOS)
.buttonStyle(.plain)
#endif
#endif #endif
} }
} }

View File

@@ -29,9 +29,16 @@ struct HistorySettings: View {
List { List {
sections sections
} }
#if os(tvOS)
.listStyle(.plain)
#elseif os(iOS)
.listStyle(.insetGrouped)
#endif
#endif #endif
} }
#if os(tvOS) #if os(tvOS)
.buttonStyle(.plain)
.toggleStyle(TVOSPlainToggleStyle())
.frame(maxWidth: 1000) .frame(maxWidth: 1000)
#elseif os(iOS) #elseif os(iOS)
.listStyle(.insetGrouped) .listStyle(.insetGrouped)

View File

@@ -22,7 +22,9 @@ struct LocationsSettings: View {
List { List {
settings settings
} }
#if os(iOS) #if os(tvOS)
.listStyle(.plain)
#elseif os(iOS)
.listStyle(.insetGrouped) .listStyle(.insetGrouped)
#endif #endif
#endif #endif
@@ -42,6 +44,8 @@ struct LocationsSettings: View {
InstanceForm(savedInstanceID: $savedFormInstanceID) InstanceForm(savedInstanceID: $savedFormInstanceID)
} }
#if os(tvOS) #if os(tvOS)
.buttonStyle(.plain)
.toggleStyle(TVOSPlainToggleStyle())
.frame(maxWidth: 1000) .frame(maxWidth: 1000)
#endif #endif
.navigationTitle("Locations") .navigationTitle("Locations")

View File

@@ -53,9 +53,16 @@ struct PlayerControlsSettings: View {
List { List {
sections sections
} }
#if os(tvOS)
.listStyle(.plain)
#elseif os(iOS)
.listStyle(.insetGrouped)
#endif
#endif #endif
} }
#if os(tvOS) #if os(tvOS)
.buttonStyle(.plain)
.toggleStyle(TVOSPlainToggleStyle())
.frame(maxWidth: 1000) .frame(maxWidth: 1000)
#elseif os(iOS) #elseif os(iOS)
.listStyle(.insetGrouped) .listStyle(.insetGrouped)
@@ -143,6 +150,45 @@ struct PlayerControlsSettings: View {
#endif #endif
} }
#if os(tvOS)
// Custom implementation for tvOS to avoid focus overlay
return VStack(alignment: .leading, spacing: 0) {
Text("System controls buttons")
.font(.headline)
.padding(.vertical, 8)
Button(action: { systemControlsCommands = .seek }) {
HStack {
Text(labelText("Seek".localized()))
Spacer()
if systemControlsCommands == .seek {
Image(systemName: "checkmark")
.foregroundColor(.accentColor)
}
}
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.padding(.vertical, 4)
Button(action: {
systemControlsCommands = .restartAndAdvanceToNext
player.updateRemoteCommandCenter()
}) {
HStack {
Text(labelText("Restart/Play next".localized()))
Spacer()
if systemControlsCommands == .restartAndAdvanceToNext {
Image(systemName: "checkmark")
.foregroundColor(.accentColor)
}
}
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.padding(.vertical, 4)
}
#else
return Picker("System controls buttons", selection: $systemControlsCommands) { return Picker("System controls buttons", selection: $systemControlsCommands) {
Text(labelText("Seek".localized())).tag(SystemControlsCommands.seek) Text(labelText("Seek".localized())).tag(SystemControlsCommands.seek)
Text(labelText("Restart/Play next".localized())).tag(SystemControlsCommands.restartAndAdvanceToNext) Text(labelText("Restart/Play next".localized())).tag(SystemControlsCommands.restartAndAdvanceToNext)
@@ -151,6 +197,7 @@ struct PlayerControlsSettings: View {
player.updateRemoteCommandCenter() player.updateRemoteCommandCenter()
} }
.modifier(SettingsPickerModifier()) .modifier(SettingsPickerModifier())
#endif
} }
@ViewBuilder private var controlsLayoutFooter: some View { @ViewBuilder private var controlsLayoutFooter: some View {

View File

@@ -69,9 +69,16 @@ struct PlayerSettings: View {
List { List {
sections sections
} }
#if os(tvOS)
.listStyle(.plain)
#elseif os(iOS)
.listStyle(.insetGrouped)
#endif
#endif #endif
} }
#if os(tvOS) #if os(tvOS)
.buttonStyle(.plain)
.toggleStyle(TVOSPlainToggleStyle())
.frame(maxWidth: 1000) .frame(maxWidth: 1000)
#elseif os(iOS) #elseif os(iOS)
.listStyle(.insetGrouped) .listStyle(.insetGrouped)

View File

@@ -24,12 +24,19 @@ struct QualitySettings: View {
List { List {
sections sections
} }
#if os(tvOS)
.listStyle(.plain)
#elseif os(iOS)
.listStyle(.insetGrouped)
#endif
#endif #endif
} }
.sheet(isPresented: $presentingProfileForm) { .sheet(isPresented: $presentingProfileForm) {
QualityProfileForm(qualityProfileID: $editedProfileID) QualityProfileForm(qualityProfileID: $editedProfileID)
} }
#if os(tvOS) #if os(tvOS)
.buttonStyle(.plain)
.toggleStyle(TVOSPlainToggleStyle())
.frame(maxWidth: 1000) .frame(maxWidth: 1000)
#elseif os(iOS) #elseif os(iOS)
.listStyle(.insetGrouped) .listStyle(.insetGrouped)

View File

@@ -164,6 +164,7 @@ struct SettingsView: View {
Text("Not Selected") Text("Not Selected")
} }
} }
.buttonStyle(.plain)
} }
Divider() Divider()
} }
@@ -175,30 +176,45 @@ struct SettingsView: View {
} label: { } label: {
Label("Browsing", systemImage: "list.and.film").labelStyle(SettingsLabel()) Label("Browsing", systemImage: "list.and.film").labelStyle(SettingsLabel())
} }
#if os(tvOS)
.buttonStyle(.plain)
#endif
NavigationLink { NavigationLink {
PlayerSettings() PlayerSettings()
} label: { } label: {
Label("Player", systemImage: "play.rectangle").labelStyle(SettingsLabel()) Label("Player", systemImage: "play.rectangle").labelStyle(SettingsLabel())
} }
#if os(tvOS)
.buttonStyle(.plain)
#endif
NavigationLink { NavigationLink {
PlayerControlsSettings() PlayerControlsSettings()
} label: { } label: {
Label("Controls", systemImage: "hand.tap").labelStyle(SettingsLabel()) Label("Controls", systemImage: "hand.tap").labelStyle(SettingsLabel())
} }
#if os(tvOS)
.buttonStyle(.plain)
#endif
NavigationLink { NavigationLink {
QualitySettings() QualitySettings()
} label: { } label: {
Label("Quality", systemImage: "4k.tv").labelStyle(SettingsLabel()) Label("Quality", systemImage: "4k.tv").labelStyle(SettingsLabel())
} }
#if os(tvOS)
.buttonStyle(.plain)
#endif
NavigationLink { NavigationLink {
HistorySettings() HistorySettings()
} label: { } label: {
Label("History", systemImage: "clock.arrow.circlepath").labelStyle(SettingsLabel()) Label("History", systemImage: "clock.arrow.circlepath").labelStyle(SettingsLabel())
} }
#if os(tvOS)
.buttonStyle(.plain)
#endif
if !accounts.isEmpty { if !accounts.isEmpty {
NavigationLink { NavigationLink {
@@ -206,6 +222,9 @@ struct SettingsView: View {
} label: { } label: {
Label("SponsorBlock", systemImage: "dollarsign.circle").labelStyle(SettingsLabel()) Label("SponsorBlock", systemImage: "dollarsign.circle").labelStyle(SettingsLabel())
} }
#if os(tvOS)
.buttonStyle(.plain)
#endif
} }
NavigationLink { NavigationLink {
@@ -213,12 +232,18 @@ struct SettingsView: View {
} label: { } label: {
Label("Locations", systemImage: "globe").labelStyle(SettingsLabel()) Label("Locations", systemImage: "globe").labelStyle(SettingsLabel())
} }
#if os(tvOS)
.buttonStyle(.plain)
#endif
NavigationLink { NavigationLink {
AdvancedSettings() AdvancedSettings()
} label: { } label: {
Label("Advanced", systemImage: "wrench.and.screwdriver").labelStyle(SettingsLabel()) Label("Advanced", systemImage: "wrench.and.screwdriver").labelStyle(SettingsLabel())
} }
#if os(tvOS)
.buttonStyle(.plain)
#endif
} }
#if os(tvOS) #if os(tvOS)
.padding(.horizontal, 20) .padding(.horizontal, 20)
@@ -232,6 +257,9 @@ struct SettingsView: View {
} label: { } label: {
Label("Help", systemImage: "questionmark.circle").labelStyle(SettingsLabel()) Label("Help", systemImage: "questionmark.circle").labelStyle(SettingsLabel())
} }
#if os(tvOS)
.buttonStyle(.plain)
#endif
} }
#if os(tvOS) #if os(tvOS)
.padding(.horizontal, 20) .padding(.horizontal, 20)
@@ -287,6 +315,7 @@ struct SettingsView: View {
Label("Import Settings", systemImage: "square.and.arrow.down") Label("Import Settings", systemImage: "square.and.arrow.down")
.labelStyle(SettingsLabel()) .labelStyle(SettingsLabel())
} }
.buttonStyle(.plain)
.padding(.horizontal, 20) .padding(.horizontal, 20)
#else #else
Button(action: importSettings) { Button(action: importSettings) {

View File

@@ -23,9 +23,14 @@ struct SponsorBlockSettings: View {
List { List {
sections sections
} }
#if os(tvOS)
.listStyle(.plain)
#endif
#endif #endif
} }
#if os(tvOS) #if os(tvOS)
.buttonStyle(.plain)
.toggleStyle(TVOSPlainToggleStyle())
.frame(maxWidth: 1000) .frame(maxWidth: 1000)
#else #else
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)

View File

@@ -0,0 +1,17 @@
import SwiftUI
#if os(tvOS)
struct TVOSPlainToggleStyle: ToggleStyle {
func makeBody(configuration: Configuration) -> some View {
Button(action: { configuration.isOn.toggle() }) {
HStack {
configuration.label
Spacer()
Image(systemName: configuration.isOn ? "checkmark.circle.fill" : "circle")
.foregroundColor(configuration.isOn ? .accentColor : .secondary)
}
}
.buttonStyle(.plain)
}
}
#endif

View File

@@ -3,14 +3,28 @@ import SwiftUI
struct SettingsPickerModifier: ViewModifier { struct SettingsPickerModifier: ViewModifier {
func body(content: Content) -> some View { func body(content: Content) -> some View {
content
#if os(tvOS) #if os(tvOS)
.pickerStyle(.inline) content
#endif .pickerStyle(.inline)
#if os(iOS) .onAppear {
.pickerStyle(.automatic) // Force refresh to apply button style to picker options
}
#elseif os(iOS)
content
.pickerStyle(.automatic)
#else #else
.labelsHidden() content
.labelsHidden()
#endif #endif
} }
} }
#if os(tvOS)
// Extension to help remove picker row backgrounds
extension View {
func pickerRowStyle() -> some View {
self.buttonStyle(.plain)
.listRowBackground(Color.clear)
}
}
#endif

View File

@@ -941,6 +941,9 @@
37E04C0F275940FB00172673 /* VerticalScrollingFix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E04C0E275940FB00172673 /* VerticalScrollingFix.swift */; }; 37E04C0F275940FB00172673 /* VerticalScrollingFix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E04C0E275940FB00172673 /* VerticalScrollingFix.swift */; };
37E084AC2753D95F00039B7D /* AccountsNavigationLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */; }; 37E084AC2753D95F00039B7D /* AccountsNavigationLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */; };
37E084AD2753D95F00039B7D /* AccountsNavigationLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */; }; 37E084AD2753D95F00039B7D /* AccountsNavigationLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */; };
37E32DD52EC0D63600A63F29 /* TVOSPlainToggleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E32DD42EC0D63600A63F29 /* TVOSPlainToggleStyle.swift */; };
37E32DD62EC0D63600A63F29 /* TVOSPlainToggleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E32DD42EC0D63600A63F29 /* TVOSPlainToggleStyle.swift */; };
37E32DD72EC0D63600A63F29 /* TVOSPlainToggleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E32DD42EC0D63600A63F29 /* TVOSPlainToggleStyle.swift */; };
37E64DD126D597EB00C71877 /* SubscribedChannelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscribedChannelsModel.swift */; }; 37E64DD126D597EB00C71877 /* SubscribedChannelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscribedChannelsModel.swift */; };
37E64DD226D597EB00C71877 /* SubscribedChannelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscribedChannelsModel.swift */; }; 37E64DD226D597EB00C71877 /* SubscribedChannelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscribedChannelsModel.swift */; };
37E64DD326D597EB00C71877 /* SubscribedChannelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscribedChannelsModel.swift */; }; 37E64DD326D597EB00C71877 /* SubscribedChannelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscribedChannelsModel.swift */; };
@@ -1502,6 +1505,7 @@
37E04C0E275940FB00172673 /* VerticalScrollingFix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerticalScrollingFix.swift; sourceTree = "<group>"; }; 37E04C0E275940FB00172673 /* VerticalScrollingFix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerticalScrollingFix.swift; sourceTree = "<group>"; };
37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsNavigationLink.swift; sourceTree = "<group>"; }; 37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsNavigationLink.swift; sourceTree = "<group>"; };
37E21DC52CDE528A008DF47C /* ta */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ta; path = ta.lproj/Localizable.strings; sourceTree = "<group>"; }; 37E21DC52CDE528A008DF47C /* ta */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ta; path = ta.lproj/Localizable.strings; sourceTree = "<group>"; };
37E32DD42EC0D63600A63F29 /* TVOSPlainToggleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVOSPlainToggleStyle.swift; sourceTree = "<group>"; };
37E64DD026D597EB00C71877 /* SubscribedChannelsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscribedChannelsModel.swift; sourceTree = "<group>"; }; 37E64DD026D597EB00C71877 /* SubscribedChannelsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscribedChannelsModel.swift; sourceTree = "<group>"; };
37E6D79B2944AE1A00550C3D /* FeedModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedModel.swift; sourceTree = "<group>"; }; 37E6D79B2944AE1A00550C3D /* FeedModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedModel.swift; sourceTree = "<group>"; };
37E6D79F2944CD3800550C3D /* CacheStatusHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheStatusHeader.swift; sourceTree = "<group>"; }; 37E6D79F2944CD3800550C3D /* CacheStatusHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheStatusHeader.swift; sourceTree = "<group>"; };
@@ -1937,6 +1941,7 @@
376BE50627347B57009AD608 /* SettingsHeader.swift */, 376BE50627347B57009AD608 /* SettingsHeader.swift */,
37B044B626F7AB9000E1419D /* SettingsView.swift */, 37B044B626F7AB9000E1419D /* SettingsView.swift */,
374C053427242D9F009BDDBE /* SponsorBlockSettings.swift */, 374C053427242D9F009BDDBE /* SponsorBlockSettings.swift */,
37E32DD42EC0D63600A63F29 /* TVOSPlainToggleStyle.swift */,
); );
path = Settings; path = Settings;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -3303,6 +3308,7 @@
373031F528383A89000CFD59 /* PiPDelegate.swift in Sources */, 373031F528383A89000CFD59 /* PiPDelegate.swift in Sources */,
37F5E8BA291BEF69006C15F5 /* BaseCacheModel.swift in Sources */, 37F5E8BA291BEF69006C15F5 /* BaseCacheModel.swift in Sources */,
371AC09F294D13AA0085989E /* UnwatchedFeedCountModel.swift in Sources */, 371AC09F294D13AA0085989E /* UnwatchedFeedCountModel.swift in Sources */,
37E32DD72EC0D63600A63F29 /* TVOSPlainToggleStyle.swift in Sources */,
370015A928BBAE7F000149FD /* ProgressBar.swift in Sources */, 370015A928BBAE7F000149FD /* ProgressBar.swift in Sources */,
37C3A24927235FAA0087A57A /* ChannelPlaylistCell.swift in Sources */, 37C3A24927235FAA0087A57A /* ChannelPlaylistCell.swift in Sources */,
377E17142928265900894889 /* ListRowSeparator+Backport.swift in Sources */, 377E17142928265900894889 /* ListRowSeparator+Backport.swift in Sources */,
@@ -3529,6 +3535,7 @@
37F961A027BD90BB00058149 /* PlayerBackendType.swift in Sources */, 37F961A027BD90BB00058149 /* PlayerBackendType.swift in Sources */,
3776924F294630110055EC18 /* ChannelAvatarView.swift in Sources */, 3776924F294630110055EC18 /* ChannelAvatarView.swift in Sources */,
37BA221229526A19000DAD1F /* ControlsGradientView.swift in Sources */, 37BA221229526A19000DAD1F /* ControlsGradientView.swift in Sources */,
37E32DD62EC0D63600A63F29 /* TVOSPlainToggleStyle.swift in Sources */,
37BC50AD2778BCBA00510953 /* HistoryModel.swift in Sources */, 37BC50AD2778BCBA00510953 /* HistoryModel.swift in Sources */,
37A362BB2953707F00BDF328 /* ClearQueueButton.swift in Sources */, 37A362BB2953707F00BDF328 /* ClearQueueButton.swift in Sources */,
3752069E285E910600CA655F /* ChapterView.swift in Sources */, 3752069E285E910600CA655F /* ChapterView.swift in Sources */,
@@ -3854,6 +3861,7 @@
377692582946476F0055EC18 /* ChannelPlaylistsCacheModel.swift in Sources */, 377692582946476F0055EC18 /* ChannelPlaylistsCacheModel.swift in Sources */,
371B7E5E27596B8400D21217 /* Comment.swift in Sources */, 371B7E5E27596B8400D21217 /* Comment.swift in Sources */,
37732FF22703A26300F04329 /* AccountValidationStatus.swift in Sources */, 37732FF22703A26300F04329 /* AccountValidationStatus.swift in Sources */,
37E32DD52EC0D63600A63F29 /* TVOSPlainToggleStyle.swift in Sources */,
371CC76E29466F5A00979C1A /* AccountsViewModel.swift in Sources */, 371CC76E29466F5A00979C1A /* AccountsViewModel.swift in Sources */,
3756C2AC2861151C00E4B059 /* NetworkStateModel.swift in Sources */, 3756C2AC2861151C00E4B059 /* NetworkStateModel.swift in Sources */,
37F5E8B8291BE9D0006C15F5 /* URLBookmarkModel.swift in Sources */, 37F5E8B8291BE9D0006C15F5 /* URLBookmarkModel.swift in Sources */,