Internal tooling for Mac utility for storage management.
Swift
98.7%
JSON
1.1%
Markdown
0.2%
//
// FullDiskAccessView.swift
// MUA
//
// Created by Mitchel Volkering on 21/12/2025.
//
import SwiftUI
/// A view that explains and guides users to grant Full Disk Access
/// Follows Apple's guidelines: inform user why access is needed, then open System Settings
struct FullDiskAccessView: View {
@ObservedObject var accessManager = FullDiskAccessManager.shared
@Environment(\.dismiss) private var dismiss
let onContinueWithoutAccess: () -> Void
let onAccessGranted: () -> Void
@State private var isCheckingAccess = false
var body: some View {
VStack(spacing: 0) {
// Header illustration
headerSection
// Content
ScrollView {
VStack(spacing: 24) {
explanationSection
stepsSection
noteSection
}
.padding(.horizontal, 40)
.padding(.vertical, 24)
}
Divider()
// Actions
actionButtons
}
.frame(width: 520, height: 580)
.background(Color(nsColor: .windowBackgroundColor))
}
private var headerSection: some View {
ZStack {
// Background gradient
LinearGradient(
colors: [.blue.opacity(0.15), .purple.opacity(0.1)],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
VStack(spacing: 16) {
// Icon with glass effect
ZStack {
Circle()
.fill(
.linearGradient(
colors: [.blue.opacity(0.7), .purple.opacity(0.5)],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.frame(width: 80, height: 80)
.overlay(
Circle()
.fill(
.linearGradient(
colors: [.white.opacity(0.4), .clear],
startPoint: .topLeading,
endPoint: .center
)
)
)
.overlay(
Circle()
.strokeBorder(.white.opacity(0.3), lineWidth: 1.5)
)
.shadow(color: .blue.opacity(0.3), radius: 15)
Image(systemName: "lock.shield.fill")
.font(.system(size: 36))
.foregroundStyle(.white)
}
Text("Full Disk Access Required")
.font(.title2)
.fontWeight(.semibold)
}
.padding(.vertical, 32)
}
.frame(height: 180)
}
private var explanationSection: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Why does MUA need this?")
.font(.headline)
Text("To accurately analyze disk usage across your entire system, MUA needs permission to read all files and folders. Without Full Disk Access, some system folders and application data will be hidden from the analysis.")
.font(.subheadline)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
// What we can access without FDA
VStack(alignment: .leading, spacing: 8) {
accessItem(granted: true, text: "Your Documents, Downloads, Desktop")
accessItem(granted: true, text: "Applications folder")
accessItem(granted: false, text: "Mail, Messages, Safari data")
accessItem(granted: false, text: "Other apps' private data")
accessItem(granted: false, text: "System caches and logs")
}
.padding(.top, 8)
}
}
private func accessItem(granted: Bool, text: String) -> some View {
HStack(spacing: 10) {
Image(systemName: granted ? "checkmark.circle.fill" : "xmark.circle.fill")
.foregroundStyle(granted ? .green : .orange)
Text(text)
.font(.subheadline)
.foregroundStyle(granted ? .primary : .secondary)
}
}
private var stepsSection: some View {
VStack(alignment: .leading, spacing: 16) {
Text("How to enable")
.font(.headline)
VStack(alignment: .leading, spacing: 12) {
stepRow(number: 1, text: "Click \"Open System Settings\" below")
stepRow(number: 2, text: "Find MUA in the list")
stepRow(number: 3, text: "Toggle the switch to enable access")
stepRow(number: 4, text: "Return to MUA (you may need to relaunch)")
}
}
}
private func stepRow(number: Int, text: String) -> some View {
HStack(alignment: .top, spacing: 12) {
Text("\(number)")
.font(.caption)
.fontWeight(.bold)
.foregroundStyle(.white)
.frame(width: 22, height: 22)
.background(Color.accentColor, in: Circle())
Text(text)
.font(.subheadline)
}
}
private var noteSection: some View {
HStack(spacing: 12) {
Image(systemName: "info.circle.fill")
.foregroundStyle(.blue)
Text("MUA never sends your data anywhere. All analysis happens locally on your Mac.")
.font(.caption)
.foregroundStyle(.secondary)
}
.padding()
.background(.blue.opacity(0.1), in: RoundedRectangle(cornerRadius: 10))
}
private var actionButtons: some View {
HStack(spacing: 12) {
Button("Continue Without Full Access") {
onContinueWithoutAccess()
}
.buttonStyle(.bordered)
Spacer()
if isCheckingAccess {
ProgressView()
.scaleEffect(0.8)
.padding(.horizontal, 8)
}
Button("I've Granted Access") {
isCheckingAccess = true
accessManager.refreshAccessStatus()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
isCheckingAccess = false
if accessManager.hasFullDiskAccess {
onAccessGranted()
}
}
}
.buttonStyle(.bordered)
Button("Open System Settings") {
accessManager.openFullDiskAccessSettings()
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}
#Preview {
FullDiskAccessView(
onContinueWithoutAccess: {},
onAccessGranted: {}
)
}
About
Internal tooling for Mac utility for storage management.
0 stars
0 forks