mirror of
https://github.com/Finb/Bark.git
synced 2025-12-08 21:36:01 +00:00
添加导入导出功能
This commit is contained in:
parent
c70251a5b4
commit
dc3d9b7915
@ -108,6 +108,7 @@
|
|||||||
06BBB8C12567B3EF0076F63E /* BaseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BBB8C02567B3EF0076F63E /* BaseTableViewCell.swift */; };
|
06BBB8C12567B3EF0076F63E /* BaseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BBB8C02567B3EF0076F63E /* BaseTableViewCell.swift */; };
|
||||||
06BBB8C92567B6730076F63E /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BBB8C82567B6730076F63E /* Operators.swift */; };
|
06BBB8C92567B6730076F63E /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BBB8C82567B6730076F63E /* Operators.swift */; };
|
||||||
06BBB8CE2567B8E60076F63E /* silence.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06BBB8CD2567B8E60076F63E /* silence.caf */; };
|
06BBB8CE2567B8E60076F63E /* silence.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06BBB8CD2567B8E60076F63E /* silence.caf */; };
|
||||||
|
06BD4DAA2901352E003364DB /* Object+Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BD4DA92901352E003364DB /* Object+Dictionary.swift */; };
|
||||||
06C2CF232685B88D0034B127 /* TextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2CF222685B88D0034B127 /* TextCell.swift */; };
|
06C2CF232685B88D0034B127 /* TextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2CF222685B88D0034B127 /* TextCell.swift */; };
|
||||||
06C2CF252685BDB80034B127 /* SpacerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2CF242685BDB80034B127 /* SpacerCell.swift */; };
|
06C2CF252685BDB80034B127 /* SpacerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2CF242685BDB80034B127 /* SpacerCell.swift */; };
|
||||||
06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5952C2480E3F8006B98F3 /* LabelCell.swift */; };
|
06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5952C2480E3F8006B98F3 /* LabelCell.swift */; };
|
||||||
@ -274,6 +275,7 @@
|
|||||||
06BBB8C02567B3EF0076F63E /* BaseTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTableViewCell.swift; sourceTree = "<group>"; };
|
06BBB8C02567B3EF0076F63E /* BaseTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
06BBB8C82567B6730076F63E /* Operators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = "<group>"; };
|
06BBB8C82567B6730076F63E /* Operators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = "<group>"; };
|
||||||
06BBB8CD2567B8E60076F63E /* silence.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = silence.caf; sourceTree = "<group>"; };
|
06BBB8CD2567B8E60076F63E /* silence.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = silence.caf; sourceTree = "<group>"; };
|
||||||
|
06BD4DA92901352E003364DB /* Object+Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Object+Dictionary.swift"; sourceTree = "<group>"; };
|
||||||
06C2CF222685B88D0034B127 /* TextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextCell.swift; sourceTree = "<group>"; };
|
06C2CF222685B88D0034B127 /* TextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextCell.swift; sourceTree = "<group>"; };
|
||||||
06C2CF242685BDB80034B127 /* SpacerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpacerCell.swift; sourceTree = "<group>"; };
|
06C2CF242685BDB80034B127 /* SpacerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpacerCell.swift; sourceTree = "<group>"; };
|
||||||
06C5952C2480E3F8006B98F3 /* LabelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelCell.swift; sourceTree = "<group>"; };
|
06C5952C2480E3F8006B98F3 /* LabelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelCell.swift; sourceTree = "<group>"; };
|
||||||
@ -404,6 +406,7 @@
|
|||||||
children = (
|
children = (
|
||||||
06B1158E247BB1FB006D91FB /* Message.swift */,
|
06B1158E247BB1FB006D91FB /* Message.swift */,
|
||||||
064CABA5256BE9510018155C /* PreviewModel.swift */,
|
064CABA5256BE9510018155C /* PreviewModel.swift */,
|
||||||
|
06BD4DA92901352E003364DB /* Object+Dictionary.swift */,
|
||||||
);
|
);
|
||||||
path = Model;
|
path = Model;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -952,6 +955,7 @@
|
|||||||
060481EE250F404500BC9799 /* SoundsViewController.swift in Sources */,
|
060481EE250F404500BC9799 /* SoundsViewController.swift in Sources */,
|
||||||
0603706D20E23EC000F4CA05 /* BarkSFSafariViewController.swift in Sources */,
|
0603706D20E23EC000F4CA05 /* BarkSFSafariViewController.swift in Sources */,
|
||||||
06AE311A266F4E6600B39FBB /* GroupFilterViewModel.swift in Sources */,
|
06AE311A266F4E6600B39FBB /* GroupFilterViewModel.swift in Sources */,
|
||||||
|
06BD4DAA2901352E003364DB /* Object+Dictionary.swift in Sources */,
|
||||||
06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */,
|
06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */,
|
||||||
068EC15A27ED99E700D5D11E /* ServerListViewModel.swift in Sources */,
|
068EC15A27ED99E700D5D11E /* ServerListViewModel.swift in Sources */,
|
||||||
06172FDC27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift in Sources */,
|
06172FDC27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift in Sources */,
|
||||||
|
|||||||
@ -119,3 +119,7 @@ deleteFailed = "You must have at least one server configured.";
|
|||||||
deletedSuccessfully = "Server has been deleted successfully.";
|
deletedSuccessfully = "Server has been deleted successfully.";
|
||||||
resetFailed = "Reset Failed";
|
resetFailed = "Reset Failed";
|
||||||
resetFailed2 = "Bark could not get a DeviceToken from the server.";
|
resetFailed2 = "Bark could not get a DeviceToken from the server.";
|
||||||
|
export = "Export";
|
||||||
|
import = "Import";
|
||||||
|
exportOrImport = "Exporting or importing messages";
|
||||||
|
items = "messages";
|
||||||
|
|||||||
@ -122,3 +122,7 @@ deleteFailed = "不能删除,服务器至少需保留一个";
|
|||||||
deletedSuccessfully = "删除成功";
|
deletedSuccessfully = "删除成功";
|
||||||
resetFailed = "重置失败";
|
resetFailed = "重置失败";
|
||||||
resetFailed2 = "重置失败,没有获取到 DeviceToken";
|
resetFailed2 = "重置失败,没有获取到 DeviceToken";
|
||||||
|
export = "导出";
|
||||||
|
import = "导入";
|
||||||
|
exportOrImport = "导出或导入消息列表";
|
||||||
|
items = "条消息";
|
||||||
|
|||||||
@ -7,9 +7,13 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Material
|
import Material
|
||||||
|
import RxCocoa
|
||||||
import RxDataSources
|
import RxDataSources
|
||||||
|
import RxSwift
|
||||||
import UIKit
|
import UIKit
|
||||||
class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel> {
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
|
class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel>, UIDocumentPickerDelegate {
|
||||||
let tableView: UITableView = {
|
let tableView: UITableView = {
|
||||||
let tableView = UITableView()
|
let tableView = UITableView()
|
||||||
tableView.separatorStyle = .none
|
tableView.separatorStyle = .none
|
||||||
@ -32,12 +36,92 @@ class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel
|
|||||||
make.edges.equalToSuperview()
|
make.edges.equalToSuperview()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 导入、导出操作枚举
|
||||||
|
enum BackupOrRestoreActionEnum {
|
||||||
|
case export, `import`(data: Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UIDocumentPickerDelegate delegate
|
||||||
|
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||||
|
guard let url = urls.first else { return }
|
||||||
|
let canAccessingResource = url.startAccessingSecurityScopedResource()
|
||||||
|
guard canAccessingResource else { return }
|
||||||
|
|
||||||
|
let fileCoordinator = NSFileCoordinator()
|
||||||
|
let err = NSErrorPointer(nilLiteral: ())
|
||||||
|
fileCoordinator.coordinate(readingItemAt: url, error: err) { url in
|
||||||
|
if let data = try? Data(contentsOf: url) {
|
||||||
|
self.backupOrRestoreActionRelay.accept(.import(data: data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
url.stopAccessingSecurityScopedResource()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 导入、导出事件
|
||||||
|
let backupOrRestoreActionRelay = PublishRelay<BackupOrRestoreActionEnum>()
|
||||||
|
|
||||||
|
/// 生成导入导出事件
|
||||||
|
func getBackupOrRestoreAction() -> (Driver<Void>, Driver<Data>) {
|
||||||
|
let backupOrRestoreAction = self.tableView.rx
|
||||||
|
.modelSelected(MessageSettingItem.self)
|
||||||
|
.filter { item in
|
||||||
|
if case MessageSettingItem.backup = item {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
.flatMapLatest { [weak self] _ in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return Observable<BackupOrRestoreActionEnum>.empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||||
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("export"), style: .default, handler: { _ in
|
||||||
|
strongSelf.backupOrRestoreActionRelay.accept(.export)
|
||||||
|
}))
|
||||||
|
|
||||||
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("import"), style: .default, handler: { [weak self] _ in
|
||||||
|
if #available(iOS 14.0, *) {
|
||||||
|
let supportedType: [UTType] = [UTType.json]
|
||||||
|
let pickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: supportedType, asCopy: false)
|
||||||
|
pickerViewController.delegate = self
|
||||||
|
self?.present(pickerViewController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil))
|
||||||
|
strongSelf.navigationController?.present(alertController, animated: true, completion: nil)
|
||||||
|
|
||||||
|
return strongSelf.backupOrRestoreActionRelay.asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
let backupAction = backupOrRestoreAction
|
||||||
|
.filter { action in
|
||||||
|
if case .export = action { return true } else { return false }
|
||||||
|
}
|
||||||
|
.map { _ in () }
|
||||||
|
.asDriver(onErrorDriveWith: .empty())
|
||||||
|
|
||||||
|
let restoreAction = backupOrRestoreAction
|
||||||
|
.compactMap { action in
|
||||||
|
if case let .import(data) = action { return data } else { return nil }
|
||||||
|
}
|
||||||
|
.asDriver(onErrorDriveWith: .empty())
|
||||||
|
|
||||||
|
return (backupAction, restoreAction)
|
||||||
|
}
|
||||||
|
|
||||||
override func bindViewModel() {
|
override func bindViewModel() {
|
||||||
|
let actions = getBackupOrRestoreAction()
|
||||||
let output = viewModel.transform(
|
let output = viewModel.transform(
|
||||||
input: MessageSettingsViewModel.Input(
|
input: MessageSettingsViewModel.Input(
|
||||||
itemSelected: self.tableView.rx.modelSelected(MessageSettingItem.self).asDriver(),
|
itemSelected: self.tableView.rx.modelSelected(MessageSettingItem.self).asDriver(),
|
||||||
deviceToken: Client.shared.deviceToken.asDriver()
|
deviceToken: Client.shared.deviceToken.asDriver(),
|
||||||
|
backupAction: actions.0,
|
||||||
|
restoreAction: actions.1,
|
||||||
|
viewDidAppear: self.rx.methodInvoked(#selector(viewDidAppear(_:)))
|
||||||
|
.map { _ in () }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -52,6 +136,12 @@ class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel
|
|||||||
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(iCloudStatusCell.self)") {
|
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(iCloudStatusCell.self)") {
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
case let .backup(viewModel):
|
||||||
|
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(MutableTextCell.self)") as? MutableTextCell {
|
||||||
|
cell.textLabel?.textColor = BKColor.blue.darken1
|
||||||
|
cell.bindViewModel(model: viewModel)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
case let .archiveSetting(viewModel):
|
case let .archiveSetting(viewModel):
|
||||||
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(ArchiveSettingCell.self)") as? ArchiveSettingCell {
|
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(ArchiveSettingCell.self)") as? ArchiveSettingCell {
|
||||||
cell.bindViewModel(model: viewModel)
|
cell.bindViewModel(model: viewModel)
|
||||||
@ -80,17 +170,47 @@ class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel
|
|||||||
return UITableViewCell()
|
return UITableViewCell()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置项数据源
|
||||||
output.settings
|
output.settings
|
||||||
.drive(tableView.rx.items(dataSource: dataSource))
|
.drive(tableView.rx.items(dataSource: dataSource))
|
||||||
.disposed(by: rx.disposeBag)
|
.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
// 打开 URL 操作
|
||||||
output.openUrl.drive { [weak self] url in
|
output.openUrl.drive { [weak self] url in
|
||||||
self?.navigationController?.present(BarkSFSafariViewController(url: url), animated: true, completion: nil)
|
self?.navigationController?.present(BarkSFSafariViewController(url: url), animated: true, completion: nil)
|
||||||
}.disposed(by: rx.disposeBag)
|
}.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
// 复制 deviceToken 操作
|
||||||
output.copyDeviceToken.drive { [weak self] deviceToken in
|
output.copyDeviceToken.drive { [weak self] deviceToken in
|
||||||
UIPasteboard.general.string = deviceToken
|
UIPasteboard.general.string = deviceToken
|
||||||
self?.showSnackbar(text: NSLocalizedString("Copy"))
|
self?.showSnackbar(text: NSLocalizedString("Copy"))
|
||||||
}.disposed(by: rx.disposeBag)
|
}.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
// 导出数据
|
||||||
|
output.exportData.drive { [weak self] data in
|
||||||
|
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let tempDirectoryURL = fileManager.temporaryDirectory
|
||||||
|
let fileName = "bark_messages_\(Date().formatString(format: "yyyy_MM_dd_HH_mm_ss")).json"
|
||||||
|
let linkURL = tempDirectoryURL.appendingPathComponent(fileName)
|
||||||
|
|
||||||
|
do {
|
||||||
|
// 清空temp文件夹
|
||||||
|
try fileManager
|
||||||
|
.contentsOfDirectory(at: tempDirectoryURL, includingPropertiesForKeys: nil, options: .skipsSubdirectoryDescendants)
|
||||||
|
.forEach { file in
|
||||||
|
try? fileManager.removeItem(atPath: file.path)
|
||||||
|
}
|
||||||
|
// 写入临时文件
|
||||||
|
try data.write(to: linkURL)
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
// Hope nothing happens
|
||||||
|
}
|
||||||
|
|
||||||
|
let activityController = UIActivityViewController(activityItems: [linkURL], applicationActivities: nil)
|
||||||
|
self?.navigationController?.present(activityController, animated: true, completion: nil)
|
||||||
|
|
||||||
|
}.disposed(by: rx.disposeBag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,28 +8,88 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Material
|
import Material
|
||||||
|
import RealmSwift
|
||||||
import RxCocoa
|
import RxCocoa
|
||||||
import RxDataSources
|
import RxDataSources
|
||||||
import RxSwift
|
import RxSwift
|
||||||
|
import SwiftyJSON
|
||||||
|
|
||||||
class MessageSettingsViewModel: ViewModel, ViewModelType {
|
class MessageSettingsViewModel: ViewModel, ViewModelType {
|
||||||
struct Input {
|
struct Input {
|
||||||
var itemSelected: Driver<MessageSettingItem>
|
var itemSelected: Driver<MessageSettingItem>
|
||||||
var deviceToken: Driver<String?>
|
var deviceToken: Driver<String?>
|
||||||
|
var backupAction: Driver<Void>
|
||||||
|
var restoreAction: Driver<Data>
|
||||||
|
var viewDidAppear: Observable<Void>
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Output {
|
struct Output {
|
||||||
var settings: Driver<[SectionModel<String, MessageSettingItem>]>
|
var settings: Driver<[SectionModel<String, MessageSettingItem>]>
|
||||||
var openUrl: Driver<URL>
|
var openUrl: Driver<URL>
|
||||||
var copyDeviceToken: Driver<String>
|
var copyDeviceToken: Driver<String>
|
||||||
|
var exportData: Driver<Data>
|
||||||
}
|
}
|
||||||
|
|
||||||
func transform(input: Input) -> Output {
|
func transform(input: Input) -> Output {
|
||||||
|
|
||||||
|
let restoreSuccess = input
|
||||||
|
.restoreAction
|
||||||
|
.compactMap { data -> Void? in
|
||||||
|
guard let json = try? JSON(data: data), let arr = json.array else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let realm = try? Realm() else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
try? realm.write {
|
||||||
|
for message in arr {
|
||||||
|
guard let id = message["id"].string else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
guard let createDate = message["createDate"].int64 else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = message["title"].string
|
||||||
|
let body = message["body"].string
|
||||||
|
let url = message["url"].string
|
||||||
|
let group = message["group"].string
|
||||||
|
|
||||||
|
let messageObject = Message()
|
||||||
|
messageObject.id = id
|
||||||
|
messageObject.title = title
|
||||||
|
messageObject.body = body
|
||||||
|
messageObject.url = url
|
||||||
|
messageObject.group = group
|
||||||
|
messageObject.createDate = Date(timeIntervalSince1970: TimeInterval(createDate))
|
||||||
|
realm.add(messageObject, update: .modified)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ()
|
||||||
|
}.asObservable().share()
|
||||||
|
|
||||||
let settings: [MessageSettingItem] = {
|
let settings: [MessageSettingItem] = {
|
||||||
var settings = [MessageSettingItem]()
|
var settings = [MessageSettingItem]()
|
||||||
settings.append(.label(text: "iCloud"))
|
settings.append(.label(text: "iCloud"))
|
||||||
settings.append(.iCloudStatus)
|
settings.append(.iCloudStatus)
|
||||||
settings.append(.label(text: NSLocalizedString("iCloudSync")))
|
settings.append(.label(text: NSLocalizedString("iCloudSync")))
|
||||||
|
settings.append(.backup(viewModel: MutableTextCellViewModel(
|
||||||
|
title: "\(NSLocalizedString("export"))/\(NSLocalizedString("import"))",
|
||||||
|
text: Observable.merge([restoreSuccess, input.viewDidAppear])
|
||||||
|
.map { _ in
|
||||||
|
if let realm = try? Realm() {
|
||||||
|
return realm.objects(Message.self)
|
||||||
|
.filter("isDeleted != true")
|
||||||
|
.count
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
.map { count in
|
||||||
|
"\(count) \(NSLocalizedString("items"))"
|
||||||
|
}
|
||||||
|
.asDriver(onErrorDriveWith: .empty()))
|
||||||
|
))
|
||||||
|
settings.append(.label(text: NSLocalizedString("exportOrImport")))
|
||||||
settings.append(.archiveSetting(viewModel: ArchiveSettingCellViewModel(on: ArchiveSettingManager.shared.isArchive)))
|
settings.append(.archiveSetting(viewModel: ArchiveSettingCellViewModel(on: ArchiveSettingManager.shared.isArchive)))
|
||||||
settings.append(.label(text: NSLocalizedString("archiveNote")))
|
settings.append(.label(text: NSLocalizedString("archiveNote")))
|
||||||
|
|
||||||
@ -112,11 +172,30 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 导出数据
|
||||||
|
let exportSuccess = input.backupAction
|
||||||
|
.asObservable()
|
||||||
|
.subscribe(on: ConcurrentDispatchQueueScheduler(qos: .userInitiated))
|
||||||
|
.compactMap { _ in
|
||||||
|
if let realm = try? Realm() {
|
||||||
|
let messages = realm.objects(Message.self)
|
||||||
|
.filter("isDeleted != true")
|
||||||
|
|
||||||
|
var arr = [[String: AnyObject]]()
|
||||||
|
for message in messages {
|
||||||
|
arr.append(message.toDictionary())
|
||||||
|
}
|
||||||
|
return try? JSON(arr).rawData(options: JSONSerialization.WritingOptions.prettyPrinted)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return Output(
|
return Output(
|
||||||
settings: Driver<[SectionModel<String, MessageSettingItem>]>
|
settings: Driver<[SectionModel<String, MessageSettingItem>]>
|
||||||
.just([SectionModel(model: "model", items: settings)]),
|
.just([SectionModel(model: "model", items: settings)]),
|
||||||
openUrl: openUrl,
|
openUrl: openUrl,
|
||||||
copyDeviceToken: copyDeviceToken)
|
copyDeviceToken: copyDeviceToken,
|
||||||
|
exportData: exportSuccess.asDriver(onErrorDriveWith: .empty()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +208,8 @@ enum MessageSettingItem {
|
|||||||
case archiveSetting(viewModel: ArchiveSettingCellViewModel)
|
case archiveSetting(viewModel: ArchiveSettingCellViewModel)
|
||||||
// 带 详细按钮的 文本cell
|
// 带 详细按钮的 文本cell
|
||||||
case detail(title: String?, text: String?, textColor: UIColor?, url: URL?)
|
case detail(title: String?, text: String?, textColor: UIColor?, url: URL?)
|
||||||
|
// 备份还原按钮
|
||||||
|
case backup(viewModel: MutableTextCellViewModel)
|
||||||
// deviceToken
|
// deviceToken
|
||||||
case deviceToken(viewModel: MutableTextCellViewModel)
|
case deviceToken(viewModel: MutableTextCellViewModel)
|
||||||
// 分隔线
|
// 分隔线
|
||||||
|
|||||||
40
Model/Object+Dictionary.swift
Normal file
40
Model/Object+Dictionary.swift
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// Object+Dictionary.swift
|
||||||
|
// Bark
|
||||||
|
//
|
||||||
|
// Created by huangfeng on 2022/10/20.
|
||||||
|
// Copyright © 2022 Fin. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import RealmSwift
|
||||||
|
|
||||||
|
extension Object {
|
||||||
|
func toDictionary() -> [String: AnyObject] {
|
||||||
|
let properties = self.objectSchema.properties.map { $0.name }
|
||||||
|
var dicProps = [String: AnyObject]()
|
||||||
|
for (key, value) in self.dictionaryWithValues(forKeys: properties) {
|
||||||
|
if let value = value as? ListBase {
|
||||||
|
dicProps[key] = value.toArray1() as AnyObject
|
||||||
|
} else if let value = value as? Object {
|
||||||
|
dicProps[key] = value.toDictionary() as AnyObject
|
||||||
|
} else if let value = value as? Date {
|
||||||
|
dicProps[key] = Int64(value.timeIntervalSince1970) as AnyObject
|
||||||
|
} else {
|
||||||
|
dicProps[key] = value as AnyObject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dicProps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ListBase {
|
||||||
|
func toArray1() -> [AnyObject] {
|
||||||
|
var _toArray = [AnyObject]()
|
||||||
|
for i in 0 ..< self._rlmArray.count {
|
||||||
|
let obj = unsafeBitCast(self._rlmArray[i], to: Object.self)
|
||||||
|
_toArray.append(obj.toDictionary() as AnyObject)
|
||||||
|
}
|
||||||
|
return _toArray
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,6 +16,7 @@ class iCloudStatusCell: UITableViewCell {
|
|||||||
self.backgroundColor = BKColor.background.secondary
|
self.backgroundColor = BKColor.background.secondary
|
||||||
self.textLabel?.text = NSLocalizedString("iCloudSatatus")
|
self.textLabel?.text = NSLocalizedString("iCloudSatatus")
|
||||||
self.detailTextLabel?.text = ""
|
self.detailTextLabel?.text = ""
|
||||||
|
self.detailTextLabel?.textColor = BKColor.grey.darken2
|
||||||
CKContainer.default().accountStatus { status, _ in
|
CKContainer.default().accountStatus { status, _ in
|
||||||
dispatch_sync_safely_main_queue {
|
dispatch_sync_safely_main_queue {
|
||||||
switch status {
|
switch status {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user