Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions DevCleaner/Model/XcodeFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,14 @@ final public class XcodeFiles {
selected: false)
}

private func derivedDataFolderName(from url: URL) -> String {
let splitted = url.lastPathComponent.split(separator: "-", omittingEmptySubsequences: true).dropLast()
return splitted.joined(separator: "-").replacingOccurrences(of: "_", with: " ")
}

private func derivedDataEntry(from location: URL) -> DerivedDataFileEntry? {
// drop last part that's the kind of id of a project
let splitted = location.lastPathComponent.split(separator: "-", omittingEmptySubsequences: true).dropLast()

// create project name
let name = splitted.joined(separator: "-").replacingOccurrences(of: "_", with: " ") // replace all dashes with spaces

let name = self.derivedDataFolderName(from: location)

// find project folder path
let infoPath = location.appendingPathComponent("info.plist")

Expand Down Expand Up @@ -615,32 +616,47 @@ final public class XcodeFiles {

// scan for derived data projects
var results: [XcodeFileEntry] = []
var otherFolders: [URL] = []
for derivedDataLocation in derivedDataLocations {
if let projectsFolders = try? FileManager.default.contentsOfDirectory(at: derivedDataLocation, includingPropertiesForKeys: nil, options: Self.scanFileEnumerationOptions) {
for projectFolder in projectsFolders {
// ignore "ModuleCache" folder
if projectFolder.lastPathComponent == "ModuleCache" {
continue
}

if let projectEntry = self.derivedDataEntry(from: projectFolder) {
projectEntry.addPath(path: projectFolder)

results.append(projectEntry)
} else {
otherFolders.append(projectFolder)
}
}
}
}

// sort
for entry in results {
entry.recalculateSize()
}

results = results.sorted { lhs, rhs in
return lhs.size > rhs.size
}


// group unrecognized folders under an "Other" node, appended after the sorted results
if !otherFolders.isEmpty {
let otherEntry = XcodeFileEntry(label: "Other", extraInfo: "", icon: .system(name: NSImage.folderName), selected: false)
for folder in otherFolders {
let name = self.derivedDataFolderName(from: folder)
let childEntry = DerivedDataFileEntry(projectName: name, pathUrl: folder, selected: false)
childEntry.addPath(path: folder)
otherEntry.addChild(item: childEntry)
}
otherEntry.recalculateSize()
results.append(otherEntry)
}

return results
}

Expand Down
1 change: 1 addition & 0 deletions DevCleaner/Resources/Manual/manual.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ <h4>Archives</h4>
<h4>Derived Data</h4>
<p>When you develop your app, Xcode caches your symbols, dependencies, logs, text - all to develop fast and to keep code completion working. Unfortunately this data tend to take a lot of space, especially for big projects. Also, they're not deleted automatically. This may be especially problematic if you open a lot of various projects.</p>
<p>Removing derived data is not harmful as Xcode can recreate them when you open project again. Although it's not recommended for projects that you work on often. For this reason nothing is selected by default.</p>
<p>Some folders inside <code>DerivedData</code> may not be recognized as a specific project — for example orphaned caches left behind after a project was deleted or renamed, or entries that were corrupted and are missing their metadata. These folders are grouped under an <b>Other</b> node at the bottom of the Derived Data list so you can inspect and clean them too.</p>

<h4>Documentation Cache</h4>
<p>Modern Xcodes are caching documentation that is accessed via the web. Unfortunately older caches are not cleaned up by Xcode and significant
Expand Down