Skip to content

Commit

Permalink
Swift 5.7 regex (#796)
Browse files Browse the repository at this point in the history
* Migrate to regex introduced in swift 5.7
Migrate swift-tools-version to 5.9
Uplift target for OpenHABCore to iOS 16, watchOS 9 - Consistently upgrade target of watch app to watchOS 9

* Fixing deprecation issue: 'SecTrustGetCertificateAtIndex' was deprecated in watchOS 8.0: renamed to 'SecTrustCopyCertificateChain(_:)'
Adding test for regex used for OpenHABStateDescription.numberPattern
Adding swift 5.7 regex for OpenHABRootViewController.uiCommandAction()
LocalizationsTests migrated to swift 5.7 regex
  • Loading branch information
timbms authored Sep 7, 2024
1 parent 0b22bdc commit 3ed161e
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 109 deletions.
47 changes: 20 additions & 27 deletions NotificationService/NotificationService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,35 +165,28 @@ class NotificationService: UNNotificationServiceExtension {
return
}
if let state = item.state {
do {
// Extract MIME type and base64 string
let pattern = "^data:(.*?);base64,(.*)$"
let regex = try NSRegularExpression(pattern: pattern, options: [])
if let match = regex.firstMatch(in: state, options: [], range: NSRange(location: 0, length: state.utf16.count)) {
let mimeTypeRange = Range(match.range(at: 1), in: state)
let base64Range = Range(match.range(at: 2), in: state)
if let mimeTypeRange, let base64Range {
let mimeType = String(state[mimeTypeRange])
let base64String = String(state[base64Range])
if let imageData = Data(base64Encoded: base64String) {
// Create a temporary file URL
let tempDirectory = FileManager.default.temporaryDirectory
let tempFileURL = tempDirectory.appendingPathComponent(UUID().uuidString)
do {
try imageData.write(to: tempFileURL)
os_log("Image saved to temporary file: %{PUBLIC}@", log: .default, type: .info, tempFileURL.absoluteString)
self.attachFile(localURL: tempFileURL, mimeType: mimeType, completion: completion)
return
} catch {
os_log("Failed to write image data to file: %{PUBLIC}@", log: .default, type: .error, error.localizedDescription)
}
} else {
os_log("Failed to decode base64 string to Data", log: .default, type: .error)
}
// Extract MIME type and base64 string
let pattern = /^data:(.*?);base64,(.*)$/
if let firstMatch = state.firstMatch(of: pattern) {
let mimeType = String(firstMatch.1)
let base64String = String(firstMatch.2)
if let imageData = Data(base64Encoded: base64String) {
// Create a temporary file URL
let tempDirectory = FileManager.default.temporaryDirectory
let tempFileURL = tempDirectory.appendingPathComponent(UUID().uuidString)
do {
try imageData.write(to: tempFileURL)
os_log("Image saved to temporary file: %{PUBLIC}@", log: .default, type: .info, tempFileURL.absoluteString)
self.attachFile(localURL: tempFileURL, mimeType: mimeType, completion: completion)
return
} catch {
os_log("Failed to write image data to file: %{PUBLIC}@", log: .default, type: .error, error.localizedDescription)
}
} else {
os_log("Failed to decode base64 string to Data", log: .default, type: .error)
}
} catch {
os_log("Failed to parse data: %{PUBLIC}@", log: .default, type: .error, error.localizedDescription)
} else {
os_log("Failed to parse data: %{PUBLIC}@", log: .default, type: .error, error?.localizedDescription ?? "")
}
}
completion(nil)
Expand Down
14 changes: 8 additions & 6 deletions OpenHABCore/Package.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// swift-tools-version:5.5
// swift-tools-version:5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "OpenHABCore",
platforms: [.iOS(.v12), .watchOS(.v6)],
platforms: [.iOS(.v16), .watchOS(.v9)],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
Expand All @@ -15,8 +15,8 @@ let package = Package(
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(name: "Alamofire", url: "https://github.com/Alamofire/Alamofire.git", from: "5.0.0"),
.package(name: "Kingfisher", url: "https://github.com/onevcat/Kingfisher.git", from: "7.0.0")
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.0.0"),
.package(url: "https://github.com/onevcat/Kingfisher.git", from: "7.0.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand All @@ -26,14 +26,16 @@ let package = Package(
dependencies: [
.product(name: "Alamofire", package: "Alamofire", condition: .when(platforms: [.iOS, .watchOS])),
.product(name: "Kingfisher", package: "Kingfisher", condition: .when(platforms: [.iOS, .watchOS]))
]
],
swiftSettings: [.enableUpcomingFeature("BareSlashRegexLiterals")]
),
.testTarget(
name: "OpenHABCoreTests",
dependencies: ["OpenHABCore"],
resources: [
.process("Resources")
]
],
swiftSettings: [.enableUpcomingFeature("BareSlashRegexLiterals")]
)
]
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class OpenHABStateDescription {

public var numberPattern: String?

init(minimum: Double?, maximum: Double?, step: Double?, readOnly: Bool?, options: [OpenHABOptions]?, pattern: String?) {
init(minimum: Double?, maximum: Double?, step: Double?, readOnly: Bool?, options: [OpenHABOptions]?, pattern tobeSearched: String?) {
self.minimum = minimum ?? 0.0
self.maximum = maximum ?? 100.0
self.step = step ?? 1.0
Expand All @@ -30,16 +30,12 @@ public class OpenHABStateDescription {

// Remove transformation instructions (e.g. for 'MAP(foo.map):%s' keep only '%s')

let regexPattern = #"^[A-Z]+(\(.*\))?:(.*)$"#
let regex = try? NSRegularExpression(pattern: regexPattern, options: .caseInsensitive)
if let pattern {
let nsrange = NSRange(pattern.startIndex ..< pattern.endIndex, in: pattern)
if let match = regex?.firstMatch(in: pattern, options: [], range: nsrange) {
if let range = Range(match.range(at: 2), in: pattern) {
numberPattern = String(pattern[range])
}
let regexPattern = /^[A-Z]+(\(.*\))?:(.*)$/.ignoresCase()
if let tobeSearched {
if let firstMatch = tobeSearched.firstMatch(of: regexPattern) {
numberPattern = String(firstMatch.2)
} else {
numberPattern = pattern
numberPattern = tobeSearched
}
} else {
numberPattern = nil
Expand Down
8 changes: 3 additions & 5 deletions OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,9 @@ public class OpenHABWidget: NSObject, MKAnnotation, Identifiable {

// Text between square brackets
public var labelValue: String? {
// Swift 5 raw strings
let regex = try? NSRegularExpression(pattern: #"\[(.*?)\]"#, options: [.dotMatchesLineSeparators])
guard let match = regex?.firstMatch(in: label, options: [], range: NSRange(location: 0, length: (label as NSString).length)) else { return nil }
guard let range = Range(match.range(at: 1), in: label) else { return nil }
return String(label[range])
let pattern = /\[(.*?)\]/.dotMatchesNewlines()
guard let firstMatch = label.firstMatch(of: pattern) else { return nil }
return String(firstMatch.1)
}

public var coordinate: CLLocationCoordinate2D {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,17 +224,11 @@ public class ServerCertificateManager: ServerTrustManager, ServerTrustEvaluating
func getLeafCertificate(trust: SecTrust?) -> SecCertificate? {
// Returns the leaf certificate from a SecTrust object (that is always the
// certificate at index 0).
var result: SecCertificate?

if let trust {
if SecTrustGetCertificateCount(trust) > 0 {
result = SecTrustGetCertificateAtIndex(trust, 0)
return result
} else {
return nil
}
if let trust, SecTrustGetCertificateCount(trust) > 0, let certificates = SecTrustCopyCertificateChain(trust) as? [SecCertificate] {
certificates[0]
} else {
return nil
nil
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,16 @@ final class OpenHABCoreGeneralTests: XCTestCase {
XCTAssertEqual(urlc, URL(string: "http://192.169.2.1/icon/switch?state=OFF&format=SVG"), "Check endpoint creation")
}

func testLabelVale() {
func testLabelValue() {
let widget = OpenHABWidget()
widget.label = "llldl [llsl]"
XCTAssertEqual(widget.labelValue, "llsl")
widget.label = "llllsl[kkks] llls"
XCTAssertEqual(widget.labelValue, "kkks")
}

func testOpenHABStateDescription() {
let openHABStateDescription = OpenHABStateDescription(minimum: 0.0, maximum: 1.0, step: 0.2, readOnly: true, options: nil, pattern: "MAP(foo.map):%s")
XCTAssertEqual(openHABStateDescription.numberPattern, "%s")
}
}
6 changes: 3 additions & 3 deletions openHAB.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1815,7 +1815,7 @@
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = openHAB;
WATCHOS_DEPLOYMENT_TARGET = 6.0;
WATCHOS_DEPLOYMENT_TARGET = 8.0;
};
name = Debug;
};
Expand Down Expand Up @@ -1903,7 +1903,7 @@
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
VERSIONING_SYSTEM = "apple-generic";
WATCHOS_DEPLOYMENT_TARGET = 7.0;
WATCHOS_DEPLOYMENT_TARGET = 9.0;
};
name = Debug;
};
Expand Down Expand Up @@ -1950,7 +1950,7 @@
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
VERSIONING_SYSTEM = "apple-generic";
WATCHOS_DEPLOYMENT_TARGET = 7.0;
WATCHOS_DEPLOYMENT_TARGET = 9.0;
};
name = Release;
};
Expand Down
47 changes: 20 additions & 27 deletions openHAB/OpenHABRootViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -202,34 +202,27 @@ class OpenHABRootViewController: UIViewController {

private func uiCommandAction(_ command: String) {
os_log("navigateCommandAction: %{PUBLIC}@", log: .notifications, type: .info, command)
let pattern = "^(/basicui/app\\?.*|/.*|.*)$"

do {
let regex = try NSRegularExpression(pattern: pattern, options: [])
let nsString = command as NSString
let results = regex.matches(in: command, options: [], range: NSRange(location: 0, length: nsString.length))

if let match = results.first {
let pathRange = match.range(at: 1)
let path = nsString.substring(with: pathRange)
os_log("navigateCommandAction path: %{PUBLIC}@", log: .notifications, type: .info, path)
if currentView != webViewController {
switchView(target: .webview)
}
if path.starts(with: "/basicui/app?") {
// TODO: this is a sitemap, we should use the native renderer
// temp hack right now to just use a webview
webViewController.loadWebView(force: true, path: path)
} else if path.starts(with: "/") {
// have the webview load this path itself
webViewController.loadWebView(force: true, path: path)
} else {
// have the mainUI handle the navigation
webViewController.navigateCommand(path)
}
let regexPattern = /^(\/basicui\/app\\?.*|\/.*|.*)$/
if let firstMatch = command.firstMatch(of: regexPattern) {
let path = String(firstMatch.1)
os_log("navigateCommandAction path: %{PUBLIC}@", log: .notifications, type: .info, path)
if currentView != webViewController {
switchView(target: .webview)
}
} catch {
os_log("Invalid regex: %{PUBLIC}@", log: .notifications, type: .error, error.localizedDescription)
if path.starts(with: "/basicui/app?") {
// TODO: this is a sitemap, we should use the native renderer
// temp hack right now to just use a webview
webViewController.loadWebView(force: true, path: path)
} else if path.starts(with: "/") {
// have the webview load this path itself
webViewController.loadWebView(force: true, path: path)
} else {
// have the mainUI handle the navigation
webViewController.navigateCommand(path)
}

} else {
os_log("Invalid regex: %{PUBLIC}@", log: .notifications, type: .error, command)
}
}

Expand Down
26 changes: 11 additions & 15 deletions openHABTestsSwift/LocalizationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,18 @@ class LocalizationTests: XCTestCase {
for language in LocalizationTests.localizations {
print("Testing language: '\(language)'.")
for tuple in LocalizationTests.localizedFormatStrings {
do {
guard let translation = tuple.key.localized(for: language)?.replacingOccurrences(of: "%%", with: "") else {
XCTFail("Failed to get translation for key '\(tuple.key)' in language '\(language)'.")
continue
}

XCTAssertNotEqual(translation, "__MISSING__", "Missing translation for key '\(tuple.key)' in language '\(language)'.")
let formatSpecifiersRegEx = try NSRegularExpression(pattern: "%(?:\\d+\\$)?[+-]?(?:[lh]{0,2})(?:[qLztj])?(?:[ 0]|'.{1})?\\d*(?:\\.\\d?)?[@dDiuUxXoOfeEgGcCsSpaAFn]")
let numberOfMatches = formatSpecifiersRegEx.numberOfMatches(in: translation, options: [], range: NSRange(location: 0, length: translation.utf16.count))
XCTAssertEqual(numberOfMatches, tuple.arguments.count, "Invalid number of format specifiers for key '\(tuple.key)' in language '\(language)'.")
} catch {
XCTFail("Failed to create regular expression for key '\(tuple.key)' in language '\(language)'.")
guard let translation = tuple.key.localized(for: language)?.replacingOccurrences(of: "%%", with: "") else {
XCTFail("Failed to get translation for key '\(tuple.key)' in language '\(language)'.")
continue
}
let translation = tuple.key.localizedWithFormat(for: language, arguments: tuple.arguments)
XCTAssertNotNil(translation, "Failed to get translation for key '\(tuple.key)' in language '\(language)'.")
print("Translation: \(tuple.key) = \(translation ?? "FAILED")")
XCTAssertNotEqual(translation, "__MISSING__", "Missing translation for key '\(tuple.key)' in language '\(language)'.")
let regex = /%(?:\d+\$)?[+-]?(?:[lh]{0,2})(?:[qLztj])?(?:[ 0]|'.{1})?\d*(?:\\.\d?)?[@dDiuUxXoOfeEgGcCsSpaAFn]/
let numberOfMatches = translation.matches(of: regex).count
XCTAssertEqual(numberOfMatches, tuple.arguments.count, "Invalid number of format specifiers for key '\(tuple.key)' in language '\(language)'.")

let translationResult = tuple.key.localizedWithFormat(for: language, arguments: tuple.arguments)
XCTAssertNotNil(translationResult, "Failed to get translation for key '\(tuple.key)' in language '\(language)'.")
print("Translation: \(tuple.key) = \(translation)")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,10 @@ class ObservableOpenHABWidget: NSObject, MKAnnotation, Identifiable, ObservableO
}

// Text between square brackets
var labelValue: String? {
// Swift 5 raw strings
let regex = try? NSRegularExpression(pattern: #"\[(.*?)\]"#, options: [])
guard let match = regex?.firstMatch(in: label, options: [], range: NSRange(location: 0, length: (label as NSString).length)) else { return nil }
guard let range = Range(match.range(at: 1), in: label) else { return nil }
return String(label[range])
public var labelValue: String? {
let pattern = /\[(.*?)\]/.dotMatchesNewlines()
guard let firstMatch = label.firstMatch(of: pattern) else { return nil }
return String(firstMatch.1)
}

var coordinate: CLLocationCoordinate2D {
Expand Down

0 comments on commit 3ed161e

Please sign in to comment.