Skip to content

Commit b256de1

Browse files
authored
Xcode 16 beta 1 and more OLED devices (#1188)
* Add new OLED iPads to the OLED device list * Fix compiler warnings in Xcode 16 beta 1 and update build settings
1 parent 2c1a8bb commit b256de1

File tree

11 files changed

+114
-66
lines changed

11 files changed

+114
-66
lines changed

AwfulCore/Sources/AwfulCore/Data/CachePruner.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Foundation
77

88
private let Log = Logger.get()
99

10-
final class CachePruner: Operation {
10+
final class CachePruner: Operation, @unchecked Sendable {
1111
let managedObjectContext: NSManagedObjectContext
1212

1313
init(managedObjectContext context: NSManagedObjectContext) {

AwfulSettingsUI/Sources/AwfulSettingsUI/Localizable.xcstrings

-3
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,6 @@
135135
},
136136
"Scale Text %@%%" : {
137137

138-
},
139-
"Settings" : {
140-
141138
},
142139
"Show Avatars" : {
143140

Config/Common-Debug.xcconfig

+4
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33
// Anything shared by multiple build configurations should go in the common config.
44
#include "Common.xcconfig"
55

6+
DEBUG_INFORMATION_FORMAT = dwarf
7+
68
ENABLE_TESTABILITY = YES
79

810
GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1
911
GCC_OPTIMIZATION_LEVEL = 0
1012

13+
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE
14+
1115
ONLY_ACTIVE_ARCH = YES
1216

1317
OTHER_CFLAGS = -ftrapv

Config/Common-Release.xcconfig

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
ASSETCATALOG_COMPILER_OPTIMIZATION = time
77

8-
GCC_PREPROCESSOR_DEFINITIONS = NDEBUG=1
8+
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
99

1010
LLVM_LTO = YES
1111

Config/Common.xcconfig

+17-5
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,24 @@
55

66
ALWAYS_SEARCH_USER_PATHS = NO
77

8+
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES
9+
810
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES
11+
CLANG_ANALYZER_NONNULL = YES
12+
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE
913

10-
CLANG_CXX_LANGUAGE_STANDARD = gnu++17
11-
CLANG_CXX_LIBRARY = libc++
14+
CLANG_CXX_LANGUAGE_STANDARD = gnu++20
1215

1316
CLANG_ENABLE_MODULES = YES
1417
CLANG_ENABLE_OBJC_ARC = YES
18+
CLANG_ENABLE_OBJC_WEAK = YES
1519

1620
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES
1721
CLANG_WARN_BOOL_CONVERSION = YES
1822
CLANG_WARN_COMMA = YES
1923
CLANG_WARN_CONSTANT_CONVERSION = YES
2024
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES
25+
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR
2126
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
2227
CLANG_WARN_EMPTY_BODY = YES
2328
CLANG_WARN_ENUM_CONVERSION = YES
@@ -26,10 +31,12 @@ CLANG_WARN_INT_CONVERSION = YES
2631
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES
2732
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
2833
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES
34+
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR
2935
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES
3036
CLANG_WARN_STRICT_PROTOTYPES = YES
3137
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES
3238
CLANG_WARN_SUSPICIOUS_MOVE = YES
39+
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
3340
CLANG_WARN_UNREACHABLE_CODE = YES
3441

3542
COPY_PHASE_STRIP = NO
@@ -39,14 +46,14 @@ DYLIB_CURRENT_VERSION = 70300
3946
ENABLE_NS_ASSERTIONS = YES
4047
ENABLE_STRICT_OBJC_MSGSEND = YES
4148

42-
GCC_C_LANGUAGE_STANDARD = gnu99
49+
GCC_C_LANGUAGE_STANDARD = gnu17
4350
GCC_NO_COMMON_BLOCKS = YES
4451
GCC_THREADSAFE_STATICS = NO
4552
GCC_TREAT_WARNINGS_AS_ERRORS = YES
4653

4754
GCC_WARN_64_TO_32_BIT_CONVERSION = YES
4855
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES
49-
GCC_WARN_ABOUT_RETURN_TYPE = YES
56+
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR
5057
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES
5158
GCC_WARN_SIGN_COMPARE = YES
5259
GCC_WARN_UNDECLARED_SELECTOR = YES
@@ -56,15 +63,20 @@ GCC_WARN_UNUSED_LABEL = YES
5663
GCC_WARN_UNUSED_VARIABLE = YES
5764

5865
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks
66+
LOCALIZATION_PREFERS_STRING_CATALOGS = YES
5967

6068
MARKETING_VERSION = 7.3
6169

70+
MTL_ENABLE_DEBUG_INFO = NO
71+
MTL_FAST_MATH = YES
72+
6273
RUN_CLANG_STATIC_ANALYZER = YES
6374

6475
SDKROOT = iphoneos
6576

77+
SWIFT_EMIT_LOC_STRINGS = YES
6678
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES
67-
SWIFT_VERSION = 5.5
79+
SWIFT_VERSION = 5.0
6880

6981
TARGETED_DEVICE_FAMILY = 1,2
7082

ImgurAnonymousAPI/Sources/ImgurAnonymousAPI/AsynchronousOperation.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import Foundation
1414
* Can conveniently locate dependent operations with a particular result type.
1515
* Error reporting via convenient `throw`.
1616
*/
17-
internal class AsynchronousOperation<T>: Foundation.Operation {
17+
internal class AsynchronousOperation<T>: Foundation.Operation, @unchecked Sendable {
1818
private let queue = DispatchQueue(label: "com.nolanw.ImgurAnonymousAPI.async-operation-state")
1919
private(set) var result: Result<T>?
2020
private var _state: AsynchronousOperationState = .ready

ImgurAnonymousAPI/Sources/ImgurAnonymousAPI/ImageOperations.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ internal enum ImageError: LocalizedError {
4444

4545
The image is resized without being fully dragged into memory.
4646
*/
47-
internal final class ResizeImage: AsynchronousOperation<ImageFile> {
48-
47+
internal final class ResizeImage: AsynchronousOperation<ImageFile>, @unchecked Sendable {
48+
4949
private let maximumFileSizeBytes: Int
5050

5151
init(maximumFileSizeBytes: Int) {
@@ -134,8 +134,8 @@ import Photos
134134

135135
/// Retrieves image data for a `PHAsset` and saves it to a file in a temporary folder.
136136
@available(macOS 10.13, tvOS 10.0, *)
137-
internal final class SavePHAsset: AsynchronousOperation<ImageFile> {
138-
137+
internal final class SavePHAsset: AsynchronousOperation<ImageFile>, @unchecked Sendable {
138+
139139
/// Returns `true` when the app has a photo library usage description and the user has authorized said access.
140140
static var hasRequiredPhotoLibraryAuthorization: Bool {
141141

@@ -199,7 +199,7 @@ import MobileCoreServices
199199
import UIKit
200200

201201
/// Writes the image data to file in a temporary folder.
202-
internal final class SaveUIImage: AsynchronousOperation<ImageFile> {
202+
internal final class SaveUIImage: AsynchronousOperation<ImageFile>, @unchecked Sendable {
203203
private let image: UIImage
204204

205205
init(_ image: UIImage) {

ImgurAnonymousAPI/Sources/ImgurAnonymousAPI/TemporaryFolderOperations.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ internal struct TemporaryFolder {
77
}
88

99
/// Creates a randomly-named folder in a temporary directory.
10-
internal final class MakeTemporaryFolder: AsynchronousOperation<TemporaryFolder> {
10+
internal final class MakeTemporaryFolder: AsynchronousOperation<TemporaryFolder>, @unchecked Sendable {
1111
override func execute() throws {
1212
let url = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
1313
.appendingPathComponent(UUID().uuidString, isDirectory: true)
@@ -20,7 +20,7 @@ internal final class MakeTemporaryFolder: AsynchronousOperation<TemporaryFolder>
2020
}
2121

2222
/// Attempts to delete a temporary folder, but doesn't throw an error on failure (presumably the operating system will clean up after us).
23-
internal final class DeleteTemporaryFolder: AsynchronousOperation<Void> {
23+
internal final class DeleteTemporaryFolder: AsynchronousOperation<Void>, @unchecked Sendable {
2424
override func execute() throws {
2525
do {
2626
let tempFolder = try firstDependencyValue(ofType: TemporaryFolder.self)

ImgurAnonymousAPI/Sources/ImgurAnonymousAPI/URLSessionOperations.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ extension ImgurUploader.Either: Decodable {
6767
}
6868

6969
/// Runs a URLSessionTask and decodes the response data as JSON.
70-
internal final class FetchURL<T: Decodable>: AsynchronousOperation<T> {
70+
internal final class FetchURL<T: Decodable>: AsynchronousOperation<T>, @unchecked Sendable {
7171
private var task: URLSessionDataTask?
7272

7373
init(urlSession: URLSession, request: URLRequest) {
@@ -111,7 +111,7 @@ internal final class FetchURL<T: Decodable>: AsynchronousOperation<T> {
111111
}
112112

113113
/// Sends a multipart/form-data upload request, then parses the response's body and headers.
114-
internal final class UploadImageAsFormData: AsynchronousOperation<ImgurUploader.UploadResponse> {
114+
internal final class UploadImageAsFormData: AsynchronousOperation<ImgurUploader.UploadResponse>, @unchecked Sendable {
115115
private let request: URLRequest
116116
private var task: URLSessionUploadTask?
117117
private let urlSession: URLSession

ImgurAnonymousAPI/Sources/ImgurAnonymousAPI/WriteMultipartFormData.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ internal enum WriteError: CustomNSError {
2828
}
2929
}
3030

31-
internal final class WriteMultipartFormData: AsynchronousOperation<FormDataFile> {
31+
internal final class WriteMultipartFormData: AsynchronousOperation<FormDataFile>, @unchecked Sendable {
3232
override func execute() throws {
3333
let tempFolder = try firstDependencyValue(ofType: TemporaryFolder.self)
3434
let imageFile = try firstDependencyValue(ofType: ImageFile.self)

SystemCapabilities/Sources/SystemCapabilities/OLED.swift

+80-45
Original file line numberDiff line numberDiff line change
@@ -7,62 +7,97 @@ import Logger
77

88
private let Log = Logger.get()
99

10+
/// Device information that's not particularly convenient to retrieve.
1011
public enum SystemCapabilities {
1112

13+
/// Whether the device's main screen is OLED.
1214
public static let oled: Bool = {
1315
// Models are listed at https://ipsw.me/ and/or http://theiphonewiki.com/wiki/Models
16+
// Display technology is in each model's Wikipedia page.
1417
// Not gonna bother trying to guess at future models.
15-
let scanner = Scanner(string: modelIdentifier)
16-
guard scanner.scanString("iPhone") != nil,
17-
let major = scanner.scanInt(),
18-
scanner.scanString(",") != nil,
19-
let minor = scanner.scanInt()
20-
else { return false }
21-
switch (major, minor) {
22-
case (10, 3), (10, 6), // iPhone X
23-
(11, 2), // iPhone XS
24-
(11, 4), (11, 6), // iPhone XS Max
25-
(12, 3), // iPhone 11 Pro
26-
(12, 5), // iPhone 11 Pro Max
27-
(13, 1), // iPhone 12 Mini
28-
(13, 2), // iPhone 12
29-
(13, 3), // iPhone 12 Pro
30-
(13, 4), // iPhone 12 Pro Max
31-
(14, 2), // iPhone 13 Pro
32-
(14, 3), // iPhone 13 Pro Max
33-
(14, 4), // iPhone 13 Mini
34-
(14, 5), // iPhone 13
35-
(14, 7), // iPhone 14
36-
(14, 8), // iPhone 14 Plus
37-
(15, 2), // iPhone 14 Pro
38-
(15, 3), // iPhone 14 Pro Max
39-
(15, 4), // iPhone 15
40-
(15, 5), // iPhone 15 Plus
41-
(16, 1), // iPhone 15 Pro
42-
(16, 2): // iPhone 15 Pro Max
43-
return true
44-
default:
45-
return false
18+
guard let modelID = ModelIdentifier.makeFromSysctl() else { return false }
19+
switch modelID.device {
20+
case .iPhone:
21+
switch (modelID.major, modelID.minor) {
22+
case (10,3), (10,6), // iPhone X
23+
(11,2), // iPhone XS
24+
(11,4), (11,6), // iPhone XS Max
25+
(12,3), // iPhone 11 Pro
26+
(12,5), // iPhone 11 Pro Max
27+
(13,1), // iPhone 12 Mini
28+
(13,2), // iPhone 12
29+
(13,3), // iPhone 12 Pro
30+
(13,4), // iPhone 12 Pro Max
31+
(14,2), // iPhone 13 Pro
32+
(14,3), // iPhone 13 Pro Max
33+
(14,4), // iPhone 13 Mini
34+
(14,5), // iPhone 13
35+
(14,7), // iPhone 14
36+
(14,8), // iPhone 14 Plus
37+
(15,2), // iPhone 14 Pro
38+
(15,3), // iPhone 14 Pro Max
39+
(15,4), // iPhone 15
40+
(15,5), // iPhone 15 Plus
41+
(16,1), // iPhone 15 Pro
42+
(16,2): // iPhone 15 Pro Max
43+
return true
44+
case (_,_):
45+
return false
46+
}
47+
case .iPad:
48+
switch (modelID.major, modelID.minor) {
49+
case (16,3), (16,4), (16,5), (16,6): // iPad Pro (M4) aka iPad Pro (7th generation)
50+
return true
51+
case (_,_):
52+
return false
53+
}
4654
}
4755
}()
4856
}
4957

50-
private let modelIdentifier: String = {
51-
var size: Int = 0
52-
guard sysctlbyname("hw.machine", nil, &size, nil, 0) == 0 else {
53-
Log.e("could not find model identifier: could not get buffer size")
54-
return ""
58+
private struct ModelIdentifier {
59+
let device: Device
60+
let major: Int
61+
let minor: Int
62+
63+
enum Device {
64+
case iPad, iPhone
5565
}
5666

57-
let bufferSize = Int(size) + 1
58-
let buffer = UnsafeMutablePointer<CChar>.allocate(capacity: bufferSize)
59-
defer { buffer.deallocate() }
67+
init?(_ rawValue: String) {
68+
let scanner = Scanner(string: rawValue)
69+
if scanner.scanString("iPhone") != nil {
70+
device = .iPhone
71+
} else if scanner.scanString("iPad") != nil {
72+
device = .iPad
73+
} else {
74+
return nil
75+
}
6076

61-
guard sysctlbyname("hw.machine", buffer, &size, nil, 0) == 0 else {
62-
Log.e("could not find model identifier")
63-
return ""
77+
guard let major = scanner.scanInt(),
78+
scanner.scanString(",") != nil,
79+
let minor = scanner.scanInt()
80+
else { return nil }
81+
self.major = major
82+
self.minor = minor
6483
}
6584

66-
buffer[Int(size)] = 0
67-
return String(cString: buffer)
68-
}()
85+
static func makeFromSysctl() -> ModelIdentifier? {
86+
var size: Int = 0
87+
guard sysctlbyname("hw.machine", nil, &size, nil, 0) == 0 else {
88+
Log.e("could not find model identifier: could not get buffer size")
89+
return nil
90+
}
91+
92+
let buffer = UnsafeMutablePointer<CChar>.allocate(capacity: size + 1)
93+
defer { buffer.deallocate() }
94+
95+
guard sysctlbyname("hw.machine", buffer, &size, nil, 0) == 0 else {
96+
Log.e("could not find model identifier")
97+
return nil
98+
}
99+
100+
buffer[Int(size)] = 0
101+
return ModelIdentifier(String(cString: buffer))
102+
}
103+
}

0 commit comments

Comments
 (0)