Skip to content

Instantly share code, notes, and snippets.

@simonbs
Last active November 14, 2022 01:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simonbs/6387d695d73a4118e668ce240fec55d1 to your computer and use it in GitHub Desktop.
Save simonbs/6387d695d73a4118e668ce240fec55d1 to your computer and use it in GitHub Desktop.
Sample app that lets you browse Xcode releases. Depends on ScriptUI, my yet-to-be-releases project for building native SwiftUI interfaces in JavaScript.
UI.view = RootView()
function RootView() {
const [isLoading, setLoading] = createSignal(true)
const [xcodeGroups, setXcodeGroups] = createSignal([])
fetchXcodes().then((xcodes) => {
setLoading(false)
setXcodeGroups(groupXcodes(xcodes))
})
return () => {
NavigationStack(() => {
Group(() => {
if (isLoading()) {
LoadingView()
} else {
XcodeListView(xcodeGroups())
}
})
.background(Color("#f2f2f7"))
.navigationTitle("Xcode Releases")
})
}
}
function XcodeListView(xcodeGroups) {
return List(() => {
for (const xcodeGroup of xcodeGroups) {
Section(() => {
for (xcode of xcodeGroup.xcodes) {
NavigationLink(() => {
XcodeDetailView(xcode)
}, () => {
XcodeRowView(xcode)
})
}
}, () => {
Text(xcodeGroup.year)
})
}
})
}
function LoadingView() {
ZStack(() => {
Color("#f2f2f7")
VStack({spacing: 25}, () => {
ProgressView()
.scaleEffect(2)
Text("Loading releases...")
})
})
}
function XcodeRowView(xcode) {
VStack({alignment: HorizontalAlignment.leading}, () => {
Text(xcodeTitle(xcode))
Text(releaseDate(xcode)).foregroundColor(Color.secondary)
})
}
function XcodeDetailView(xcode) {
List(() => {
Section(() => {
TitleDetailRowView("Release Date", releaseDate(xcode))
TitleDetailRowView("Build", xcode.version.build)
TitleDetailRowView("Required OS", "macOS " + xcode.requires)
})
if (xcode.hasOwnProperty("links")) {
Section(() => {
if (xcode.links.hasOwnProperty("notes")) {
Button(() => {
Safari.open(xcode.links.notes.url)
}, () => {
HStack(() => {
Text("Release Notes")
Spacer()
Image.system("arrow.up.forward.app")
})
})
}
if (xcode.links.hasOwnProperty("download")) {
Button(() => {
Safari.open(xcode.links.download.url)
}, () => {
HStack(() => {
Text("Download")
Spacer()
Image.system("arrow.up.forward.app")
})
})
}
})
}
if (xcode.hasOwnProperty("sdks")) {
Section(() => {
if (xcode.sdks.hasOwnProperty("macOS")) {
for (version of xcode.sdks["macOS"]) {
TitleDetailRowView("macOS", versionTitle(version, true))
}
}
if (xcode.sdks.hasOwnProperty("iOS")) {
TitleDetailRowView("iOS", versionTitle(version, true))
}
if (xcode.sdks.hasOwnProperty("watchOS")) {
TitleDetailRowView("watchOS", versionTitle(version, true))
}
if (xcode.sdks.hasOwnProperty("tvOS")) {
TitleDetailRowView("tvOS", versionTitle(version, true))
}
}, () => {
Text("SDKs")
})
}
if (xcode.hasOwnProperty("compilers")) {
Section(() => {
if (xcode.compilers.hasOwnProperty("swift")) {
for (version of xcode.compilers["swift"]) {
TitleDetailRowView("Swift", versionTitle(version, true))
}
}
if (xcode.compilers.hasOwnProperty("clang")) {
for (version of xcode.compilers["clang"]) {
TitleDetailRowView("Clang", versionTitle(version, true))
}
}
}, () => {
Text("Compilers")
})
}
if (xcode.hasOwnProperty("checksums") && xcode.checksums.hasOwnProperty("sha1")) {
Section(() => {
Text(xcode.checksums.sha1)
}, () => {
Text("Checksum")
})
}
})
.navigationTitle(xcodeTitle(xcode))
.navigationBarTitleDisplayMode(NavigationBarTitleDisplayMode.inline)
}
function TitleDetailRowView(title, detail) {
HStack(() => {
if (title != null && title.length > 0) {
Text(title)
}
if (detail != null && detail.length > 0) {
Spacer()
Text(detail).foregroundColor(Color.secondary)
}
})
}
/**
* Formatters
*/
function xcodeTitle(xcode) {
return xcode.name + " " + versionTitle(xcode.version)
}
function versionTitle(version, includeBuild) {
let release = version.release
let str = version.number
if (includeBuild && version.hasOwnProperty("build")) {
str += " (" + version.build + ")"
}
if (release.hasOwnProperty("gm") && release.gm) {
str += " GM"
} else if (release.hasOwnProperty("gmseed")) {
str += " GM Seed " + release.gmseed
} else if (release.hasOwnProperty("beta")) {
str += " beta " + release.beta
} else if (release.hasOwnProperty("dp")) {
str += " Developer Preview " + release.dp
}
return str
}
function releaseDate(xcode) {
let date = new Date(xcode.date.year, xcode.date.month - 1, xcode.date.day)
let dateFormatter = new DateFormatter()
dateFormatter.locale = "en_US"
dateFormatter.useLongDateStyle()
dateFormatter.useNoTimeStyle()
return dateFormatter.string(date)
}
/**
* Data
*/
async function fetchXcodes() {
let url = "https://xcodereleases.com/api/all.json"
let request = new Request(url)
return await request.loadJSON()
}
function groupXcodes(xcodes) {
var groups = {}
for (xcode of xcodes) {
let key = xcode.date.year
let groupValues = groups[key]
if (groupValues != null) {
groupValues.push(xcode)
groups[key] = groupValues
} else {
groups[key] = [xcode]
}
}
var result = []
let keys = Object.keys(groups).sort((a, b) => b - a)
for (key of keys) {
result.push({year: key, xcodes: groups[key]})
}
return result
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment