Skip to content

Instantly share code, notes, and snippets.

@kylehowells
Created May 4, 2022 23:24
Show Gist options
  • Save kylehowells/a194288cce9b2501c8f2f26f0c527829 to your computer and use it in GitHub Desktop.
Save kylehowells/a194288cce9b2501c8f2f26f0c527829 to your computer and use it in GitHub Desktop.
UIViewController subclass to show both the Settings app displayed free space, and the real free space.
//
// FreeSpaceViewController.swift
// Free Space
//
// Created by Kyle Howells on 04/05/2022.
//
import UIKit
class FreeSpaceViewController: UIViewController {
let byteFormatter:ByteCountFormatter = {
let formatter = ByteCountFormatter()
formatter.allowedUnits = .useAll
formatter.countStyle = .file
formatter.includesUnit = true
formatter.isAdaptive = true
return formatter
}()
private let textView:UITextView = {
let textView = UITextView()
textView.backgroundColor = UIColor.white
textView.textColor = UIColor.black
textView.font = UIFont.systemFont(ofSize: 14, weight: .medium)
textView.isEditable = false
textView.contentInsetAdjustmentBehavior = .never
return textView
}()
// MARK: - View Setup
let settingsTitleLabel:UILabel = {
let l = UILabel()
l.font = UIFont.systemFont(ofSize: 16, weight: .medium)
l.text = "Settings App (Important Files)"
return l
}()
let settingsStorageBar:KHFileStorageView = KHFileStorageView()
let realTitleLabel:UILabel = {
let l = UILabel()
l.font = UIFont.systemFont(ofSize: 16, weight: .medium)
l.text = "Real Storage Usage"
return l
}()
let realStorageBar:KHFileStorageView = KHFileStorageView()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// formatter.string(fromByteCount: 123456789000) // '123.46 GB'
// formatter.string(fromByteCount: 0) // 'Zero KB'
// self.byteFormatter
self.view.addSubview(self.textView)
self.view.addSubview(self.settingsStorageBar)
self.view.addSubview(self.settingsTitleLabel)
self.view.addSubview(self.realStorageBar)
self.view.addSubview(self.realTitleLabel)
var text = "\n"
text += "Total Space: \( self.byteFormatter.string(fromByteCount: Int64(getTotalSpace() ?? 0))) \n"
text += "Total Real Free Space: \( self.byteFormatter.string(fromByteCount: Int64(getRealFreeSpace() ?? 0))) \n"
text += "Total Important Free Space: \( self.byteFormatter.string(fromByteCount: getImportantFreeSpace() ?? 0)) \n"
text += "Total Opportunistic Free Space: \( self.byteFormatter.string(fromByteCount: getOpportunisticFreeSpace() ?? 0)) \n"
self.textView.text = text
guard let _totalSpace = getTotalSpace() else { return }
guard let _freeSpace = getRealFreeSpace() else { return }
guard let importantFreeSpace = getImportantFreeSpace() else { return }
//guard let opportunisticFreeSpace = getOpportunisticFreeSpace() else { return }
let totalSpace = Int64(_totalSpace)
let freeSpace = Int64(_freeSpace)
self.settingsStorageBar.totalSpace = totalSpace
self.settingsStorageBar.usedSpace = (totalSpace - importantFreeSpace)
self.realStorageBar.totalSpace = totalSpace
self.realStorageBar.usedSpace = (totalSpace - freeSpace)
}
// MARK: - Layout
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let size = self.view.bounds.size
//let safeArea = self.view.safeAreaInsets
let width = size.width * 0.9
// - Settings
self.settingsTitleLabel.frame = {
var frame = CGRect()
frame.size.height = self.settingsTitleLabel.intrinsicContentSize.height
frame.size.width = width
frame.origin.x = (size.width - frame.width) * 0.5
frame.origin.y = (size.height * 0.3)
return frame
}()
self.settingsStorageBar.frame = {
var frame = CGRect()
frame.size.height = self.settingsStorageBar.intrinsicContentSize.height
frame.size.width = width
frame.origin.x = (size.width - frame.width) * 0.5
frame.origin.y = self.settingsTitleLabel.frame.maxY + 10
return frame
}()
// - Real
self.realTitleLabel.frame = {
var frame = CGRect()
frame.size.height = self.settingsTitleLabel.intrinsicContentSize.height
frame.size.width = width
frame.origin.x = (size.width - frame.width) * 0.5
frame.origin.y = self.settingsStorageBar.frame.maxY + 40
return frame
}()
self.realStorageBar.frame = {
var frame = CGRect()
frame.size.width = width
frame.size.height = self.realStorageBar.intrinsicContentSize.height
frame.origin.x = (size.width - frame.width) * 0.5
frame.origin.y = self.realTitleLabel.frame.maxY + 10
return frame
}()
self.textView.frame = {
var frame = CGRect()
frame.origin.y = self.realStorageBar.frame.maxY + 30
frame.size.height = size.height - frame.origin.y
frame.size.width = width
frame.origin.x = (size.width - frame.width) * 0.5
return frame
}()
}
}
// MARK: - Get Free Space
func getTotalSpace() -> Int? {
let fileURL = URL(fileURLWithPath: NSHomeDirectory() as String)
do {
let values = try fileURL.resourceValues(forKeys: [.volumeTotalCapacityKey])
if let capacity = values.volumeTotalCapacity {
print("Total capacity: \(capacity)")
return capacity
} else {
print("Capacity is unavailable")
}
} catch {
print("Error retrieving capacity: \(error.localizedDescription)")
}
return nil
}
func getRealFreeSpace() -> Int? {
let fileURL = URL(fileURLWithPath: NSHomeDirectory() as String)
do {
let values = try fileURL.resourceValues(forKeys: [.volumeAvailableCapacityKey])
if let capacity = values.volumeAvailableCapacity {
print("Available capacity: \(capacity)")
return capacity
} else {
print("Capacity is unavailable")
}
} catch {
print("Error retrieving capacity: \(error.localizedDescription)")
}
return nil
}
func getImportantFreeSpace() -> Int64? {
let fileURL = URL(fileURLWithPath: NSHomeDirectory() as String)
do {
let values = try fileURL.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey])
if let capacity: Int64 = values.volumeAvailableCapacityForImportantUsage {
print("Available capacity for important usage: \(capacity)")
return capacity
} else {
print("Capacity is unavailable")
}
} catch {
print("Error retrieving capacity: \(error.localizedDescription)")
}
return nil
}
func getOpportunisticFreeSpace() -> Int64? {
let fileURL = URL(fileURLWithPath: NSHomeDirectory() as String)
do {
let values = try fileURL.resourceValues(forKeys: [.volumeAvailableCapacityForOpportunisticUsageKey])
if let capacity: Int64 = values.volumeAvailableCapacityForOpportunisticUsage {
print("Available capacity for important usage: \(capacity)")
return capacity
} else {
print("Capacity is unavailable")
}
} catch {
print("Error retrieving capacity: \(error.localizedDescription)")
}
return nil
}
//
// KHFileStorageView.swift
// Free Space
//
// Created by Kyle Howells on 05/05/2022.
//
import Foundation
import UIKit
class KHFileStorageView: UIView {
// MARK: - Properties
var totalSpace:Int64 = 100 {
didSet {
self.update()
}
}
var usedSpace:Int64 = 50 {
didSet {
self.update()
}
}
let byteFormatter:ByteCountFormatter = {
let formatter = ByteCountFormatter()
formatter.allowedUnits = .useAll
formatter.countStyle = .file
formatter.includesUnit = true
formatter.isAdaptive = true
return formatter
}()
// MARK: - Views
let usedBackgroundView:UIView = {
let view = UIView()
view.backgroundColor = UIColor(red: 44.0/255.0, green: 67.0/255.0, blue: 136.0/255.0, alpha: 1.0)
return view
}()
let usedSpaceLabel:UILabel = {
let l = UILabel()
l.textColor = UIColor(red: 44.0/255.0, green: 67.0/255.0, blue: 136.0/255.0, alpha: 1.0)
l.font = UIFont.systemFont(ofSize: 13, weight: .medium)
return l
}()
let freeBackgroundView:UIView = {
let view = UIView()
view.backgroundColor = UIColor(red: 206.0/255.0, green: 206.0/255.0, blue: 207.0/255.0, alpha: 1.0)
return view
}()
let freeSpaceLabel:UILabel = {
let l = UILabel()
l.textColor = UIColor(red: 206.0/255.0, green: 206.0/255.0, blue: 207.0/255.0, alpha: 1.0)
l.font = UIFont.systemFont(ofSize: 13, weight: .medium)
return l
}()
// MARK: - Setup
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.commonInit()
}
func commonInit() {
self.addSubview(self.freeSpaceLabel)
self.addSubview(self.freeBackgroundView)
self.addSubview(self.usedSpaceLabel)
self.addSubview(self.usedBackgroundView)
}
private func update() {
let usedSpaceString = self.byteFormatter.string(fromByteCount: self.usedSpace)
self.usedSpaceLabel.text = "Used: \(usedSpaceString)"
let freeSpaceString = self.byteFormatter.string(fromByteCount: self.totalSpace - self.usedSpace)
self.freeSpaceLabel.text = "Free: \(freeSpaceString)"
self.setNeedsLayout()
}
// MARK: - Size
private let barHeight:CGFloat = 30
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: size.width, height: self.barHeight + self.freeSpaceLabel.font.lineHeight)
}
override var intrinsicContentSize: CGSize {
return CGSize(
width: 320,
height: self.barHeight + self.freeSpaceLabel.font.lineHeight
)
}
// MARK: - Layout
override func layoutSubviews() {
super.layoutSubviews()
let size = self.bounds.size
let usedPercent = CGFloat(self.usedSpace) / CGFloat(self.totalSpace)
let usedSpaceWidth = size.width * usedPercent
self.usedBackgroundView.frame = {
var frame = CGRect()
frame.size.width = usedSpaceWidth
frame.size.height = self.barHeight
frame.origin.x = 0
frame.origin.y = 0
return frame
}()
self.usedSpaceLabel.frame = {
var frame = CGRect()
frame.size = self.usedSpaceLabel.intrinsicContentSize
frame.origin.x = 0
frame.origin.y = self.usedBackgroundView.frame.maxY
return frame
}()
let freePercent = CGFloat(self.totalSpace - self.usedSpace) / CGFloat(self.totalSpace)
let freeSpaceWidth = size.width * freePercent
self.freeBackgroundView.frame = {
var frame = CGRect()
frame.size.width = freeSpaceWidth
frame.size.height = self.barHeight
frame.origin.x = size.width - frame.width
frame.origin.y = 0
return frame
}()
self.freeSpaceLabel.frame = {
var frame = CGRect()
frame.size = self.freeSpaceLabel.intrinsicContentSize
frame.origin.x = size.width - frame.width
frame.origin.y = self.freeBackgroundView.frame.maxY
return frame
}()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment