Skip to content

Commit bec420b

Browse files
committed
first commit
1 parent ab68637 commit bec420b

File tree

4 files changed

+366
-4
lines changed

4 files changed

+366
-4
lines changed

utiluti.xcodeproj/project.pbxproj

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
/* Begin PBXBuildFile section */
1010
C6C84541291AB2CF00AF407A /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C84540291AB2CF00AF407A /* main.swift */; };
11+
C6C84549291AB2E800AF407A /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = C6C84548291AB2E800AF407A /* Algorithms */; };
12+
C6C8454B291AB30100AF407A /* LSKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C8454A291AB30100AF407A /* LSKit.swift */; };
13+
C6C8454E291AB58200AF407A /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = C6C8454D291AB58200AF407A /* ArgumentParser */; };
1114
/* End PBXBuildFile section */
1215

1316
/* Begin PBXCopyFilesBuildPhase section */
@@ -24,14 +27,17 @@
2427

2528
/* Begin PBXFileReference section */
2629
C6C8453D291AB2CF00AF407A /* utiluti */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = utiluti; sourceTree = BUILT_PRODUCTS_DIR; };
27-
C6C84540291AB2CF00AF407A /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
30+
C6C84540291AB2CF00AF407A /* main.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; tabWidth = 2; };
31+
C6C8454A291AB30100AF407A /* LSKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = LSKit.swift; sourceTree = "<group>"; tabWidth = 2; };
2832
/* End PBXFileReference section */
2933

3034
/* Begin PBXFrameworksBuildPhase section */
3135
C6C8453A291AB2CF00AF407A /* Frameworks */ = {
3236
isa = PBXFrameworksBuildPhase;
3337
buildActionMask = 2147483647;
3438
files = (
39+
C6C8454E291AB58200AF407A /* ArgumentParser in Frameworks */,
40+
C6C84549291AB2E800AF407A /* Algorithms in Frameworks */,
3541
);
3642
runOnlyForDeploymentPostprocessing = 0;
3743
};
@@ -58,6 +64,7 @@
5864
isa = PBXGroup;
5965
children = (
6066
C6C84540291AB2CF00AF407A /* main.swift */,
67+
C6C8454A291AB30100AF407A /* LSKit.swift */,
6168
);
6269
path = utiluti;
6370
sourceTree = "<group>";
@@ -78,6 +85,10 @@
7885
dependencies = (
7986
);
8087
name = utiluti;
88+
packageProductDependencies = (
89+
C6C84548291AB2E800AF407A /* Algorithms */,
90+
C6C8454D291AB58200AF407A /* ArgumentParser */,
91+
);
8192
productName = utiluti;
8293
productReference = C6C8453D291AB2CF00AF407A /* utiluti */;
8394
productType = "com.apple.product-type.tool";
@@ -106,6 +117,10 @@
106117
Base,
107118
);
108119
mainGroup = C6C84534291AB2CF00AF407A;
120+
packageReferences = (
121+
C6C84547291AB2E800AF407A /* XCRemoteSwiftPackageReference "swift-algorithms" */,
122+
C6C8454C291AB58200AF407A /* XCRemoteSwiftPackageReference "swift-argument-parser" */,
123+
);
109124
productRefGroup = C6C8453E291AB2CF00AF407A /* Products */;
110125
projectDirPath = "";
111126
projectRoot = "";
@@ -121,6 +136,7 @@
121136
buildActionMask = 2147483647;
122137
files = (
123138
C6C84541291AB2CF00AF407A /* main.swift in Sources */,
139+
C6C8454B291AB30100AF407A /* LSKit.swift in Sources */,
124140
);
125141
runOnlyForDeploymentPostprocessing = 0;
126142
};
@@ -177,7 +193,7 @@
177193
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
178194
GCC_WARN_UNUSED_FUNCTION = YES;
179195
GCC_WARN_UNUSED_VARIABLE = YES;
180-
MACOSX_DEPLOYMENT_TARGET = 13.0;
196+
MACOSX_DEPLOYMENT_TARGET = 10.14;
181197
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
182198
MTL_FAST_MATH = YES;
183199
ONLY_ACTIVE_ARCH = YES;
@@ -231,7 +247,7 @@
231247
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
232248
GCC_WARN_UNUSED_FUNCTION = YES;
233249
GCC_WARN_UNUSED_VARIABLE = YES;
234-
MACOSX_DEPLOYMENT_TARGET = 13.0;
250+
MACOSX_DEPLOYMENT_TARGET = 10.14;
235251
MTL_ENABLE_DEBUG_INFO = NO;
236252
MTL_FAST_MATH = YES;
237253
SDKROOT = macosx;
@@ -246,6 +262,8 @@
246262
CODE_SIGN_STYLE = Automatic;
247263
DEVELOPMENT_TEAM = JME5BW3F3R;
248264
ENABLE_HARDENED_RUNTIME = YES;
265+
MACOSX_DEPLOYMENT_TARGET = 10.14;
266+
PRODUCT_BUNDLE_IDENTIFIER = com.scriptingosx.utiluti;
249267
PRODUCT_NAME = "$(TARGET_NAME)";
250268
SWIFT_VERSION = 5.0;
251269
};
@@ -257,6 +275,8 @@
257275
CODE_SIGN_STYLE = Automatic;
258276
DEVELOPMENT_TEAM = JME5BW3F3R;
259277
ENABLE_HARDENED_RUNTIME = YES;
278+
MACOSX_DEPLOYMENT_TARGET = 10.14;
279+
PRODUCT_BUNDLE_IDENTIFIER = com.scriptingosx.utiluti;
260280
PRODUCT_NAME = "$(TARGET_NAME)";
261281
SWIFT_VERSION = 5.0;
262282
};
@@ -284,6 +304,38 @@
284304
defaultConfigurationName = Release;
285305
};
286306
/* End XCConfigurationList section */
307+
308+
/* Begin XCRemoteSwiftPackageReference section */
309+
C6C84547291AB2E800AF407A /* XCRemoteSwiftPackageReference "swift-algorithms" */ = {
310+
isa = XCRemoteSwiftPackageReference;
311+
repositoryURL = "https://github.com/apple/swift-algorithms.git";
312+
requirement = {
313+
kind = upToNextMajorVersion;
314+
minimumVersion = 1.0.0;
315+
};
316+
};
317+
C6C8454C291AB58200AF407A /* XCRemoteSwiftPackageReference "swift-argument-parser" */ = {
318+
isa = XCRemoteSwiftPackageReference;
319+
repositoryURL = "https://github.com/apple/swift-argument-parser.git";
320+
requirement = {
321+
kind = upToNextMajorVersion;
322+
minimumVersion = 1.0.0;
323+
};
324+
};
325+
/* End XCRemoteSwiftPackageReference section */
326+
327+
/* Begin XCSwiftPackageProductDependency section */
328+
C6C84548291AB2E800AF407A /* Algorithms */ = {
329+
isa = XCSwiftPackageProductDependency;
330+
package = C6C84547291AB2E800AF407A /* XCRemoteSwiftPackageReference "swift-algorithms" */;
331+
productName = Algorithms;
332+
};
333+
C6C8454D291AB58200AF407A /* ArgumentParser */ = {
334+
isa = XCSwiftPackageProductDependency;
335+
package = C6C8454C291AB58200AF407A /* XCRemoteSwiftPackageReference "swift-argument-parser" */;
336+
productName = ArgumentParser;
337+
};
338+
/* End XCSwiftPackageProductDependency section */
287339
};
288340
rootObject = C6C84535291AB2CF00AF407A /* Project object */;
289341
}

utiluti.xcodeproj/xcuserdata/armin.xcuserdatad/xcschemes/xcschememanagement.plist

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,13 @@
1010
<integer>0</integer>
1111
</dict>
1212
</dict>
13+
<key>SuppressBuildableAutocreation</key>
14+
<dict>
15+
<key>C6C8453C291AB2CF00AF407A</key>
16+
<dict>
17+
<key>primary</key>
18+
<true/>
19+
</dict>
20+
</dict>
1321
</dict>
1422
</plist>

utiluti/LSKit.swift

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
//
2+
// LSKit.swift
3+
// LSKit
4+
//
5+
// Created by Armin Briegel on 2021-08-30.
6+
//
7+
8+
import Foundation
9+
import AppKit
10+
import UniformTypeIdentifiers
11+
12+
class LSKit {
13+
14+
/**
15+
returns a list of URLs to applications that can open URLs starting with the scheme
16+
- Parameter scheme: url scheme (excluding the `:` or `/`, e.g. `http`)
17+
- Returns: array of app URLs
18+
*/
19+
static func appURLs(forScheme scheme: String) -> [URL] {
20+
guard let url = URL(string: "\(scheme):") else { return [URL]() }
21+
22+
if #available(macOS 12, *) {
23+
//print("running on macOS 12, using NSWorkspace")
24+
let ws = NSWorkspace.shared
25+
return ws.urlsForApplications(toOpen: url)
26+
} else {
27+
var urlList = [URL]()
28+
if let result = LSCopyApplicationURLsForURL(url as CFURL, .all) {
29+
let cfURLList = result.takeRetainedValue() as Array
30+
for item in cfURLList {
31+
if let appURL = item as? URL {
32+
urlList.append(appURL)
33+
}
34+
}
35+
}
36+
return urlList
37+
}
38+
}
39+
40+
/**
41+
returns URL to the default application for URLs starting with scheme
42+
- Parameter scheme: url scheme (excluding the `:` or `/`, e.g. `http`)
43+
- Returns: urls to default application
44+
*/
45+
static func defaultAppURL(forScheme scheme: String) -> URL? {
46+
guard let url = URL(string: "\(scheme):") else { return nil }
47+
48+
if #available(macOS 12, *) {
49+
//print("running on macOS 12, using NSWorkspace")
50+
let ws = NSWorkspace.shared
51+
return ws.urlForApplication(toOpen: url)
52+
} else {
53+
if let result = LSCopyDefaultApplicationURLForURL(url as CFURL, .all, nil) {
54+
let appURL = result.takeRetainedValue() as URL
55+
return appURL
56+
}
57+
return nil
58+
}
59+
}
60+
61+
/**
62+
set the default app for scheme to the app with the identifier
63+
- Parameters:
64+
- identifier: bundle id of the new default application
65+
- scheme: url scheme (excluding the `:` or `/`, e.g. `http`)
66+
- Returns: OSStatus (discardable)
67+
*/
68+
@discardableResult static func setDefaultApp(identifier: String, forScheme scheme: String) -> OSStatus {
69+
if #available(macOS 12, *) {
70+
// print("running on macOS 12, using NSWorkspace")
71+
let ws = NSWorkspace.shared
72+
73+
// since the new NSWorkspace function is asynchronous we have to use semaphores here
74+
let semaphore = DispatchSemaphore(value: 0)
75+
var errCode: OSStatus = 0
76+
77+
guard let appURL = ws.urlForApplication(withBundleIdentifier: identifier) else { return 1 }
78+
ws.setDefaultApplication(at: appURL, toOpenURLsWithScheme: scheme) { err in
79+
// err is an NSError wrapped in a CocoaError
80+
if let err = err as? CocoaError {
81+
if let underlyingError = err.errorUserInfo["NSUnderlyingError"] as? NSError {
82+
errCode = OSStatus(clamping: underlyingError.code)
83+
}
84+
}
85+
semaphore.signal()
86+
}
87+
semaphore.wait()
88+
return errCode
89+
} else {
90+
return LSSetDefaultHandlerForURLScheme(scheme as CFString, identifier as CFString)
91+
}
92+
}
93+
94+
/**
95+
returns a list of URLs to applications that can open the given type identifier
96+
- Parameter forTypeIdentifier: Uniform Type Identifier, e.g. `public.html`
97+
- Returns: array of URLs to apps
98+
*/
99+
static func appURLs(forTypeIdentifier utidentifier: String) -> [URL] {
100+
if #available(macOS 12, *) {
101+
print("running on macOS 12, using NSWorkspace")
102+
let ws = NSWorkspace.shared
103+
guard let utype = UTType(utidentifier) else {
104+
return [URL]()
105+
}
106+
return ws.urlsForApplications(toOpen: utype)
107+
} else {
108+
var urlList = [URL]()
109+
if let result = LSCopyAllRoleHandlersForContentType(utidentifier as CFString, .all) {
110+
let cfURLList = result.takeRetainedValue() as Array
111+
for item in cfURLList {
112+
if let appURL = item as? URL {
113+
urlList.append(appURL)
114+
}
115+
}
116+
}
117+
return urlList
118+
}
119+
}
120+
121+
/**
122+
returns URL to the default application for the given type identifier
123+
- Parameter forTypeIdentifier: url scheme (excluding the `:` or `/`, e.g. `http`)
124+
- Returns: url to default application
125+
*/
126+
static func defaultAppURL(forTypeIdentifier utidentifier: String) -> URL? {
127+
if #available(macOS 12, *) {
128+
//print("running on macOS 12, using NSWorkspace")
129+
guard let utype = UTType(utidentifier) else {
130+
return nil
131+
}
132+
let ws = NSWorkspace.shared
133+
return ws.urlForApplication(toOpen: utype)
134+
} else {
135+
if let result = LSCopyDefaultApplicationURLForContentType(utidentifier as CFString, .all, nil) {
136+
let appURL = result.takeRetainedValue() as URL
137+
return appURL
138+
}
139+
return nil
140+
}
141+
}
142+
143+
/**
144+
set the default app for type identifier to the app with the bundle indentifier
145+
- Parameters:
146+
- identifier: bundle id of the new default application
147+
- forTypeIdentifier: uniform type identifier ( e.g. `public.html`)
148+
- Returns: OSStatus (discardable)
149+
*/
150+
@discardableResult static func setDefaultApp(identifier: String, forTypeIdentifier utidentifier: String) -> OSStatus {
151+
if #available(macOS 12, *) {
152+
// print("running on macOS 12, using NSWorkspace")
153+
guard let utype = UTType(utidentifier) else {
154+
return 1
155+
}
156+
157+
let ws = NSWorkspace.shared
158+
159+
// since the new NSWorkspace function is asynchronous we have to use semaphores here
160+
let semaphore = DispatchSemaphore(value: 0)
161+
var errCode: OSStatus = 0
162+
163+
guard let appURL = ws.urlForApplication(withBundleIdentifier: identifier) else { return 1 }
164+
ws.setDefaultApplication(at: appURL, toOpen: utype) { err in
165+
// err is an NSError wrapped in a CocoaError
166+
if let err = err as? CocoaError {
167+
if let underlyingError = err.errorUserInfo["NSUnderlyingError"] as? NSError {
168+
errCode = OSStatus(clamping: underlyingError.code)
169+
}
170+
}
171+
semaphore.signal()
172+
}
173+
semaphore.wait()
174+
return errCode
175+
} else {
176+
return LSSetDefaultRoleHandlerForContentType(utidentifier as CFString, .all, identifier as CFString)
177+
}
178+
}
179+
}

0 commit comments

Comments
 (0)