aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoopesh Chander <roop@roopc.net>2019-03-28 19:28:27 +0530
committerRoopesh Chander <roop@roopc.net>2019-03-28 19:28:27 +0530
commit6175de0438b2e501815aec7cd625babf1f48387d (patch)
tree103ca327b4c88b9c565e7d06d0de81816e6dbd77
parentiOS: Xcode: Minor project rearrangement (diff)
downloadwireguard-apple-6175de0438b2e501815aec7cd625babf1f48387d.tar.xz
wireguard-apple-6175de0438b2e501815aec7cd625babf1f48387d.zip
iOS: Ability to view the log
Signed-off-by: Roopesh Chander <roop@roopc.net>
-rw-r--r--WireGuard/WireGuard.xcodeproj/project.pbxproj4
-rw-r--r--WireGuard/WireGuard/Base.lproj/Localizable.strings10
-rw-r--r--WireGuard/WireGuard/UI/iOS/ViewController/LogViewController.swift127
-rw-r--r--WireGuard/WireGuard/UI/iOS/ViewController/SettingsTableViewController.swift47
4 files changed, 146 insertions, 42 deletions
diff --git a/WireGuard/WireGuard.xcodeproj/project.pbxproj b/WireGuard/WireGuard.xcodeproj/project.pbxproj
index 53bc8c1..9ae6f0f 100644
--- a/WireGuard/WireGuard.xcodeproj/project.pbxproj
+++ b/WireGuard/WireGuard.xcodeproj/project.pbxproj
@@ -160,6 +160,7 @@
6FDB3C3C21DCF6BB00A0C0BF /* TunnelViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C3C217F09E9003482A3 /* TunnelViewModel.swift */; };
6FDB6D13224A15BF00EE4BC3 /* LogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDB6D12224A15BE00EE4BC3 /* LogViewController.swift */; };
6FDB6D15224CB2CE00EE4BC3 /* LogViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDB6D14224CB2CE00EE4BC3 /* LogViewCell.swift */; };
+ 6FDB6D18224CC05A00EE4BC3 /* LogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDB6D16224CC04E00EE4BC3 /* LogViewController.swift */; };
6FDEF7E421846C1A00D8FBF6 /* libwg-go.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6FDEF7E321846C1A00D8FBF6 /* libwg-go.a */; };
6FDEF7E62185EFB200D8FBF6 /* QRScanViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDEF7E52185EFAF00D8FBF6 /* QRScanViewController.swift */; };
6FDEF7FB21863B6100D8FBF6 /* unzip.c in Sources */ = {isa = PBXBuildFile; fileRef = 6FDEF7F621863B6100D8FBF6 /* unzip.c */; };
@@ -364,6 +365,7 @@
6FDB3C3A21DCF47400A0C0BF /* TunnelDetailTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelDetailTableViewController.swift; sourceTree = "<group>"; };
6FDB6D12224A15BE00EE4BC3 /* LogViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogViewController.swift; sourceTree = "<group>"; };
6FDB6D14224CB2CE00EE4BC3 /* LogViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewCell.swift; sourceTree = "<group>"; };
+ 6FDB6D16224CC04E00EE4BC3 /* LogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewController.swift; sourceTree = "<group>"; };
6FDEF7E321846C1A00D8FBF6 /* libwg-go.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libwg-go.a"; sourceTree = BUILT_PRODUCTS_DIR; };
6FDEF7E52185EFAF00D8FBF6 /* QRScanViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRScanViewController.swift; sourceTree = "<group>"; };
6FDEF7F621863B6100D8FBF6 /* unzip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unzip.c; sourceTree = "<group>"; };
@@ -461,6 +463,7 @@
6F7774DF217181B1006A79B3 /* MainViewController.swift */,
6F8F0D7622267C57000E8335 /* SSIDOptionEditTableViewController.swift */,
6F9B8A8D223398610041B9C4 /* SSIDOptionDetailTableViewController.swift */,
+ 6FDB6D16224CC04E00EE4BC3 /* LogViewController.swift */,
);
path = ViewController;
sourceTree = "<group>";
@@ -1343,6 +1346,7 @@
6B586C53220CBA6D00427C51 /* Data+KeyEncoding.swift in Sources */,
6F693A562179E556008551C1 /* Endpoint.swift in Sources */,
6FDEF7E62185EFB200D8FBF6 /* QRScanViewController.swift in Sources */,
+ 6FDB6D18224CC05A00EE4BC3 /* LogViewController.swift in Sources */,
6FFA5D952194454A0001E2F7 /* NETunnelProviderProtocol+Extension.swift in Sources */,
5FF7B96221CC95DE00A7DD74 /* InterfaceConfiguration.swift in Sources */,
5F4541A921C451D100994C13 /* TunnelStatus.swift in Sources */,
diff --git a/WireGuard/WireGuard/Base.lproj/Localizable.strings b/WireGuard/WireGuard/Base.lproj/Localizable.strings
index 0872596..c4e9cb1 100644
--- a/WireGuard/WireGuard/Base.lproj/Localizable.strings
+++ b/WireGuard/WireGuard/Base.lproj/Localizable.strings
@@ -209,10 +209,14 @@
"settingsSectionTitleExportConfigurations" = "Export configurations";
"settingsExportZipButtonTitle" = "Export zip archive";
-"settingsSectionTitleTunnelLog" = "Tunnel log";
-"settingsExportLogFileButtonTitle" = "Export log file";
+"settingsSectionTitleTunnelLog" = "Log";
+"settingsViewLogButtonTitle" = "View log";
-// Settings alerts
+// Log view
+
+"logViewTitle" = "Log";
+
+// Log alerts
"alertUnableToRemovePreviousLogTitle" = "Log export failed";
"alertUnableToRemovePreviousLogMessage" = "The pre-existing log could not be cleared";
diff --git a/WireGuard/WireGuard/UI/iOS/ViewController/LogViewController.swift b/WireGuard/WireGuard/UI/iOS/ViewController/LogViewController.swift
new file mode 100644
index 0000000..bcfbaf5
--- /dev/null
+++ b/WireGuard/WireGuard/UI/iOS/ViewController/LogViewController.swift
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
+
+import UIKit
+
+class LogViewController: UIViewController {
+
+ let textView: UITextView = {
+ let textView = UITextView()
+ textView.isEditable = false
+ textView.isSelectable = false
+ textView.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)
+ textView.adjustsFontForContentSizeCategory = true
+ return textView
+ }()
+
+ let busyIndicator: UIActivityIndicatorView = {
+ let busyIndicator = UIActivityIndicatorView(style: .gray)
+ busyIndicator.hidesWhenStopped = true
+ return busyIndicator
+ }()
+
+ var logViewHelper: LogViewHelper?
+ var isFetchingLogEntries = false
+ private var updateLogEntriesTimer: Timer?
+
+ override func loadView() {
+ view = UIView()
+ view.backgroundColor = .white
+
+ view.addSubview(textView)
+ textView.translatesAutoresizingMaskIntoConstraints = false
+ NSLayoutConstraint.activate([
+ textView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
+ textView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
+ textView.topAnchor.constraint(equalTo: view.topAnchor),
+ textView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
+ ])
+
+ view.addSubview(busyIndicator)
+ busyIndicator.translatesAutoresizingMaskIntoConstraints = false
+ NSLayoutConstraint.activate([
+ busyIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),
+ busyIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor)
+ ])
+
+ busyIndicator.startAnimating()
+
+ logViewHelper = LogViewHelper(logFilePath: FileManager.logFileURL?.path)
+ startUpdatingLogEntries()
+ }
+
+ override func viewDidLoad() {
+ title = tr("logViewTitle")
+ navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(saveTapped(sender:)))
+ }
+
+ func updateLogEntries() {
+ guard !isFetchingLogEntries else { return }
+ isFetchingLogEntries = true
+ logViewHelper?.fetchLogEntriesSinceLastFetch { [weak self] fetchedLogEntries in
+ guard let self = self else { return }
+ defer {
+ self.isFetchingLogEntries = false
+ }
+ if self.busyIndicator.isAnimating {
+ self.busyIndicator.stopAnimating()
+ }
+ guard !fetchedLogEntries.isEmpty else { return }
+ let isScrolledToEnd = self.textView.contentSize.height - self.textView.bounds.height - self.textView.contentOffset.y < 1
+ let text = fetchedLogEntries.reduce("") { $0 + $1.text() + "\n" }
+ self.textView.insertText(text)
+ if isScrolledToEnd {
+ let endOfCurrentText = NSRange(location: (self.textView.text as NSString).length, length: 0)
+ self.textView.scrollRangeToVisible(endOfCurrentText)
+ }
+ }
+ }
+
+ func startUpdatingLogEntries() {
+ updateLogEntries()
+ updateLogEntriesTimer?.invalidate()
+ let timer = Timer(timeInterval: 1 /* second */, repeats: true) { [weak self] _ in
+ self?.updateLogEntries()
+ }
+ updateLogEntriesTimer = timer
+ RunLoop.main.add(timer, forMode: .common)
+ }
+
+ @objc func saveTapped(sender: AnyObject) {
+ guard let destinationDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
+
+ let dateFormatter = ISO8601DateFormatter()
+ dateFormatter.formatOptions = [.withFullDate, .withTime, .withTimeZone] // Avoid ':' in the filename
+ let timeStampString = dateFormatter.string(from: Date())
+ let destinationURL = destinationDir.appendingPathComponent("wireguard-log-\(timeStampString).txt")
+
+ DispatchQueue.global(qos: .userInitiated).async {
+
+ if FileManager.default.fileExists(atPath: destinationURL.path) {
+ let isDeleted = FileManager.deleteFile(at: destinationURL)
+ if !isDeleted {
+ ErrorPresenter.showErrorAlert(title: tr("alertUnableToRemovePreviousLogTitle"), message: tr("alertUnableToRemovePreviousLogMessage"), from: self)
+ return
+ }
+ }
+
+ let isWritten = Logger.global?.writeLog(to: destinationURL.path) ?? false
+
+ DispatchQueue.main.async {
+ guard isWritten else {
+ ErrorPresenter.showErrorAlert(title: tr("alertUnableToWriteLogTitle"), message: tr("alertUnableToWriteLogMessage"), from: self)
+ return
+ }
+ let activityVC = UIActivityViewController(activityItems: [destinationURL], applicationActivities: nil)
+ if let sender = sender as? UIBarButtonItem {
+ activityVC.popoverPresentationController?.barButtonItem = sender
+ }
+ activityVC.completionWithItemsHandler = { _, _, _, _ in
+ // Remove the exported log file after the activity has completed
+ _ = FileManager.deleteFile(at: destinationURL)
+ }
+ self.present(activityVC, animated: true)
+ }
+ }
+ }
+}
diff --git a/WireGuard/WireGuard/UI/iOS/ViewController/SettingsTableViewController.swift b/WireGuard/WireGuard/UI/iOS/ViewController/SettingsTableViewController.swift
index ff83b2c..9956b7b 100644
--- a/WireGuard/WireGuard/UI/iOS/ViewController/SettingsTableViewController.swift
+++ b/WireGuard/WireGuard/UI/iOS/ViewController/SettingsTableViewController.swift
@@ -10,14 +10,14 @@ class SettingsTableViewController: UITableViewController {
case iosAppVersion
case goBackendVersion
case exportZipArchive
- case exportLogFile
+ case viewLog
var localizedUIString: String {
switch self {
case .iosAppVersion: return tr("settingsVersionKeyWireGuardForIOS")
case .goBackendVersion: return tr("settingsVersionKeyWireGuardGoBackend")
case .exportZipArchive: return tr("settingsExportZipButtonTitle")
- case .exportLogFile: return tr("settingsExportLogFileButtonTitle")
+ case .viewLog: return tr("settingsViewLogButtonTitle")
}
}
}
@@ -25,7 +25,7 @@ class SettingsTableViewController: UITableViewController {
let settingsFieldsBySection: [[SettingsFields]] = [
[.iosAppVersion, .goBackendVersion],
[.exportZipArchive],
- [.exportLogFile]
+ [.viewLog]
]
let tunnelsManager: TunnelsManager?
@@ -108,41 +108,10 @@ class SettingsTableViewController: UITableViewController {
}
}
- func exportLogForLastActivatedTunnel(sourceView: UIView) {
- guard let destinationDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
+ func presentLogView() {
+ let logVC = LogViewController()
+ navigationController?.pushViewController(logVC, animated: true)
- let dateFormatter = ISO8601DateFormatter()
- dateFormatter.formatOptions = [.withFullDate, .withTime, .withTimeZone] // Avoid ':' in the filename
- let timeStampString = dateFormatter.string(from: Date())
- let destinationURL = destinationDir.appendingPathComponent("wireguard-log-\(timeStampString).txt")
-
- DispatchQueue.global(qos: .userInitiated).async {
-
- if FileManager.default.fileExists(atPath: destinationURL.path) {
- let isDeleted = FileManager.deleteFile(at: destinationURL)
- if !isDeleted {
- ErrorPresenter.showErrorAlert(title: tr("alertUnableToRemovePreviousLogTitle"), message: tr("alertUnableToRemovePreviousLogMessage"), from: self)
- return
- }
- }
-
- let isWritten = Logger.global?.writeLog(to: destinationURL.path) ?? false
-
- DispatchQueue.main.async {
- guard isWritten else {
- ErrorPresenter.showErrorAlert(title: tr("alertUnableToWriteLogTitle"), message: tr("alertUnableToWriteLogMessage"), from: self)
- return
- }
- let activityVC = UIActivityViewController(activityItems: [destinationURL], applicationActivities: nil)
- activityVC.popoverPresentationController?.sourceView = sourceView
- activityVC.popoverPresentationController?.sourceRect = sourceView.bounds
- activityVC.completionWithItemsHandler = { _, _, _, _ in
- // Remove the exported log file after the activity has completed
- _ = FileManager.deleteFile(at: destinationURL)
- }
- self.present(activityVC, animated: true)
- }
- }
}
}
@@ -192,11 +161,11 @@ extension SettingsTableViewController {
}
return cell
} else {
- assert(field == .exportLogFile)
+ assert(field == .viewLog)
let cell: ButtonCell = tableView.dequeueReusableCell(for: indexPath)
cell.buttonText = field.localizedUIString
cell.onTapped = { [weak self] in
- self?.exportLogForLastActivatedTunnel(sourceView: cell.button)
+ self?.presentLogView()
}
return cell
}