Skip to content

Commit ee44085

Browse files
authored
Merge pull request #2 from ericceg/package-app
Package app
2 parents 6d1f1e3 + 7bd23f0 commit ee44085

10 files changed

Lines changed: 253 additions & 1 deletion

File tree

.github/workflows/build-dmg.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Build DMG
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
push:
7+
branches:
8+
- main
9+
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
build-dmg:
15+
runs-on: macos-15
16+
17+
steps:
18+
- name: Check out repository
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Node.js
22+
uses: actions/setup-node@v4
23+
with:
24+
node-version: "22"
25+
26+
- name: Build DMG
27+
run: make dmg
28+
29+
- name: Upload DMG artifact
30+
uses: actions/upload-artifact@v4
31+
with:
32+
name: CroPDF-dmg
33+
path: dist/CroPDF.dmg
34+
if-no-files-found: error

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.DS_Store
22
/.build
3+
/dist
34
/Packages
45
xcuserdata/
56
DerivedData/

Makefile

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
APP_NAME := CroPDF
2+
EXECUTABLE := CroPDFMacOS
3+
CONFIGURATION ?= release
4+
DIST_DIR := $(CURDIR)/dist
5+
APP_DIR := $(DIST_DIR)/$(APP_NAME).app
6+
CONTENTS_DIR := $(APP_DIR)/Contents
7+
MACOS_DIR := $(CONTENTS_DIR)/MacOS
8+
RESOURCES_DIR := $(CONTENTS_DIR)/Resources
9+
DMG_PATH := $(DIST_DIR)/$(APP_NAME).dmg
10+
CREATE_DMG_VERSION ?= 8.0.0
11+
12+
.PHONY: dmg clean
13+
14+
dmg:
15+
@set -euo pipefail; \
16+
swift build --disable-sandbox -c "$(CONFIGURATION)"; \
17+
BIN_DIR="$$(swift build --disable-sandbox -c "$(CONFIGURATION)" --show-bin-path)"; \
18+
rm -rf "$(APP_DIR)"; \
19+
mkdir -p "$(MACOS_DIR)" "$(RESOURCES_DIR)"; \
20+
cp "$$BIN_DIR/$(EXECUTABLE)" "$(MACOS_DIR)/$(EXECUTABLE)"; \
21+
cp -R "$$BIN_DIR/$(EXECUTABLE)_$(EXECUTABLE).bundle" "$(RESOURCES_DIR)/$(EXECUTABLE)_$(EXECUTABLE).bundle"; \
22+
cp "$(CURDIR)/src/Resources/$(APP_NAME).icns" "$(RESOURCES_DIR)/$(APP_NAME).icns"; \
23+
cp "$(CURDIR)/scripts/Info.plist" "$(CONTENTS_DIR)/Info.plist"; \
24+
rm -f "$(DMG_PATH)"; \
25+
npx --yes "create-dmg@$(CREATE_DMG_VERSION)" --overwrite --no-version-in-filename --no-code-sign "$(APP_DIR)" "$(DIST_DIR)"; \
26+
rm -rf "$(APP_DIR)"; \
27+
echo "Built $(DMG_PATH)"
28+
29+
clean:
30+
rm -rf "$(DIST_DIR)"

Package.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ let package = Package(
99
targets: [
1010
.executableTarget(
1111
name: "CroPDFMacOS",
12-
path: "src"
12+
path: "src",
13+
resources: [
14+
.process("Resources"),
15+
]
1316
),
1417
]
1518
)

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,17 @@ swift run CroPDFMacOS /path/to/file.pdf --page 12
4141
```
4242

4343
You can also open the package directly in Xcode and run it as a macOS app target.
44+
45+
## Package As .app
46+
47+
There is no dedicated `.app` packaging command anymore. The supported packaging output is the DMG.
48+
49+
## Package As .dmg
50+
51+
```bash
52+
make dmg
53+
```
54+
55+
This builds a temporary app bundle, packages it with `create-dmg`, and leaves you with `dist/CroPDF.dmg`. The intermediate `dist/CroPDF.app` is removed automatically.
56+
57+
`Node.js` and `npm` are required for the DMG step because the script downloads `create-dmg` on demand.

assets/CroPDF.svg

Lines changed: 120 additions & 0 deletions
Loading

scripts/Info.plist

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleDevelopmentRegion</key>
6+
<string>en</string>
7+
<key>CFBundleExecutable</key>
8+
<string>CroPDFMacOS</string>
9+
<key>CFBundleIconFile</key>
10+
<string>CroPDF</string>
11+
<key>CFBundleIdentifier</key>
12+
<string>com.ericceglie.CroPDF</string>
13+
<key>CFBundleInfoDictionaryVersion</key>
14+
<string>6.0</string>
15+
<key>CFBundleName</key>
16+
<string>CroPDF</string>
17+
<key>CFBundlePackageType</key>
18+
<string>APPL</string>
19+
<key>CFBundleShortVersionString</key>
20+
<string>1.0</string>
21+
<key>CFBundleVersion</key>
22+
<string>1</string>
23+
<key>LSMinimumSystemVersion</key>
24+
<string>14.0</string>
25+
<key>NSHighResolutionCapable</key>
26+
<true/>
27+
<key>NSPrincipalClass</key>
28+
<string>NSApplication</string>
29+
</dict>
30+
</plist>

src/CroPDFMacOSApp.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,30 @@ import SwiftUI
33

44
final class CroPDFAppDelegate: NSObject, NSApplicationDelegate {
55
func applicationDidFinishLaunching(_ notification: Notification) {
6+
if let iconImage = appIconImage() {
7+
NSApp.applicationIconImage = iconImage
8+
}
9+
610
NSApp.setActivationPolicy(.regular)
711
NSApp.activate(ignoringOtherApps: true)
812
NSApp.windows.first?.makeKeyAndOrderFront(nil)
913
}
14+
15+
private func appIconImage() -> NSImage? {
16+
let candidates = [
17+
("CroPDF", "icns"),
18+
("CroPDFIcon", "png"),
19+
]
20+
21+
for (name, ext) in candidates {
22+
if let iconURL = Bundle.module.url(forResource: name, withExtension: ext),
23+
let iconImage = NSImage(contentsOf: iconURL) {
24+
return iconImage
25+
}
26+
}
27+
28+
return nil
29+
}
1030
}
1131

1232
@main

src/Resources/CroPDF.icns

221 KB
Binary file not shown.

src/Resources/CroPDFIcon.png

82.9 KB
Loading

0 commit comments

Comments
 (0)