mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-27 10:07:40 +00:00
Merge pull request #10446 from keymanapp/chore/ios/8737-remove-xcglogger
chore(ios): eliminate xcglogger dependency
This commit is contained in:
commit
2ede49035a
63 changed files with 736 additions and 497 deletions
|
|
@ -1,5 +1,4 @@
|
|||
github "weichsel/ZIPFoundation" ~> 0.9
|
||||
github "keymanapp/dependency-XCGLogger" "master"
|
||||
github "devicekit/DeviceKit" ~> 5.0
|
||||
github "ashleymills/Reachability.swift"
|
||||
github "getsentry/sentry-cocoa" ~> 8.7.0
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
github "ashleymills/Reachability.swift" "v5.1.0"
|
||||
github "devicekit/DeviceKit" "5.1.0"
|
||||
github "getsentry/sentry-cocoa" "8.15.2"
|
||||
github "keymanapp/dependency-XCGLogger" "57a7b975dbb6fe4fe90cef3d1bc52b8adbd89113"
|
||||
github "weichsel/ZIPFoundation" "0.9.17"
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
1645D5972036C9F80076C51B /* KMPKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1645D5962036C9F80076C51B /* KMPKeyboard.swift */; };
|
||||
165EB3A12098993900040A69 /* KeyboardError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165EB3A02098993900040A69 /* KeyboardError.swift */; };
|
||||
296EF2C72AFA26C700E3E384 /* ZIPFoundation.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 296EF2C62AFA26C700E3E384 /* ZIPFoundation.xcframework */; };
|
||||
29B30C232B564F9900C342A4 /* KeymanEngineLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B30C222B564F9900C342A4 /* KeymanEngineLogger.swift */; };
|
||||
377D10DE26846B8900467431 /* SpacebarTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377D10DD26846B8900467431 /* SpacebarTextViewController.swift */; };
|
||||
6CD5DFAA150F6DC8007A5DDE /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 6CD5DFA8150F6DC8007A5DDE /* icon.png */; };
|
||||
6CD5DFAB150F6DC8007A5DDE /* icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6CD5DFA9150F6DC8007A5DDE /* icon@2x.png */; };
|
||||
|
|
@ -70,7 +71,6 @@
|
|||
C0452BAD1F9F21270064431A /* Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0452BAC1F9F21270064431A /* Keyboard.swift */; };
|
||||
C0452BAF1F9F22A80064431A /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0452BAE1F9F22A80064431A /* Font.swift */; };
|
||||
C055E6EB1F99ED090035C2DD /* RegisteredFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = C055E6EA1F99ED090035C2DD /* RegisteredFont.swift */; };
|
||||
C05B14331FD914870082A316 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = C05B14321FD914870082A316 /* Log.swift */; };
|
||||
C05F43311FBD62550058CBD4 /* JSONDecoder.DateDecodingStrategy+ISO8601Fallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = C05F43301FBD62550058CBD4 /* JSONDecoder.DateDecodingStrategy+ISO8601Fallback.swift */; };
|
||||
C06085B41F9485E40057E5B9 /* UIButton+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06085B31F9485E40057E5B9 /* UIButton+Helpers.swift */; };
|
||||
C06D37341F81F5C300F61AE0 /* HTTPDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED71B41F6BBFAF002A2FD6 /* HTTPDownloader.swift */; };
|
||||
|
|
@ -121,10 +121,8 @@
|
|||
CE46D65D247B93C9005FD506 /* (null) in Resources */ = {isa = PBXBuildFile; };
|
||||
CE5C8BE324B5B3BA00FAFB7F /* Queries+LexicalModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5C8BE224B5B3BA00FAFB7F /* Queries+LexicalModel.swift */; };
|
||||
CE5C8BE524B5BD1C00FAFB7F /* QueryModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5C8BE424B5BD1C00FAFB7F /* QueryModelTests.swift */; };
|
||||
CE5EDDC526522EAA001733AC /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC026522EA9001733AC /* XCGLogger.xcframework */; };
|
||||
CE5EDDC726522EAA001733AC /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC226522EAA001733AC /* Sentry.xcframework */; };
|
||||
CE5EDDC826522EAA001733AC /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC326522EAA001733AC /* Reachability.xcframework */; };
|
||||
CE5EDDC926522EAA001733AC /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC426522EAA001733AC /* ObjcExceptionBridging.xcframework */; };
|
||||
CE5EDDD52652372A001733AC /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDBB26522EA3001733AC /* DeviceKit.xcframework */; };
|
||||
CE67D961228A6F190029F2B5 /* KeyboardCommandStructs.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE67D960228A6F190029F2B5 /* KeyboardCommandStructs.swift */; };
|
||||
CE71705823A9C14D00A924A1 /* ResourceFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE71705723A9C14D00A924A1 /* ResourceFileManager.swift */; };
|
||||
|
|
@ -327,6 +325,7 @@
|
|||
29B27FEF29062CF50036917E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/ResourceInfoView.strings; sourceTree = "<group>"; };
|
||||
29B27FF029062D100036917E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
29B27FF129062D190036917E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nl; path = nl.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
29B30C222B564F9900C342A4 /* KeymanEngineLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeymanEngineLogger.swift; sourceTree = "<group>"; };
|
||||
29BFA75E28293287009FCCC3 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/ResourceInfoView.strings; sourceTree = "<group>"; };
|
||||
29BFA75F282934B4009FCCC3 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
29BFA760282934BB009FCCC3 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = it; path = it.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
|
|
@ -391,7 +390,6 @@
|
|||
C0452BAC1F9F21270064431A /* Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keyboard.swift; sourceTree = "<group>"; };
|
||||
C0452BAE1F9F22A80064431A /* Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = "<group>"; };
|
||||
C055E6EA1F99ED090035C2DD /* RegisteredFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisteredFont.swift; sourceTree = "<group>"; };
|
||||
C05B14321FD914870082A316 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
|
||||
C05F43301FBD62550058CBD4 /* JSONDecoder.DateDecodingStrategy+ISO8601Fallback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSONDecoder.DateDecodingStrategy+ISO8601Fallback.swift"; sourceTree = "<group>"; };
|
||||
C06085B31F9485E40057E5B9 /* UIButton+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Helpers.swift"; sourceTree = "<group>"; };
|
||||
C06D372B1F81F4E100F61AE0 /* KeymanEngine.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KeymanEngine.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
|
@ -436,10 +434,8 @@
|
|||
CE5C8BE224B5B3BA00FAFB7F /* Queries+LexicalModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Queries+LexicalModel.swift"; sourceTree = "<group>"; };
|
||||
CE5C8BE424B5BD1C00FAFB7F /* QueryModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryModelTests.swift; sourceTree = "<group>"; };
|
||||
CE5EDDBB26522EA3001733AC /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = ../../Carthage/Build/DeviceKit.xcframework; sourceTree = "<group>"; };
|
||||
CE5EDDC026522EA9001733AC /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = ../../Carthage/Build/XCGLogger.xcframework; sourceTree = "<group>"; };
|
||||
CE5EDDC226522EAA001733AC /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = "<group>"; };
|
||||
CE5EDDC326522EAA001733AC /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = ../../Carthage/Build/Reachability.xcframework; sourceTree = "<group>"; };
|
||||
CE5EDDC426522EAA001733AC /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = ../../Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = "<group>"; };
|
||||
CE67D960228A6F190029F2B5 /* KeyboardCommandStructs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardCommandStructs.swift; sourceTree = "<group>"; };
|
||||
CE71705723A9C14D00A924A1 /* ResourceFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourceFileManager.swift; sourceTree = "<group>"; };
|
||||
CE71705E23A9C97F00A924A1 /* PackageInstallViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PackageInstallViewController.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -561,11 +557,9 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
296EF2C72AFA26C700E3E384 /* ZIPFoundation.xcframework in Frameworks */,
|
||||
CE5EDDC526522EAA001733AC /* XCGLogger.xcframework in Frameworks */,
|
||||
CE5EDDD52652372A001733AC /* DeviceKit.xcframework in Frameworks */,
|
||||
CE5EDDC726522EAA001733AC /* Sentry.xcframework in Frameworks */,
|
||||
CE5EDDC826522EAA001733AC /* Reachability.xcframework in Frameworks */,
|
||||
CE5EDDC926522EAA001733AC /* ObjcExceptionBridging.xcframework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -950,10 +944,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
296EF2C62AFA26C700E3E384 /* ZIPFoundation.xcframework */,
|
||||
CE5EDDC426522EAA001733AC /* ObjcExceptionBridging.xcframework */,
|
||||
CE5EDDC326522EAA001733AC /* Reachability.xcframework */,
|
||||
CE5EDDC226522EAA001733AC /* Sentry.xcframework */,
|
||||
CE5EDDC026522EA9001733AC /* XCGLogger.xcframework */,
|
||||
CE5EDDBB26522EA3001733AC /* DeviceKit.xcframework */,
|
||||
9A079DE52231A69D00581263 /* Foundation.framework */,
|
||||
);
|
||||
|
|
@ -1000,10 +992,10 @@
|
|||
CE7A26DA23CEEF640005955C /* Colors.swift */,
|
||||
CE7A26D723CEEC630005955C /* Colors+Extension.swift */,
|
||||
C0959CD31F99C44E00B616BC /* Constants.swift */,
|
||||
29B30C222B564F9900C342A4 /* KeymanEngineLogger.swift */,
|
||||
CEE0321224C58C90005BFC73 /* KeymanHosts.swift */,
|
||||
C0324B921F87689B00AF3785 /* KeymanURLProtocol.swift */,
|
||||
C0A93A531F8B21240079948B /* Manager.swift */,
|
||||
C05B14321FD914870082A316 /* Log.swift */,
|
||||
CEA70CCE24CAC3AE001C12E6 /* Obsoletions.swift */,
|
||||
CE9E95CC24CE786900F6DD78 /* UniversalLinks.swift */,
|
||||
);
|
||||
|
|
@ -1439,7 +1431,6 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C05B14331FD914870082A316 /* Log.swift in Sources */,
|
||||
9A079E43223B61AE00581263 /* FullLexicalModelID.swift in Sources */,
|
||||
C06D37341F81F5C300F61AE0 /* HTTPDownloader.swift in Sources */,
|
||||
C0452BAF1F9F22A80064431A /* Font.swift in Sources */,
|
||||
|
|
@ -1525,6 +1516,7 @@
|
|||
C06085B41F9485E40057E5B9 /* UIButton+Helpers.swift in Sources */,
|
||||
C0959CD41F99C44E00B616BC /* Constants.swift in Sources */,
|
||||
C0452BAD1F9F21270064431A /* Keyboard.swift in Sources */,
|
||||
29B30C232B564F9900C342A4 /* KeymanEngineLogger.swift in Sources */,
|
||||
CE79B24923C711FF007E72AE /* KeyboardScaleMap.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Reachability
|
||||
import os.log
|
||||
|
||||
open class Alerts {
|
||||
public typealias AcceptanceHandler = ((UIAlertAction)) -> Void
|
||||
|
|
@ -48,8 +49,8 @@ open class Alerts {
|
|||
do {
|
||||
try networkReachable = Reachability(hostname: KeymanHosts.KEYMAN_COM.host!)
|
||||
} catch {
|
||||
log.debug("reachability could not start")
|
||||
}
|
||||
os_log("Reachability could not start", log:KeymanEngineLogger.ui, type: .debug)
|
||||
}
|
||||
|
||||
if networkReachable?.connection == Reachability.Connection.unavailable || networkReachable == nil {
|
||||
showConnectionErrorAlert(in: vc, handler: handler)
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import XCGLogger
|
||||
import Sentry
|
||||
import os.log
|
||||
|
||||
/**
|
||||
* This class centralizes the methods used among the KeymanEngine library, Keyman app, and SWKeyboard app-ex for Sentry-based
|
||||
|
|
@ -26,10 +26,10 @@ public class SentryManager {
|
|||
#if NO_SENTRY
|
||||
// If doing development debugging (and NOT for Sentry code), silence Sentry reporting.
|
||||
let allowEnabled = false
|
||||
log.debug("Sentry error logging disabled for development mode.")
|
||||
os_log("Sentry error logging disabled for development mode.", log: KeymanEngineLogger.settings, type: .debug)
|
||||
#else
|
||||
let allowEnabled = true
|
||||
log.debug("Sentry error logging enabled.")
|
||||
os_log("Sentry error logging enabled.", log: KeymanEngineLogger.settings, type: .debug)
|
||||
#endif
|
||||
|
||||
let infoDict = Bundle(for: SentryManager.self).infoDictionary
|
||||
|
|
@ -90,98 +90,55 @@ public class SentryManager {
|
|||
#endif
|
||||
}
|
||||
|
||||
private static func mapLoggingLevel(_ level: Sentry.SentryLevel) -> XCGLogger.Level {
|
||||
switch(level) {
|
||||
case .none:
|
||||
return XCGLogger.Level.none
|
||||
case .debug:
|
||||
return .debug
|
||||
case .info:
|
||||
return .info
|
||||
case .warning:
|
||||
return .warning
|
||||
case .error:
|
||||
return .error
|
||||
case .fatal:
|
||||
return .severe
|
||||
default:
|
||||
return .info
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures a Sentry event and copies its message to the engine's logging mechanism.
|
||||
* If the logging level is not specified, the Sentry event's log-level will be used as a default.
|
||||
*
|
||||
* Will safely bypass the Sentry component if not activated by the app, only logging the
|
||||
* message in such scenarios.
|
||||
* Captures a Sentry event and safely bypass the Sentry component if not activated by the app.
|
||||
*/
|
||||
public static func captureAndLog(_ event: Sentry.Event, logLevel: XCGLogger.Level? = nil) {
|
||||
public static func capture(_ event: Sentry.Event) {
|
||||
// Guarded in case a library consumer decides against initializing Sentry.
|
||||
if _started {
|
||||
SentrySDK.capture(event: event)
|
||||
}
|
||||
|
||||
let level = logLevel ?? mapLoggingLevel(event.level)
|
||||
log.logln(event.message?.formatted, level: level)
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures a Sentry event and copies its message to the engine's logging mechanism.
|
||||
* If the logging level is not specified, the Sentry event's log-level will be used as a default.
|
||||
*
|
||||
* Will safely bypass the Sentry component if not activated by the app, only logging the
|
||||
* message in such scenarios.
|
||||
* Captures a Sentry event for the specified error. If the logging level is not specified, it will default to .error.
|
||||
* Will safely bypass the Sentry component if not activated by the app.
|
||||
*/
|
||||
public static func captureAndLog(_ error: Error, message: String? = nil, sentryLevel: Sentry.SentryLevel = .error, logLevel: XCGLogger.Level? = nil) {
|
||||
public static func capture(_ error: Error, message: String? = nil, sentryLevel: Sentry.SentryLevel = .error) {
|
||||
let event = Sentry.Event(error: error)
|
||||
event.level = sentryLevel
|
||||
if let message = message {
|
||||
event.message = SentryMessage(formatted: message)
|
||||
}
|
||||
|
||||
self.captureAndLog(event, logLevel: logLevel)
|
||||
self.capture(event)
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a SentryEvent around a message and also passes it to the engine's logging mechanism.
|
||||
* If the logging level is not specified, it will default to .error.
|
||||
*
|
||||
* Will safely bypass the Sentry component if not activated by the app, only logging the
|
||||
* message in such scenarios.
|
||||
* Constructs a SentryEvent around a message. If the logging level is not specified, it will default to .error.
|
||||
* Will safely bypass the Sentry component if not activated by the app.
|
||||
*/
|
||||
public static func captureAndLog(_ message: String, sentryLevel: Sentry.SentryLevel = .error, logLevel: XCGLogger.Level? = nil) {
|
||||
public static func capture(_ message: String, sentryLevel: Sentry.SentryLevel = .error) {
|
||||
let event = Sentry.Event(level: sentryLevel)
|
||||
event.message = SentryMessage(formatted: message)
|
||||
|
||||
self.captureAndLog(event, logLevel: logLevel)
|
||||
self.capture(event)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Sentry breadcrumb and copies its message to the engine's logging mechanism.
|
||||
* If the logging level is not specified, the Sentry event's log-level will be used as a default.
|
||||
*
|
||||
* Will safely bypass the Sentry component if not activated by the app, only logging the
|
||||
* message in such scenarios.
|
||||
* Adds a Sentry breadcrumb. Will safely bypass the Sentry component if not activated by the app.
|
||||
*/
|
||||
public static func breadcrumbAndLog(crumb: Sentry.Breadcrumb, logLevel: XCGLogger.Level? = nil) {
|
||||
public static func breadcrumb(crumb: Sentry.Breadcrumb) {
|
||||
// Guarded in case a library consumer decides against initializing Sentry.
|
||||
if _started {
|
||||
SentrySDK.addBreadcrumb(crumb)
|
||||
}
|
||||
|
||||
let level = logLevel ?? mapLoggingLevel(crumb.level)
|
||||
log.logln(crumb.message, level: level)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Sentry breadcrumb and copies its message to the engine's logging mechanism.
|
||||
* If the logging level is not specified, the Sentry event's log-level will be used as a default.
|
||||
*
|
||||
* Will safely bypass the Sentry component if not activated by the app, only logging the
|
||||
* message in such scenarios.
|
||||
* Adds a Sentry breadcrumb. Will safely bypass the Sentry component if not activated by the app.
|
||||
*/
|
||||
public static func breadcrumbAndLog(_ message: String, category: String? = nil, sentryLevel: Sentry.SentryLevel = .info, logLevel: XCGLogger.Level? = nil) {
|
||||
public static func breadcrumb(_ message: String, category: String? = nil, sentryLevel: Sentry.SentryLevel = .info) {
|
||||
let crumb = Sentry.Breadcrumb()
|
||||
crumb.level = sentryLevel
|
||||
if let category = category {
|
||||
|
|
@ -189,7 +146,7 @@ public class SentryManager {
|
|||
}
|
||||
crumb.message = message
|
||||
|
||||
self.breadcrumbAndLog(crumb: crumb, logLevel: logLevel)
|
||||
self.breadcrumb(crumb: crumb)
|
||||
}
|
||||
|
||||
public static func forceError() {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
public extension UserDefaults {
|
||||
func installableKeyboards(forKey key: String) -> [InstallableKeyboard]? {
|
||||
|
|
@ -18,7 +19,9 @@ public extension UserDefaults {
|
|||
do {
|
||||
return try array.map { try decoder.decode(InstallableKeyboard.self, from: $0) }
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Error decoding keyboards: \(error)")
|
||||
let message = "Error decoding keyboards: \(error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -31,7 +34,9 @@ public extension UserDefaults {
|
|||
do {
|
||||
return try array.map { try decoder.decode(InstallableLexicalModel.self, from: $0) }
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Error decoding lexical models: \(error)")
|
||||
let message = "Error decoding lexical models: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +66,9 @@ public extension UserDefaults {
|
|||
let array = try keyboards.map { try encoder.encode($0) }
|
||||
set(array, forKey: key)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Error encoding keyboards: \(error)")
|
||||
let message = "Error encoding keyboards: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +82,9 @@ public extension UserDefaults {
|
|||
let array = try lexicalModels.map { try encoder.encode($0) }
|
||||
set(array, forKey: key)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Error encoding lexicalModels: \(error)")
|
||||
let message = "Error encoding lexicalModels: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +95,9 @@ public extension UserDefaults {
|
|||
do {
|
||||
return try PropertyListDecoder().decode(FullKeyboardID.self, from: data)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Error decoding FullKeyboardID: \(error)")
|
||||
let message = "Error decoding FullKeyboardID: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -100,7 +111,9 @@ public extension UserDefaults {
|
|||
let data = try PropertyListEncoder().encode(id)
|
||||
set(data, forKey: key)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Error encoding FullKeyboardID: \(error)")
|
||||
let message = "Error encoding FullKeyboardID: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +124,9 @@ public extension UserDefaults {
|
|||
do {
|
||||
return try PropertyListDecoder().decode(FullLexicalModelID.self, from: data)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Error decoding FullLexicalModelID: \(error)")
|
||||
let message = "Error decoding FullLexicalModelID: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -125,7 +140,9 @@ public extension UserDefaults {
|
|||
let data = try PropertyListEncoder().encode(id)
|
||||
set(data, forKey: key)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Error encoding FullLexicalModelID: \(error)")
|
||||
let message = "Error encoding FullLexicalModelID: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
protocol HTTPDownloadDelegate: class {
|
||||
protocol HTTPDownloadDelegate: AnyObject {
|
||||
func downloadRequestStarted(_ request: HTTPDownloadRequest)
|
||||
func downloadRequestFinished(_ request: HTTPDownloadRequest)
|
||||
func downloadRequestFailed(_ request: HTTPDownloadRequest, with: Error?)
|
||||
|
|
@ -99,8 +100,8 @@ class HTTPDownloader: NSObject {
|
|||
}
|
||||
|
||||
// Successful download.
|
||||
log.debug("Downloaded file \(currentRequest.url) as \(location), " +
|
||||
"to be copied to \(currentRequest.destinationFile ?? "nil")")
|
||||
let message = "Downloaded file \(currentRequest.url) as \(location), to be copied to \(currentRequest.destinationFile ?? "nil")"
|
||||
os_log("%{public}s", log: KeymanEngineLogger.resources, type: .debug, message)
|
||||
|
||||
// If a destination file for the download has already been specified, let's go ahead and copy it over.
|
||||
if let destFile = currentRequest.destinationFile {
|
||||
|
|
@ -115,7 +116,7 @@ class HTTPDownloader: NSObject {
|
|||
try FileManager.default.copyItem(at: location,
|
||||
to: destFileUrl)
|
||||
} catch {
|
||||
log.error("Error saving the download: \(error)")
|
||||
os_log("Error saving the download: %{public}s", log: KeymanEngineLogger.resources, type: .error, error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import Foundation
|
||||
import WebKit
|
||||
import DeviceKit
|
||||
import os.log
|
||||
|
||||
public class PackageInstallViewController<Resource: LanguageResource>: UIViewController, UITableViewDelegate, UITableViewDataSource, UITabBarControllerDelegate, UIAdaptivePresentationControllerDelegate {
|
||||
private enum NavigationMode: Int { // b/c hashable
|
||||
|
|
@ -97,7 +98,7 @@ public class PackageInstallViewController<Resource: LanguageResource>: UIViewCon
|
|||
var preinstalleds: Set<String> = Set()
|
||||
|
||||
guard let typedPackage = package as? TypedKeymanPackage<Resource> else {
|
||||
log.warning("Cannot check for previously-installed resources of unexpected type")
|
||||
os_log("Cannot check for previously-installed resources of unexpected type", log:KeymanEngineLogger.resources, type: .error)
|
||||
return preinstalleds
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import AudioToolbox
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
public enum GlobeKeyTapBehaviour {
|
||||
case switchToNextKeyboard
|
||||
|
|
@ -367,7 +368,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate {
|
|||
// This should allow us to debug any failures of this assumption.
|
||||
// So far, only occurs when debugging a breakpoint during a touch event on BKSP,
|
||||
// so all seems good.
|
||||
log.verbose("Failed to swallow a recent textDidChange call!")
|
||||
os_log("Failed to swallow a recent textDidChange call!", log: KeymanEngineLogger.ui, type: .default)
|
||||
}
|
||||
self.swallowBackspaceTextChange = true
|
||||
break
|
||||
|
|
@ -538,7 +539,9 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate {
|
|||
func clearText() {
|
||||
setContextState(text: nil, range: NSRange(location: 0, length: 0))
|
||||
keymanWeb.resetContext()
|
||||
SentryManager.breadcrumbAndLog("Cleared text.")
|
||||
let message = "Cleared text."
|
||||
os_log("%{public}s", log: KeymanEngineLogger.ui, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
}
|
||||
|
||||
func resetContext() {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import Foundation
|
||||
import DeviceKit
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
/**
|
||||
* Documents the basic size properties of the default system keyboard, _as seen from within a `UIInputView` or its `UIInputViewController`_.
|
||||
|
|
@ -168,13 +169,12 @@ class KeyboardScaleMap {
|
|||
* the largest device smaller than or equal to the detected dimensions.
|
||||
*/
|
||||
private static func getUnknownDeviceMapping(screenSize _screenSize: CGSize = UIScreen.main.bounds.size, asPhone: Bool? = nil) -> Device {
|
||||
// Shouldn't happen, but just in case.
|
||||
if _screenSize == CGSize.zero {
|
||||
// // This would notify us whenever new devices are out that we haven't build a mapping for.
|
||||
// SentryManager.captureAndLog("Cannot detect device dimensions; defaulting to smallest device for form factor.", sentryLevel: .info)
|
||||
|
||||
// We haven't actually updated things here in a while, so we'll just breadcrumb for now.
|
||||
SentryManager.breadcrumbAndLog("Cannot detect device dimensions; defaulting to smallest device for form factor.", sentryLevel: .error)
|
||||
// Shouldn't happen, but just in case.
|
||||
if _screenSize == CGSize.zero {
|
||||
// This would notify us whenever new devices are out that we haven't build a mapping for.
|
||||
let message = "Cannot detect device dimensions; defaulting to smallest device for form factor."
|
||||
os_log("%{public}s", log: KeymanEngineLogger.ui, type: .error, message)
|
||||
SentryManager.capture(message, sentryLevel: .error)
|
||||
}
|
||||
|
||||
// Convert to CGSize in portrait orientation.
|
||||
|
|
@ -228,7 +228,7 @@ class KeyboardScaleMap {
|
|||
default:
|
||||
if !isUnknown {
|
||||
// We can still perform a mapping, but it's not ideal.
|
||||
log.warning("Keyboard scaling definition missing for device \(device.description)")
|
||||
os_log("Keyboard scaling definition missing for device %{public}s", log: KeymanEngineLogger.ui, type: .default, device.description)
|
||||
}
|
||||
|
||||
// The expected case: isUnknown = true.
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import UIKit
|
|||
import WebKit
|
||||
import AudioToolbox
|
||||
import Sentry
|
||||
import XCGLogger
|
||||
import os.log
|
||||
|
||||
private let keyboardChangeHelpText = NSLocalizedString("keyboard-help-change", bundle: engineBundle, comment: "")
|
||||
|
||||
|
|
@ -195,7 +195,9 @@ extension KeymanWebViewController {
|
|||
let cmd = "executePopupKey(\"\(id)\",\"\(escapedText)\");"
|
||||
webView!.evaluateJavaScript(cmd, completionHandler: nil)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error)
|
||||
let message = "\(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -246,7 +248,8 @@ extension KeymanWebViewController {
|
|||
self.currentText = String(jsonText)
|
||||
webView!.evaluateJavaScript("setKeymanVal(\"\(jsonText)\");", completionHandler: nil)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error.localizedDescription)
|
||||
os_log("%{public}s", log: KeymanEngineLogger.engine, type: .error, error.localizedDescription)
|
||||
SentryManager.capture(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -288,12 +291,14 @@ extension KeymanWebViewController {
|
|||
// failed to initialize properly.
|
||||
guard FileManager.default.fileExists(atPath: fileURL.path) else {
|
||||
let event = Sentry.Event(level: .error)
|
||||
event.message = SentryMessage(formatted: "File missing for keyboard")
|
||||
let errorMessage = "File missing for keyboard"
|
||||
event.message = SentryMessage(formatted: errorMessage)
|
||||
event.extra = [ "id": keyboard.id, "file": fileURL ]
|
||||
if let packageID = keyboard.packageID {
|
||||
event.extra?["package"] = packageID
|
||||
}
|
||||
SentryManager.captureAndLog(event)
|
||||
SentryManager.capture(event)
|
||||
os_log("%{public}s id: %s file: %{public}s", log: KeymanEngineLogger.resources, type: .error, errorMessage, keyboard.id, fileURL as CVarArg)
|
||||
throw KeyboardError.fileMissing
|
||||
}
|
||||
|
||||
|
|
@ -311,12 +316,14 @@ extension KeymanWebViewController {
|
|||
data = try JSONSerialization.data(withJSONObject: stub, options: [])
|
||||
} catch {
|
||||
let event = Sentry.Event(error: error)
|
||||
let errorMessage = "Failed to serialize keyboard stub:"
|
||||
event.message = SentryMessage(formatted: "Failed to serialize keyboard stub: \(error)")
|
||||
event.extra = [:]
|
||||
event.extra!["id"] = stub["KI"]
|
||||
event.extra!["package"] = stub["KP"]
|
||||
|
||||
SentryManager.captureAndLog(event)
|
||||
SentryManager.capture(event)
|
||||
os_log("%{public}s id: %{public}s file: %{public}s", log: KeymanEngineLogger.resources, type: .error, errorMessage, keyboard.id, fileURL as CVarArg)
|
||||
throw KeyboardError.keyboardLoadingError
|
||||
}
|
||||
guard let stubString = String(data: data, encoding: .utf8) else {
|
||||
|
|
@ -326,12 +333,13 @@ extension KeymanWebViewController {
|
|||
event.extra!["id"] = stub["KI"]
|
||||
event.extra!["package"] = stub["KP"]
|
||||
|
||||
SentryManager.captureAndLog(event)
|
||||
os_log("Failed to create keyboard stub string for embedded KMW", log: KeymanEngineLogger.ui, type: .error)
|
||||
SentryManager.capture(event)
|
||||
throw KeyboardError.keyboardLoadingError
|
||||
}
|
||||
|
||||
SentryManager.breadcrumbAndLog("Keyboard stub built for \(keyboard.id)", logLevel: XCGLogger.Level.none)
|
||||
log.info("Keyboard stub: \(stubString)")
|
||||
let message = "Keyboard stub built for \(keyboard.id)"
|
||||
os_log("%{public}s", log: KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
webView!.evaluateJavaScript("setKeymanLanguage(\(stubString));", completionHandler: nil)
|
||||
}
|
||||
|
||||
|
|
@ -349,12 +357,15 @@ extension KeymanWebViewController {
|
|||
|
||||
guard FileManager.default.fileExists(atPath: fileURL.path) else {
|
||||
let event = Sentry.Event(level: .error)
|
||||
event.message = SentryMessage(formatted: "File missing for lexical model")
|
||||
let errorMessage = "File missing for lexical model"
|
||||
event.message = SentryMessage(formatted: errorMessage)
|
||||
event.extra = [ "id": lexicalModel.id, "file": fileURL ]
|
||||
if let packageID = lexicalModel.packageID {
|
||||
event.extra?["package"] = packageID
|
||||
}
|
||||
SentryManager.captureAndLog(event)
|
||||
|
||||
os_log("%{public}s: %{public}s", log: KeymanEngineLogger.resources, type: .error, errorMessage, lexicalModel.id)
|
||||
SentryManager.capture(event)
|
||||
throw KeyboardError.fileMissing
|
||||
}
|
||||
|
||||
|
|
@ -363,25 +374,30 @@ extension KeymanWebViewController {
|
|||
data = try JSONSerialization.data(withJSONObject: stub, options: [])
|
||||
} catch {
|
||||
let event = Sentry.Event(error: error)
|
||||
let errorMessage = "Failed to serialize lexical model stub:"
|
||||
event.message = SentryMessage(formatted: "Failed to serialize lexical model stub: \(error)")
|
||||
event.extra = [:]
|
||||
event.extra!["id"] = stub["id"]
|
||||
|
||||
SentryManager.captureAndLog(event)
|
||||
os_log("%{public}s: %{public}s", log: KeymanEngineLogger.resources, type: .error, errorMessage, error.localizedDescription)
|
||||
SentryManager.capture(event)
|
||||
throw KeyboardError.lexicalModelLoadingError
|
||||
}
|
||||
guard let stubString = String(data: data, encoding: .utf8) else {
|
||||
let event = Sentry.Event(level: .error)
|
||||
event.message = SentryMessage(formatted: "Failed to create lexical model stub string for embedded KMW")
|
||||
let errorMessage = "Failed to create lexical model stub string for embedded KMW"
|
||||
event.message = SentryMessage(formatted: errorMessage)
|
||||
event.extra = [:]
|
||||
event.extra!["id"] = stub["id"]
|
||||
|
||||
SentryManager.captureAndLog(event)
|
||||
os_log("%{public}s", log: KeymanEngineLogger.resources, type: .error, errorMessage)
|
||||
SentryManager.capture(event)
|
||||
throw KeyboardError.lexicalModelLoadingError
|
||||
}
|
||||
|
||||
SentryManager.breadcrumbAndLog("LexicalModel stub built for \(lexicalModel.id)", logLevel: XCGLogger.Level.none)
|
||||
log.info("LexicalModel stub: \(stubString)")
|
||||
let message = "LexicalModel stub built for \(lexicalModel.id)"
|
||||
os_log("%{public}s", log: KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
|
||||
if lexicalModel.languageID == Manager.shared.currentKeyboardID?.languageID {
|
||||
// We're registering a lexical model for the now-current keyboard.
|
||||
|
|
@ -404,7 +420,9 @@ extension KeymanWebViewController {
|
|||
}
|
||||
|
||||
func showBanner(_ display: Bool) {
|
||||
SentryManager.breadcrumbAndLog("Changing banner's alwaysShow property to \(display).", category: "engine", sentryLevel: .debug)
|
||||
let message = "Changing banner's alwaysShow property to \(display)"
|
||||
os_log("%{public}s", log: KeymanEngineLogger.settings, type: .debug, message)
|
||||
SentryManager.breadcrumb(message, category: "engine", sentryLevel: .debug)
|
||||
webView?.evaluateJavaScript("showBanner(\(display ? "true" : "false"))", completionHandler: nil)
|
||||
}
|
||||
|
||||
|
|
@ -416,7 +434,8 @@ extension KeymanWebViewController {
|
|||
} else {
|
||||
logString = path
|
||||
}
|
||||
log.debug("Banner image path: '\(logString).'")
|
||||
let message = "Banner image path: '\(logString).'"
|
||||
os_log("%{public}s", log: KeymanEngineLogger.ui, type: .debug, message)
|
||||
webView?.evaluateJavaScript("setBannerImage(\"\(path)\");", completionHandler: nil)
|
||||
}
|
||||
|
||||
|
|
@ -519,7 +538,8 @@ extension KeymanWebViewController: WKScriptMessageHandler {
|
|||
subkeyIDs.append(values[0])
|
||||
subkeyTexts.append(values[1].stringFromUTF16CodeUnits() ?? "")
|
||||
default:
|
||||
log.warning("Unexpected subkey key: \(key)")
|
||||
let message = "Unexpected subkey key: \(key)"
|
||||
os_log("%{public}s", log: KeymanEngineLogger.ui, type: .error, message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -552,7 +572,10 @@ extension KeymanWebViewController: WKScriptMessageHandler {
|
|||
// This may need filtering for proper use with Sentry?
|
||||
// Then again, if KMW is logging it... we already have to worry
|
||||
// about it showing up in Web-oriented Sentry logs.
|
||||
SentryManager.breadcrumbAndLog("KMW Log: \(message)", category: "engine")
|
||||
|
||||
let logMessage = "KMW Log: \(message)"
|
||||
os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, logMessage)
|
||||
SentryManager.breadcrumb(logMessage)
|
||||
} else if fragment.hasPrefix("#beep-") {
|
||||
beep(self)
|
||||
delegate?.beep(self)
|
||||
|
|
@ -565,9 +588,12 @@ extension KeymanWebViewController: WKScriptMessageHandler {
|
|||
|
||||
do {
|
||||
let cmd = try decoder.decode(SuggestionPopup.self, from: cmdData!)
|
||||
log.verbose("Longpress detected on suggestion: \"\(cmd.suggestion.displayAs)\".")
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Unexpected JSON parse error: \(error).")
|
||||
let message = "Longpress detected on suggestion: \"\(cmd.suggestion.displayAs)\"."
|
||||
os_log("%{public}s", log: KeymanEngineLogger.ui, type: .debug, message)
|
||||
} catch {
|
||||
let message = "Unexpected JSON parse error: \(error)."
|
||||
os_log("%{public}s", log:KeymanEngineLogger.engine, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
|
||||
// Will need processing upon extraction from the resulting object.
|
||||
|
|
@ -579,7 +605,9 @@ extension KeymanWebViewController: WKScriptMessageHandler {
|
|||
// let frame = KeymanWebViewController.keyFrame(x: x, y: y, w: w, h: h)
|
||||
|
||||
} else {
|
||||
SentryManager.captureAndLog("Unexpected KMW event: \(fragment)")
|
||||
let message = "Unexpected KMW event: \(fragment)"
|
||||
os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, message)
|
||||
SentryManager.capture(message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -636,7 +664,9 @@ extension KeymanWebViewController: KeymanWebDelegate {
|
|||
delegate?.keyboardLoaded(keymanWeb)
|
||||
|
||||
isLoading = false
|
||||
SentryManager.breadcrumbAndLog("Loaded keyboard.", sentryLevel: .debug, logLevel: .info)
|
||||
let message = "Loaded keyboard."
|
||||
os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, message)
|
||||
SentryManager.breadcrumb(message, sentryLevel: .debug)
|
||||
|
||||
self.setSentryState()
|
||||
resizeKeyboard()
|
||||
|
|
@ -663,7 +693,9 @@ extension KeymanWebViewController: KeymanWebDelegate {
|
|||
newKb = userKbs[0]
|
||||
}
|
||||
}
|
||||
SentryManager.breadcrumbAndLog("Setting initial keyboard.", category: "engine")
|
||||
let message = "Setting initial keyboard."
|
||||
os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
|
||||
// Compare against resetKeyboard & Manager.setKeyboard;
|
||||
// setting this to `nil` allows us to force keyboard reloads when needed.
|
||||
|
|
@ -688,7 +720,9 @@ extension KeymanWebViewController: KeymanWebDelegate {
|
|||
_ = Manager.shared.setKeyboard(Defaults.keyboard)
|
||||
}
|
||||
} catch {
|
||||
SentryManager.captureAndLog("Could not load default keyboard as a fallback for keyboard loading failure", sentryLevel: .fatal)
|
||||
let message = "Could not load default keyboard as a fallback for keyboard loading failure"
|
||||
os_log("%{public}s", log: KeymanEngineLogger.engine, type: .error, message)
|
||||
SentryManager.capture(message, sentryLevel: .fatal)
|
||||
}
|
||||
newKb = Defaults.keyboard
|
||||
}
|
||||
|
|
@ -1066,13 +1100,19 @@ extension KeymanWebViewController {
|
|||
Manager.shared.currentKeyboardID = nil
|
||||
|
||||
if let keyboard = keyboard {
|
||||
SentryManager.breadcrumbAndLog("Current keyboard is set.", category: "engine")
|
||||
let message = "Current keyboard is set."
|
||||
os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
_ = Manager.shared.setKeyboard(keyboard)
|
||||
} else if let keyboard = Storage.active.userDefaults.userKeyboards?[safe: 0] {
|
||||
SentryManager.breadcrumbAndLog("Using user's default keyboard.", category: "engine")
|
||||
let message = "Using user's default keyboard."
|
||||
os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
_ = Manager.shared.setKeyboard(keyboard)
|
||||
} else {
|
||||
SentryManager.breadcrumbAndLog("Using app-default keyboard.", category: "engine")
|
||||
let message = "Using app-default keyboard."
|
||||
os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
_ = Manager.shared.setKeyboard(Defaults.keyboard)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
public class KeyboardKeymanPackage : TypedKeymanPackage<InstallableKeyboard> {
|
||||
internal var keyboards: [KMPKeyboard]!
|
||||
|
|
@ -22,7 +23,9 @@ public class KeyboardKeymanPackage : TypedKeymanPackage<InstallableKeyboard> {
|
|||
if(keyboard.isValid && FileManager.default.fileExists(atPath: self.sourceFolder.appendingPathComponent("\(keyboard.keyboardId).js").path)) {
|
||||
keyboards.append(keyboard)
|
||||
} else {
|
||||
SentryManager.breadcrumbAndLog("\(keyboard.name) not valid / corresponding file not found")
|
||||
let message = "\(keyboard.name) not valid / corresponding file not found"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* Keyman is copyright (C) SIL International. MIT License.
|
||||
*
|
||||
* KeymanEngineLogger.swift
|
||||
* KeymanEngine
|
||||
*
|
||||
* Created by Shawn Schantz on 2024-01-16.
|
||||
*
|
||||
* Basic logging capability provided by wrapping Apple's Unified Logging
|
||||
* Framework. Works with iOS 10 and later.
|
||||
* Log statements are written with a call the `os_log` API which takes a
|
||||
* message, an `OSLog` reference and log level.
|
||||
* Several static references to `OSLog` objects are defined, each of which
|
||||
* determines the subsystem and category of subsequent log statements.
|
||||
* Both subsystem and category are useful for filtering with the Console app.
|
||||
* Use the same subsystem name for all `OSLog` objects created here, but specify
|
||||
* a unique category to identify related functionality.
|
||||
* Add as many static `OSLog` resources as necessary to support new log
|
||||
* categories.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
// bundle for engine is "org.sil.Keyman.ios.Engine"
|
||||
// but selecting this subsystem name for consistency across apps
|
||||
private let subsystem = "com.keyman.ios.engine"
|
||||
|
||||
public struct KeymanEngineLogger {
|
||||
static let engine = OSLog(subsystem: subsystem, category: "Engine")
|
||||
static let migration = OSLog(subsystem: subsystem, category: "Migration")
|
||||
static let resources = OSLog(subsystem: subsystem, category: "Resources")
|
||||
static let settings = OSLog(subsystem: subsystem, category: "Settings")
|
||||
static let ui = OSLog(subsystem: subsystem, category: "UI")
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
import ZIPFoundation
|
||||
import os.log
|
||||
|
||||
// KMPErrors may be passed to UIAlertControllers, so they need localization.
|
||||
public enum KMPError : String, Error {
|
||||
|
|
@ -245,7 +246,7 @@ public class KeymanPackage {
|
|||
try FileManager.default.removeItem(at: self.sourceFolder)
|
||||
}
|
||||
} catch {
|
||||
log.debug("Could not remove temporary extraction site on package deinit")
|
||||
os_log("Could not remove temporary extraction site on package deinit", log:KeymanEngineLogger.resources, type: .error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -436,7 +437,8 @@ public class KeymanPackage {
|
|||
let minVersion = Version(minSupportedVersion)
|
||||
|
||||
if (currentVersion.major < minVersion!.major) {
|
||||
log.error("package '\(packageName)' could not be installed because it requires version \(minSupportedVersion) of Keyman")
|
||||
let message = "package '\(packageName)' could not be installed because it requires version \(minSupportedVersion) of Keyman"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
throw KMPError.unsupportedKeymanVersion
|
||||
}
|
||||
}
|
||||
|
|
@ -449,7 +451,9 @@ public class KeymanPackage {
|
|||
let package = try KeymanPackage.parse(destination)
|
||||
complete(package)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, sentryLevel: .info)
|
||||
let message = "Keyboard installation error: \(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
SentryManager.capture(error, message: message, sentryLevel: .info)
|
||||
complete(nil)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
class KeymanURLProtocol: URLProtocol, NSURLConnectionDataDelegate {
|
||||
private static let protocolHandledKey = "KMURLProtocolHandledKey"
|
||||
|
|
@ -32,7 +33,7 @@ class KeymanURLProtocol: URLProtocol, NSURLConnectionDataDelegate {
|
|||
|
||||
override func startLoading() {
|
||||
guard let mutableRequest = request as? NSMutableURLRequest else {
|
||||
log.error("Bridge to NSMutableURLRequest failed")
|
||||
os_log("Bridge to NSMutableURLRequest failed", log:KeymanEngineLogger.resources, type: .error)
|
||||
return
|
||||
}
|
||||
URLProtocol.setProperty(true, forKey: KeymanURLProtocol.protocolHandledKey, in: mutableRequest)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
private let toolbarButtonTag = 100
|
||||
private let toolbarLabelTag = 101
|
||||
|
|
@ -53,19 +54,19 @@ class LanguageLMDetailViewController: UITableViewController, UIAlertViewDelegate
|
|||
forName: Notifications.packageDownloadFailed,
|
||||
observer: self,
|
||||
function: LanguageLMDetailViewController.lexicalModelDownloadFailed)
|
||||
log.info("viewDidLoad: LanguageLMDetailViewController (registered for lexicalModelDownloadStarted)")
|
||||
os_log("viewDidLoad: LanguageLMDetailViewController (registered for lexicalModelDownloadStarted)", log:KeymanEngineLogger.ui, type: .info)
|
||||
}
|
||||
|
||||
override open func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
log.info("willAppear: LanguageLMDetailViewController")
|
||||
os_log("viewWillAppear: LanguageLMDetailViewController", log:KeymanEngineLogger.ui, type: .info)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
log.info("didAppear: LanguageLMDetailViewController")
|
||||
|
||||
os_log("viewDidAppear: LanguageLMDetailViewController", log:KeymanEngineLogger.ui, type: .info)
|
||||
|
||||
navigationController?.setToolbarHidden(true, animated: true)
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +157,7 @@ class LanguageLMDetailViewController: UITableViewController, UIAlertViewDelegate
|
|||
}
|
||||
|
||||
private func lexicalModelDownloadStarted() {
|
||||
log.info("lexicalModelDownloadStarted: LanguageLMDetailViewController")
|
||||
os_log("lexicalModelDownloadStarted: LanguageLMDetailViewController", log:KeymanEngineLogger.resources, type: .info)
|
||||
view.isUserInteractionEnabled = false
|
||||
|
||||
navigationItem.setHidesBackButton(true, animated: true)
|
||||
|
|
@ -164,8 +165,8 @@ class LanguageLMDetailViewController: UITableViewController, UIAlertViewDelegate
|
|||
}
|
||||
|
||||
private func lexicalModelDownloadFailed() {
|
||||
log.info("lexicalModelDownloadFailed: LanguageLMDetailViewController")
|
||||
view.isUserInteractionEnabled = true
|
||||
os_log("lexicalModelDownloadFailed: LanguageLMDetailViewController", log:KeymanEngineLogger.resources, type: .info)
|
||||
view.isUserInteractionEnabled = true
|
||||
navigationItem.setHidesBackButton(false, animated: true)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
// It replaces the original in every case but one.
|
||||
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
private let toolbarButtonTag = 100
|
||||
|
||||
|
|
@ -27,7 +28,7 @@ class KeyboardSwitcherViewController: UITableViewController, UIAlertViewDelegate
|
|||
//NEEDED?
|
||||
navigationItem.rightBarButtonItem = nil
|
||||
|
||||
log.info("didLoad: KeyboardSwitcherViewController")
|
||||
os_log("didLoad: KeyboardSwitcherViewController", log: KeymanEngineLogger.ui, type: .info)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
public class LexicalModelKeymanPackage : TypedKeymanPackage<InstallableLexicalModel> {
|
||||
internal var models : [KMPLexicalModel]!
|
||||
|
|
@ -24,7 +25,9 @@ public class LexicalModelKeymanPackage : TypedKeymanPackage<InstallableLexicalMo
|
|||
if(model.isValid && FileManager.default.fileExists(atPath: self.sourceFolder.appendingPathComponent("\(model.lexicalModelId).model.js").path)) {
|
||||
models.append(model)
|
||||
} else {
|
||||
SentryManager.breadcrumbAndLog("\(model.name) not valid / corresponding file not found")
|
||||
let message = "\(model.name) not valid / corresponding file not found"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
private let toolbarButtonTag = 100
|
||||
private let toolbarLabelTag = 101
|
||||
|
|
@ -57,7 +58,7 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
observer: self,
|
||||
function: LexicalModelPickerViewController.lexicalModelDownloadFailed)
|
||||
|
||||
log.info("didLoad: LexicalModelPickerViewController (registered lexicalModelDownloadCompleted et al)")
|
||||
os_log("viewDidLoad: LexicalModelPickerViewController (registered lexicalModelDownloadCompleted et al)", log:KeymanEngineLogger.ui, type: .info)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
|
|
@ -69,8 +70,8 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
log.info("didAppear: LexicalModelPickerViewController")
|
||||
|
||||
os_log("viewDidAppear: LexicalModelPickerViewController", log:KeymanEngineLogger.ui, type: .info)
|
||||
|
||||
scroll(toSelectedLexicalModel: false)
|
||||
}
|
||||
|
||||
|
|
@ -176,8 +177,8 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
}
|
||||
|
||||
private func lexicalModelDownloadCompleted() {
|
||||
log.info("lexicalModelDownloadCompleted LexicalModelPicker")
|
||||
|
||||
os_log("lexicalModelDownloadCompleted LexicalModelPicker", log:KeymanEngineLogger.ui, type: .info)
|
||||
|
||||
// Actually used now.
|
||||
view.isUserInteractionEnabled = true
|
||||
navigationItem.leftBarButtonItem?.isEnabled = true
|
||||
|
|
@ -286,7 +287,8 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
//get list of lexical models for this languageID and show it
|
||||
Queries.LexicalModel.fetch(forLanguageCode: language.id) { result, error in
|
||||
if let error = error {
|
||||
log.info("Failed to fetch lexical model list for "+self.language.id+". error: "+error.localizedDescription)
|
||||
let errorMessage = "Failed to fetch lexical model list for \(self.language.id). Error: \(error.localizedDescription)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, errorMessage)
|
||||
DispatchQueue.main.async {
|
||||
self.lexicalModelDownloadFailed(PackageDownloadFailedNotification(packageKey: nil, error: error))
|
||||
}
|
||||
|
|
@ -301,7 +303,8 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
if result.count == 0 {
|
||||
self.noModelsAvailable(cause: "empty")
|
||||
} else {
|
||||
log.info("Fetched lexical model list for "+self.language.id+".")
|
||||
let message = "Fetched lexical model list for \(self.language.id)."
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
let packages: [(InstallableLexicalModel, URL)] = result.map { ($0.modelFor(languageID: self.language.id)!, URL.init(string: $0.packageFilename)!) }
|
||||
// show the list of lexical models (on the main thread)
|
||||
DispatchQueue.main.async {
|
||||
|
|
@ -317,11 +320,11 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
}
|
||||
|
||||
func noModelsAvailable(cause: String = "nil") {
|
||||
let msg = NSLocalizedString("menu-lexical-model-none-message", bundle: engineBundle, comment: "")
|
||||
let logMsg = "No lexical models available for language \(language.id): (\(cause))"
|
||||
log.info(logMsg)
|
||||
let message = NSLocalizedString("menu-lexical-model-none-message", bundle: engineBundle, comment: "")
|
||||
let logMessage = "No lexical models available for language \(language.id): (\(cause))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, logMessage)
|
||||
|
||||
let alertController = UIAlertController(title: title, message: msg,
|
||||
let alertController = UIAlertController(title: title, message: message,
|
||||
preferredStyle: UIAlertController.Style.alert)
|
||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("command-ok", bundle: engineBundle, comment: ""),
|
||||
style: UIAlertAction.Style.default,
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
//
|
||||
// Log.swift
|
||||
// KeymanEngine
|
||||
//
|
||||
// Created by Gabriel Wong on 2017-12-07.
|
||||
// Copyright © 2017 SIL International. All rights reserved.
|
||||
//
|
||||
|
||||
import XCGLogger
|
||||
|
||||
// From XCGLogger docs:
|
||||
// Note: This creates the log object lazily, which means it's not created until it's actually needed.
|
||||
public let log: XCGLogger = {
|
||||
// Default: the 'console', which is read by Xcode but doesn't reach the system logs.
|
||||
let mainLog = XCGLogger(identifier: "KeymanEngine", includeDefaultDestinations: false)
|
||||
|
||||
// Ensures our log messages go out to the device's system log as well as the console.
|
||||
let systemLogDest = AppleSystemLogDestination(identifier: "")
|
||||
systemLogDest.showLogIdentifier = true
|
||||
|
||||
mainLog.add(destination: systemLogDest)
|
||||
|
||||
// Temporary logging level to ensure that app details are reported properly.
|
||||
mainLog.outputLevel = .info
|
||||
mainLog.logAppDetails()
|
||||
|
||||
#if DEBUG
|
||||
mainLog.outputLevel = .debug
|
||||
#else
|
||||
mainLog.outputLevel = .warning
|
||||
#endif
|
||||
|
||||
return mainLog
|
||||
}()
|
||||
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
import UIKit
|
||||
import WebKit
|
||||
import XCGLogger
|
||||
import DeviceKit
|
||||
import Reachability
|
||||
import os.log
|
||||
|
||||
typealias FetchKeyboardsBlock = ([String: Any]?) -> Void
|
||||
|
||||
|
|
@ -211,7 +211,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
do {
|
||||
try Storage.active.copyKMWFiles(from: Resources.bundle)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Failed to copy KMW files from bundle: \(error)")
|
||||
let message = ("Failed to copy KMW files from bundle: \(error)")
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(error, message:message)
|
||||
}
|
||||
|
||||
updateUserKeyboards(with: Defaults.keyboard)
|
||||
|
|
@ -219,7 +221,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
do {
|
||||
try reachability = Reachability(hostname: KeymanHosts.API_KEYMAN_COM.host!)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Could not start Reachability object: \(error)")
|
||||
let message = ("Could not start Reachability object: \(error)")
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(error, message:message)
|
||||
}
|
||||
|
||||
if(!Util.isSystemKeyboard) {
|
||||
|
|
@ -228,7 +232,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
do {
|
||||
try reachability.startNotifier()
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "failed to start Reachability notifier: \(error)")
|
||||
let message = ("failed to start Reachability notifier: \(error)")
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(error, message:message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -294,11 +300,15 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
// We MUST NOT shortcut this method as a result; doing so may (rarely) result in the infamous
|
||||
// blank keyboard bug!
|
||||
if kb.fullID == currentKeyboardID && !self.isSystemKeyboard && !inputViewController.shouldReload {
|
||||
SentryManager.breadcrumbAndLog("Keyboard unchanged: \(kb.fullID)")
|
||||
let message = "Keyboard unchanged: \(kb.fullID)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
return false
|
||||
}
|
||||
|
||||
SentryManager.breadcrumbAndLog("Setting language: \(kb.fullID)")
|
||||
let message = "Setting language: \(kb.fullID)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
SentryManager.breadcrumb("Setting language: \(kb.fullID)")
|
||||
|
||||
currentKeyboardID = kb.fullID
|
||||
|
||||
|
|
@ -361,7 +371,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
if !Migrations.resourceHasPackageMetadata(keyboard) {
|
||||
let wrappedKbds = Migrations.migrateToKMPFormat([keyboard])
|
||||
guard wrappedKbds.count == 1 else {
|
||||
SentryManager.captureAndLog("Could not properly import keyboard")
|
||||
let message = "Could not properly import keyboard"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
return
|
||||
}
|
||||
kbdToInstall = wrappedKbds[0]
|
||||
|
|
@ -398,7 +410,8 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
try ResourceFileManager.shared.install(resourceWithID: fullId, from: package)
|
||||
}
|
||||
catch {
|
||||
log.error("Could not add lexical model for id '\(lexicalModelId)' and languageId '\(languageId)' due to error: \(error)")
|
||||
let message = "Could not add lexical model for id '\(lexicalModelId)' and languageId '\(languageId)' due to error: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
}
|
||||
|
||||
if let lexicalModel = Storage.active.userDefaults.userLexicalModel(withFullID: fullId) {
|
||||
|
|
@ -421,7 +434,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
|
||||
/// Registers a lexical model with KMW.
|
||||
public func registerLexicalModel(_ lm: InstallableLexicalModel) -> Bool {
|
||||
SentryManager.breadcrumbAndLog("Setting lexical model: \(lm.fullID)")
|
||||
let message = "Setting lexical model: \(lm.fullID)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
|
||||
currentLexicalModelID = lm.fullID
|
||||
|
||||
|
|
@ -462,7 +477,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
if !Migrations.resourceHasPackageMetadata(lexicalModel) {
|
||||
let wrappedModels = Migrations.migrateToKMPFormat([lexicalModel])
|
||||
guard wrappedModels.count == 1 else {
|
||||
SentryManager.captureAndLog("Could not properly import lexical model")
|
||||
let message = "Could not properly import lexical model"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.capture(message)
|
||||
return
|
||||
}
|
||||
modelToInstall = wrappedModels[0]
|
||||
|
|
@ -502,7 +519,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
userData.set([Date()], forKey: Key.synchronizeSWKeyboard)
|
||||
userData.synchronize()
|
||||
|
||||
SentryManager.breadcrumbAndLog("Removing keyboard with ID \(kb.id) and languageID \(kb.languageID)")
|
||||
let message = ("Removing keyboard with ID \(kb.id) and languageID \(kb.languageID)")
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
|
||||
// Set a new keyboard if deleting the current one
|
||||
if kb.fullID == currentKeyboardID {
|
||||
|
|
@ -516,12 +535,18 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
// rather than just 'no matching keyboards'.
|
||||
let keyboardDir = Storage.active.resourceDir(for: kb)!
|
||||
FontManager.shared.unregisterFonts(in: keyboardDir, fromSystemOnly: false)
|
||||
SentryManager.breadcrumbAndLog("Deleting directory \(keyboardDir)")
|
||||
let message = "Deleting directory \(keyboardDir)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
if (try? FileManager.default.removeItem(at: keyboardDir)) == nil {
|
||||
SentryManager.captureAndLog("Failed to delete \(keyboardDir) when removing keyboard")
|
||||
let message = "Failed to delete \(keyboardDir) when removing keyboard"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
SentryManager.capture(message)
|
||||
}
|
||||
} else {
|
||||
SentryManager.breadcrumbAndLog("User has another language installed. Skipping delete of keyboard files.")
|
||||
let message = "User has another language installed. Skipping delete of keyboard files."
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(name: Notifications.keyboardRemoved, object: self, value: kb)
|
||||
|
|
@ -570,7 +595,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
}
|
||||
|
||||
let lm = userLexicalModels[index]
|
||||
SentryManager.breadcrumbAndLog("Removing lexical model with ID \(lm.id) and languageID \(lm.languageID) from user list of all models")
|
||||
let message = "Removing lexical model with ID \(lm.id) and languageID \(lm.languageID) from user list of all models"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
userLexicalModels.remove(at: index)
|
||||
ud.userLexicalModels = userLexicalModels
|
||||
ud.set([Date()], forKey: Key.synchronizeSWLexicalModel)
|
||||
|
|
@ -580,7 +607,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
|
||||
/// Removes the lexical model at index from the lexical models list if it exists.
|
||||
public func removeLexicalModelFromLanguagePreference(userDefs ud: UserDefaults, _ lm: InstallableLexicalModel) {
|
||||
SentryManager.breadcrumbAndLog("Removing lexical model with ID \(lm.id) and languageID \(lm.languageID) from per-language prefs")
|
||||
let message = "Removing lexical model with ID \(lm.id) and languageID \(lm.languageID) from per-language prefs"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
ud.set(preferredLexicalModelID: nil, forKey: lm.languageID)
|
||||
}
|
||||
|
||||
|
|
@ -601,7 +630,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
if let first_lm = userLexicalModels.first(where: {$0.languageID == lm.languageID}) {
|
||||
_ = registerLexicalModel(first_lm)
|
||||
} else {
|
||||
SentryManager.breadcrumbAndLog("no more lexical models available for language \(lm.fullID)")
|
||||
let message = "no more lexical models available for language \(lm.fullID)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
currentLexicalModelID = nil
|
||||
}
|
||||
}
|
||||
|
|
@ -609,12 +640,18 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
if !userLexicalModels.contains(where: { $0.id == lm.id }) {
|
||||
let lexicalModelDir = Storage.active.resourceDir(for: lm)!
|
||||
FontManager.shared.unregisterFonts(in: lexicalModelDir, fromSystemOnly: false)
|
||||
SentryManager.breadcrumbAndLog("Deleting directory \(lexicalModelDir)")
|
||||
let message = "Deleting directory \(lexicalModelDir)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
if (try? FileManager.default.removeItem(at: lexicalModelDir)) == nil {
|
||||
SentryManager.captureAndLog("Failed to delete \(lexicalModelDir) when removing lexical model")
|
||||
let message = "Failed to delete \(lexicalModelDir) when removing lexical model"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
SentryManager.capture(message)
|
||||
}
|
||||
} else {
|
||||
SentryManager.breadcrumbAndLog("User has another language installed. Skipping delete of lexical model files.")
|
||||
let message = "User has another language installed. Skipping delete of lexical model files."
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(name: Notifications.lexicalModelRemoved, object: self, value: lm)
|
||||
|
|
@ -706,7 +743,7 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
}
|
||||
|
||||
@objc func reachabilityChanged(_ notification: Notification) {
|
||||
log.debug {
|
||||
let message = { [self] in
|
||||
let reachStr: String
|
||||
switch reachability.connection {
|
||||
case Reachability.Connection.wifi:
|
||||
|
|
@ -718,6 +755,7 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
}
|
||||
return "Reachability changed to '\(reachStr)'"
|
||||
}
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, message())
|
||||
}
|
||||
|
||||
// MARK: - Loading custom keyboards
|
||||
|
|
@ -793,7 +831,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
try shared.copyFiles(to: nonShared)
|
||||
FontManager.shared.registerCustomFonts()
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Failed to copy from shared container: \(error)")
|
||||
let message = ("Failed to copy from shared container: \(error)")
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(error, message:message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
import Sentry
|
||||
import os.log
|
||||
|
||||
private let userInfoKey = "value"
|
||||
|
||||
|
|
@ -42,7 +43,9 @@ public extension NotificationCenter {
|
|||
if let value = notification.userInfo?[userInfoKey] as? T {
|
||||
block(value)
|
||||
} else {
|
||||
SentryManager.captureAndLog("Unexpected userInfo in notification: \(String(describing: notification))")
|
||||
let message = "Unexpected userInfo in notification: \(String(describing: notification))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
SentryManager.capture(message)
|
||||
}
|
||||
}
|
||||
return NotificationObserver(observer: observer, center: self)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
extension Queries {
|
||||
public class LexicalModel {
|
||||
|
|
@ -56,7 +57,9 @@ extension Queries {
|
|||
var urlComponents = MODEL_ENDPOINT
|
||||
|
||||
urlComponents.queryItems = [URLQueryItem(name: "q", value: "bcp47:\(bcp47)")]
|
||||
SentryManager.breadcrumbAndLog("Querying package versions through API endpoint: \(urlComponents.url!)")
|
||||
let message = "Querying package versions through API endpoint: \(urlComponents.url!)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
|
||||
// Step 2: configure the completion closure.
|
||||
let completionClosure = Queries.jsonDataTaskCompletionAdapter(resultType: [Result].self, completionBlock: fetchCompletion)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
extension Queries {
|
||||
// Kept internal; the query is 'publicly exposed' through KeymanPackage and ResourceFileManager.
|
||||
|
|
@ -116,7 +117,9 @@ extension Queries {
|
|||
}
|
||||
|
||||
urlComponents.queryItems = queryItems + [URLQueryItem(name: "platform", value: "ios")]
|
||||
SentryManager.breadcrumbAndLog("Querying package versions through API endpoint: \(urlComponents.url!)")
|
||||
let message = "Querying package versions through API endpoint: \(urlComponents.url!)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
|
||||
// Step 2: configure the completion closure.
|
||||
let completionClosure = Queries.jsonDataTaskCompletionAdapter(resultType: Result.self, completionBlock: fetchCompletion)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import CoreText
|
||||
import Foundation
|
||||
import UIKit // for UIFont
|
||||
import os.log
|
||||
|
||||
public class FontManager {
|
||||
public static let shared = FontManager()
|
||||
|
|
@ -49,13 +50,15 @@ public class FontManager {
|
|||
|
||||
private func readFontName(at url: URL) -> String? {
|
||||
guard let provider = CGDataProvider(url: url as CFURL) else {
|
||||
log.error("Failed to open \(url)")
|
||||
let message = "Failed to open \(url)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
return nil
|
||||
}
|
||||
guard let font = CGFont(provider),
|
||||
let name = font.postScriptName
|
||||
else {
|
||||
log.error("Failed to read font at \(url)")
|
||||
let message = "Failed to read font at \(url)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
return nil
|
||||
}
|
||||
return name as String
|
||||
|
|
@ -84,13 +87,16 @@ public class FontManager {
|
|||
didRegister = CTFontManagerRegisterFontsForURL(url as CFURL, .none, &errorRef)
|
||||
let error = errorRef?.takeRetainedValue() // Releases errorRef
|
||||
if !didRegister {
|
||||
log.error("Failed to register font \(fontName) at \(url) reason: \(String(describing: error))")
|
||||
let message = "Failed to register font \(fontName) at \(url) reason: \(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
} else {
|
||||
log.info("Registered font \(fontName) at \(url)")
|
||||
let message = "Registered font \(fontName) at \(url)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
}
|
||||
} else {
|
||||
didRegister = false
|
||||
log.info("Did not register font at \(url) because font name \(fontName) is already registered")
|
||||
let message = "Did not register font at \(url) because font name \(fontName) is already registered"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
}
|
||||
let font = RegisteredFont(name: fontName, isRegistered: didRegister)
|
||||
fonts[url] = font
|
||||
|
|
@ -111,11 +117,13 @@ public class FontManager {
|
|||
let didUnregister = CTFontManagerUnregisterFontsForURL(url as CFURL, .none, &errorRef)
|
||||
let error = errorRef?.takeRetainedValue() // Releases errorRef
|
||||
if didUnregister {
|
||||
log.info("Unregistered font \(font.name) at \(url)")
|
||||
let message = "Unregistered font \(font.name) at \(url)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
font.isRegistered = false
|
||||
fonts[url] = font
|
||||
} else {
|
||||
log.error("Failed to unregister font \(font.name) at \(url) reason: \(String(describing: error))")
|
||||
let message = "Failed to unregister font \(font.name) at \(url) reason: \(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -128,7 +136,8 @@ public class FontManager {
|
|||
|
||||
public func registerFonts(in directory: URL) {
|
||||
guard let urls = try? FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) else {
|
||||
log.error("Could not list contents of directory \(directory)")
|
||||
let message = "Could not list contents of directory \(directory)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
return
|
||||
}
|
||||
for url in urls where url.lastPathComponent.hasFontExtension {
|
||||
|
|
@ -138,7 +147,8 @@ public class FontManager {
|
|||
|
||||
public func unregisterFonts(in directory: URL, fromSystemOnly: Bool = true) {
|
||||
guard let urls = try? FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) else {
|
||||
log.error("Could not list contents of directory \(directory)")
|
||||
let message = "Could not list contents of directory \(directory)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
return
|
||||
}
|
||||
for url in urls where url.lastPathComponent.hasFontExtension {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
/// Mainly differs from the API `Keyboard` by having an associated language.
|
||||
public struct InstallableKeyboard: Codable, KMPInitializableLanguageResource {
|
||||
|
|
@ -107,7 +108,9 @@ public struct InstallableKeyboard: Codable, KMPInitializableLanguageResource {
|
|||
|
||||
let languageMatches = metadata.languages.compactMap { return $0.languageId == lgCode ? $0.name : nil }
|
||||
if (languageMatches.isEmpty) {
|
||||
SentryManager.captureAndLog("Could not find languageId '\(lgCode)' for package '\(packageID)'", sentryLevel: .warning)
|
||||
let message = "Could not find languageId '\(lgCode)' for package '\(packageID)'"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .default, message)
|
||||
SentryManager.capture(message, sentryLevel: .warning)
|
||||
}
|
||||
guard languageMatches.count >= 1 else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
import Sentry
|
||||
import os.log
|
||||
|
||||
private enum MigrationLevel {
|
||||
static let initial = 0
|
||||
|
|
@ -63,13 +64,17 @@ public enum Migrations {
|
|||
migrateUserDefaultsToStructs(storage: storage)
|
||||
storage.userDefaults.migrationLevel = MigrationLevel.migratedUserDefaultsToStructs
|
||||
} else {
|
||||
SentryManager.breadcrumbAndLog("UserDefaults migration to structs already performed. Skipping.", category: "migration")
|
||||
let message = "UserDefaults migration to structs already performed. Skipping."
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
}
|
||||
if storage.userDefaults.migrationLevel < MigrationLevel.migratedForKMP {
|
||||
migrateForKMP(storage: storage)
|
||||
storage.userDefaults.migrationLevel = MigrationLevel.migratedForKMP
|
||||
} else {
|
||||
SentryManager.breadcrumbAndLog("KMP directory migration already performed. Skipping.", category: "migration")
|
||||
let message = "KMP directory migration already performed. Skipping."
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
}
|
||||
|
||||
// Version-based migrations
|
||||
|
|
@ -78,10 +83,14 @@ public enum Migrations {
|
|||
do {
|
||||
try migrateDocumentsFromPreBrowser()
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Could not migrate Documents directory contents: \(error)")
|
||||
let message = "Could not migrate Documents directory contents: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
} else {
|
||||
SentryManager.breadcrumbAndLog("Documents directory structure compatible with \(Version.fileBrowserImplemented)")
|
||||
let message = "Documents directory structure compatible with \(Version.fileBrowserImplemented)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
}
|
||||
|
||||
if version < Version.packageBasedFileReorg {
|
||||
|
|
@ -89,12 +98,16 @@ public enum Migrations {
|
|||
try migrateCloudResourcesToKMPFormat()
|
||||
} catch {
|
||||
let event = Sentry.Event(level: .error)
|
||||
event.message = SentryMessage(formatted: "Could not migrate pre-existing resources to KMP-style file organization")
|
||||
let message = "Could not migrate pre-existing resources to KMP-style file organization"
|
||||
event.message = SentryMessage(formatted: message)
|
||||
event.extra = [ "priorVersion": version ]
|
||||
SentryManager.captureAndLog(event)
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
SentryManager.capture(event)
|
||||
}
|
||||
} else {
|
||||
SentryManager.breadcrumbAndLog("Resource directories already migrated to package-based format; kmp.jsons already exist.", category: "migration")
|
||||
let message = "Resource directories already migrated to package-based format; kmp.jsons already exist."
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +116,9 @@ public enum Migrations {
|
|||
|
||||
static func detectLegacyKeymanVersion() -> [Version] {
|
||||
// While the 'key' used to track version info existed before v12, it was unused until then.
|
||||
SentryManager.breadcrumbAndLog("Prior engine version unknown; attepting to auto-detect.", category: "migration")
|
||||
let message = "Prior engine version unknown; attepting to auto-detect."
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
|
||||
// Detect possible version matches.
|
||||
let userResources = Storage.active.userDefaults.userResources ?? []
|
||||
|
|
@ -259,7 +274,9 @@ public enum Migrations {
|
|||
do {
|
||||
try Storage.active.installDefaultKeyboard(from: Resources.bundle)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Failed to copy default keyboard from bundle: \(error)")
|
||||
let message = "Failed to copy default keyboard from bundle: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -272,7 +289,9 @@ public enum Migrations {
|
|||
do {
|
||||
try Storage.active.installDefaultLexicalModel(from: Resources.bundle)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Failed to copy default lexical model from bundle: \(error)")
|
||||
let message = "Failed to copy default lexical model from bundle: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -284,13 +303,17 @@ public enum Migrations {
|
|||
guard let userKeyboardObject = storage.userDefaults.object(forKey: Key.userKeyboardsList),
|
||||
let currentKeyboardObject = storage.userDefaults.object(forKey: Key.userCurrentKeyboard)
|
||||
else {
|
||||
SentryManager.breadcrumbAndLog("User keyboard list or current keyboard missing. Skipping migration.", category: "migration")
|
||||
let message = "User keyboard list or current keyboard missing. Skipping migration."
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
return
|
||||
}
|
||||
guard let oldUserKeyboards = userKeyboardObject as? [[String: String]],
|
||||
let oldCurrentKeyboard = currentKeyboardObject as? [String: String]
|
||||
else {
|
||||
SentryManager.captureAndLog("User keyboard list or current keyboard has an unexpected type")
|
||||
let message = "User keyboard list or current keyboard has an unexpected type"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message)
|
||||
SentryManager.capture(message)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -306,14 +329,18 @@ public enum Migrations {
|
|||
}
|
||||
|
||||
private static func installableKeyboard(from kbDict: [String: String]) -> InstallableKeyboard? {
|
||||
SentryManager.breadcrumbAndLog("Migrating keyboard dictionary for '\(String(describing: kbDict["kbId"]))", category: "migration", sentryLevel: .debug)
|
||||
let message = "Migrating keyboard dictionary for '\(String(describing: kbDict["kbId"]))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .debug, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
guard let id = kbDict["kbId"],
|
||||
let name = kbDict["kbName"],
|
||||
let languageID = kbDict["langId"],
|
||||
let languageName = kbDict["langName"],
|
||||
let version = kbDict["version"]
|
||||
else {
|
||||
SentryManager.captureAndLog("Missing required fields in keyboard dictionary: \(kbDict)")
|
||||
let messageOne = "Missing required fields in keyboard dictionary: \(kbDict)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .default, messageOne)
|
||||
SentryManager.capture(message)
|
||||
return nil
|
||||
}
|
||||
let rtl = kbDict["rtl"] == "Y"
|
||||
|
|
@ -329,8 +356,11 @@ public enum Migrations {
|
|||
font: displayFont,
|
||||
oskFont: oskFont,
|
||||
isCustom: isCustom)
|
||||
SentryManager.breadcrumbAndLog("Migrated keyboard dictionary to keyboard \(kb.id)")
|
||||
log.debug(kb)
|
||||
let messageTwo = "Migrated keyboard dictionary to keyboard \(kb.id)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, messageTwo)
|
||||
SentryManager.breadcrumb(message)
|
||||
let messageThree = "\(kb)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .debug, messageThree)
|
||||
return kb
|
||||
}
|
||||
|
||||
|
|
@ -339,13 +369,17 @@ public enum Migrations {
|
|||
let languageID = kbDict["langId"]
|
||||
else {
|
||||
let event = Sentry.Event(level: .error)
|
||||
event.message = SentryMessage(formatted: "Missing required fields in keyboard dictionary for FullKeyboardID")
|
||||
let message = "Missing required fields in keyboard dictionary for FullKeyboardID"
|
||||
event.message = SentryMessage(formatted: message)
|
||||
event.extra = ["kbId": kbDict["kbId"] ?? "nil", "langId": kbDict["langId"] ?? "nil"]
|
||||
SentryManager.captureAndLog(event)
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
SentryManager.capture(event)
|
||||
return nil
|
||||
}
|
||||
let id = FullKeyboardID(keyboardID: keyboardID, languageID: languageID)
|
||||
SentryManager.breadcrumbAndLog("Migrated keyboard dictionary to \(id)", category: "migration")
|
||||
let message = "Migrated keyboard dictionary to \(id)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
return id
|
||||
}
|
||||
|
||||
|
|
@ -354,15 +388,18 @@ public enum Migrations {
|
|||
return nil
|
||||
}
|
||||
guard let data = jsonString.data(using: .utf8) else {
|
||||
log.error("Failed to encode string: \(jsonString)")
|
||||
let message = "Failed to encode string: \(jsonString)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
return nil
|
||||
}
|
||||
guard let fontDict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] else {
|
||||
log.error("Error parsing String as JSON: \(jsonString)")
|
||||
return nil
|
||||
let message = "Error parsing String as JSON: \(jsonString)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
return nil
|
||||
}
|
||||
guard let family = fontDict["family"] as? String else {
|
||||
log.error("Missing 'family' String: \(fontDict)")
|
||||
let message = "Missing 'family' String: \(fontDict)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
return nil
|
||||
}
|
||||
let files: [String]
|
||||
|
|
@ -371,7 +408,8 @@ public enum Migrations {
|
|||
} else if let filesArray = fontDict["files"] as? [String] {
|
||||
files = filesArray
|
||||
} else {
|
||||
log.error("Missing 'files': \(fontDict)")
|
||||
let message = "Missing 'files': \(fontDict)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
return nil
|
||||
}
|
||||
return Font(family: family, source: files)
|
||||
|
|
@ -382,10 +420,14 @@ public enum Migrations {
|
|||
let languageDir = storage.baseDir.appendingPathComponent("languages")
|
||||
let fontDir = storage.baseDir.appendingPathComponent("fonts")
|
||||
|
||||
SentryManager.breadcrumbAndLog("Migrating from base directory: \(storage.baseDir)", category: "migration")
|
||||
let message = "Migrating from base directory: \(storage.baseDir)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
|
||||
guard var userKeyboards = storage.userDefaults.userKeyboards else {
|
||||
SentryManager.breadcrumbAndLog("No user keyboards to migrate", category: "migration")
|
||||
let message = "No user keyboards to migrate"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -393,7 +435,8 @@ public enum Migrations {
|
|||
for i in userKeyboards.indices {
|
||||
let keyboard = userKeyboards[i]
|
||||
guard let version = latestKeyboardFileVersion(withID: keyboard.id, dirPath: languageDir.path) else {
|
||||
log.warning("Could not find JS file for keyboard \(keyboard.id) in \(languageDir.path)")
|
||||
let message = "Could not find JS file for keyboard \(keyboard.id) in \(languageDir.path)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .default, message)
|
||||
continue
|
||||
}
|
||||
var urls = urlsForKeyboard[keyboard.id] ?? Set()
|
||||
|
|
@ -402,12 +445,14 @@ public enum Migrations {
|
|||
let fontFiles = (keyboard.font?.source ?? []) + (keyboard.oskFont?.source ?? [])
|
||||
for file in fontFiles {
|
||||
guard file.hasFontExtension else {
|
||||
log.info("Skipping copy of \(file) for keyboard \(keyboard.id) since it is not a font file.")
|
||||
let message = "Skipping copy of \(file) for keyboard \(keyboard.id) since it is not a font file."
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message)
|
||||
continue
|
||||
}
|
||||
let url = fontDir.appendingPathComponent(file)
|
||||
guard FileManager.default.fileExists(atPath: url.path) else {
|
||||
log.warning("Font file \(url) for keyboard \(keyboard.id) does not exist")
|
||||
let message = "Font file \(url) for keyboard \(keyboard.id) does not exist"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .default, message)
|
||||
continue
|
||||
}
|
||||
urls.insert(url)
|
||||
|
|
@ -426,7 +471,8 @@ public enum Migrations {
|
|||
withIntermediateDirectories: true,
|
||||
attributes: nil)
|
||||
} catch {
|
||||
log.error("Failed to create keyboard directory at \(keyboardDir)")
|
||||
let message = "Failed to create keyboard directory at \(keyboardDir)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -436,13 +482,15 @@ public enum Migrations {
|
|||
do {
|
||||
try FileManager.default.copyItem(at: srcURL, to: dstURL)
|
||||
} catch {
|
||||
log.error("Failed to copy from \(srcURL) to \(dstURL) for keyboard \(keyboardID)")
|
||||
let message = "Failed to copy from \(srcURL) to \(dstURL) for keyboard \(keyboardID)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
successful = false
|
||||
}
|
||||
}
|
||||
if successful {
|
||||
successfulKeyboards.append(keyboardID)
|
||||
log.info("Succesfully copied keyboard files for keyboard \(keyboardID)")
|
||||
let message = "Succesfully copied keyboard files for keyboard \(keyboardID)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -476,7 +524,9 @@ public enum Migrations {
|
|||
}
|
||||
|
||||
static func migrateDocumentsFromPreBrowser() throws {
|
||||
SentryManager.breadcrumbAndLog("Cleaning Documents folder due to 12.0 installation artifacts", category: "migration")
|
||||
let message = "Cleaning Documents folder due to 12.0 installation artifacts"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
|
||||
// Actually DO it.
|
||||
let documentFolderURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||||
|
|
@ -491,16 +541,22 @@ public enum Migrations {
|
|||
let destFile = fileURL.lastPathComponent.replacingOccurrences(of: ".kmp.zip", with: ".kmp")
|
||||
let destURL = fileURL.deletingLastPathComponent().appendingPathComponent(destFile)
|
||||
|
||||
SentryManager.breadcrumbAndLog("\(fileURL) -> \(destURL)", category: "migration", sentryLevel: .debug)
|
||||
let message = "\(fileURL) -> \(destURL)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .debug, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
try FileManager.default.moveItem(at: fileURL, to: destURL)
|
||||
} else if fileURL.lastPathComponent == "temp" {
|
||||
// Removes the 'temp' installation directory; that shouldn't be visible to users.
|
||||
SentryManager.breadcrumbAndLog("Deleting directory: \(fileURL)", category: "migration", sentryLevel: .debug)
|
||||
let message = "Deleting directory: \(fileURL)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .debug, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
try FileManager.default.removeItem(at: fileURL)
|
||||
} else if fileURL.lastPathComponent.hasSuffix(".kmp") {
|
||||
// Do nothing; this file is fine.
|
||||
} else {
|
||||
SentryManager.breadcrumbAndLog("Unexpected file found in documents folder during upgrade: \(fileURL)", category: "migration")
|
||||
let message = "Unexpected file found in documents folder during upgrade: \(fileURL)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .debug, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -534,10 +590,12 @@ public enum Migrations {
|
|||
}
|
||||
} catch {
|
||||
let event = Sentry.Event(level: .error)
|
||||
event.message = SentryMessage(formatted: "Could not remove version number from filename")
|
||||
let message = "Could not remove version number from filename"
|
||||
event.message = SentryMessage(formatted: message)
|
||||
event.extra = ["package" : resource.packageID ?? "<no package>", "id": resource.id, "location": srcLocation ]
|
||||
|
||||
SentryManager.captureAndLog(event)
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
SentryManager.capture(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -554,12 +612,16 @@ public enum Migrations {
|
|||
let tuple = try (ResourceFileManager.shared.prepareKMPInstall(from: filePath), filePath)
|
||||
return tuple
|
||||
} catch {
|
||||
SentryManager.captureAndLog("Could not load kmp.info for local package during migration: \(file)")
|
||||
let message = "Could not load kmp.info for local package during migration: \(file)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
SentryManager.captureAndLog("Could not check contents of Documents directory for resource-migration assist")
|
||||
let message = "Could not check contents of Documents directory for resource-migration assist"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
}
|
||||
|
||||
// Filters out Packages that don't contain the matching resource type.
|
||||
|
|
@ -596,7 +658,9 @@ public enum Migrations {
|
|||
matched.append(package.findResource(withID: resource.typedFullID)!)
|
||||
}
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Could not install resource from locally-cached package: \(String(describing: error))")
|
||||
let message = "Could not install resource from locally-cached package: \(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -664,9 +728,11 @@ public enum Migrations {
|
|||
// write to location.
|
||||
} catch {
|
||||
let event = Sentry.Event(level: .error)
|
||||
event.message = SentryMessage(formatted: "Could not generate kmp.json for legacy resource!")
|
||||
let message = "Could not generate kmp.json for legacy resource!"
|
||||
event.message = SentryMessage(formatted: message)
|
||||
os_log("%{public}s", log: KeymanEngineLogger.engine, type: .error, message)
|
||||
event.extra = [ "resourceId": package.id, "type": package.resourceType()]
|
||||
SentryManager.captureAndLog(event)
|
||||
SentryManager.capture(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
public enum DownloadError : Error {
|
||||
public enum Cause: Error {
|
||||
|
|
@ -241,26 +242,30 @@ public class ResourceDownloadManager {
|
|||
Queries.LexicalModel.fetchModels(forLanguageCode: languageID, withSession: session) { results, error in
|
||||
if let error = error {
|
||||
// We never quite started downloading the lexical model, so there's no download to have failed.
|
||||
log.info("Failed to fetch lexical model list for "+languageID+". error: "+error.localizedDescription)
|
||||
let message = "Failed to fetch lexical model list for \(languageID), error: \(error.localizedDescription)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
try? completionClosure?(nil, error)
|
||||
return
|
||||
}
|
||||
|
||||
guard let results = results else { // Should not be possible.
|
||||
//TODO: put up an alert instead
|
||||
log.info("No lexical models available for language \(languageID) (nil)")
|
||||
let message = "No lexical models available for language \(languageID) (nil)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
try? completionClosure?(nil, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if results.count == 0 {
|
||||
log.info("No lexical models available for language \(languageID) (empty)")
|
||||
let message = "No lexical models available for language \(languageID) (empty)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
try? completionClosure?(nil, nil)
|
||||
// We automatically use the first model in the list.
|
||||
} else {
|
||||
let lexicalModel = results[0].0
|
||||
let lmFullID = results[0].0.fullID
|
||||
log.info("Fetched lexical model list for "+languageID+".")
|
||||
let message = "Fetched lexical model list for \(languageID)."
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
|
||||
let closure = completionClosure ?? self.standardLexicalModelInstallCompletionBlock(forFullID: lmFullID)
|
||||
let downloadURL = self.defaultDownloadURL(forPackage: lexicalModel.packageKey,
|
||||
|
|
@ -496,7 +501,8 @@ public class ResourceDownloadManager {
|
|||
from: downloadURL) { package, error in
|
||||
guard let package = package, error == nil else {
|
||||
let errString = error != nil ? String(describing: error!) : ""
|
||||
log.error("Could not successfully download package \(key) for update: \(errString)")
|
||||
let errorMessage = "Could not successfully download package \(key) for update: \(errString)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -531,11 +537,15 @@ public class ResourceDownloadManager {
|
|||
// The reason we're deprecating it; only returns the first model, even if more language pairings are installed.
|
||||
return package.installables[0][0]
|
||||
} else {
|
||||
SentryManager.captureAndLog("Specified package (at \(packageURL)) does not contain lexical models: \(KMPError.invalidPackage)")
|
||||
let message = "Specified package (at \(packageURL)) does not contain lexical models: \(KMPError.invalidPackage)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.capture(message)
|
||||
return nil
|
||||
}
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Error occurred while attempting to install package from \(packageURL): \(String(describing: error))")
|
||||
let message = "Error occurred while attempting to install package from \(packageURL): \(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -560,7 +570,9 @@ public class ResourceDownloadManager {
|
|||
try handler?(package, error)
|
||||
self.resourceDownloadCompleted(with: package)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Unhandled error occurred after resource successfully downloaded: \(String(describing: error))")
|
||||
let message = "Unhandled error occurred after resource successfully downloaded: \(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
self.resourceDownloadFailed(withKey: packageKey, with: error)
|
||||
}
|
||||
|
||||
|
|
@ -615,7 +627,9 @@ public class ResourceDownloadManager {
|
|||
if let package = package {
|
||||
do {
|
||||
try ResourceFileManager.shared.install(resourceWithID: fullID, from: package)
|
||||
SentryManager.breadcrumbAndLog("successfully parsed the keyboard in: \(package.sourceFolder)")
|
||||
let message = "successfully parsed the keyboard in: \(package.sourceFolder)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
|
||||
// Maintains legacy behavior; automatically sets the newly-downloaded keyboard as active.
|
||||
if let keyboard = package.findResource(withID: fullID) {
|
||||
|
|
@ -626,13 +640,18 @@ public class ResourceDownloadManager {
|
|||
self.downloadLexicalModelsForLanguageIfExists(languageID: fullID.languageID)
|
||||
}
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Keyboard installation error: \(String(describing: error))")
|
||||
let message = "Keyboard installation error: \(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
} else if let error = error {
|
||||
// Often a download error.
|
||||
log.error("Installation failed: \(String(describing: error))")
|
||||
let errorMessage = "Installation failed: \(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage)
|
||||
} else {
|
||||
SentryManager.captureAndLog("Unknown error when attempting to install \(fullID.description))")
|
||||
let message = "Unknown error when attempting to install \(fullID.description))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -643,18 +662,25 @@ public class ResourceDownloadManager {
|
|||
do {
|
||||
// A raw port of the queue's old installation method for lexical models.
|
||||
try ResourceFileManager.shared.finalizePackageInstall(package, isCustom: false)
|
||||
SentryManager.breadcrumbAndLog("successfully parsed the lexical model in: \(package.sourceFolder)")
|
||||
let message = "successfully parsed the lexical model in: \(package.sourceFolder)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
|
||||
if let installedLexicalModel = package.findResource(withID: fullID) {
|
||||
_ = Manager.shared.registerLexicalModel(installedLexicalModel)
|
||||
}
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Error installing the lexical model: \(String(describing: error))")
|
||||
let message = "Error installing the lexical model: \(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
} else if let error = error {
|
||||
log.error("Error downloading the lexical model \(String(describing: error))")
|
||||
let errorMessage = "Error downloading the lexical model \(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage)
|
||||
} else {
|
||||
SentryManager.captureAndLog("Unknown error when attempting to install \(fullID.description)")
|
||||
let message = "Unknown error when attempting to install \(fullID.description)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
import Reachability
|
||||
import os.log
|
||||
|
||||
enum DownloadNode {
|
||||
case simpleBatch(AnyDownloadBatch)
|
||||
|
|
@ -279,7 +280,9 @@ class ResourceDownloadQueue: HTTPDownloadDelegate {
|
|||
do {
|
||||
try reachability = Reachability(hostname: KeymanHosts.API_KEYMAN_COM.host!)
|
||||
} catch {
|
||||
SentryManager.captureAndLog("Could not start Reachability object: \(error)")
|
||||
let message = "Could not start Reachability object: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
|
||||
self.session = session
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
/**
|
||||
* This class stores common methods used for installing language resources, regardless of source.
|
||||
|
|
@ -40,7 +41,9 @@ public class ResourceFileManager {
|
|||
do {
|
||||
try Storage.active.installDefaultKeyboard(from: Resources.bundle)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Failed to copy default keyboard from bundle: \(error)")
|
||||
let message = "Failed to copy default keyboard from bundle: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(error, message:message)
|
||||
}
|
||||
}
|
||||
Migrations.engineVersion = Version.latestFeature
|
||||
|
|
@ -154,7 +157,9 @@ public class ResourceFileManager {
|
|||
try copyWithOverwrite(from: url, to: destinationUrl)
|
||||
return destinationUrl
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error)
|
||||
let message = "\(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(error, message:message)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -166,7 +171,9 @@ public class ResourceFileManager {
|
|||
*/
|
||||
public func prepareKMPInstall(from url: URL) throws -> KeymanPackage {
|
||||
// Once selected, start the standard install process.
|
||||
SentryManager.breadcrumbAndLog("Opening KMP from \(url)")
|
||||
let message = "Opening KMP from \(url)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
|
||||
// Step 1: Copy it to a temporary location, making it a .zip in the process
|
||||
let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
|
||||
|
|
@ -176,7 +183,8 @@ public class ResourceFileManager {
|
|||
do {
|
||||
try copyWithOverwrite(from: url, to: archiveUrl)
|
||||
} catch {
|
||||
log.error(error)
|
||||
let errorMessage = "\(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage)
|
||||
throw KMPError.copyFiles
|
||||
}
|
||||
|
||||
|
|
@ -208,7 +216,8 @@ public class ResourceFileManager {
|
|||
let kmp = try prepareKMPInstall(from: url)
|
||||
completionHandler(kmp, nil)
|
||||
} catch {
|
||||
log.error(error)
|
||||
let errorMessage = "\(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage)
|
||||
completionHandler(nil, error)
|
||||
}
|
||||
}
|
||||
|
|
@ -333,7 +342,8 @@ public class ResourceFileManager {
|
|||
try finalizePackageInstall(package, isCustom: isCustom)
|
||||
completionHandler(nil)
|
||||
} catch {
|
||||
log.error(error)
|
||||
let errorMessage = "\(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage)
|
||||
completionHandler(error)
|
||||
}
|
||||
}
|
||||
|
|
@ -360,7 +370,8 @@ public class ResourceFileManager {
|
|||
where FullID.Resource.Package: TypedKeymanPackage<FullID.Resource> {
|
||||
if fullIDs.contains(where: { package.findResource(withID: $0) == nil }) {
|
||||
let missingResource = fullIDs.first(where: { package.findResource(withID: $0) == nil })!
|
||||
log.error("Resource with full ID \(missingResource.description) not in package")
|
||||
let errorMessage = "Resource with full ID \(missingResource.description) not in package"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage)
|
||||
throw KMPError.resourceNotInPackage
|
||||
}
|
||||
|
||||
|
|
@ -376,7 +387,8 @@ public class ResourceFileManager {
|
|||
try copyWithOverwrite(from: package.sourceFolder,
|
||||
to: Storage.active.packageDir(for: package)!)
|
||||
} catch {
|
||||
log.error("Could not create installation directory and/or copy resources: \(error)")
|
||||
let errorMessage = "Could not create installation directory and/or copy resources: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage)
|
||||
throw KMPError.fileSystem
|
||||
}
|
||||
|
||||
|
|
@ -413,7 +425,9 @@ public class ResourceFileManager {
|
|||
if !FileManager.default.fileExists(atPath: path) {
|
||||
// Is 'internal' and only called after packages have been installed,
|
||||
// thus when the files should already be in-place.
|
||||
SentryManager.captureAndLog("Could not add resource of type: \(resource.fullID.type) with ID: \(resource.id) because the resource file does not exist")
|
||||
let message = "Could not add resource of type: \(resource.fullID.type) with ID: \(resource.id) because the resource file does not exist"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -445,6 +459,8 @@ public class ResourceFileManager {
|
|||
|
||||
userDefaults.set([Date()], forKey: Key.synchronizeSWKeyboard)
|
||||
userDefaults.synchronize()
|
||||
SentryManager.breadcrumbAndLog("Added \(resource.fullID.type) with ID: \(resource.id) and language code: \(resource.languageID)")
|
||||
let message = "Added \(resource.fullID.type) with ID: \(resource.id) and language code: \(resource.languageID)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
import Sentry
|
||||
import os.log
|
||||
|
||||
// MARK: - Static members
|
||||
extension Storage {
|
||||
|
|
@ -17,7 +18,9 @@ extension Storage {
|
|||
if paths.isEmpty {
|
||||
return nil
|
||||
}
|
||||
SentryManager.breadcrumbAndLog("Storage.nonShared: using path \(paths[0])")
|
||||
let message = "Storage.nonShared: using path \(paths[0])"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
return Storage(baseURL: paths[0], userDefaults: UserDefaults.standard)
|
||||
}()
|
||||
|
||||
|
|
@ -81,7 +84,9 @@ class Storage {
|
|||
attributes: nil)
|
||||
return newDir
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Failed to create subdirectory at \(newDir)")
|
||||
let message = ("Failed to create subdirectory at \(newDir)")
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(error, message:message)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -198,7 +203,9 @@ class Storage {
|
|||
contents = try FileManager.default.contentsOfDirectory(at: keyboardDir,
|
||||
includingPropertiesForKeys: [.isDirectoryKey])
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Failed to list contents at \(keyboardDir) with error \(error)")
|
||||
let message = ("Failed to list contents at \(keyboardDir) with error \(error)")
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(error, message:message)
|
||||
return nil
|
||||
}
|
||||
return contents.filter { url in
|
||||
|
|
@ -206,7 +213,9 @@ class Storage {
|
|||
let values = try url.resourceValues(forKeys: [.isDirectoryKey])
|
||||
return values.isDirectory ?? false
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error)
|
||||
let message = "\(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -263,7 +272,9 @@ extension Storage {
|
|||
let package = try ResourceFileManager.shared.prepareKMPInstall(from: defaultKMPFile) as! KeyboardKeymanPackage
|
||||
try ResourceFileManager.shared.install(resourceWithID: Defaults.keyboard.fullID, from: package)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Failed to install the default keyboard from the bundled KMP: \(error)")
|
||||
let message = "Failed to install the default keyboard from the bundled KMP: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(error, message:message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -287,13 +298,17 @@ extension Storage {
|
|||
// Install all languages for the model, not just the default-listed one.
|
||||
try ResourceFileManager.shared.install(resourcesWithIDs: package.installables[0].map { $0.fullID }, from: package)
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "Failed to install the default lexical model from the bundled KMP: \(error)")
|
||||
let message = "Failed to install the default lexical model from the bundled KMP: \(error)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(error, message:message)
|
||||
}
|
||||
}
|
||||
|
||||
func copyFiles(to dst: Storage) throws {
|
||||
if FileManager.default.fileExists(atPath: dst.baseDir.path) {
|
||||
SentryManager.breadcrumbAndLog("Deleting \(dst.baseDir) for copy from \(baseDir) to \(dst.baseDir)")
|
||||
let message = "Deleting \(dst.baseDir) for copy from \(baseDir) to \(dst.baseDir)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
try FileManager.default.removeItem(at: dst.baseDir)
|
||||
}
|
||||
try FileManager.default.copyItem(at: baseDir, to: dst.baseDir)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import WebKit
|
||||
import os.log
|
||||
|
||||
class ResourceInfoViewController<Resource: LanguageResource>: UIViewController, UIAlertViewDelegate, UITableViewDelegate, UITableViewDataSource {
|
||||
// The data backing our UI text in the UITableView.
|
||||
|
|
@ -84,7 +85,8 @@ class ResourceInfoViewController<Resource: LanguageResource>: UIViewController,
|
|||
if let qrImg = generateQRCode(from: resourceURL) {
|
||||
qrImageView.image = qrImg
|
||||
} else {
|
||||
log.info("Unable to generate QR code for URL: \(resourceURL)")
|
||||
let message = "Unable to generate QR code for URL: \(resourceURL)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
}
|
||||
} else {
|
||||
// No resource-sharing link available. Hide the text label!
|
||||
|
|
@ -117,7 +119,7 @@ class ResourceInfoViewController<Resource: LanguageResource>: UIViewController,
|
|||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
navigationController?.setToolbarHidden(true, animated: true)
|
||||
log.info("didAppear: ResourceInfoViewController")
|
||||
os_log("didAppear: ResourceInfoViewController", log:KeymanEngineLogger.resources, type: .info)
|
||||
}
|
||||
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
//
|
||||
import QuartzCore
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
private let activityViewTag = -2
|
||||
private let toolbarButtonTag = 100
|
||||
|
|
@ -88,7 +89,7 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie
|
|||
navigationItem.rightBarButtonItem = addButton
|
||||
}
|
||||
|
||||
log.info("viewDidLoad: InstalledLanguagesViewController (registered for keyboardDownloadStarted)")
|
||||
os_log("viewDidLoad: InstalledLanguagesViewController (registered for keyboardDownloadStarted)", log:KeymanEngineLogger.resources, type: .info)
|
||||
}
|
||||
|
||||
override public func viewWillAppear(_ animated: Bool) {
|
||||
|
|
@ -104,8 +105,8 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie
|
|||
if numberOfSections(in: tableView) == 0 {
|
||||
showActivityView()
|
||||
}
|
||||
log.info("didAppear: InstalledLanguagesViewController")
|
||||
|
||||
os_log("didAppear: InstalledLanguagesViewController", log:KeymanEngineLogger.ui, type: .info)
|
||||
|
||||
// Are there updates worth doing?
|
||||
if isDidUpdateCheck {
|
||||
// Nope; don't make an 'update' button.
|
||||
|
|
@ -171,11 +172,14 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie
|
|||
for lm in userLexicalModels {
|
||||
let l = lm.languageID
|
||||
if let langName = keyboardLanguages[l]?.name {
|
||||
log.info("keyboard language \(l) \(langName) has lexical model")
|
||||
let message = "keyboard language \(l) \(langName) has lexical model"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
} else {
|
||||
// Legacy behavior: we automatically install all MTNT language codes, even without
|
||||
// a matching keyboard for the more specific variant(s).
|
||||
SentryManager.breadcrumbAndLog("lexical model language \(l) has no keyboard installed!")
|
||||
let message = "lexical model language \(l) has no keyboard installed!"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -279,7 +283,8 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie
|
|||
}
|
||||
|
||||
private func packageDownloadStarted(key: KeymanPackage.Key) {
|
||||
log.info("download started for \(key.type.rawValue): InstalledLanguagesViewController")
|
||||
let message = "download started for \(key.type.rawValue): InstalledLanguagesViewController"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
view.isUserInteractionEnabled = false
|
||||
navigationItem.setHidesBackButton(true, animated: true)
|
||||
|
||||
|
|
@ -299,7 +304,7 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie
|
|||
}
|
||||
|
||||
private func packageDownloadCompleted(package: KeymanPackage) {
|
||||
log.info("lexicalModelDownloadCompleted: InstalledLanguagesViewController")
|
||||
os_log("lexicalModelDownloadCompleted: InstalledLanguagesViewController", log:KeymanEngineLogger.resources, type: .info)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
var msg: String
|
||||
|
|
@ -331,7 +336,7 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie
|
|||
return
|
||||
}
|
||||
|
||||
log.info("keyboardDownloadFailed: InstalledLanguagesViewController")
|
||||
os_log("keyboardDownloadFailed: InstalledLanguagesViewController", log:KeymanEngineLogger.resources, type: .info)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
var msg: String
|
||||
|
|
@ -353,7 +358,7 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie
|
|||
}
|
||||
|
||||
private func batchUpdateStarted(_: [AnyLanguageResource]) {
|
||||
log.info("batchUpdateStarted: InstalledLanguagesViewController")
|
||||
os_log("batchUpdateStarted: InstalledLanguagesViewController", log:KeymanEngineLogger.resources, type: .info)
|
||||
view.isUserInteractionEnabled = false
|
||||
navigationItem.setHidesBackButton(true, animated: true)
|
||||
|
||||
|
|
@ -447,7 +452,8 @@ extension InstalledLanguagesViewController {
|
|||
case .error(let error):
|
||||
// Note: Errors may result from network issues.
|
||||
if let error = error {
|
||||
log.error(String(describing: error))
|
||||
let errorMessage = "\(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage)
|
||||
}
|
||||
case .success(let package, let fullID):
|
||||
ResourceFileManager.shared.doInstallPrompt(for: package as! KeyboardKeymanPackage,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import UIKit
|
||||
import Sentry
|
||||
import os.log
|
||||
|
||||
private let toolbarButtonTag = 100
|
||||
|
||||
|
|
@ -38,7 +39,8 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
super.viewDidLoad()
|
||||
let titleFormat = NSLocalizedString("menu-langsettings-title", bundle: engineBundle, comment: "")
|
||||
title = String.localizedStringWithFormat(titleFormat, language.name)
|
||||
log.info("viewDidLoad: LanguageSettingsViewController title: \(title ?? "<empty>")")
|
||||
let message = "viewDidLoad: LanguageSettingsViewController title: \(title ?? "<empty>")"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .info, message)
|
||||
|
||||
if Manager.shared.canAddNewKeyboards {
|
||||
let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self,
|
||||
|
|
@ -50,7 +52,7 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
log.info("didAppear: LanguageSettingsViewController")
|
||||
os_log("viewDidAppear: LanguageSettingsViewController", log:KeymanEngineLogger.ui, type: .info)
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -309,7 +311,8 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
case .error(let error):
|
||||
if let error = error {
|
||||
// Note: Errors may result from network issues.
|
||||
log.error(String(describing: error))
|
||||
let errorMessage = "\(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .error, errorMessage)
|
||||
}
|
||||
case .success(let package, let fullID):
|
||||
ResourceFileManager.shared.doInstallPrompt(for: package as! KeyboardKeymanPackage,
|
||||
|
|
@ -346,8 +349,10 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
|
||||
// If user defaults for keyboards list does not exist, do nothing.
|
||||
guard let globalUserKeyboards = userData.userKeyboards else {
|
||||
SentryManager.captureAndLog("no keyboards in the global keyboards list!")
|
||||
return nil
|
||||
let message = "no keyboards in the global keyboards list!"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
return nil
|
||||
}
|
||||
|
||||
if let index = globalUserKeyboards.firstIndex(where: { $0.fullID == matchingFullID }) {
|
||||
|
|
@ -361,7 +366,8 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
event.extra = ["id": matchingFullID]
|
||||
SentrySDK.capture(event: event)
|
||||
|
||||
log.error("this keyboard \(matchingFullID) not found among user's installed keyboards!")
|
||||
let errorMessage = "this keyboard \(matchingFullID) not found among user's installed keyboards!"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -373,7 +379,9 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
|
||||
// If user defaults for keyboards list does not exist, do nothing.
|
||||
guard let globalUserKeyboards = userData.userKeyboards else {
|
||||
SentryManager.captureAndLog("no keyboards in the global keyboards list!")
|
||||
let message = "No keyboards in the global keyboards list!"
|
||||
os_log("%{public}s", log: KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture("no keyboards in the global keyboards list!")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -392,7 +400,8 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
event.extra = ["id": matchingFullID]
|
||||
SentrySDK.capture(event: event)
|
||||
|
||||
log.error("this keyboard \(matchingFullID) not found among user's installed keyboards!")
|
||||
let errorMessage = "this keyboard \(matchingFullID) not found among user's installed keyboards!"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
open class SettingsViewController: UITableViewController {
|
||||
private var itemsArray = [[String: String]]()
|
||||
|
|
@ -16,7 +17,7 @@ open class SettingsViewController: UITableViewController {
|
|||
super.viewWillAppear(animated)
|
||||
|
||||
loadUserLanguages()
|
||||
log.info("willAppear: SettingsViewController")
|
||||
os_log("viewWillAppear: SettingsViewController", log:KeymanEngineLogger.settings, type: .info)
|
||||
}
|
||||
|
||||
override open func viewDidLoad() {
|
||||
|
|
@ -40,7 +41,7 @@ open class SettingsViewController: UITableViewController {
|
|||
let sb : UIStoryboard = UIStoryboard(name: "Settings", bundle: nil)
|
||||
if let vc = sb.instantiateInitialViewController() {
|
||||
launchingVC.present(vc, animated: true, completion: {
|
||||
log.info("presented settings")
|
||||
os_log("presented settings", log:KeymanEngineLogger.settings, type: .info)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -199,7 +200,9 @@ open class SettingsViewController: UITableViewController {
|
|||
case "systemkeyboardsettings", "installfile", "forcederror", "spacebartext":
|
||||
break
|
||||
default:
|
||||
SentryManager.captureAndLog("unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")")
|
||||
let message = "unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
cell.accessoryType = .none
|
||||
}
|
||||
|
||||
|
|
@ -272,7 +275,9 @@ open class SettingsViewController: UITableViewController {
|
|||
case "showbanner", "showgetstarted":
|
||||
cell.detailTextLabel?.isEnabled = false
|
||||
default:
|
||||
SentryManager.captureAndLog("unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")")
|
||||
let message = "unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -296,7 +301,9 @@ open class SettingsViewController: UITableViewController {
|
|||
case "systemkeyboardsettings":
|
||||
guard let appSettings = URL(string: UIApplication.openSettingsURLString) else {
|
||||
// It is an error if the option is displayed but unusable. That's bad UI.
|
||||
SentryManager.captureAndLog("Could not launch keyboard settings menu")
|
||||
let message = "Could not launch keyboard settings menu"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
return
|
||||
}
|
||||
UniversalLinks.externalLinkLauncher?(appSettings)
|
||||
|
|
@ -304,7 +311,9 @@ open class SettingsViewController: UITableViewController {
|
|||
if let block = Manager.shared.fileBrowserLauncher {
|
||||
block(navigationController!)
|
||||
} else {
|
||||
SentryManager.captureAndLog("Listener for framework signal to launch file browser is missing")
|
||||
let message = "Listener for framework signal to launch file browser is missing"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
}
|
||||
case "forcederror":
|
||||
SentryManager.forceError()
|
||||
|
|
@ -357,9 +366,9 @@ open class SettingsViewController: UITableViewController {
|
|||
|
||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||
override open func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
log.info("prepare for segue")
|
||||
// Get the new view controller using segue.destination.
|
||||
// Pass the selected object to the new view controller.
|
||||
os_log("prepare for segue", log:KeymanEngineLogger.settings, type: .info)
|
||||
// Get the new view controller using segue.destination.
|
||||
// Pass the selected object to the new view controller.
|
||||
}
|
||||
|
||||
// MARK: - language access -
|
||||
|
|
@ -405,11 +414,14 @@ open class SettingsViewController: UITableViewController {
|
|||
for lm in userLexicalModels {
|
||||
let l = lm.languageID
|
||||
if let langName = keyboardLanguages[l]?.name {
|
||||
log.info("keyboard language \(l) \(langName) has lexical model")
|
||||
let message = "keyboard language \(l) \(langName) has lexical model"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
} else {
|
||||
// Legacy behavior: we automatically install all MTNT language codes, even without
|
||||
// a matching keyboard for the more specific variant(s).
|
||||
SentryManager.breadcrumbAndLog("lexical model language \(l) has no keyboard installed!")
|
||||
let message = "lexical model language \(l) has no keyboard installed!"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message)
|
||||
SentryManager.breadcrumb(message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -438,7 +450,9 @@ open class SettingsViewController: UITableViewController {
|
|||
nc.pushViewController(vc, animated: true)
|
||||
setIsDoneButtonEnabled(nc, true)
|
||||
} else {
|
||||
SentryManager.captureAndLog("no navigation controller for showing languages???")
|
||||
let message = ("No navigation controller for showing languages???")
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -448,7 +462,9 @@ open class SettingsViewController: UITableViewController {
|
|||
nc.pushViewController(vc, animated: true)
|
||||
setIsDoneButtonEnabled(nc, true)
|
||||
} else {
|
||||
SentryManager.captureAndLog("no navigation controller for showing spacebarText options")
|
||||
let message = ("No navigation controller for showing spacebarText options")
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import AudioToolbox
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
public class TextField: UITextField, KeymanResponder {
|
||||
// viewController should be set to main view controller to enable keyboard picker.
|
||||
|
|
@ -83,7 +84,7 @@ public class TextField: UITextField, KeymanResponder {
|
|||
}
|
||||
|
||||
if delegate !== delegateProxy {
|
||||
log.error("Trying to set TextField's delegate directly. Use setKeymanDelegate() instead.")
|
||||
os_log("Trying to set TextField's delegate directly. Use setKeymanDelegate() instead.", log:KeymanEngineLogger.ui, type: .error)
|
||||
}
|
||||
super.delegate = delegateProxy
|
||||
}
|
||||
|
|
@ -95,7 +96,8 @@ public class TextField: UITextField, KeymanResponder {
|
|||
// All of the normal UITextFieldDelegate methods are supported.
|
||||
public func setKeymanDelegate(_ keymanDelegate: TextFieldDelegate?) {
|
||||
delegateProxy.keymanDelegate = keymanDelegate
|
||||
log.debug("TextField: \(self.hashValue) keymanDelegate set to: \(keymanDelegate.debugDescription)")
|
||||
let message = "TextField: \(self.hashValue) keymanDelegate set to: \(keymanDelegate.debugDescription)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, message)
|
||||
}
|
||||
|
||||
public override var text: String! {
|
||||
|
|
@ -146,7 +148,8 @@ public class TextField: UITextField, KeymanResponder {
|
|||
font = UIFont.systemFont(ofSize: fontSize)
|
||||
}
|
||||
|
||||
log.debug("TextField \(self.hashValue) setFont: \(font?.familyName ?? "nil")")
|
||||
let message = "TextField \(self.hashValue) setFont: \(font?.familyName ?? "nil")"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .debug, message)
|
||||
}
|
||||
|
||||
@objc func enableInputClickSound() {
|
||||
|
|
@ -171,7 +174,8 @@ extension KeymanResponder where Self: TextField {
|
|||
// Dismisses the keyboard if this textview is the first responder.
|
||||
// - Use this instead of [resignFirstResponder] as it also resigns the Keyman keyboard's responders.
|
||||
public func dismissKeyboard() {
|
||||
log.debug("TextField: \(self.hashValue) dismissing keyboard. Was first responder: \(isFirstResponder)")
|
||||
let message = "TextField: \(self.hashValue) dismissing keyboard. Was first responder: \(isFirstResponder)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, message)
|
||||
resignFirstResponder()
|
||||
Manager.shared.inputViewController.endEditing(true)
|
||||
}
|
||||
|
|
@ -258,10 +262,12 @@ extension TextField: UITextFieldDelegate {
|
|||
font = UIFont.systemFont(ofSize: fontSize)
|
||||
}
|
||||
|
||||
log.debug("TextField: \(self.hashValue) setFont: \(font?.familyName ?? "nil")")
|
||||
let messageOne = "TextField: \(self.hashValue) setFont: \(font?.familyName ?? "nil")"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, messageOne)
|
||||
|
||||
log.debug("TextField: \(self.hashValue) Became first responder. Value: \(String(describing: text))")
|
||||
}
|
||||
let messageTwo = "TextField: \(self.hashValue) Became first responder. Value: \(String(describing: text))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, messageTwo)
|
||||
}
|
||||
|
||||
public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
|
||||
if textField == self {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import AudioToolbox
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
public class TextView: UITextView, KeymanResponder {
|
||||
// viewController should be set to main view controller to enable keyboard picker.
|
||||
|
|
@ -80,8 +81,8 @@ public class TextView: UITextView, KeymanResponder {
|
|||
}
|
||||
|
||||
if delegate !== delegateProxy {
|
||||
log.error("Trying to set TextView's delegate directly. Use setKeymanDelegate() instead.")
|
||||
}
|
||||
os_log("Trying to set TextView's delegate directly. Use setKeymanDelegate() instead.", log:KeymanEngineLogger.ui, type: .error)
|
||||
}
|
||||
super.delegate = delegateProxy
|
||||
}
|
||||
}
|
||||
|
|
@ -92,7 +93,8 @@ public class TextView: UITextView, KeymanResponder {
|
|||
// - All of the normal UITextViewDelegate methods are supported.
|
||||
public func setKeymanDelegate(_ keymanDelegate: TextViewDelegate?) {
|
||||
delegateProxy.keymanDelegate = keymanDelegate
|
||||
log.debug("TextView: \(self.hashValue) keymanDelegate set to: \(keymanDelegate.debugDescription)")
|
||||
let message = "TextView: \(self.hashValue) keymanDelegate set to: \(keymanDelegate.debugDescription)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, message)
|
||||
}
|
||||
|
||||
public override var text: String! {
|
||||
|
|
@ -133,7 +135,8 @@ public class TextView: UITextView, KeymanResponder {
|
|||
font = UIFont.systemFont(ofSize: fontSize)
|
||||
}
|
||||
|
||||
log.debug("TextView: \(self.hashValue) setFont: \(font?.familyName ?? "nil")")
|
||||
let message = "TextView: \(self.hashValue) setFont: \(font?.familyName ?? "nil")"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, message)
|
||||
}
|
||||
|
||||
// MARK: iOS 7 TextView Scroll bug fix
|
||||
|
|
@ -168,7 +171,8 @@ extension KeymanResponder where Self: TextView {
|
|||
// Dismisses the keyboard if this textview is the first responder.
|
||||
// - Use this instead of [resignFirstResponder] as it also resigns the Keyman keyboard's responders.
|
||||
public func dismissKeyboard() {
|
||||
log.debug("TextView: \(self.hashValue) dismissing keyboard. Was first responder: \(isFirstResponder)")
|
||||
let message = "TextView: \(self.hashValue) dismissing keyboard. Was first responder: \(isFirstResponder)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, message)
|
||||
resignFirstResponder()
|
||||
Manager.shared.inputViewController.endEditing(true)
|
||||
}
|
||||
|
|
@ -251,9 +255,11 @@ extension TextView: UITextViewDelegate {
|
|||
font = UIFont.systemFont(ofSize: fontSize)
|
||||
}
|
||||
|
||||
log.debug("TextView: \(self.hashValue) setFont: \(font?.familyName ?? "nil")")
|
||||
let messageOne = "TextView: \(self.hashValue) setFont: \(font?.familyName ?? "nil")"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, messageOne)
|
||||
|
||||
log.debug("TextView: \(self.hashValue) Became first responder. Value: \(String(describing: text))")
|
||||
let messageTwo = "TextView: \(self.hashValue) Became first responder. Value: \(String(describing: text))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, messageTwo)
|
||||
}
|
||||
|
||||
public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import XCTest
|
||||
@testable import KeymanEngine
|
||||
import DeviceKit
|
||||
import os.log
|
||||
|
||||
extension KeyboardSize: Equatable {
|
||||
public static func == (lhs: KeyboardSize, rhs: KeyboardSize) -> Bool {
|
||||
|
|
@ -51,7 +52,8 @@ class KeyboardScaleTests: XCTestCase {
|
|||
|
||||
func testScaleForUnknown() {
|
||||
let device = Device.unknown("granny smith")
|
||||
log.info(device.isPad)
|
||||
let message = "\(device.isPad)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.ui, type: .info, message)
|
||||
let size1 = CGSize(width: 375, height: 667) // iPhone 8 device height
|
||||
|
||||
let mappedScale1 = KeyboardScaleMap.getDeviceDefaultKeyboardScale(forPortrait: true,
|
||||
|
|
|
|||
|
|
@ -127,13 +127,11 @@ class KeymanPackageTests: XCTestCase {
|
|||
|
||||
do {
|
||||
if let kmp = try KeymanPackage.extract(fileUrl: khmerPackageZip, destination: destinationFolderURL) {
|
||||
log.info("*** first unzip of \(kmp.id)")
|
||||
do {
|
||||
// clear directory before second extract
|
||||
try KeymanPackage.clearDirectory(destination: destinationFolderURL)
|
||||
|
||||
if let secondKmp = try KeymanPackage.extract(fileUrl: khmerPackageZip, destination: destinationFolderURL) {
|
||||
log.info("*** second unzip of \(secondKmp.id)")
|
||||
} else {
|
||||
XCTAssert(false, "*** second unzip failed")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import XCTest
|
||||
import os.log
|
||||
@testable import KeymanEngine
|
||||
|
||||
class ResourceUpdateTests: XCTestCase {
|
||||
|
|
@ -60,7 +61,7 @@ class ResourceUpdateTests: XCTestCase {
|
|||
// Uses the XCTest 'attachment' system to retrieve the desired file.
|
||||
self.add(try TestUtils.EngineStateBundler.createBundle(withName: "khmer_angkor update base"))
|
||||
|
||||
log.info("Bundle archived and attached to test's report.")
|
||||
os_log("Bundle archived and attached to test's report.", log:KeymanEngineLogger.resources, type: .info)
|
||||
}
|
||||
|
||||
func testCacheCurrent() {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import XCGLogger
|
||||
import XCTest
|
||||
|
||||
extension TestUtils.Downloading {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
import ZIPFoundation
|
||||
import os.log
|
||||
|
||||
@testable import KeymanEngine
|
||||
|
||||
|
|
@ -41,13 +42,16 @@ extension TestUtils {
|
|||
|
||||
try FileManager.default.createDirectory(at: bundleConstructionURL.appendingPathComponent("Library"), withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
log.info("Documents directory source: \(documentsDirectory)")
|
||||
let messageOne = "Documents directory source: \(documentsDirectory)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, messageOne)
|
||||
try FileManager.default.copyItem(at: documentsDirectory, to: bundleConstructionURL.appendingPathComponent("Documents"))
|
||||
|
||||
log.info("Library source: \(storageDirectory)")
|
||||
let messageTwo = "Library source: \(storageDirectory)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, messageTwo)
|
||||
try FileManager.default.copyItem(at: storageDirectory, to: bundleConstructionURL.appendingPathComponent("Library").appendingPathComponent("keyman"))
|
||||
|
||||
log.info("Preferences directory source: \(preferencesDirectory)")
|
||||
let messageThree = "Preferences directory source: \(preferencesDirectory)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, messageThree)
|
||||
let pListPath = bundleConstructionURL.appendingPathComponent("Library").appendingPathComponent("Preferences")
|
||||
|
||||
let testEngineFilename = "com.keyman.testing.KeymanEngineTestHost.plist"
|
||||
|
|
@ -68,7 +72,8 @@ extension TestUtils {
|
|||
let archiveURL = bundleConstructionURL.appendingPathComponent("bundleArchive.zip")
|
||||
do {
|
||||
_ = try Archive(url: archiveURL, accessMode: .create)
|
||||
log.info("archiveURL: \(archiveURL)")
|
||||
let message = "archiveURL: \(archiveURL)"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
|
||||
let attachment = XCTAttachment(contentsOfFile: archiveURL)
|
||||
attachment.lifetime = .keepAlways
|
||||
return attachment
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@
|
|||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
@testable import KeymanEngine
|
||||
|
||||
/**
|
||||
|
|
@ -40,8 +41,9 @@ enum TestUtils {
|
|||
try FileManager.default.removeItem(at: item)
|
||||
}
|
||||
} catch {
|
||||
log.error(error)
|
||||
XCTFail("Could not clear a directory because of error: \(error)")
|
||||
let message = "Could not clear a directory because of error: \(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .debug, message)
|
||||
XCTFail("Could not clear a directory because of error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +59,8 @@ enum TestUtils {
|
|||
withIntermediateDirectories: true,
|
||||
attributes: nil)
|
||||
} catch {
|
||||
log.error(error)
|
||||
let message = "Could not properly erase & reinit KeymanEngine Storage because of error: \(String(describing: error))"
|
||||
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .debug, message)
|
||||
XCTFail("Could not properly erase & reinit KeymanEngine Storage because of error: \(error)")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
165EB39C2097181D00040A69 /* KMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165EB39B2097181D00040A69 /* KMView.swift */; };
|
||||
296EF2C42AFA267500E3E384 /* ZIPFoundation.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 296EF2C32AFA267500E3E384 /* ZIPFoundation.xcframework */; };
|
||||
296EF2C52AFA267500E3E384 /* ZIPFoundation.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 296EF2C32AFA267500E3E384 /* ZIPFoundation.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
29B30C212B56325D00C342A4 /* KeymanLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29CA9A612B4F9B0B00EAB099 /* KeymanLogger.swift */; };
|
||||
29CA9A622B4F9B0B00EAB099 /* KeymanLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29CA9A612B4F9B0B00EAB099 /* KeymanLogger.swift */; };
|
||||
981AFA8F19EF44DE006706BF /* 724-info@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9815725518E4F0930014DF0C /* 724-info@2x.png */; };
|
||||
981AFA9619EF44DE006706BF /* MainViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 98ABADCE176935E500B62590 /* MainViewController_iPhone.xib */; };
|
||||
981AFA9819EF44DE006706BF /* 724-info.png in Resources */ = {isa = PBXBuildFile; fileRef = 9815725418E4F0930014DF0C /* 724-info.png */; };
|
||||
|
|
@ -116,7 +118,6 @@
|
|||
C055F0B11F60E8D400140735 /* GetStartedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C055F0B01F60E8D400140735 /* GetStartedViewController.swift */; };
|
||||
C055F0B31F60F7A400140735 /* DropDownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C055F0B21F60F7A400140735 /* DropDownView.swift */; };
|
||||
C055F0B51F610FB200140735 /* DropDownListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C055F0B41F610FB200140735 /* DropDownListView.swift */; };
|
||||
C059FCC01FD927EF00BD1A64 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = C059FCBF1FD927EF00BD1A64 /* Log.swift */; };
|
||||
C0A1A6311F5E4F4300EAF822 /* InfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A1A6301F5E4F4300EAF822 /* InfoViewController.swift */; };
|
||||
C0A1A6331F5E749000EAF822 /* SetUpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A1A6321F5E749000EAF822 /* SetUpViewController.swift */; };
|
||||
C0A1A6351F5E786A00EAF822 /* BookmarksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A1A6341F5E786A00EAF822 /* BookmarksViewController.swift */; };
|
||||
|
|
@ -149,14 +150,10 @@
|
|||
CEBD34322654FE3500EB2EA8 /* KeymanEngine.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE80AD32257F2B4A008D2150 /* KeymanEngine.framework */; };
|
||||
CEBD343B2654FEB400EB2EA8 /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34352654FEB400EB2EA8 /* DeviceKit.xcframework */; };
|
||||
CEBD343C2654FEB400EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34352654FEB400EB2EA8 /* DeviceKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD343D2654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34362654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework */; };
|
||||
CEBD343E2654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34362654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD343F2654FEB400EB2EA8 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34372654FEB400EB2EA8 /* Reachability.xcframework */; };
|
||||
CEBD34402654FEB400EB2EA8 /* Reachability.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34372654FEB400EB2EA8 /* Reachability.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD34412654FEB400EB2EA8 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */; };
|
||||
CEBD34422654FEB400EB2EA8 /* Sentry.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD34432654FEB400EB2EA8 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34392654FEB400EB2EA8 /* XCGLogger.xcframework */; };
|
||||
CEBD34442654FEB400EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34392654FEB400EB2EA8 /* XCGLogger.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEF4E55623E95B7B0065B9C7 /* ImageBanner.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEF4E55523E95B7B0065B9C7 /* ImageBanner.xib */; };
|
||||
CEF4E55723E95B7B0065B9C7 /* ImageBanner.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEF4E55523E95B7B0065B9C7 /* ImageBanner.xib */; };
|
||||
CEF4E55923E967140065B9C7 /* ImageBannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF4E55823E967140065B9C7 /* ImageBannerViewController.swift */; };
|
||||
|
|
@ -182,11 +179,9 @@
|
|||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
CEBD34402654FEB400EB2EA8 /* Reachability.xcframework in Embed Frameworks */,
|
||||
CEBD343E2654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */,
|
||||
296EF2C52AFA267500E3E384 /* ZIPFoundation.xcframework in Embed Frameworks */,
|
||||
CEBD343C2654FEB400EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */,
|
||||
CEBD34422654FEB400EB2EA8 /* Sentry.xcframework in Embed Frameworks */,
|
||||
CEBD34442654FEB400EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */,
|
||||
CE80AD34257F2B4B008D2150 /* KeymanEngine.framework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
|
|
@ -215,6 +210,7 @@
|
|||
29BFA75D282931F8009FCCC3 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
29BFA769282946EC009FCCC3 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
29C1E17028001EC200759EDE /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
29CA9A612B4F9B0B00EAB099 /* KeymanLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeymanLogger.swift; sourceTree = "<group>"; };
|
||||
37378F4222C9AA610043F22B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainViewController_iPad.xib; sourceTree = "<group>"; };
|
||||
37378F4322C9AA630043F22B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainViewController_iPhone.xib; sourceTree = "<group>"; };
|
||||
9815725018E4F0930014DF0C /* 702-share.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "702-share.png"; path = "Keyman Icons/702-share.png"; sourceTree = "<group>"; };
|
||||
|
|
@ -319,7 +315,6 @@
|
|||
C055F0B01F60E8D400140735 /* GetStartedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = GetStartedViewController.swift; sourceTree = "<group>"; tabWidth = 2; };
|
||||
C055F0B21F60F7A400140735 /* DropDownView.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = DropDownView.swift; sourceTree = "<group>"; tabWidth = 2; };
|
||||
C055F0B41F610FB200140735 /* DropDownListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = DropDownListView.swift; sourceTree = "<group>"; tabWidth = 2; };
|
||||
C059FCBF1FD927EF00BD1A64 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
|
||||
C0A1A6301F5E4F4300EAF822 /* InfoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = InfoViewController.swift; sourceTree = "<group>"; tabWidth = 2; };
|
||||
C0A1A6321F5E749000EAF822 /* SetUpViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = SetUpViewController.swift; sourceTree = "<group>"; tabWidth = 2; };
|
||||
C0A1A6341F5E786A00EAF822 /* BookmarksViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = BookmarksViewController.swift; sourceTree = "<group>"; tabWidth = 2; };
|
||||
|
|
@ -341,10 +336,8 @@
|
|||
CEACC90F25F07D5A006EAB45 /* km */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = km; path = km.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
CEACC91225F07D77006EAB45 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
CEBD34352654FEB400EB2EA8 /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = ../../Carthage/Build/DeviceKit.xcframework; sourceTree = "<absolute>"; };
|
||||
CEBD34362654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = ../../Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = "<absolute>"; };
|
||||
CEBD34372654FEB400EB2EA8 /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = ../../Carthage/Build/Reachability.xcframework; sourceTree = "<absolute>"; };
|
||||
CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = "<absolute>"; };
|
||||
CEBD34392654FEB400EB2EA8 /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = ../../Carthage/Build/XCGLogger.xcframework; sourceTree = "<absolute>"; };
|
||||
CEBD3458265511B700EB2EA8 /* am */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = am; path = am.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
CEEC468226F2F7BA009A5B7D /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
CEEF81B92673019600EE6A07 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
|
|
@ -358,11 +351,9 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CEBD343F2654FEB400EB2EA8 /* Reachability.xcframework in Frameworks */,
|
||||
CEBD343D2654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */,
|
||||
296EF2C42AFA267500E3E384 /* ZIPFoundation.xcframework in Frameworks */,
|
||||
CEBD343B2654FEB400EB2EA8 /* DeviceKit.xcframework in Frameworks */,
|
||||
CEBD34412654FEB400EB2EA8 /* Sentry.xcframework in Frameworks */,
|
||||
CEBD34432654FEB400EB2EA8 /* XCGLogger.xcframework in Frameworks */,
|
||||
CE80AD33257F2B4A008D2150 /* KeymanEngine.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -546,10 +537,8 @@
|
|||
children = (
|
||||
296EF2C32AFA267500E3E384 /* ZIPFoundation.xcframework */,
|
||||
CEBD34352654FEB400EB2EA8 /* DeviceKit.xcframework */,
|
||||
CEBD34362654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework */,
|
||||
CEBD34372654FEB400EB2EA8 /* Reachability.xcframework */,
|
||||
CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */,
|
||||
CEBD34392654FEB400EB2EA8 /* XCGLogger.xcframework */,
|
||||
CE80AD32257F2B4A008D2150 /* KeymanEngine.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
|
|
@ -568,8 +557,8 @@
|
|||
16A9229D20325253003CC98E /* Info.plist */,
|
||||
162E2C9420926C8600F40769 /* Classes */,
|
||||
C0E943F91F6124E100E7D98C /* AppDelegate.swift */,
|
||||
C059FCBF1FD927EF00BD1A64 /* Log.swift */,
|
||||
CE7FF1EF239A0293007859D9 /* PackageBrowserViewController.swift */,
|
||||
29CA9A612B4F9B0B00EAB099 /* KeymanLogger.swift */,
|
||||
);
|
||||
path = Keyman;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1042,9 +1031,9 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
165EB39C2097181D00040A69 /* KMView.swift in Sources */,
|
||||
29CA9A622B4F9B0B00EAB099 /* KeymanLogger.swift in Sources */,
|
||||
C055F0B11F60E8D400140735 /* GetStartedViewController.swift in Sources */,
|
||||
C055F0B51F610FB200140735 /* DropDownListView.swift in Sources */,
|
||||
C059FCC01FD927EF00BD1A64 /* Log.swift in Sources */,
|
||||
CE7FF1F0239A0293007859D9 /* PackageBrowserViewController.swift in Sources */,
|
||||
C0E943F81F61234C00E7D98C /* UIImage+Helpers.swift in Sources */,
|
||||
C0E943FE1F61377900E7D98C /* MainViewController.swift in Sources */,
|
||||
|
|
@ -1070,6 +1059,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CEF4E55A23E967140065B9C7 /* ImageBannerViewController.swift in Sources */,
|
||||
29B30C212B56325D00C342A4 /* KeymanLogger.swift in Sources */,
|
||||
C0FF769B1F5D368C00BD23C3 /* KeyboardViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import KeymanEngine
|
|||
import UIKit
|
||||
import WebKit
|
||||
import Sentry
|
||||
import os.log
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
|
@ -49,7 +50,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
vc.present(nvc, animated: true, completion: nil)
|
||||
}
|
||||
} else {
|
||||
SentryManager.captureAndLog("Cannot find app's root UIViewController")
|
||||
let message = "Cannot find app's root UIViewController"
|
||||
os_log("%{public}s", log: KeymanLogger.ui, type: .info, message)
|
||||
SentryManager.capture(message)
|
||||
}
|
||||
|
||||
return true
|
||||
|
|
@ -60,8 +63,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
SentryManager.start()
|
||||
// Forces the logs to initialize, as their definitions result in lazy init.
|
||||
// These references have been configured to also log app details.
|
||||
_ = log
|
||||
_ = KeymanEngine.log
|
||||
|
||||
// In iOS 15, navigation bars become transparent by default when the edge
|
||||
// of the scrollable content aligns with the edge of the navigation bar.
|
||||
|
|
@ -139,7 +140,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
ResourceFileManager.shared.promptPackageInstall(of: package, in: nvc, isCustom: true)
|
||||
vc.present(nvc, animated: true, completion: nil)
|
||||
} else {
|
||||
SentryManager.captureAndLog("Cannot find app's root UIViewController")
|
||||
let message = "Cannot find app's root UIViewController"
|
||||
os_log("%{public}s", log: KeymanLogger.ui, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import KeymanEngine
|
||||
import WebKit
|
||||
import Reachability
|
||||
import os
|
||||
|
||||
class InfoViewController: UIViewController, WKNavigationDelegate {
|
||||
@IBOutlet var webView: WKWebView!
|
||||
|
|
@ -41,7 +42,9 @@ class InfoViewController: UIViewController, WKNavigationDelegate {
|
|||
try networkReachable = Reachability(hostname: "keyman.com")
|
||||
try networkReachable?.startNotifier()
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "error starting Reachability notifier: \(error)")
|
||||
let message = "error starting Reachability notifier: \(error)"
|
||||
os_log("%{public}s", log:KeymanLogger.ui, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +70,7 @@ class InfoViewController: UIViewController, WKNavigationDelegate {
|
|||
func webView(_ webView: WKWebView, didFailNavigation error: Error) {
|
||||
UIApplication.shared.isNetworkActivityIndicatorVisible = false
|
||||
updateButtons()
|
||||
log.debug(error)
|
||||
os_log("%{public}s", log: KeymanLogger.ui, type: .error, error.localizedDescription)
|
||||
}
|
||||
|
||||
@IBAction func back(_ sender: Any) {
|
||||
|
|
@ -100,6 +103,6 @@ class InfoViewController: UIViewController, WKNavigationDelegate {
|
|||
let appVersion = Version.current.majorMinor
|
||||
let url = "\(KeymanHosts.HELP_KEYMAN_COM)/products/iphone-and-ipad/\(appVersion.plainString)/"
|
||||
webView.load(URLRequest(url: URL(string: url)!))
|
||||
log.debug("Info page URL: \(url)")
|
||||
os_log("Info page URL: %{public}s", log: KeymanLogger.ui, type: .debug, url)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import KeymanEngine
|
||||
import UIKit
|
||||
import QuartzCore
|
||||
import os
|
||||
|
||||
// Internal strings
|
||||
private let baseUri = "https://r.keymanweb.com/20/fonts/get_mobileconfig.php?id="
|
||||
|
|
@ -191,7 +192,7 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg
|
|||
userData.set(self.textView?.text, forKey: userTextKey)
|
||||
userData.set(self.textSize.description, forKey: userTextSizeKey)
|
||||
userData.synchronize()
|
||||
log.debug("saving text size: \(textSize.description).")
|
||||
os_log("saving text size: %{public}s", log: KeymanLogger.settings, type: .debug, textSize.description)
|
||||
}
|
||||
|
||||
private func calculateDefaultTextSize() -> CGFloat {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import KeymanEngine
|
||||
import WebKit
|
||||
import Reachability
|
||||
import os
|
||||
|
||||
// TODO: Refactor common functionality from InfoViewController
|
||||
class SetUpViewController: UIViewController, WKNavigationDelegate {
|
||||
|
|
@ -35,7 +36,9 @@ class SetUpViewController: UIViewController, WKNavigationDelegate {
|
|||
try networkReachable = Reachability(hostname: "keyman.com")
|
||||
try networkReachable?.startNotifier()
|
||||
} catch {
|
||||
SentryManager.captureAndLog(error, message: "error thrown starting Reachability notifier: \(error)")
|
||||
let message = "error thrown starting Reachability notifier: \(error)"
|
||||
os_log("%{public}s", log:KeymanLogger.ui, type: .error, message)
|
||||
SentryManager.capture(error, message: message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,6 +77,6 @@ class SetUpViewController: UIViewController, WKNavigationDelegate {
|
|||
let url = "\(KeymanHosts.HELP_KEYMAN_COM)/products/iphone-and-ipad/\(appVersion.plainString)"
|
||||
+ "/start/installing-system-keyboard?embed=ios"
|
||||
webView.load(URLRequest(url: URL(string: url)!))
|
||||
log.debug("Set up page URL: \(url)")
|
||||
os_log("Set up page URL: %{public}s", log: KeymanLogger.resources, type: .debug, url)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
33
ios/keyman/Keyman/Keyman/KeymanLogger.swift
Normal file
33
ios/keyman/Keyman/Keyman/KeymanLogger.swift
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* Keyman is copyright (C) SIL International. MIT License.
|
||||
*
|
||||
* KeymanLogger.swift
|
||||
* Keyman
|
||||
*
|
||||
* Created by Shawn Schantz on 2024-01-11.
|
||||
*
|
||||
* Basic logging capability provided by wrapping Apple's Unified Logging
|
||||
* Framework. Works with iOS 10 and later.
|
||||
* Log statements are written with a call the `os_log` API which takes a
|
||||
* message, an `OSLog` reference and log level.
|
||||
* Several static references to `OSLog` objects are defined, each of which
|
||||
* determines the subsystem and category of subsequent log statements.
|
||||
* Both subsystem and category are useful for filtering with the Console app.
|
||||
* Use the same subsystem name for all `OSLog` objects created here, but specify
|
||||
* a unique category to identify related functionality.
|
||||
* Add as many static `OSLog` resources as necessary to support new log
|
||||
* categories.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
// bundle for engine is "Tavultesoft.Keyman"
|
||||
// but selecting this subsystem name for consistency across apps
|
||||
private let subsystem = "com.keyman.ios.app"
|
||||
|
||||
public struct KeymanLogger {
|
||||
static let ui = OSLog(subsystem: subsystem, category: "UI")
|
||||
static let resources = OSLog(subsystem: subsystem, category: "Resources")
|
||||
static let settings = OSLog(subsystem: subsystem, category: "Settings")
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
//
|
||||
// Log.swift
|
||||
// Keyman
|
||||
//
|
||||
// Created by Gabriel Wong on 2017-12-07.
|
||||
// Copyright © 2017 SIL International. All rights reserved.
|
||||
//
|
||||
|
||||
import XCGLogger
|
||||
|
||||
// From XCGLogger docs:
|
||||
// Note: This creates the log object lazily, which means it's not created until it's actually needed.
|
||||
public let log: XCGLogger = {
|
||||
// Default: the 'console', which is read by Xcode but doesn't reach the system logs.
|
||||
let mainLog = XCGLogger(identifier: "Keyman", includeDefaultDestinations: false)
|
||||
|
||||
// Ensures our log messages go out to the device's system log as well as the console.
|
||||
let systemLogDest = AppleSystemLogDestination(identifier: "")
|
||||
systemLogDest.showLogIdentifier = true
|
||||
|
||||
mainLog.add(destination: systemLogDest)
|
||||
|
||||
// Temporary logging level to ensure that app details are reported properly.
|
||||
mainLog.outputLevel = .info
|
||||
mainLog.logAppDetails()
|
||||
|
||||
#if DEBUG
|
||||
mainLog.outputLevel = .debug
|
||||
#else
|
||||
mainLog.outputLevel = .warning
|
||||
#endif
|
||||
|
||||
return mainLog
|
||||
}()
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import KeymanEngine // for log commands
|
||||
import os
|
||||
|
||||
/**
|
||||
* Takes in a XIB spec for an image banner and makes it renderable, applying size constraints when rendered
|
||||
|
|
@ -49,7 +50,8 @@ class ImageBannerViewController: UIViewController {
|
|||
|
||||
let frame = CGRect(origin: self.view.frame.origin, size: size)
|
||||
|
||||
log.debug("Rendering banner image of size \(size)")
|
||||
os_log("Rendering banner image of size %{public}s", log: KeymanLogger.ui, type: .debug, NSCoder.string(for: size))
|
||||
|
||||
self.view.frame = frame
|
||||
widthConstraint?.constant = size.width
|
||||
heightConstraint?.constant = size.height
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import KeymanEngine
|
||||
import UIKit
|
||||
import Sentry
|
||||
import os.log
|
||||
|
||||
class KeyboardViewController: InputViewController {
|
||||
var topBarImageSource: ImageBannerViewController!
|
||||
|
|
@ -21,8 +22,6 @@ class KeyboardViewController: InputViewController {
|
|||
// is enabled. They seem to get blocked otherwise, except in the Simulator.
|
||||
SentryManager.start(sendingEnabled: true)
|
||||
}
|
||||
_ = log
|
||||
_ = KeymanEngine.log
|
||||
|
||||
Manager.applicationGroupIdentifier = "group.KM4I"
|
||||
|
||||
|
|
@ -70,7 +69,9 @@ class KeyboardViewController: InputViewController {
|
|||
|
||||
let imgPath = getTopBarImage(size: size)
|
||||
guard let path = imgPath else {
|
||||
SentryManager.captureAndLog("No image specified for the image banner!")
|
||||
let message = "No image specified for the image banner!"
|
||||
os_log("%{public}s", log: KeymanLogger.ui, type: .error, message)
|
||||
SentryManager.capture(message)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,14 +18,10 @@
|
|||
CEBD337E26549EC100EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD337C26549EC100EB2EA8 /* KeymanEngine.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD33C62654CB0E00EB2EA8 /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33B92654CA4B00EB2EA8 /* DeviceKit.xcframework */; };
|
||||
CEBD33C72654CB0F00EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33B92654CA4B00EB2EA8 /* DeviceKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD33C82654CB1000EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BC2654CA5900EB2EA8 /* ObjcExceptionBridging.xcframework */; };
|
||||
CEBD33C92654CB1100EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BC2654CA5900EB2EA8 /* ObjcExceptionBridging.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD33CA2654CB1300EB2EA8 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BF2654CA5900EB2EA8 /* Reachability.xcframework */; };
|
||||
CEBD33CB2654CB1300EB2EA8 /* Reachability.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BF2654CA5900EB2EA8 /* Reachability.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD33CC2654CB1400EB2EA8 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BE2654CA5900EB2EA8 /* Sentry.xcframework */; };
|
||||
CEBD33CD2654CB1500EB2EA8 /* Sentry.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BE2654CA5900EB2EA8 /* Sentry.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD33CE2654CB1600EB2EA8 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BD2654CA5900EB2EA8 /* XCGLogger.xcframework */; };
|
||||
CEBD33CF2654CB1700EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BD2654CA5900EB2EA8 /* XCGLogger.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CED2AC85260AFADC00A408D2 /* ekwtamil99uni.kmp in Resources */ = {isa = PBXBuildFile; fileRef = CED2AC84260AFADC00A408D2 /* ekwtamil99uni.kmp */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
|
|
@ -37,11 +33,9 @@
|
|||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
CEBD33CB2654CB1300EB2EA8 /* Reachability.xcframework in Embed Frameworks */,
|
||||
CEBD33C92654CB1100EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */,
|
||||
290FB45C2AFB234900249D58 /* ZIPFoundation.xcframework in Embed Frameworks */,
|
||||
CEBD33C72654CB0F00EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */,
|
||||
CEBD33CD2654CB1500EB2EA8 /* Sentry.xcframework in Embed Frameworks */,
|
||||
CEBD33CF2654CB1700EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */,
|
||||
CEBD337E26549EC100EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
|
|
@ -60,8 +54,6 @@
|
|||
C0324B831F87172900AF3785 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
CEBD337C26549EC100EB2EA8 /* KeymanEngine.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = KeymanEngine.xcframework; sourceTree = "<group>"; };
|
||||
CEBD33B92654CA4B00EB2EA8 /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = ../../Carthage/Build/DeviceKit.xcframework; sourceTree = "<group>"; };
|
||||
CEBD33BC2654CA5900EB2EA8 /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = ../../Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = "<group>"; };
|
||||
CEBD33BD2654CA5900EB2EA8 /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = ../../Carthage/Build/XCGLogger.xcframework; sourceTree = "<group>"; };
|
||||
CEBD33BE2654CA5900EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = "<group>"; };
|
||||
CEBD33BF2654CA5900EB2EA8 /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = ../../Carthage/Build/Reachability.xcframework; sourceTree = "<group>"; };
|
||||
CED2AC84260AFADC00A408D2 /* ekwtamil99uni.kmp */ = {isa = PBXFileReference; lastKnownFileType = file; path = ekwtamil99uni.kmp; sourceTree = "<group>"; };
|
||||
|
|
@ -72,13 +64,11 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CEBD33C82654CB1000EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */,
|
||||
CEBD33CA2654CB1300EB2EA8 /* Reachability.xcframework in Frameworks */,
|
||||
290FB45B2AFB234900249D58 /* ZIPFoundation.xcframework in Frameworks */,
|
||||
CEBD33CC2654CB1400EB2EA8 /* Sentry.xcframework in Frameworks */,
|
||||
CEBD33C62654CB0E00EB2EA8 /* DeviceKit.xcframework in Frameworks */,
|
||||
CEBD337D26549EC100EB2EA8 /* KeymanEngine.xcframework in Frameworks */,
|
||||
CEBD33CE2654CB1600EB2EA8 /* XCGLogger.xcframework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -128,10 +118,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
290FB45A2AFB234800249D58 /* ZIPFoundation.xcframework */,
|
||||
CEBD33BC2654CA5900EB2EA8 /* ObjcExceptionBridging.xcframework */,
|
||||
CEBD33BF2654CA5900EB2EA8 /* Reachability.xcframework */,
|
||||
CEBD33BE2654CA5900EB2EA8 /* Sentry.xcframework */,
|
||||
CEBD33BD2654CA5900EB2EA8 /* XCGLogger.xcframework */,
|
||||
CEBD33B92654CA4B00EB2EA8 /* DeviceKit.xcframework */,
|
||||
CEBD337C26549EC100EB2EA8 /* KeymanEngine.xcframework */,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
func application(_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? = nil) -> Bool {
|
||||
KeymanEngine.log.outputLevel = .debug
|
||||
KeymanEngine.log.logAppDetails()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,22 +23,16 @@
|
|||
CEBD33D92654CB7800EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33D72654CB7800EB2EA8 /* DeviceKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD33E22654CB8700EB2EA8 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */; };
|
||||
CEBD33E32654CB8700EB2EA8 /* Reachability.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD33E42654CB8800EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DD2654CB8700EB2EA8 /* ObjcExceptionBridging.xcframework */; };
|
||||
CEBD33E52654CB8800EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DD2654CB8700EB2EA8 /* ObjcExceptionBridging.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD33E62654CB8800EB2EA8 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DE2654CB8700EB2EA8 /* Sentry.xcframework */; };
|
||||
CEBD33E72654CB8800EB2EA8 /* Sentry.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DE2654CB8700EB2EA8 /* Sentry.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD33E82654CB8800EB2EA8 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DF2654CB8700EB2EA8 /* XCGLogger.xcframework */; };
|
||||
CEBD33E92654CB8800EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DF2654CB8700EB2EA8 /* XCGLogger.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBEFB94231FB4E000F8F67C /* banner-portrait-text.png in Resources */ = {isa = PBXBuildFile; fileRef = CEBEFB92231FB4DF00F8F67C /* banner-portrait-text.png */; };
|
||||
CEBEFB95231FB4E000F8F67C /* banner-landscape-text.png in Resources */ = {isa = PBXBuildFile; fileRef = CEBEFB93231FB4DF00F8F67C /* banner-landscape-text.png */; };
|
||||
CED2AC8D260B07CF00A408D2 /* ekwtamil99uni.kmp in Resources */ = {isa = PBXBuildFile; fileRef = CED2AC8C260B07CF00A408D2 /* ekwtamil99uni.kmp */; };
|
||||
CEDB3276265C953A000A2009 /* KeymanEngine.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33D22654CB6A00EB2EA8 /* KeymanEngine.xcframework */; };
|
||||
CEDB3277265C953A000A2009 /* KeymanEngine.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33D22654CB6A00EB2EA8 /* KeymanEngine.xcframework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
CEDB3279265C9B15000A2009 /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33D72654CB7800EB2EA8 /* DeviceKit.xcframework */; };
|
||||
CEDB327A265C9B33000A2009 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DD2654CB8700EB2EA8 /* ObjcExceptionBridging.xcframework */; };
|
||||
CEDB327B265C9B33000A2009 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */; };
|
||||
CEDB327C265C9B33000A2009 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DE2654CB8700EB2EA8 /* Sentry.xcframework */; };
|
||||
CEDB327D265C9B33000A2009 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DF2654CB8700EB2EA8 /* XCGLogger.xcframework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
|
@ -70,12 +64,10 @@
|
|||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
CEBD33E32654CB8700EB2EA8 /* Reachability.xcframework in Embed Frameworks */,
|
||||
CEBD33E52654CB8800EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */,
|
||||
290FB45F2AFB270D00249D58 /* ZIPFoundation.xcframework in Embed Frameworks */,
|
||||
CEBD33E72654CB8800EB2EA8 /* Sentry.xcframework in Embed Frameworks */,
|
||||
CEBD33D92654CB7800EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */,
|
||||
CEBD33D42654CB6A00EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */,
|
||||
CEBD33E92654CB8800EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -110,9 +102,7 @@
|
|||
CEBD33D22654CB6A00EB2EA8 /* KeymanEngine.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = KeymanEngine.xcframework; sourceTree = "<group>"; };
|
||||
CEBD33D72654CB7800EB2EA8 /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = ../../Carthage/Build/DeviceKit.xcframework; sourceTree = "<group>"; };
|
||||
CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = ../../Carthage/Build/Reachability.xcframework; sourceTree = "<group>"; };
|
||||
CEBD33DD2654CB8700EB2EA8 /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = ../../Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = "<group>"; };
|
||||
CEBD33DE2654CB8700EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = "<group>"; };
|
||||
CEBD33DF2654CB8700EB2EA8 /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = ../../Carthage/Build/XCGLogger.xcframework; sourceTree = "<group>"; };
|
||||
CEBEFB92231FB4DF00F8F67C /* banner-portrait-text.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "banner-portrait-text.png"; sourceTree = "<group>"; };
|
||||
CEBEFB93231FB4DF00F8F67C /* banner-landscape-text.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "banner-landscape-text.png"; sourceTree = "<group>"; };
|
||||
CED2AC8C260B07CF00A408D2 /* ekwtamil99uni.kmp */ = {isa = PBXFileReference; lastKnownFileType = file; path = ekwtamil99uni.kmp; sourceTree = "<group>"; };
|
||||
|
|
@ -124,11 +114,9 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CEBD33E22654CB8700EB2EA8 /* Reachability.xcframework in Frameworks */,
|
||||
CEBD33E42654CB8800EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */,
|
||||
290FB45E2AFB270D00249D58 /* ZIPFoundation.xcframework in Frameworks */,
|
||||
CEBD33E62654CB8800EB2EA8 /* Sentry.xcframework in Frameworks */,
|
||||
CEBD33D82654CB7800EB2EA8 /* DeviceKit.xcframework in Frameworks */,
|
||||
CEBD33E82654CB8800EB2EA8 /* XCGLogger.xcframework in Frameworks */,
|
||||
CEBD33D32654CB6A00EB2EA8 /* KeymanEngine.xcframework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -137,11 +125,9 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CEDB327A265C9B33000A2009 /* ObjcExceptionBridging.xcframework in Frameworks */,
|
||||
CEDB327B265C9B33000A2009 /* Reachability.xcframework in Frameworks */,
|
||||
290FB4602AFB271C00249D58 /* ZIPFoundation.xcframework in Frameworks */,
|
||||
CEDB327C265C9B33000A2009 /* Sentry.xcframework in Frameworks */,
|
||||
CEDB327D265C9B33000A2009 /* XCGLogger.xcframework in Frameworks */,
|
||||
CEDB3279265C9B15000A2009 /* DeviceKit.xcframework in Frameworks */,
|
||||
CEDB3276265C953A000A2009 /* KeymanEngine.xcframework in Frameworks */,
|
||||
);
|
||||
|
|
@ -196,10 +182,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
290FB45D2AFB270D00249D58 /* ZIPFoundation.xcframework */,
|
||||
CEBD33DD2654CB8700EB2EA8 /* ObjcExceptionBridging.xcframework */,
|
||||
CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */,
|
||||
CEBD33DE2654CB8700EB2EA8 /* Sentry.xcframework */,
|
||||
CEBD33DF2654CB8700EB2EA8 /* XCGLogger.xcframework */,
|
||||
CEBD33D72654CB7800EB2EA8 /* DeviceKit.xcframework */,
|
||||
CEBD33D22654CB6A00EB2EA8 /* KeymanEngine.xcframework */,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -15,10 +15,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
func application(_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? = nil) -> Bool {
|
||||
// Set desired level before release
|
||||
KeymanEngine.log.outputLevel = .debug
|
||||
KeymanEngine.log.logAppDetails()
|
||||
|
||||
// Replace with your application group id
|
||||
// Ensure this happens before installing any keyboards or models within the engine
|
||||
// whenever using App Group Identifiers.
|
||||
|
|
|
|||
|
|
@ -11,10 +11,6 @@ import UIKit
|
|||
|
||||
class KeyboardViewController: InputViewController {
|
||||
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
||||
// Set desired level before release
|
||||
KeymanEngine.log.outputLevel = .debug
|
||||
KeymanEngine.log.logAppDetails()
|
||||
|
||||
// Replace with your application group id
|
||||
Manager.applicationGroupIdentifier = "group.KMSample"
|
||||
|
||||
|
|
@ -56,7 +52,7 @@ class KeyboardViewController: InputViewController {
|
|||
func setupTopBarImage(isPortrait: Bool) {
|
||||
let imgPath = getTopBarImage(isPortrait: isPortrait)
|
||||
guard let path = imgPath else {
|
||||
log.error("No image specified for the image banner!")
|
||||
print("No image specified for the image banner!")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
github "weichsel/ZIPFoundation" ~> 0.9
|
||||
github "keymanapp/dependency-XCGLogger" "master"
|
||||
github "devicekit/DeviceKit" ~> 5.0
|
||||
github "ashleymills/Reachability.swift"
|
||||
github "getsentry/sentry-cocoa" ~> 8.7.0
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
github "ashleymills/Reachability.swift" "v5.1.0"
|
||||
github "devicekit/DeviceKit" "5.1.0"
|
||||
github "getsentry/sentry-cocoa" "8.15.2"
|
||||
github "keymanapp/dependency-XCGLogger" "57a7b975dbb6fe4fe90cef3d1bc52b8adbd89113"
|
||||
github "weichsel/ZIPFoundation" "0.9.17"
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
2990208E278FF402004F18CF /* KeyboardTableController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2990208D278FF402004F18CF /* KeyboardTableController.swift */; };
|
||||
2990209127901DD7004F18CF /* KeyboardDetailCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2990209027901DD7004F18CF /* KeyboardDetailCell.swift */; };
|
||||
2993F6E62791621D009E1352 /* KeyboardDetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2993F6E52791621D009E1352 /* KeyboardDetailController.swift */; };
|
||||
29B30C252B5A8A4800C342A4 /* FVLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B30C242B5A8A4800C342A4 /* FVLogger.swift */; };
|
||||
29B30C262B5A8A4800C342A4 /* FVLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B30C242B5A8A4800C342A4 /* FVLogger.swift */; };
|
||||
37C183B922932AD2009F9EFD /* FVKeyboardList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C183B822932AD2009F9EFD /* FVKeyboardList.swift */; };
|
||||
37C183BD2293321C009F9EFD /* Instructions in Resources */ = {isa = PBXBuildFile; fileRef = 37C183BC2293321C009F9EFD /* Instructions */; };
|
||||
37EA1F23228A1F57003E710C /* FVInputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EA1F22228A1F57003E710C /* FVInputViewController.swift */; };
|
||||
|
|
@ -52,20 +54,14 @@
|
|||
CEBD34032654D41200EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34012654D41200EB2EA8 /* KeymanEngine.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD341A2654D76600EB2EA8 /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34052654D4EB00EB2EA8 /* DeviceKit.xcframework */; };
|
||||
CEBD341B2654D76600EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34052654D4EB00EB2EA8 /* DeviceKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD341D2654D76800EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34082654D5AA00EB2EA8 /* ObjcExceptionBridging.xcframework */; };
|
||||
CEBD341E2654D76800EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34082654D5AA00EB2EA8 /* ObjcExceptionBridging.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD341F2654D76900EB2EA8 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */; };
|
||||
CEBD34202654D76900EB2EA8 /* Reachability.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD34222654D76B00EB2EA8 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */; };
|
||||
CEBD34232654D76B00EB2EA8 /* Sentry.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD34252654D76C00EB2EA8 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */; };
|
||||
CEBD34262654D76C00EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEBD34472654FF3300EB2EA8 /* KeymanEngine.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34012654D41200EB2EA8 /* KeymanEngine.xcframework */; };
|
||||
CEDB327F265C9C58000A2009 /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34052654D4EB00EB2EA8 /* DeviceKit.xcframework */; };
|
||||
CEDB3280265C9C58000A2009 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34082654D5AA00EB2EA8 /* ObjcExceptionBridging.xcframework */; };
|
||||
CEDB3281265C9C58000A2009 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */; };
|
||||
CEDB3282265C9C58000A2009 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */; };
|
||||
CEDB3283265C9C58000A2009 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
|
|
@ -76,11 +72,9 @@
|
|||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
CEBD34202654D76900EB2EA8 /* Reachability.xcframework in Embed Frameworks */,
|
||||
CEBD341E2654D76800EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */,
|
||||
290FB4642AFB358F00249D58 /* ZIPFoundation.xcframework in Embed Frameworks */,
|
||||
CEBD341B2654D76600EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */,
|
||||
CEBD34232654D76B00EB2EA8 /* Sentry.xcframework in Embed Frameworks */,
|
||||
CEBD34262654D76C00EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */,
|
||||
CEBD34032654D41200EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
|
|
@ -108,6 +102,7 @@
|
|||
2990208D278FF402004F18CF /* KeyboardTableController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardTableController.swift; sourceTree = "<group>"; };
|
||||
2990209027901DD7004F18CF /* KeyboardDetailCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardDetailCell.swift; sourceTree = "<group>"; };
|
||||
2993F6E52791621D009E1352 /* KeyboardDetailController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardDetailController.swift; sourceTree = "<group>"; };
|
||||
29B30C242B5A8A4800C342A4 /* FVLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FVLogger.swift; sourceTree = "<group>"; };
|
||||
37C183B822932AD2009F9EFD /* FVKeyboardList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FVKeyboardList.swift; sourceTree = "<group>"; };
|
||||
37C183BC2293321C009F9EFD /* Instructions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Instructions; path = FirstVoices/Instructions; sourceTree = "<group>"; };
|
||||
37EA1F22228A1F57003E710C /* FVInputViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FVInputViewController.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -145,9 +140,7 @@
|
|||
98C9A9FA1BFC1111009E9A4F /* SWKeyboard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = SWKeyboard.entitlements; sourceTree = "<group>"; };
|
||||
CEBD34012654D41200EB2EA8 /* KeymanEngine.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = KeymanEngine.xcframework; sourceTree = "<group>"; };
|
||||
CEBD34052654D4EB00EB2EA8 /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = Carthage/Build/DeviceKit.xcframework; sourceTree = "<group>"; };
|
||||
CEBD34082654D5AA00EB2EA8 /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = "<group>"; };
|
||||
CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = Carthage/Build/Sentry.xcframework; sourceTree = "<group>"; };
|
||||
CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = Carthage/Build/XCGLogger.xcframework; sourceTree = "<group>"; };
|
||||
CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = Carthage/Build/Reachability.xcframework; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
|
|
@ -156,13 +149,11 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CEBD341D2654D76800EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */,
|
||||
CEBD341F2654D76900EB2EA8 /* Reachability.xcframework in Frameworks */,
|
||||
290FB4632AFB358F00249D58 /* ZIPFoundation.xcframework in Frameworks */,
|
||||
CEBD34222654D76B00EB2EA8 /* Sentry.xcframework in Frameworks */,
|
||||
CEBD341A2654D76600EB2EA8 /* DeviceKit.xcframework in Frameworks */,
|
||||
CEBD34022654D41200EB2EA8 /* KeymanEngine.xcframework in Frameworks */,
|
||||
CEBD34252654D76C00EB2EA8 /* XCGLogger.xcframework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -171,11 +162,9 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CEDB327F265C9C58000A2009 /* DeviceKit.xcframework in Frameworks */,
|
||||
CEDB3280265C9C58000A2009 /* ObjcExceptionBridging.xcframework in Frameworks */,
|
||||
290FB4652AFB359A00249D58 /* ZIPFoundation.xcframework in Frameworks */,
|
||||
CEDB3281265C9C58000A2009 /* Reachability.xcframework in Frameworks */,
|
||||
CEDB3282265C9C58000A2009 /* Sentry.xcframework in Frameworks */,
|
||||
CEDB3283265C9C58000A2009 /* XCGLogger.xcframework in Frameworks */,
|
||||
CEBD34472654FF3300EB2EA8 /* KeymanEngine.xcframework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -240,6 +229,7 @@
|
|||
9889E8C71BFA9CEC00019560 /* LaunchScreen.storyboard */,
|
||||
9889E8C21BFA9CEC00019560 /* Main.storyboard */,
|
||||
9889E8B91BFA9CEC00019560 /* Supporting Files */,
|
||||
29B30C242B5A8A4800C342A4 /* FVLogger.swift */,
|
||||
);
|
||||
path = FirstVoices;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -292,10 +282,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
290FB4622AFB358E00249D58 /* ZIPFoundation.xcframework */,
|
||||
CEBD34082654D5AA00EB2EA8 /* ObjcExceptionBridging.xcframework */,
|
||||
CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */,
|
||||
CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */,
|
||||
CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */,
|
||||
CEBD34052654D4EB00EB2EA8 /* DeviceKit.xcframework */,
|
||||
CEBD34012654D41200EB2EA8 /* KeymanEngine.xcframework */,
|
||||
);
|
||||
|
|
@ -501,6 +489,7 @@
|
|||
2990209127901DD7004F18CF /* KeyboardDetailCell.swift in Sources */,
|
||||
37EA1F4622921BAD003E710C /* KeyboardInfo.swift in Sources */,
|
||||
296E22E527ED8FA4007BDA12 /* DownloadStatusToolbar.swift in Sources */,
|
||||
29B30C252B5A8A4800C342A4 /* FVLogger.swift in Sources */,
|
||||
294D40E9279FE62600DB37F6 /* KeyboardRepository.swift in Sources */,
|
||||
2990208E278FF402004F18CF /* KeyboardTableController.swift in Sources */,
|
||||
37EA1F31228B7661003E710C /* FVShared.swift in Sources */,
|
||||
|
|
@ -513,6 +502,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
29B30C262B5A8A4800C342A4 /* FVLogger.swift in Sources */,
|
||||
37EA1F32228BCDA7003E710C /* FVShared.swift in Sources */,
|
||||
37EA1F23228A1F57003E710C /* FVInputViewController.swift in Sources */,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -22,13 +22,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
FVRegionStorage.upgrade()
|
||||
|
||||
#if DEBUG
|
||||
KeymanEngine.log.outputLevel = .debug
|
||||
KeymanEngine.log.logAppDetails()
|
||||
#else
|
||||
KeymanEngine.log.outputLevel = .warning
|
||||
#endif
|
||||
|
||||
// Replace with your application group id
|
||||
Manager.applicationGroupIdentifier = FVConstants.groupID
|
||||
Manager.shared.spacebarText = .KEYBOARD
|
||||
|
|
|
|||
31
oem/firstvoices/ios/FirstVoices/FVLogger.swift
Normal file
31
oem/firstvoices/ios/FirstVoices/FVLogger.swift
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Keyman is copyright (C) SIL International. MIT License.
|
||||
*
|
||||
* FVLogger.swift
|
||||
* FirstVoices
|
||||
*
|
||||
* Created by Shawn Schantz on 2024-01-19.
|
||||
*
|
||||
* Basic logging capability provided by wrapping Apple's Unified Logging
|
||||
* Framework. Works with iOS 10 and later.
|
||||
* Log statements are written with a call the `os_log` API which takes a
|
||||
* message, an `OSLog` reference and log level.
|
||||
* Several static references to `OSLog` objects are defined, each of which
|
||||
* determines the subsystem and category of subsequent log statements.
|
||||
* Both subsystem and category are useful for filtering with the Console app.
|
||||
* Use the same subsystem name for all `OSLog` objects created here, but specify
|
||||
* a unique category to identify related functionality.
|
||||
* Add as many static `OSLog` resources as necessary to support new log
|
||||
* categories.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
private let subsystem = "com.firstvoices.keyboards"
|
||||
|
||||
public struct FVLogger {
|
||||
static let ui = OSLog(subsystem: subsystem, category: "UI")
|
||||
static let resources = OSLog(subsystem: subsystem, category: "Resources")
|
||||
static let settings = OSLog(subsystem: subsystem, category: "Settings")
|
||||
}
|
||||
|
|
@ -11,16 +11,11 @@
|
|||
|
||||
import KeymanEngine
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
@objc(FVInputViewController)
|
||||
class FVInputViewController: InputViewController {
|
||||
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
||||
#if DEBUG
|
||||
KeymanEngine.log.outputLevel = .debug
|
||||
KeymanEngine.log.logAppDetails()
|
||||
#else
|
||||
KeymanEngine.log.outputLevel = .warning
|
||||
#endif
|
||||
|
||||
Manager.applicationGroupIdentifier = FVConstants.groupID
|
||||
|
||||
|
|
@ -77,7 +72,7 @@ class FVInputViewController: InputViewController {
|
|||
func setupTopBarImage(isPortrait: Bool) {
|
||||
let imgPath = getTopBarImage(isPortrait: isPortrait)
|
||||
guard let path = imgPath else {
|
||||
log.error("No image specified for the image banner!")
|
||||
os_log("No image specified for the image banner!", log: FVLogger.ui, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue