├── ICloudManager ├── ICloudDocumentModel.swift ├── ICloudDocumentPickerViewController.swift ├── ICloudFileHelper.swift └── ICloudManager.swift ├── ICloudPicker.podspec ├── LICENSE └── README.md /ICloudManager/ICloudDocumentModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ICloudDocumentModel.swift 3 | // cloud 4 | // 5 | // Created by Teng Wang 王腾 on 2018/10/10. 6 | // Copyright © 2018 Teng Wang 王腾. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ICloudDocumentModel: NSObject { 12 | 13 | open var fileURL: URL? 14 | 15 | /// 文件的名称 16 | /// eg. “/tmp/scratch.tiff” -> “/tmp/scratch” 17 | /// eg. “/tmp/” -> “/tmp” 18 | /// eg. “scratch..tiff” -> “scratch.” 19 | open var fileName: String? 20 | 21 | open var fileIsExist = false 22 | 23 | /// 文件的相关属性. Use: fileAttributes.fileSize() 24 | open var fileAttributes: NSDictionary? 25 | 26 | /// 创建 ICloudDocumentModel 实例, 会进行参数的初始化操作 27 | /// 28 | /// - Parameter path: 文档j路径 29 | /// - Returns: ICloudDocumentModel 实例 30 | open class func model(path: String) -> ICloudDocumentModel { 31 | 32 | let documentModel = ICloudDocumentModel() 33 | 34 | guard !path.isEmpty else { 35 | return documentModel 36 | } 37 | 38 | documentModel.fileURL = URL.init(fileURLWithPath: path) 39 | 40 | // 读取文件名 41 | if let fileUrl = documentModel.fileURL { 42 | let pathString = NSString.init(string: fileUrl.lastPathComponent) 43 | documentModel.fileName = pathString.deletingPathExtension 44 | } 45 | 46 | let fileManager = FileManager.default 47 | documentModel.fileIsExist = fileManager.fileExists(atPath: path) 48 | 49 | // 获取文件相关属性 50 | guard let fileAttributes = ICloudFileHelper.fileAttr(atPath: path) else { 51 | return documentModel 52 | } 53 | documentModel.fileAttributes = fileAttributes 54 | 55 | return documentModel 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ICloudManager/ICloudDocumentPickerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ICloudDocumentPickerViewController.swift 3 | // cloud 4 | // 5 | // Created by Teng Wang 王腾 on 2018/10/9. 6 | // Copyright © 2018 Teng Wang 王腾. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class ICloudDocumentPickerViewController: UIDocumentPickerViewController { 12 | 13 | /// 主题颜色 14 | public var themeColor: UIColor? 15 | 16 | public override func viewDidLoad() { 17 | super.viewDidLoad() 18 | modalPresentationStyle = .fullScreen 19 | modalTransitionStyle = .coverVertical 20 | } 21 | 22 | /// 设置主题颜色 23 | /// 24 | /// - Parameter animated: 25 | public override func viewWillAppear(_ animated: Bool) { 26 | super.viewWillAppear(animated) 27 | if let themeColor = themeColor { 28 | configureThemeColor(themeColor) 29 | } 30 | } 31 | 32 | /// 恢复系统主题颜色 33 | /// 34 | /// - Parameter animated: 35 | public override func viewWillDisappear(_ animated: Bool) { 36 | super.viewWillDisappear(animated) 37 | if let _ = themeColor { 38 | configureThemeColor(.blue) 39 | } 40 | } 41 | 42 | /// 设置主题颜色 43 | /// 44 | /// - Parameter themeColor: 45 | func configureThemeColor(_ themeColor: UIColor) { 46 | UIImageView.appearance().tintColor = themeColor 47 | UITabBar.appearance().tintColor = themeColor 48 | UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = themeColor 49 | UIBarButtonItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor:themeColor], for: .normal) 50 | UILabel.appearance().tintColor = themeColor 51 | UIButton.appearance().tintColor = themeColor 52 | view.tintColor = themeColor 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ICloudManager/ICloudFileHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ICloudFileHelper.swift 3 | // cloud 4 | // 5 | // Created by Teng Wang 王腾 on 2018/10/10. 6 | // Copyright © 2018 Teng Wang 王腾. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class ICloudFileHelper: NSObject { 12 | 13 | /// 创建文件目录、如果不存在目录会创建目录 14 | /// 15 | /// - Parameter atPath: 文件目录 16 | /// - Returns: 是否创建成功 17 | @discardableResult 18 | public class func createDirectory(atPath: String) -> Bool { 19 | let fileManager = FileManager.default 20 | let result = fileManager.fileExists(atPath: atPath) 21 | if result == false { 22 | do { 23 | try fileManager.createDirectory(atPath: atPath, 24 | withIntermediateDirectories: true, 25 | attributes: nil) 26 | } catch { 27 | return false 28 | } 29 | return true 30 | } else { 31 | return true 32 | } 33 | } 34 | 35 | /// 获取单个文件大小 36 | /// 37 | /// - Parameter atPath: 文件路径 38 | /// - Returns: 文件大小 39 | public class func fileSize(atPath: String) -> Float { 40 | let fileManager = FileManager.default 41 | guard fileManager.fileExists(atPath: atPath) else { 42 | return 0.0 43 | } 44 | do { 45 | let attr = try fileManager.attributesOfItem(atPath: atPath) as NSDictionary 46 | return Float(attr.fileSize()) 47 | } catch { 48 | return 0.0 49 | } 50 | } 51 | 52 | /// 获取文件属性 53 | /// 54 | /// - Parameter atPath: 文件路径 55 | /// - Returns: 文件大小 56 | public class func fileAttr(atPath: String) -> NSDictionary? { 57 | let fileManager = FileManager.default 58 | guard fileManager.fileExists(atPath: atPath) else { 59 | return nil 60 | } 61 | do { 62 | let attr = try fileManager.attributesOfItem(atPath: atPath) as NSDictionary 63 | return attr 64 | } catch { 65 | return nil 66 | } 67 | } 68 | 69 | 70 | /// 获取文件夹的大小 71 | /// 72 | /// - Parameter atPath: 文件夹路径 73 | /// - Returns: 文件夹大小 74 | public class func forderSize(atPath: String) -> Float { 75 | 76 | let fileManager = FileManager.default 77 | guard fileManager.fileExists(atPath: atPath) else { 78 | return 0.0 79 | } 80 | guard let childFilePaths = fileManager.subpaths(atPath: atPath) else { 81 | return 0.0 82 | } 83 | 84 | var fileSize: Float = 0 85 | for path in childFilePaths { 86 | let fileAbsoluePath = atPath + "/" + path 87 | if isDirectory(atPath: fileAbsoluePath) { 88 | fileSize += 0 89 | } else { 90 | fileSize += ICloudFileHelper.fileSize(atPath: fileAbsoluePath) 91 | } 92 | } 93 | return fileSize 94 | } 95 | 96 | /// 是否是文件夹 97 | /// 98 | /// - Parameter atPath: 目录路径 99 | /// - Returns: true or false 100 | public class func isDirectory(atPath: String) -> Bool { 101 | var isDirectory: ObjCBool = ObjCBool(false) 102 | let fromExist = FileManager.default.fileExists(atPath: atPath, 103 | isDirectory: &isDirectory) 104 | return fromExist && isDirectory.boolValue 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /ICloudManager/ICloudManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // iCloudManager.swift 3 | // cloud 4 | // 5 | // Created by Teng Wang 王腾 on 2018/10/8. 6 | // Copyright © 2018 Teng Wang 王腾. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class ICloudManager: NSObject { 12 | 13 | /// 存放的默认路径 14 | public static let iCloudBoxPath = NSHomeDirectory() + "/Documents/iCloudBox/Doc/" 15 | 16 | public static let iCloudBoxDownLoadPath = NSHomeDirectory() + "/Documents/iCloudBox/Download/" 17 | 18 | /// 判断iCloud是否可用 19 | /// 20 | /// - Returns: ture false 21 | public class func iCloudEnable() -> Bool { 22 | let manager = FileManager.default 23 | if let _ = manager.url(forUbiquityContainerIdentifier: nil) { 24 | return true 25 | } else { 26 | return false 27 | } 28 | } 29 | 30 | /// 通过iCloudDocument 打开和关闭文件 31 | /// 32 | /// - Parameters: 33 | /// - documentURL: documentURL description 34 | /// - callBack: 文件数据、文件大小 kb 35 | public class func download(with documentURL: URL, callBack: @escaping (NSData?, Float) -> Void) { 36 | let iCloudDoc = iCloudDocument.init(fileURL: documentURL) 37 | iCloudDoc.open { (result) in 38 | if result { 39 | let size = ICloudFileHelper.fileSize(atPath: documentURL.path) 40 | iCloudDoc.close(completionHandler: { (_) in }) 41 | callBack(iCloudDoc.data, size) 42 | } 43 | } 44 | } 45 | 46 | /// 保存文件到本地 /Documents/iCloudBox 下 47 | /// 48 | /// - Parameters: 49 | /// - documentURL: documentURL description 50 | /// - maxSize: 保存的最大尺寸 为 nil 忽略 51 | /// - callBack: 保存h的路径 url、是否保存成功、保存失败的描述 52 | public class func save(with documentURL: URL, maxSize: Float?, callBack: @escaping (ICloudDocumentModel?, Bool, String) -> Void) { 53 | 54 | guard ICloudManager.iCloudEnable() else { 55 | callBack(nil, false, "请在设置->AppleID、iCloud->iCloud中打开访问权限") 56 | return 57 | } 58 | 59 | var forderPath = documentURL.absoluteString.replacingOccurrences(of: "file:///private/var/mobile/Library/Mobile%20Documents/", with: "") 60 | 61 | forderPath = forderPath.replacingOccurrences(of: documentURL.lastPathComponent.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "", with: "") 62 | guard let forderName = forderPath.removingPercentEncoding else { 63 | callBack(nil, false, "forderName 不存在") 64 | return 65 | } 66 | 67 | ICloudManager.download(with: documentURL) { (obj, fileSize) in 68 | 69 | if let maxSize = maxSize { 70 | guard fileSize < maxSize else { 71 | callBack(nil, false, "文件不能大于\(maxSize)m") 72 | return 73 | } 74 | } 75 | if let data = obj { 76 | let writeUrl = URL.init(fileURLWithPath: ICloudManager.iCloudBoxPath + forderName + documentURL.lastPathComponent) 77 | ICloudFileHelper.createDirectory(atPath: ICloudManager.iCloudBoxPath + forderName) 78 | do { 79 | try data.write(to: writeUrl, options: .atomic) 80 | callBack(ICloudDocumentModel.model(path: writeUrl.path), true, "文件写入成功") 81 | } catch { 82 | callBack(nil, false, "文件写入失败") 83 | } 84 | } 85 | } 86 | 87 | } 88 | 89 | /// 文件是否在 iCloudBox 存在 90 | /// 91 | /// - Parameter fileName: 文件名(fileUrl.lastPathComponent) eg. doc.text 92 | /// - Returns: 93 | open class func documentIsExists(fileName: String) -> URL? { 94 | 95 | let fileManager = FileManager.default 96 | 97 | guard let childFilePaths = fileManager.subpaths(atPath: ICloudManager.iCloudBoxDownLoadPath) else { 98 | return nil 99 | } 100 | 101 | for path in childFilePaths { 102 | // 读取文件名 103 | let fileUrl = URL.init(fileURLWithPath: ICloudManager.iCloudBoxDownLoadPath + path) 104 | if fileName == fileUrl.lastPathComponent { 105 | return fileUrl 106 | } 107 | } 108 | return nil 109 | } 110 | } 111 | 112 | extension ICloudManager { 113 | 114 | /// 清除iCloudBox本地缓存, filePath 为空 清除所有缓存 115 | /// 116 | /// - Parameter filePath: 为空 清除所有缓存 117 | public class func cleariCloudBoxCache(filePath: URL = URL(fileURLWithPath: ICloudManager.iCloudBoxPath)) { 118 | do { 119 | try FileManager.default.removeItem(at: filePath) 120 | } catch { 121 | 122 | } 123 | } 124 | 125 | /// 异步获取iCloudBox 缓存大小 126 | /// 127 | /// - Parameter callBack: 大小 kb 128 | public class func asynciCloudBoxSize(callBack: @escaping(Float) -> Void) { 129 | DispatchQueue.global().async { 130 | let size = ICloudFileHelper.forderSize(atPath: ICloudManager.iCloudBoxPath) 131 | callBack(size) 132 | } 133 | } 134 | } 135 | 136 | class iCloudDocument: UIDocument { 137 | 138 | var data: NSData? 139 | 140 | // 处理文件下载 141 | override func load(fromContents contents: Any, ofType typeName: String?) throws { 142 | if let userContent = contents as? NSData { 143 | data = userContent 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /ICloudPicker.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint ICloudPicker.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "ICloudPicker" 19 | s.version = "0.0.10" 20 | s.summary = "icloud driver picker" 21 | s.swift_version = '4.0' 22 | 23 | # This description is used to generate tags and improve search results. 24 | # * Think: What does it do? Why did you write it? What is the focus? 25 | # * Try to keep it short, snappy and to the point. 26 | # * Write the description between the DESC delimiters below. 27 | # * Finally, don't worry about the indent, CocoaPods strips it! 28 | s.description = <<-DESC 29 | icloud driver picker 30 | DESC 31 | 32 | s.homepage = "https://github.com/xiaohuochai/iCloudPicker.git" 33 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 34 | 35 | 36 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 37 | # 38 | # Licensing your code is important. See http://choosealicense.com for more info. 39 | # CocoaPods will detect a license file if there is a named LICENSE* 40 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 41 | # 42 | 43 | # s.license = "MIT" 44 | s.license = { :type => "MIT", :file => "LICENSE" } 45 | 46 | 47 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 48 | # 49 | # Specify the authors of the library, with email addresses. Email addresses 50 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 51 | # accepts just a name if you'd rather not provide an email address. 52 | # 53 | # Specify a social_media_url where others can refer to, for example a twitter 54 | # profile URL. 55 | # 56 | 57 | s.author = { "Teng Wang 王腾" => "teng.wang.o@nio.com" } 58 | # Or just: s.author = "Teng Wang 王腾" 59 | # s.authors = { "Teng Wang 王腾" => "teng.wang.o@nio.com" } 60 | # s.social_media_url = "http://twitter.com/Teng Wang 王腾" 61 | 62 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 63 | # 64 | # If this Pod runs only on iOS or OS X, then specify the platform and 65 | # the deployment target. You can optionally include the target after the platform. 66 | # 67 | 68 | # s.platform = :ios 69 | s.platform = :ios, "10.0" 70 | 71 | # When using multiple platforms 72 | s.ios.deployment_target = "10.0" 73 | # s.osx.deployment_target = "10.7" 74 | # s.watchos.deployment_target = "2.0" 75 | # s.tvos.deployment_target = "9.0" 76 | 77 | 78 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 79 | # 80 | # Specify the location from where the source should be retrieved. 81 | # Supports git, hg, bzr, svn and HTTP. 82 | # 83 | 84 | s.source = { :git => "https://github.com/xiaohuochai/iCloudPicker.git", :tag => "#{s.version}" } 85 | 86 | 87 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 88 | # 89 | # CocoaPods is smart about how it includes source code. For source files 90 | # giving a folder will include any swift, h, m, mm, c & cpp files. 91 | # For header files it will include any header in the folder. 92 | # Not including the public_header_files will make all headers public. 93 | # 94 | 95 | s.source_files = "ICloudManager/*.swift" 96 | # s.exclude_files = "" 97 | 98 | # s.public_header_files = "ICloudManager/*.swift" 99 | 100 | 101 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 102 | # 103 | # A list of resources included with the Pod. These are copied into the 104 | # target bundle with a build phase script. Anything else will be cleaned. 105 | # You can preserve files from being cleaned, please don't preserve 106 | # non-essential files like tests, examples and documentation. 107 | # 108 | 109 | # s.resource = "icon.png" 110 | # s.resources = "Resources/*.png" 111 | 112 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 113 | 114 | 115 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 116 | # 117 | # Link your library with frameworks, or libraries. Libraries do not include 118 | # the lib prefix of their name. 119 | # 120 | 121 | # s.framework = "SomeFramework" 122 | # s.frameworks = "SomeFramework", "AnotherFramework" 123 | 124 | # s.library = "iconv" 125 | # s.libraries = "iconv", "xml2" 126 | 127 | 128 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 129 | # 130 | # If your library depends on compiler flags you can set them in the xcconfig hash 131 | # where they will only apply to your library. If you depend on other Podspecs 132 | # you can include multiple dependencies to ensure it works. 133 | 134 | # s.requires_arc = true 135 | 136 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 137 | # s.dependency "JSONKit", "~> 1.4" 138 | 139 | end 140 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 wangteng 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ICloudPicker 2 | 3 | swift 版本 4 | 5 | 特点: 6 | 7 | 1、直接显示到 iCloud 云盘位置 8 | 9 | 2、可以自定义主题颜色、(微信自定义了主题颜色) 10 | 11 | 3、使用 UIDocument 来读写文件保证文件的安全访问 12 | 13 | [https://github.com/xiaohuochai/iCloudPicker.git](https://github.com/xiaohuochai/iCloudPicker.git) 14 | 15 | 16 | # Installation 17 | 18 | ## Using [CocoaPods](https://cocoapods.org): 19 | Simply add the following line to your Podfile: 20 | ``` 21 | pod 'ICloudPicker' 22 | ``` 23 | 24 | # Xcode config 25 | 26 | 27 | 28 | # Use 29 | ``` 30 | func openICloudDocumentPickerViewController() { 31 | guard ICloudManager.iCloudEnable() else { 32 | debugPrint("请在设置->AppleID、iCloud->iCloud中打开访问权限") 33 | return 34 | } 35 | 36 | let iCloudDocument = ICloudDocumentPickerViewController.init(documentTypes: ["public.data"], in: .open) 37 | iCloudDocument.themeColor = .red 38 | iCloudDocument.delegate = self 39 | self.present(iCloudDocument, animated: true) {} 40 | } 41 | ``` 42 | 43 | ``` 44 | extension ViewController: UIDocumentPickerDelegate { 45 | 46 | public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { 47 | saveiCloudDocument(urls) 48 | } 49 | 50 | public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) { 51 | saveiCloudDocument([url]) 52 | } 53 | 54 | /// 保存文件、限制文件大小为10m 55 | /// 56 | /// - Parameter url: UIDocumentPicker url 57 | func saveiCloudDocument(_ urls: [URL]) { 58 | guard let url = urls.first else { 59 | return 60 | } 61 | ICloudManager.save(with: url, maxSize: 10*1024*1024) { (documentModel, result, errorMsg) in 62 | 63 | } 64 | } 65 | } 66 | ``` 67 | 68 | # Cache 69 | ``` 70 | func iCloudBoxCache() { 71 | 72 | // 异步获取 ICloudDocument 文件大小 73 | ICloudManager.asynciCloudBoxSize { (size) in 74 | debugPrint(size) 75 | 76 | // 清除所有 本地存储的 ICloudDocument 文件 77 | ICloudManager.cleariCloudBoxCache() 78 | 79 | // 清除单个文件 80 | // ICloudManager.cleariCloudBoxCache(filePath: URL.init(fileURLWithPath: "")) 81 | } 82 | } 83 | ``` 84 | # view 85 | 86 | --------------------------------------------------------------------------------