Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,31 @@ jobs:
build:
name: CI/CD
runs-on: macos-latest
permissions:
actions: read
contents: read
security-events: write

steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4

- name: Install tooling
run: |
brew install swiftlint

- name: Generate SwiftLint SARIF
if: ${{ !cancelled() }}
run: |
swiftlint lint --no-cache --quiet --config GenericApp/.swiftlint.yml --reporter sarif GenericApp/GenericApp GenericApp/NotificationService > swiftlint.sarif

- name: Upload SwiftLint SARIF
if: ${{ always() && !cancelled() && hashFiles('swiftlint.sarif') != '' }}
uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
with:
sarif_file: swiftlint.sarif
category: swiftlint

- name: Install App Store Connect API key
env:
APP_STORE_CONNECT_PRIVATE_KEY: ${{ secrets.APP_STORE_CONNECT_PRIVATE_KEY }}
Expand Down Expand Up @@ -72,4 +92,4 @@ jobs:
# with:
# name: ipas
# path: |
# **/*.ipa
# **/*.ipa
4 changes: 4 additions & 0 deletions GenericApp/.swiftlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
disabled_rules:
- trailing_comma
- blanket_disable_command
line_length: 180
49 changes: 49 additions & 0 deletions GenericApp/GenericApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 878D2D1D249A490400011484 /* Build configuration list for PBXNativeTarget "GenericApp" */;
buildPhases = (
917687E22F8E939C00D977BD /* SwiftLint */,
878D2D05249A490300011484 /* Sources */,
878D2D06249A490300011484 /* Frameworks */,
878D2D07249A490300011484 /* Resources */,
Expand All @@ -218,6 +219,7 @@
878D2D20249A498900011484 /* Sources */,
878D2D21249A498900011484 /* Frameworks */,
878D2D22249A498900011484 /* Resources */,
917687E32F8E94E100D977BD /* SwiftLint */,
);
buildRules = (
);
Expand Down Expand Up @@ -293,6 +295,53 @@
};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
917687E22F8E939C00D977BD /* SwiftLint */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"$(SRCROOT)/GenericApp",
"$(SRCROOT)/.swiftlint.yml",
);
name = SwiftLint;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export PATH=\"/opt/homebrew/bin:/usr/local/bin:$PATH\"\n\nif [ \"${CI}\" = \"true\" ] || [ \"${GITHUB_ACTIONS}\" = \"true\" ]; then\n echo \"Skipping Xcode SwiftLint phase in CI.\"\n exit 0\nfi\n\nif command -v swiftlint >/dev/null; then\n swiftlint lint --no-cache --config \"${SRCROOT}/.swiftlint.yml\" --reporter xcode \"${SRCROOT}/GenericApp\" || true\nelse\n echo \"warning: SwiftLint not installed. Run 'brew install swiftlint' to enable local linting.\"\nfi\n";
showEnvVarsInLog = 0;
};
917687E32F8E94E100D977BD /* SwiftLint */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"$(SRCROOT)/NotificationService",
"$(SRCROOT)/.swiftlint.yml",
);
name = SwiftLint;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export PATH=\"/opt/homebrew/bin:/usr/local/bin:$PATH\"\n\nif [ \"${CI}\" = \"true\" ] || [ \"${GITHUB_ACTIONS}\" = \"true\" ]; then\n echo \"Skipping Xcode SwiftLint phase in CI.\"\n exit 0\nfi\n\nif command -v swiftlint >/dev/null; then\n swiftlint lint --no-cache --config \"${SRCROOT}/.swiftlint.yml\" --reporter xcode \"${SRCROOT}/NotificationService\" || true\nelse\n echo \"warning: SwiftLint not installed. Run 'brew install swiftlint' to enable local linting.\"\nfi\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
878D2D05249A490300011484 /* Sources */ = {
isa = PBXSourcesBuildPhase;
Expand Down
125 changes: 65 additions & 60 deletions GenericApp/GenericApp/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,19 @@
var window: UIWindow?
let gcmMessageIDKey = "gcm.message_id"

var geofenceProvider : GeofenceProvider?
var geofenceProvider: GeofenceProvider?
var fcmToken: String?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UIBarButtonItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor(named: "or_green") as Any], for: .normal)
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor(named: "or_green") as Any]
UIBarButtonItem.appearance().tintColor = UIColor(named: "or_green")

IQKeyboardManager.shared.isEnabled = true

UNUserNotificationCenter.current().delegate = self
// if the app was launched because of geofencing


// Redirects NSLog calls to file
if let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileName = "\(Date()).log"
Expand Down Expand Up @@ -87,11 +86,9 @@
}

func applicationWillResignActive(_ application: UIApplication) {

}

func applicationDidEnterBackground(_ application: UIApplication) {

}

func applicationWillEnterForeground(_ application: UIApplication) {
Expand All @@ -107,7 +104,9 @@
Messaging.messaging().apnsToken = deviceToken
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
func application(_ application: UIApplication,

Check warning on line 107 in GenericApp/GenericApp/AppDelegate.swift

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unused function parameter "application" or name it "_".

See more on https://sonarcloud.io/project/issues?id=openremote_console-ios-app&issues=AZ2QUEYh9HqeQjTAfAxG&open=AZ2QUEYh9HqeQjTAfAxG&pullRequest=42
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if let action = userInfo[DefaultsKey.actionKey] as? String {
if action == Actions.geofenceRefresh {
if let controllerGeofenceProvider = (self.window?.topController as? ORViewcontroller)?.geofenceProvider {
Expand Down Expand Up @@ -141,47 +140,55 @@
completionHandler(UIBackgroundFetchResult.newData)
}

func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
func urlSession(_ session: URLSession,

Check warning on line 143 in GenericApp/GenericApp/AppDelegate.swift

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unused function parameter "session" or name it "_".

See more on https://sonarcloud.io/project/issues?id=openremote_console-ios-app&issues=AZ2QUEYh9HqeQjTAfAxH&open=AZ2QUEYh9HqeQjTAfAxH&pullRequest=42
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
completionHandler(.performDefaultHandling, nil)
}
}
}

extension AppDelegate : UNUserNotificationCenterDelegate {
extension AppDelegate: UNUserNotificationCenterDelegate {

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
func userNotificationCenter(_ center: UNUserNotificationCenter,

Check warning on line 154 in GenericApp/GenericApp/AppDelegate.swift

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unused function parameter "center" or name it "_".

See more on https://sonarcloud.io/project/issues?id=openremote_console-ios-app&issues=AZ2QUEYh9HqeQjTAfAxI&open=AZ2QUEYh9HqeQjTAfAxI&pullRequest=42
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
var notificationId : Int64? = nil
var notificationId: Int64?

if let notificationIdString = userInfo[ActionType.notificationId] as? String{
if let notificationIdString = userInfo[ActionType.notificationId] as? String {
notificationId = Int64(notificationIdString)
}
if let notiId = notificationId, let defaults = UserDefaults(suiteName: DefaultsKey.groupEntitlement), let consoleId = defaults.string(forKey: GeofenceProvider.consoleIdKey) {
if let notiId = notificationId,
let defaults = UserDefaults(suiteName: DefaultsKey.groupEntitlement),
let consoleId = defaults.string(forKey: GeofenceProvider.consoleIdKey) {
ORNotificationResource.sharedInstance.notificationDelivered(notificationId: notiId, targetId: consoleId)
}

completionHandler([.banner, .sound])
}

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

// swiftlint:disable:next cyclomatic_complexity function_body_length
func userNotificationCenter(_ center: UNUserNotificationCenter,

Check warning on line 173 in GenericApp/GenericApp/AppDelegate.swift

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unused function parameter "center" or name it "_".

See more on https://sonarcloud.io/project/issues?id=openremote_console-ios-app&issues=AZ2QUEYh9HqeQjTAfAxJ&open=AZ2QUEYh9HqeQjTAfAxJ&pullRequest=42
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
var notificationId : Int64? = nil
var consoleId : String?
var notificationId: Int64?
var consoleId: String?
var project: ProjectConfig?

if let notificationIdString = userInfo[ActionType.notificationId] as? String{
if let notificationIdString = userInfo[ActionType.notificationId] as? String {
notificationId = Int64(notificationIdString)
}

if let userDefaults = UserDefaults(suiteName: DefaultsKey.groupEntitlement) {
consoleId = userDefaults.string(forKey: GeofenceProvider.consoleIdKey) // TODO: geofence provider should also be adapted to store "per project"

let selectedProjectId = userDefaults.string(forKey: DefaultsKey.projectKey)
if let projectsData = userDefaults.data(forKey: DefaultsKey.projectsConfigurationKey) {
let projects = (try? JSONDecoder().decode([ProjectConfig].self, from: projectsData)) ?? []
project = projects.first(where:{ $0.id == selectedProjectId })
project = projects.first(where: { $0.id == selectedProjectId })
}
}

Expand All @@ -192,19 +199,19 @@
if let urlTo = userInfo[ActionType.appUrl] as? String, !urlTo.isEmpty {
var urlRequest: URL?
if urlTo.hasPrefix("http") || urlTo.hasPrefix("https") {
urlRequest = URL(string:urlTo)
urlRequest = URL(string: urlTo)
} else {
if let url = project?.baseURL {
urlRequest = URL(string: "\(url)/console/\(urlTo)")
}
}
if let url = urlRequest{
if let InBrowser = userInfo[ActionType.openInBrowser] as? Bool, InBrowser {
if let url = urlRequest {
if let inBrowser = userInfo[ActionType.openInBrowser] as? Bool, inBrowser {
NSLog("%@", " in browser: \(url)")
UIApplication.shared.open(url)
} else {
NSLog("%@", " in app: \(url)")
(self.window?.topController as? ORViewcontroller)?.loadURL(url:url)
(self.window?.topController as? ORViewcontroller)?.loadURL(url: url)
}
}
}
Expand All @@ -213,50 +220,48 @@
if let notiId = notificationId, let conId = consoleId {
ORNotificationResource.sharedInstance.notificationAcknowledged(notificationId: notiId, targetId: conId, acknowledgement: response.actionIdentifier)
}
default :
default:
if let notiId = notificationId, let conId = consoleId {
ORNotificationResource.sharedInstance.notificationAcknowledged(notificationId: notiId, targetId: conId, acknowledgement: response.actionIdentifier)
}
if let buttonsString = userInfo[DefaultsKey.buttonsKey] as? String {
if let buttonsData = buttonsString.data(using: .utf8) {
if let buttons = try? JSONDecoder().decode([ORPushNotificationButton].self, from: buttonsData) {
for button in buttons {
if button.title == response.actionIdentifier {
if let action = button.action {
var urlRequest: URL?
if action.url.hasPrefix("http") || action.url.hasPrefix("https") {
urlRequest = URL(string:action.url)
} else {
if let url = project?.baseURL {
urlRequest = URL(string: "\(url)/console/\(action.url)")
}
for button in buttons where button.title == response.actionIdentifier {
if let action = button.action {
var urlRequest: URL?
if action.url.hasPrefix("http") || action.url.hasPrefix("https") {
urlRequest = URL(string: action.url)
} else {
if let url = project?.baseURL {
urlRequest = URL(string: "\(url)/console/\(action.url)")
}
if let url = urlRequest {
if action.silent {
let request = NSMutableURLRequest(url: url)
request.httpMethod = action.httpMethod ?? "GET"
if let body = action.data {
request.httpBody = body.data(using: .utf8)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
}
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: nil, delegateQueue : nil)
let reqDataTask = session.dataTask(with: request as URLRequest, completionHandler:{ data, response, error in
if (error != nil) {
NSLog("error %@", (error! as NSError).localizedDescription)
}
})
reqDataTask.resume()
} else if action.openInBrowser {
NSLog("%@", " in browser: \(url)")
UIApplication.shared.open(url)
} else {
NSLog("%@", " in app: \(url)")
(self.window?.topController as? ORViewcontroller)?.loadURL(url:url)
}
if let url = urlRequest {
if action.silent {
let request = NSMutableURLRequest(url: url)
request.httpMethod = action.httpMethod ?? "GET"
if let body = action.data {
request.httpBody = body.data(using: .utf8)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
}
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: nil, delegateQueue: nil)
let reqDataTask = session.dataTask(with: request as URLRequest, completionHandler: { _, _, error in
if error != nil {
NSLog("error %@", (error! as NSError).localizedDescription)
}
})
reqDataTask.resume()
} else if action.openInBrowser {
NSLog("%@", " in browser: \(url)")
UIApplication.shared.open(url)
} else {
NSLog("%@", " in app: \(url)")
(self.window?.topController as? ORViewcontroller)?.loadURL(url: url)
}
}
break
}
break
}
}
}
Expand All @@ -266,10 +271,10 @@
}
}

extension AppDelegate : MessagingDelegate {
extension AppDelegate: MessagingDelegate {
func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
if let defaults = UserDefaults(suiteName: DefaultsKey.groupEntitlement){
if let defaults = UserDefaults(suiteName: DefaultsKey.groupEntitlement) {
defaults.set(fcmToken, forKey: DefaultsKey.fcmTokenKey)
defaults.synchronize()
}
Expand Down
5 changes: 2 additions & 3 deletions GenericApp/GenericApp/UI/ProjectTableViewCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@
import UIKit
import ORLib


class ProjectTableViewCell: UITableViewCell {

@IBOutlet weak var domainLabel: UILabel!
@IBOutlet weak var appLabel: UILabel!
@IBOutlet weak var realmLabel: UILabel!

var project: ProjectConfig?

func setProject(_ project: ProjectConfig) {
self.project = project
domainLabel.text = project.domain
Expand Down
Loading