版本记录
版本号 | 时间 |
---|---|
V1.0 | 2020.05.28 星期四 |
前言
这一篇主要看下使用
Universal Type Identifiers
进行App中的数据的导入和导出。感兴趣的可以看下面几篇文章。
1. 基于Universal Type Identifiers的App数据导入和导出(一)
源码
1. Swift
首先看下工程组织结构

下面就是源码了
1. Binding.swift
import SwiftUI
public extension Binding where Value: CaseIterable & Equatable {
var caseIndex: Binding<Value.AllCases.Index> {
Binding<Value.AllCases.Index>(
// swiftlint:disable:next force_unwrapping
get: { Value.allCases.firstIndex(of: self.wrappedValue)! },
set: { self.wrappedValue = Value.allCases[$0] }
)
}
}
2. FileManager.swift
import Foundation
public extension FileManager {
static var documentsDirectoryURL: URL {
return `default`.urls(for: .documentDirectory, in: .userDomainMask)[0]
}
}
3. ForEach.swift
import SwiftUI
public extension ForEach where Content: View {
init<Base: RandomAccessCollection>(
_ base: Base,
@ViewBuilder content: @escaping (Base.Index) -> Content
)
where
Data == IndexedCollection<Base>,
Base.Element: Identifiable,
ID == Base.Element.ID {
self.init(IndexedCollection(base), id: \.element.id) { index, _ in
content(index)
}
}
}
4. IndexedCollection.swift
public struct IndexedCollection<Base: RandomAccessCollection>: RandomAccessCollection {
let base: Base
public init(_ base: Base) {
self.base = base
}
}
// MARK: RandomAccessCollection
public extension IndexedCollection {
typealias Index = Base.Index
typealias Element = (index: Index, element: Base.Element)
var startIndex: Index { base.startIndex }
var endIndex: Index { base.endIndex }
func index(after i: Index) -> Index {
base.index(after: i)
}
func index(before i: Index) -> Index {
base.index(before: i)
}
func index(_ i: Index, offsetBy distance: Int) -> Index {
base.index(i, offsetBy: distance)
}
subscript(position: Index) -> Element {
(index: position, element: base[position])
}
}
5. AppDelegate.swift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
TaskStore.shared.importPrioritizedTasks(from: url)
return true
}
func applicationDidFinishLaunching(_ application: UIApplication) {
}
}
6. SceneDelegate.swift
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo _: UISceneSession,
options _: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene
else { return }
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(
rootView: ContentView(taskStore: TaskStore.shared)
)
self.window = window
window.makeKeyAndVisible()
}
// 1
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
// 2
guard let urlContext = URLContexts.first else {
return
}
// 3
TaskStore.shared.importPrioritizedTasks(from: urlContext.url)
}
}
7. Task.swift
import Foundation
struct Task: Identifiable, Codable, Equatable {
let id = UUID()
var name: String
var completed = false
enum CodingKeys: String, CodingKey {
case id = "identifier"
case name
case completed = "isComplete"
}
}
8. Task.Priority.swift
extension Task {
enum Priority: String, CaseIterable, Codable {
// swiftlint:disable:next identifier_name
case no, low, medium, high
}
}
9. TaskStore.swift
import Combine
import Foundation
class TaskStore: ObservableObject {
static let shared = TaskStore()
static let fileExtension = "rwtl"
private var cancellables: Set<AnyCancellable> = []
let tasksDocURL = URL(
fileURLWithPath: "PrioritizedTasks",
relativeTo: FileManager.documentsDirectoryURL).appendingPathExtension(fileExtension)
@Published var prioritizedTasks: [PrioritizedTasks] = [
PrioritizedTasks(priority: .high, tasks: []),
PrioritizedTasks(priority: .medium, tasks: []),
PrioritizedTasks(priority: .low, tasks: []),
PrioritizedTasks(priority: .no, tasks: [])
]
private init() {
print(tasksDocURL)
loadPrioritizedTasks()
$prioritizedTasks
.removeDuplicates()
.sink(receiveValue: savePrioritizedTasks)
.store(in: &cancellables)
}
func getIndex(for priority: Task.Priority) -> Int {
prioritizedTasks.firstIndex { $0.priority == priority }!
}
func importPrioritizedTasks(from url: URL) {
loadPrioritizedTasks(from: url)
}
private func loadPrioritizedTasks(from url: URL? = nil) {
let urlToRead = url ?? tasksDocURL
guard FileManager.default.fileExists(atPath: urlToRead.path) else {
return
}
let decoder = PropertyListDecoder()
do {
let tasksData = try Data(contentsOf: urlToRead)
prioritizedTasks = try decoder.decode([PrioritizedTasks].self, from: tasksData)
} catch let error {
print(error)
}
}
private func savePrioritizedTasks(_ tasks: [PrioritizedTasks]) {
let encoder = PropertyListEncoder()
encoder.outputFormat = .xml
do {
let tasksData = try encoder.encode(tasks)
try tasksData.write(to: tasksDocURL, options: .atomicWrite)
} catch let error {
print(error)
}
}
}
private extension TaskStore.PrioritizedTasks {
init(priority: Task.Priority, names: [String]) {
self.init(
priority: priority,
tasks: names.map { Task(name: $0) }
)
}
}
10. TaskStore.PrioritizedTasks.swift
extension TaskStore {
struct PrioritizedTasks: Codable, Equatable {
let priority: Task.Priority
var tasks: [Task]
}
}
extension TaskStore.PrioritizedTasks: Identifiable {
var id: Task.Priority { priority }
}
11. ContentView.swift
import SwiftUI
import MessageUI
// swiftlint:disable multiple_closures_with_trailing_closure
struct ContentView: View {
@ObservedObject var taskStore: TaskStore
@State var result: Result<MFMailComposeResult, Error>?
@State var modalIsPresented = false
@State var mailViewIsPresented = false
@State var shareSheetIsPresented = false
var body: some View {
NavigationView {
List {
ForEach(taskStore.prioritizedTasks) { index in
SectionView(prioritizedTasks: self.$taskStore.prioritizedTasks, index: index)
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle("Tasks")
.navigationBarItems(
leading: EditButton(),
trailing:
HStack {
// Add Item Button
Button(action: { self.modalIsPresented = true }) {
Image(systemName: "plus")
}
.frame(width: 44, height: 44, alignment: .center)
.sheet(isPresented: $modalIsPresented) {
NewTaskView(taskStore: self.taskStore)
}
// Export Via Email
Button(action: { self.mailViewIsPresented = true }) {
Image(systemName: "envelope")
}
.frame(width: 44, height: 44, alignment: .center)
.disabled(!MFMailComposeViewController.canSendMail())
.sheet(isPresented: $mailViewIsPresented) {
MailView(
messageBody: "This is a test email string",
attachmentInfo: (
fileURL: TaskStore.shared.tasksDocURL,
mimeType: "application/xml"),
result: self.$result)
}
// Share Sheet
Button(action: { self.shareSheetIsPresented = true }) {
Image(systemName: "square.and.arrow.up")
}
.frame(width: 44, height: 44, alignment: .center)
.sheet(isPresented: $shareSheetIsPresented) {
ShareSheet(
activityItems: [TaskStore.shared.tasksDocURL],
excludedActivityTypes: [.copyToPasteboard])
}
}
)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView( taskStore: TaskStore.shared )
}
}
12. NewTaskView.swift
import SwiftUI
struct NewTaskView: View {
var taskStore: TaskStore
@Environment(\.presentationMode) var presentationMode
@State var text = ""
@State var priority: Task.Priority = .no
var body: some View {
Form {
TextField("Task Name", text: $text)
VStack {
Text("Priority")
Picker("Priority", selection: $priority.caseIndex) {
ForEach(Task.Priority.allCases.indices) { priorityIndex in
Text(
Task.Priority.allCases[priorityIndex].rawValue
.capitalized
)
.tag(priorityIndex)
}
}
.pickerStyle( SegmentedPickerStyle() )
}
Button("Add") {
let priorityIndex = self.taskStore.getIndex(for: self.priority)
self.taskStore.prioritizedTasks[priorityIndex].tasks.append(
Task(name: self.text)
)
self.presentationMode.wrappedValue.dismiss()
}
.disabled(text.isEmpty)
}
}
}
struct NewTaskView_Previews: PreviewProvider {
static var previews: some View {
NewTaskView( taskStore: TaskStore.shared )
}
}
13. RowView.swift
import SwiftUI
struct RowView: View {
@Binding var task: Task
let checkmark = Image(systemName: "checkmark")
var body: some View {
NavigationLink(
destination: TaskEditingView(task: $task)
) {
if task.completed {
checkmark
} else {
checkmark.hidden()
}
Text(task.name)
.strikethrough(task.completed)
}
}
}
struct RowView_Previews: PreviewProvider {
static var previews: some View {
RowView(
task: .constant( Task(name: "To Do") )
)
}
}
14. SectionView.swift
import SwiftUI
struct SectionView: View {
@Binding var prioritizedTasks: [TaskStore.PrioritizedTasks]
let index: Int
var body: some View {
Section(
header: Text(
"\(prioritizedTasks[self.index].priority.rawValue.capitalized) Priority"
)
) {
ForEach(prioritizedTasks[index].tasks) { i in
RowView(task: self.$prioritizedTasks[self.index].tasks[i])
}
.onMove { sourceIndices, destinationIndex in
self.prioritizedTasks[self.index].tasks.move(
fromOffsets: sourceIndices,
toOffset: destinationIndex
)
}
.onDelete { indexSet in
self.prioritizedTasks[self.index].tasks.remove(atOffsets: indexSet)
}
}
}
}
15. TaskEditingView.swift
import SwiftUI
struct TaskEditingView: View {
@Binding var task: Task
var body: some View {
Form {
TextField("Name", text: $task.name)
Toggle("Completed", isOn: $task.completed)
}.navigationBarTitle(task.name)
}
}
struct TaskEditingView_Previews: PreviewProvider {
static var previews: some View {
TaskEditingView(
task: .constant( Task(name: "To Do") )
)
}
}
16. MailView.swift
import SwiftUI
import MessageUI
typealias AttachmentInfo = (fileURL: URL, mimeType: String)
struct MailView: UIViewControllerRepresentable {
@Environment(\.presentationMode) var presentation
var messageBody: String
var attachmentInfo: AttachmentInfo?
@Binding var result: Result<MFMailComposeResult, Error>?
class Coordinator: NSObject, MFMailComposeViewControllerDelegate {
@Binding var presentation: PresentationMode
@Binding var result: Result<MFMailComposeResult, Error>?
var messageBody: String
var attachmentInfo: AttachmentInfo?
init(
presentation: Binding<PresentationMode>,
messageBody: String,
attachmentInfo: AttachmentInfo?,
result: Binding<Result<MFMailComposeResult, Error>?>
) {
_presentation = presentation
self.messageBody = messageBody
self.attachmentInfo = attachmentInfo
_result = result
}
func mailComposeController(
_ controller: MFMailComposeViewController,
didFinishWith result: MFMailComposeResult,
error: Error?
) {
defer {
$presentation.wrappedValue.dismiss()
}
if let error = error {
self.result = .failure(error)
return
}
self.result = .success(result)
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(
presentation: presentation,
messageBody: messageBody,
attachmentInfo: attachmentInfo,
result: $result)
}
func makeUIViewController(
context: UIViewControllerRepresentableContext<MailView>
) -> MFMailComposeViewController {
let viewController = MFMailComposeViewController()
viewController.mailComposeDelegate = context.coordinator
viewController.setMessageBody(context.coordinator.messageBody, isHTML: false)
if let fileURL = attachmentInfo?.fileURL,
let mimeType = attachmentInfo?.mimeType,
let fileData = try? Data(contentsOf: fileURL) {
viewController.addAttachmentData(
fileData,
mimeType: mimeType,
fileName: "ExportData.rwtl")
}
return viewController
}
func updateUIViewController(
_ uiViewController: MFMailComposeViewController,
context: UIViewControllerRepresentableContext<MailView>
) { }
}
17. ShareSheet.swift
import SwiftUI
struct ShareSheet: UIViewControllerRepresentable {
typealias Callback = (
_ activityType: UIActivity.ActivityType?,
_ completed: Bool,
_ returnedItems: [Any]?,
_ error: Error?) -> Void
var activityItems: [Any]
var applicationActivities: [UIActivity]?
var excludedActivityTypes: [UIActivity.ActivityType]?
var callback: Callback?
func makeUIViewController(context: Context) -> UIActivityViewController {
let controller = UIActivityViewController(
activityItems: activityItems,
applicationActivities: applicationActivities)
controller.excludedActivityTypes = excludedActivityTypes
controller.completionWithItemsHandler = callback
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
// nothing to do here
}
}
struct ShareSheet_Previews: PreviewProvider {
static var previews: some View {
let theShareSheet = ShareSheet(
activityItems: ["A preview string" as NSString],
excludedActivityTypes: [UIActivity.ActivityType.airDrop])
return theShareSheet
}
}
后记
本篇主要讲述了使用
Universal Type Identifiers
进行App中的数据的导入和导出,感兴趣的给个赞或者关注~~~

网友评论