[iOS 응용 개발] 파일을 추상화하는 구조체 만들기(취합)
Zenn에서는 제가 평소에 사용하던 iOS 애플리케이션에서 개발한 Tips 같은 것을 써냈으면 좋겠다고 생각했어요.
지난 8차례 동안 스위프트의 파일 조작을 간단하게 하기 위해 우리는'파일을 추상화하는 구조체'를 만들었다.
나는 지금 보도에 그 원본 코드 전체를 싣고 싶다.
복제해도 작동할 것 같아서요.
이 방법과 속성이 뭐였더라면 과거 기사를 참조하세요.
소스 코드
Xcode:12.4 Swift5
import UIKit
// MARK: ファイルを抽象化した構造体を作る(1)
struct File {
let path: String
}
extension File {
static let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(
.documentDirectory,
.userDomainMask,
true
).first!
static let libraryDirectoryPath = NSSearchPathForDirectoriesInDomains(
.libraryDirectory,
.userDomainMask,
true
).first!
static let temporaryDirectoryPath = NSTemporaryDirectory()
static let mainBundlePath = Bundle.main.bundlePath
static let documentDirectory = File(path: documentDirectoryPath)
static let libraryDirectory = File(path: libraryDirectoryPath)
static let temporaryDirectory = File(path: temporaryDirectoryPath)
static let mainBundle = File(path: mainBundlePath)
}
extension File {
func append(pathComponent: String) -> File {
return File(path: (path as NSString).appendingPathComponent(pathComponent))
}
static func + (lhs: File, rhs: String) -> File {
return lhs.append(pathComponent: rhs)
}
}
extension File: Equatable {
static func == (lhs: File, rhs: File) -> Bool {
return lhs.path == rhs.path
}
}
// MARK: ファイルを抽象化した構造体を作る(2)
extension File {
var exists: Bool {
return FileManager.default.fileExists(atPath: path)
}
}
extension File {
var parentDirectoryPath: String {
if path == "/" { return "" }
return (path as NSString).deletingLastPathComponent
}
var parentDirectory: File {
return File(path: parentDirectoryPath)
}
}
extension File {
func makeDirectory() throws {
if !exists {
try FileManager.default.createDirectory(
atPath: path,
withIntermediateDirectories: true,
attributes: nil
)
}
}
}
// MARK: ファイルを抽象化した構造体を作る(3)
extension File {
var url: URL {
return URL(fileURLWithPath: path)
}
var data: Data? {
return try? Data(contentsOf: url)
}
}
extension File {
func contents(encoding: String.Encoding) -> String? {
guard let data = self.data else { return nil }
return String(data: data, encoding: encoding)
}
var contents: String? {
return contents(encoding: .utf8)
}
func write(contents: String, encoding: String.Encoding = .utf8) throws {
try parentDirectory.makeDirectory()
try contents.write(to: url, atomically: false, encoding: encoding)
}
}
extension File {
var image: UIImage? {
guard let data = self.data else { return nil }
return UIImage(data: data)
}
// JPEGで書き込む
func write(imageAsJpeg image: UIImage, quality: CGFloat = 0.9) throws {
guard let data = image.jpegData(compressionQuality: quality) else { return }
try parentDirectory.makeDirectory()
try data.write(to: url)
}
// PNGで書き込む
func write(imageAsPng image: UIImage) throws {
guard let data = image.pngData() else { return }
try parentDirectory.makeDirectory()
try data.write(to: url)
}
}
// MARK: ファイルを抽象化した構造体を作る(4)
extension File {
var name: String {
return (path as NSString).lastPathComponent
}
var `extension`: String {
let ext = (name as NSString).pathExtension
return ext.isEmpty ? "" : ".\(ext)"
}
var extensionWithoutDot: String {
let ext = (name as NSString).pathExtension
return ext.isEmpty ? "" : "\(ext)"
}
var nameWithoutExtension: String {
return (name as NSString).deletingPathExtension
}
}
extension File {
var isFile: Bool {
var isDirectory: ObjCBool = false
if FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) {
return !isDirectory.boolValue
}
return false
}
var isDirectory: Bool {
var isDirectory: ObjCBool = false
if FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) {
return isDirectory.boolValue
}
return false
}
// こちらでも可
// var isDirectory: Bool {
// return exists && !isFile
// }
}
// MARK: ファイルを抽象化した構造体を作る(5)
extension File {
// 作成日時
var creationDate: Date? {
return attributes[.creationDate] as? Date
}
// 更新日時
var modificationDate: Date? {
return attributes[.modificationDate] as? Date
}
// ファイルサイズ
var size: UInt64 {
return attributes[.size] as? UInt64 ?? 0
}
private var attributes: [FileAttributeKey : Any] {
let attr1 = (try? FileManager.default.attributesOfFileSystem(forPath: path)) ?? [:]
let attr2 = (try? FileManager.default.attributesOfItem(atPath: path)) ?? [:]
return [attr1, attr2].reduce(into: [FileAttributeKey : Any](), { ret, attr in
ret.merge(attr) { $1 }
})
}
}
// MARK: ファイルを抽象化した構造体を作る(6)
extension File {
var files: [File] {
return filesMap { self + $0 }
}
var filePaths: [String] {
return filesMap { (self + $0).path }
}
var fileNames: [String] {
return filesMap { $0 }
}
private func filesMap<T>(_ transform: (String) throws -> (T)) rethrows -> [T] {
guard let fileNames = try? FileManager.default.contentsOfDirectory(atPath: path) else {
return []
}
return try fileNames.map { try transform($0) }
// ファイル名で並べたいときは下を使用する
// return fileNames.sorted().map { try transform($0) }
}
}
extension File: CustomStringConvertible {
var description: String {
let type = isDirectory ? "Dir" : "File"
return "<\(type) \(name)>"
}
}
// MARK: ファイルを抽象化した構造体を作る(7)
extension File {
func delete() throws {
try FileManager.default.removeItem(atPath: path)
}
func deleteAllChildren() throws {
try files.forEach { file in
try file.delete()
}
}
func copy(to destination: File, force: Bool = true) throws {
if force && destination.exists {
try destination.delete()
}
try FileManager.default.copyItem(atPath: path, toPath: destination.path)
}
func move(to destination: File, force: Bool = true) throws {
if force && destination.exists {
try destination.delete()
}
try FileManager.default.moveItem(atPath: path, toPath: destination.path)
}
func rename(to name: String, force: Bool = true) throws -> File {
let destination = File(path: parentDirectoryPath) + name
try move(to: destination, force: force)
return destination
}
}
// MARK: ファイルを抽象化した構造体を作る(8)
extension File {
func jsonDecoded<T>(_ type: T.Type) throws -> T? where T : Decodable {
guard let data = self.data else { return nil }
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return try decoder.decode(type, from: data)
}
}
extension File {
func jsonEncode<T>(_ value: T) throws -> Data where T : Encodable {
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
encoder.keyEncodingStrategy = .convertToSnakeCase
return try encoder.encode(value)
}
func writeEncodedJson<T>(_ value: T, encoding: String.Encoding = .utf8) throws where T : Encodable {
let encoded = try jsonEncode(value)
let jsonString = String(data: encoded, encoding: encoding) ?? ""
try parentDirectory.makeDirectory()
try jsonString.write(to: url, atomically: false, encoding: encoding)
}
}
extension은 상세한 분류가 있지만 보도를 위한 것이니 적당히 하세요.총결산
[iOS 응용 개발] 파일을 추상화하는 구조체 만들기(1)
+
로 연결할 수 있음근거
Equatable
CustomStringConvertible
Codable
구조화된 데이터 처리최후
이번에 제작된
File構造体
인데 직접 사용하기 편해서 소개해드렸어요.물론 다른 기능도 추가할 수 있고, 사용하지 않는 것도 삭제할 수 있다.
"괜찮을지도 몰라요"라고 생각되면 사용해 보세요.
그럼 안녕히 계세요.
Reference
이 문제에 관하여([iOS 응용 개발] 파일을 추상화하는 구조체 만들기(취합)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/nkysyuichi/articles/a99d617fd0d275텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)