mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-23 00:37:41 +00:00
refactor(ios/engine): target abstraction - ResourceFileManager.install
This commit is contained in:
parent
df2a31f075
commit
6535fac385
3 changed files with 113 additions and 96 deletions
|
|
@ -617,60 +617,11 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
throw KMPError.wrongPackageType
|
||||
}
|
||||
|
||||
for r in kmp.resources {
|
||||
let installableFiles: [(LanguageResource, [(String, URL)])] = r.installableResources.map { resource in
|
||||
// (source file, destination file)
|
||||
var set: [(String, URL)] = [(resource.sourceFilename, Storage.active.resourceURL(for: resource)!)]
|
||||
let installableFonts: [(String, URL)] = resource.fonts.map { font in
|
||||
// KMPs only list a single font file for each entry whenever one is included.
|
||||
// A pre-existing assumption.
|
||||
let fontFile = font.source[0]
|
||||
return (fontFile, Storage.active.fontURL(forResource: resource, filename: fontFile)!)
|
||||
}
|
||||
|
||||
set.append(contentsOf: installableFonts)
|
||||
|
||||
// (resource, source-destination file mapping)
|
||||
return (resource, set)
|
||||
}
|
||||
|
||||
var haveInstalledOne = false
|
||||
for set in installableFiles {
|
||||
let resource = set.0
|
||||
let files = set.1
|
||||
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: Storage.active.resourceDir(for: resource)!,
|
||||
withIntermediateDirectories: true)
|
||||
} catch {
|
||||
log.error("Could not create dir for download: \(error)")
|
||||
throw KMPError.fileSystem
|
||||
}
|
||||
|
||||
do {
|
||||
for item in files {
|
||||
var filePath = folder
|
||||
filePath.appendPathComponent(item.0)
|
||||
|
||||
// TODO: The rest of this block may be replaced with ResourceFileManager.copyWithOverwrite
|
||||
// once this method is fully abstracted and its core is placed within said class.
|
||||
if(FileManager.default.fileExists(atPath: (item.1).path)) {
|
||||
try FileManager.default.removeItem(at: item.1)
|
||||
}
|
||||
try FileManager.default.copyItem(at: filePath, to: item.1)
|
||||
|
||||
}
|
||||
} catch {
|
||||
log.error("Error saving the download: \(error)")
|
||||
throw KMPError.copyFiles
|
||||
}
|
||||
|
||||
// Only one keyboard is installed by default from a KMP.
|
||||
// Also, only the first language-pairing it contains
|
||||
if !haveInstalledOne {
|
||||
Manager.shared.addKeyboard(resource as! InstallableKeyboard)
|
||||
haveInstalledOne = true
|
||||
}
|
||||
for resourceSet in kmp.installableResourceSets {
|
||||
for resource in resourceSet {
|
||||
try ResourceFileManager.shared.install(resource, from: kmp)
|
||||
// Install the keyboard for only the first language pairing defined in the package.
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -681,48 +632,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
throw KMPError.wrongPackageType
|
||||
}
|
||||
|
||||
for r in kmp.resources {
|
||||
let installableFiles: [(LanguageResource, [(String, URL)])] = r.installableResources.map { resource in
|
||||
// (source file, destination file)
|
||||
let set: [(String, URL)] = [(resource.sourceFilename, Storage.active.resourceURL(for: resource)!)]
|
||||
// no fonts for lexical models
|
||||
|
||||
// (resource, source-destination file mapping)
|
||||
return (resource, set)
|
||||
}
|
||||
|
||||
for set in installableFiles {
|
||||
let resource = set.0
|
||||
let files = set.1
|
||||
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: Storage.active.resourceDir(for: resource)!,
|
||||
withIntermediateDirectories: true)
|
||||
} catch {
|
||||
log.error("Could not create dir for download: \(error)")
|
||||
throw KMPError.fileSystem
|
||||
}
|
||||
|
||||
do {
|
||||
for item in files {
|
||||
var filePath = folder
|
||||
filePath.appendPathComponent(item.0)
|
||||
|
||||
// TODO: The rest of this block may be replaced with ResourceFileManager.copyWithOverwrite
|
||||
// once this method is fully abstracted and its core is placed within said class.
|
||||
if(FileManager.default.fileExists(atPath: (item.1).path)) {
|
||||
try FileManager.default.removeItem(at: item.1)
|
||||
}
|
||||
try FileManager.default.copyItem(at: filePath, to: item.1)
|
||||
}
|
||||
} catch {
|
||||
log.error("Error saving the lexical model download: \(error)")
|
||||
throw KMPError.copyFiles
|
||||
}
|
||||
|
||||
// All pairings are installed for lexical models.
|
||||
// Also, all models within the KMP
|
||||
Manager.addLexicalModel(resource as! InstallableLexicalModel)
|
||||
try kmp.installableResourceSets.forEach { resourceSet in
|
||||
try resourceSet.forEach { resource in
|
||||
try ResourceFileManager.shared.install(resource, from: kmp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,4 +178,51 @@ public class ResourceFileManager {
|
|||
completionHandler(error)
|
||||
}
|
||||
}
|
||||
|
||||
public func install(_ resource: LanguageResource, from package: KeymanPackage) throws {
|
||||
// Ideally, we wouldn't need the guard... but it's best to check just in case.
|
||||
guard package.contains(resource) else {
|
||||
throw KMPError.resourceNotInPackage
|
||||
}
|
||||
|
||||
// (source, destination)
|
||||
var installableFiles: [(String, URL)] = [(resource.sourceFilename, Storage.active.resourceURL(for: resource)!)]
|
||||
let installableFonts: [(String, URL)] = resource.fonts.map { font in
|
||||
// KMPs only list a single font file for each entry whenever one is included.
|
||||
// A pre-existing assumption.
|
||||
let fontFile = font.source[0]
|
||||
return (fontFile, Storage.active.fontURL(forResource: resource, filename: fontFile)!)
|
||||
}
|
||||
|
||||
installableFiles.append(contentsOf: installableFonts)
|
||||
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: Storage.active.resourceDir(for: resource)!,
|
||||
withIntermediateDirectories: true)
|
||||
} catch {
|
||||
log.error("Could not create installation directory: \(error)")
|
||||
throw KMPError.fileSystem
|
||||
}
|
||||
|
||||
do {
|
||||
for item in installableFiles {
|
||||
var filePath = package.sourceFolder
|
||||
filePath.appendPathComponent(item.0)
|
||||
try copyWithOverwrite(from: filePath, to: item.1)
|
||||
}
|
||||
} catch {
|
||||
log.error("Error installing the resource: \(error)")
|
||||
throw KMPError.copyFiles
|
||||
}
|
||||
|
||||
// There's no generalized method for this quite yet. Manager doesn't need even
|
||||
// more of these.
|
||||
if let keyboard = resource as? InstallableKeyboard {
|
||||
Manager.shared.addKeyboard(keyboard)
|
||||
} else if let lexicalModel = resource as? InstallableLexicalModel {
|
||||
Manager.addLexicalModel(lexicalModel)
|
||||
} else {
|
||||
fatalError("Cannot install instance of unexpected LanguageResource subclass")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,4 +69,62 @@ class FileManagementTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testInstallKeyboardFromPackage() throws {
|
||||
ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.Keyboards.khmerAngkorKMP) { kmp, error in
|
||||
XCTAssertNotNil(kmp, "Failed to prepare KMP for installation")
|
||||
XCTAssertNil(error, "Error occurred while preparing KMP for installation")
|
||||
XCTAssertNotNil(kmp as? KeyboardKeymanPackage, "KMP resource type improperly recognized - expected a keyboard package!")
|
||||
|
||||
do {
|
||||
try ResourceFileManager.shared.install(TestUtils.Keyboards.khmer_angkor, from: kmp!)
|
||||
} catch {
|
||||
XCTFail("Unexpected error during KeyboardPackage install")
|
||||
}
|
||||
|
||||
let installURL = Storage.active.keyboardURL(forID: "khmer_angkor", version: "1.0.6")
|
||||
|
||||
XCTAssertTrue(FileManager.default.fileExists(atPath: installURL.path),
|
||||
"Could not find installed keyboard file")
|
||||
|
||||
let keyboards = Storage.active.userDefaults.userKeyboards!
|
||||
|
||||
XCTAssertEqual(keyboards.count, 1, "Unexpected number of keyboards were installed")
|
||||
XCTAssertEqual(keyboards[0].id, "khmer_angkor", "Installed keyboard ID mismatch")
|
||||
|
||||
// While the KMP's version of the specified InstallableKeyboard does specify Fonts,
|
||||
// the literal-based testing version does NOT. Since we're installing from the predefined,
|
||||
// test-copy instance, we expect NOT to see the font from this test's install!
|
||||
//
|
||||
// Yes, the KeymanPackage.contains check isn't exhaustive - it just does a pair of
|
||||
// id checks. This version of events couldn't happen if we thorough enough there.
|
||||
let fontURL = Storage.active.fontURL(forKeyboardID: "khmer_angkor", filename: "Mondulkiri-R.ttf")
|
||||
XCTAssertFalse(FileManager.default.fileExists(atPath: fontURL.path))
|
||||
}
|
||||
}
|
||||
|
||||
func testInstallLexicalModelFromPackage() {
|
||||
ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.LexicalModels.mtntKMP) { kmp, error in
|
||||
XCTAssertNotNil(kmp, "Failed to prepare KMP for installation")
|
||||
XCTAssertNil(error, "Error occurred while preparing KMP for installation")
|
||||
XCTAssertNotNil(kmp as? LexicalModelKeymanPackage, "KMP resource type improperly recognized - expected a lexical model package!")
|
||||
|
||||
do {
|
||||
try ResourceFileManager.shared.install(TestUtils.LexicalModels.mtnt, from: kmp!)
|
||||
} catch {
|
||||
XCTFail("Unexpected error during LexicalModelPackage install")
|
||||
}
|
||||
|
||||
let installURL = Storage.active.lexicalModelURL(forID: "nrc.en.mtnt", version: "0.1.4")
|
||||
|
||||
XCTAssertTrue(FileManager.default.fileExists(atPath: installURL.path),
|
||||
"Could not find installed lexical model file")
|
||||
|
||||
let models = Storage.active.userDefaults.userLexicalModels!
|
||||
|
||||
// This variant is selective - only a single pairing should be installed for the model.
|
||||
XCTAssertEqual(models.count, 1, "Unexpected number of models were installed")
|
||||
XCTAssertEqual(models[0].id, "nrc.en.mtnt", "Installed lexical model ID mismatch")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue