Jiseob Kim

iOS Developer

Swift - FileManager

11 Jul 2019 » Swift

์˜ค๋Š˜์˜ ์ฃผ์ œ๋Š” FileManager

์“ฐ๊ฒŒ๋œ ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค,

์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๊ณ  ์žˆ๋Š”๋ฐ, Photo kit ๊ด€๋ จ ํ•ด๋ณธ ๊ฒฝํ—˜์ด ์—†์–ด์„œ, ์ด ๋ถ€๋ถ„์„ ์ง์ ‘ ์จ๋ณผ๋ ค๊ณ  ์•จ๋ฒ” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์•ˆ์“ฐ๋Š”๋ฐ,

์˜์ƒ, ์ด๋ฏธ์ง€, ๋ผ์ด๋ธŒํฌํ† ๋ฅผ ์•ฑ๋‚ด์— ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ–ˆ๋‹ค.

NSData๋กœ ์บ์ŠคํŒ…ํ•ด์„œ Core Data์— ๋„ฃ์œผ๋ คํ–ˆ๋Š”๋ฐ, ์ด๊ฑด ์ข€ ์•„๋‹Œ๊ฑฐ ๊ฐ™์•„์„œ ์—ฌ๊ธฐ์ €๊ธฐ ๋ฌผ์–ด๋ณธ ๊ฒฐ๊ณผ

๋ฐ”์ด๋„ˆ๋ฆฌ ํŒŒ์ผ(์ด๋ฏธ์ง€, ์˜์ƒ ๋“ฑ๋“ฑ)์€ ํŒŒ์ผ์— ์ €์žฅํ•˜๊ณ ,

๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(์‚ฌ์ง„ ์ •๋ณด, ๊ด€๋ จ ์ •๋ณด)๋งŒ Core data์— ์ €์žฅ์‹œํ‚ค๋Š” ๋ฐฉ์‹์ด ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ธ๋‹ค๊ณ  ํ•ด์„œ ์จ๋ณด๊ฒŒ ๋๋‹ค.

์ˆœ์„œ

  • File Manager๋ž€?
  • ํด๋” ์ถ”๊ฐ€ ๋ฐฉ๋ฒ•์€?
  • ํŒŒ์ผ ์ถ”๊ฐ€ ๋ฐฉ๋ฒ•์€?
  • ํŒŒ์ผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐฉ๋ฒ•์€?
  • ํŒŒ์ผ ์‚ญ์ œํ•˜๋Š” ๋ฐฉ๋ฒ•์€?



File Manager๋ž€?

์•„์ดํฐ ์•ฑ๋งˆ๋‹ค ์ž๊ธฐ๋งŒ์˜ ๊ณต๊ฐ„์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”๋ฐ, ์ด ๊ณต๊ฐ„์„ ๊ด€๋ฆฌํ•˜๋Š” ๋งค๋‹ˆ์ €๋ผ ์ƒ๊ฐํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

์ด ๊ณต๊ฐ„์€ ์ผ๋ฐ˜ ๋งฅ, ์œˆ๋„์šฐ์ฒ˜๋Ÿผ Document ํด๋”, Downloadํด๋” ๋“ฑ๋“ฑ ๋‹ค์–‘ํ•œ ์ข…๋ฅ˜์˜ ํด๋”๊ฐ€ ์žˆ๋‹ค!

์ด ๊ธ€์—์„  Documentํด๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๊ฒƒ ์ €๊ฒƒ ํ•ด๋ณด๋ คํ•œ๋‹ค.

๊ฒฝ๋กœ ์ ‘๊ทผ

๋‹ค๋ฅธ ์ž‘์—…๋“ค ํ•˜๊ธฐ์ „ ๊ณตํ†ต ์‚ฌํ•ญ์ธ ํ•ด๋‹น ํด๋”๋กœ ์ ‘๊ทผ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋จผ์ € ์•Œ์•„๋ณด์ž

let fileManager = FileManager.default

fileManager๋ผ๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค. (default๋ฅผ ํ•ด์คŒ์œผ๋กœ์จ ์‹ฑ๊ธ€ํ†ค ์ธ์Šคํ„ด์Šค!)



let documentURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]

for๋Š” ํด๋”๋ฅผ ์ •ํ•ด์ฃผ๋Š” ์š”์†Œ์ž…๋‹ˆ๋‹ค. Download ํด๋” ํ˜น์€ Document ํด๋” ๋“ฑ๋“ฑ

in์€ ์ œํ•œ์„ ๊ฑธ์–ด์ฃผ๋Š” ์š”์†Œ์ž…๋‹ˆ๋‹ค. ๊ทธ ์ด์ƒ์€ ๋ชป๊ฐ€๊ฒŒ ํ•˜๋Š”!

About - Mask
view.layer.masksToBounds = true ๋งŽ์ด ๋ณด์…จ์„ ๊ฒ๋‹ˆ๋‹ค.
๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด Bound ๋ฐ–์˜ UI๋“ค์€ ์•ˆ๋ณด์ด๋Š”!!
in์˜ ์ธ์ž๋“ค ์ด๋ฆ„์— ๋ถ™์€ Mask๋Š” ์ด๋Ÿฐ ์˜๋ฏธ๋ผ ์ƒ๊ฐํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.


์• ํ”Œ์ด ์ฃผ์„์„ ๊ต‰์žฅํžˆ ์ž˜์จ๋†จ์œผ๋ฏ€๋กœ ์ข€๋” ๋””ํ…Œ์ผํ•˜๊ฒŒ ๋ณด์ž,

for enum ๊ฐ’๋“ค (Objective C Enum)

documentDirectory   // documents (Documents)
developerDirectory  // (Developer) DEPRECATED - there is no one single Developer directory.
desktopDirectory    // location of user's desktop
downloadsDirectory  // location of the user's "Downloads" directory
musicDirectory      // location of user's Music directory (~/Music)
...

๊ต‰์žฅํžˆ ์„ค๋ช…์„ ์ž˜ํ•ด๋†จ๊ณ , ๊ต‰์žฅํžˆ ๋งŽ๋‹ค. ๊ทธ ์ค‘ ์นœ์ˆ™ํ•ด ๋ณด์ด๋Š” ๋ช‡๊ฐœ๋งŒ ๊ฐ€์ ธ์™”๋‹ค. ์ด๋Ÿฐ์‹์œผ๋กœ ์ ‘๊ทผ ํด๋”๋ฅผ ์ง€์ •ํ•ด์ค€๋‹ค.


in enum ๊ฐ’๋“ค (Swift Enum)

public static var userDomainMask: FileManager.SearchPathDomainMask { get } 
// user's home directory --- place to install user's personal items (~)

public static var localDomainMask: FileManager.SearchPathDomainMask { get } 
// local to the current machine --- place to install items available to everyone on this machine (/Library)

public static var networkDomainMask: FileManager.SearchPathDomainMask { get } 
// publically available location in the local area network --- place to install items available on the network (/Network)

public static var systemDomainMask: FileManager.SearchPathDomainMask { get } 
// provided by Apple, unmodifiable (/System)

public static var allDomainsMask: FileManager.SearchPathDomainMask { get } 
// all domains: all of the above and future items

ํ„ฐ๋ฏธ๋„์„ ์กฐ๊ธˆ์ด๋ผ๋„ ์จ๋ดค๋‹ค๋ฉด ๋’ค์— ๊ด„ํ˜ธ๋งŒ ๋ด๋„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

์„ผ์Šค์žˆ๊ฒŒ ์ €๋ ‡๊ฒŒ ํ‘œํ˜„ํ•ด์ฃผ๋‹ˆ ์ข‹๋‹ค.



๊ฒฝ๋กœ ์ถ”๊ฐ€

ํ‚ค์›Œ๋“œ: appendingPathComponent

์•„์ฃผ ๊ฐ„๋‹จํ•˜๋‹ค.

let newURL = documentsURL.appendingPathComponent("App Photos")

๊ฐ„๋‹จํ•œ ์˜ˆ๋ฅผ ์จ๋ณด๋ฉด

let oldURL = URL(string: "~/Document")
let newURL = documentsURL.appendingPathComponent("Hello")
print(newURL) // ~/Document/Hello

์•„์ฃผ ๊ฐ„๋‹จ!

ํด๋” ์ถ”๊ฐ€ ๋ฐฉ๋ฒ•์€?

ํ‚ค์›Œ๋“œ: createDirectory

// 1. ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
let fileManager = FileManager.default
// 2. ๋„ํ๋จผํŠธ URL ๊ฐ€์ ธ์˜ค๊ธฐ
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
// 3. ์ƒ์„ฑํ•  ํด๋” ์ด๋ฆ„ ์ถ”๊ฐ€ํ•ด์ฃผ๊ธฐ
let directoryURL = documentsURL.appendingPathComponent("NewDirectory")
// 4. ์ƒ์„ฑํ•˜๊ธฐ
do {
    try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: false, attributes: nil)
} catch let e {
    print(e.localizedDescription)
}

์ด๊ฒƒ๋„ ์•„์ฃผ ๊ฐ„๋‹จ!

  • at : ๊ฒฝ๋กœ ๋ฐ ํด๋”๋ช…, ์œ„์—์„œ ๋งŒ๋“  URL ์‚ฌ์šฉ
  • withIntermediateDirectories : โ€œ์ค‘๊ฐ„ ๋””๋ ‰ํ† ๋ฆฌ๋“ค๋„ ๋งŒ๋“ค๊บผ์•ผ?โ€ ์ด๋Ÿฐ ์˜๋ฏธ.
  • attributes : ํŒŒ์ผ ์ ‘๊ทผ ๊ถŒํ•œ, ๊ทธ๋ฃน ๋“ฑ๋“ฑ ํด๋” ์†์„ฑ ์ •์˜


๋‹ค์‹œ ๋งํ•ด, ํ˜„์žฌ ์กด์žฌํ•˜๋Š” ํด๋”๊ฐ€ ~/Document๋งŒ ์žˆ๋Š” ์ƒํƒœ์—์„œ appendingPathComponent๋ฅผ ์—ฌ๋Ÿฌ๋ฒˆํ•ด์„œ ~/Document/path1/path2/path3๋กœ ๋งŒ๋“ค์—ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด, ๊ฐ Bool๊ฐ’์— ๋”ฐ๋ผ ์„ฑ๊ณต, ์‹คํŒจ๊ฐ€ ๋‚˜๋‰œ๋‹ค.

  • withIntermediateDirectories: true - ์ค‘๊ฐ„ ๋””๋ ‰ํ† ๋ฆฌ์ธ path1/path2๋„ ๋งŒ๋“ค๊ณ  path3๊นŒ์ง€ ๋งŒ๋“ ๋‹ค.
  • withIntermediateDirectories: false - ์ค‘๊ฐ„ ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด catch๋กœ ๋น ์ง„๋‹ค.

ํŒŒ์ผ ์ถ”๊ฐ€ ๋ฐฉ๋ฒ•์€?

ํ‚ค์›Œ๋“œ: write(to:_, atomically:_, encoding:_)

// 1. ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ - ๋™์ผ
let fileManager = FileManager.default

// 2. ๋„ํ๋จผํŠธ URL ๊ฐ€์ ธ์˜ค๊ธฐ - ๋™์ผ
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]

// 3. ํŒŒ์ผ ์ €์žฅํ•  Directory ์„ค์ •
let directoryURL = documentsURL.appendingPathComponent("NewDirectory")

// 4. File ์ด๋ฆ„ ์„ค์ •
let fileURL = directoryURL.appendingPathComponent("test.txt")

// 5. File ๋‚ด์šฉ
let text = NSString(string: "Hello world")

do {
    // 6-1. ํŒŒ์ผ ์ƒ์„ฑ
    try text.write(to: fileURL, atomically: true, encoding: String.Encoding.utf8.rawValue)
} catch let e {
    // 6-2. ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ
    print(e.localizedDescription)
}

NSString์— ๋‚ด๋ถ€ ํ•จ์ˆ˜๋กœ write๊ฐ€ ์กด์žฌํ•˜๋ฉฐ, ์ด๋ฅผ ์ด์šฉํ•ด์„œ ์“ฐ๊ธฐ๋ฅผ ํ•œ๋‹ค.

  • to: ์œ„์— ๋งŒ๋“  URL์„ ์จ์ฃผ๋ฉด ๊ทธ๋Œ€๋กœ ์ƒ์„ฑ.
  • atomically: ์ด๊ฑฐ ๋ญ”์ง€ ๋ชจ๋ฅด๊ฒ ,,, ์ฝ์–ด๋„ ๋ชจ๋ฅด๊ฒ ,,, ๋‹ค๋งŒ, true๋ฉด URL์ด ์กด์žฌ ์•ˆํ•ด๋„ ์‹œ์Šคํ…œ์ƒ ์†์ƒ์ด ์—†๋‹จ ๋‰˜์•™์Šค๋ฅผ ํ’๊ธฐ๋ฏ€๋กœ true๋งŒ,,
  • encoding: ์ธ์ฝ”๋”ฉ ์ข…๋ฅ˜!

์—ฌ๊ธฐ์„œ ์—๋Ÿฌ๊ฐ€ ๋‚ฌ๋˜๊ฒŒ ํŒŒ์ผ ๊ฒฝ๋กœ๊ฐ€ ๋ฏธ์กด์žฌ์‹œ โ€œThe file โ€œtest.txtโ€ doesnโ€™t exist.โ€ ๋ผ๊ณ  catch๋กœ ๊ฐ„๋‹ค.

test.txt๋Š” ํŒŒ์ผ์„ ์ด์ œ ๋งŒ๋“ค๋ผ๋Š”๊ฑด๋ฐ ์™œ ์—†๋‹ค๊ณ  ๊ทธ๋ž˜,,? ๋ผ๊ณ  ํ•œ์ฐธ ์ƒ๊ฐํ–ˆ๋Š”๋ฐ, ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธํ•˜๋Š๋ผ ๊ฒฝ๋กœ๋ฅผ ์ž˜๋ชป ์…‹ํŒ…ํ–ˆ์—ˆ๋‹ค.

ํ—ˆํ—ˆ,, ์ค‘๊ฐ„์— ๋‹ค๋ฅธ ๊ฒฝ๋กœ๊ฐ€ ์—†๋Š”๊ฑด๋ฐ, ์ €๋ ‡๊ฒŒ ์„ค๋ช…์„ ํ•˜๋‹ค๋‹ˆโ€ฆ ๊ทธ๋ž˜๋„ ๋ณด๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ ์ด๋ฃจ์–ด์ง„๋‹จ๊ฑธ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

์—ฌ๊ธฐ์„œ ๋˜ write ํ•  ๊ฒฝ์šฐ์—”, ๋ฎ์–ด ์”Œ์› ์—ˆ๋‹ค.

ํŒŒ์ผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐฉ๋ฒ•์€?

// 1. ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ - ๋™์ผ
let fileManager = FileManager.default

// 2. ๋„ํ๋จผํŠธ URL ๊ฐ€์ ธ์˜ค๊ธฐ - ๋™์ผ
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]

// 3. ํŒŒ์ผ์ด ์žˆ๋Š” Directory ์„ค์ •
let directoryURL = documentsURL.appendingPathComponent("NewDirectory")

// 4. ๋ถˆ๋Ÿฌ์˜ฌ ํŒŒ์ผ ์„ค์ •
let helloPath = directoryURL.appendingPathComponent("test.txt")

// Try Catch
do {
    // 5-1. ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
    let text = try String(contentsOf: helloPath, encoding: .utf8)
    print(text) // Hello world
} catch let e {
    // 5-2. ์—๋Ÿฌ์ฒ˜๋ฆฌ
    print(e.localizedDescription)
}

์ด๊ฑด ์“ฐ๊ธฐ๋ณด๋‹ค ๊ฐ„๋‹จ!

ํŒŒ์ผ ์‚ญ์ œํ•˜๋Š” ๋ฐฉ๋ฒ•์€?

// 1. ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ - ๋™์ผ
let fileManager = FileManager.default

// 2. ๋„ํ๋จผํŠธ URL ๊ฐ€์ ธ์˜ค๊ธฐ - ๋™์ผ
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]

// 3. ํŒŒ์ผ์ด ์žˆ๋Š” Directory ์„ค์ •
let directoryURL = documentsURL.appendingPathComponent("NewDirectory")

// 4. ์‚ญ์ œํ•  ํŒŒ์ผ ์„ค์ •
let helloPath = directoryURL.appendingPathComponent("test.txt")

// Try Catch
do {
    // 5-1. ์‚ญ์ œํ•˜๊ธฐ
    try fileManager.removeItem(at: fileURL)
} catch let e {
    // 5-2. ์—๋Ÿฌ์ฒ˜๋ฆฌ
    print(e.localizedDescription)
}

๊ด€๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” fileManager์—๊ฒŒ ์‹œ์ผœ์„œ ์‚ญ์ œ๋ฅผ ์‹œํ‚ค๋ฉด ๊น”๋”.

์ดํ›„ ๊ทธ ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์˜ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด Error๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

The file โ€œtest.txtโ€ couldnโ€™t be opened because there is no such file.

Q&A

Q . ํ…์ŠคํŠธ ๋ง๊ณ  ์ด๋ฏธ์ง€๋‚˜ GIF ๋“ฑ๋“ฑ ๋‹ค๋ฅธ๊ฑด ์–ด๋–ป๊ฒŒ??

A. NSData ํ˜•์‹์œผ๋กœ!

์•„๋ž˜์ฒ˜๋Ÿผํ•˜๋ฉด ์–ด๋–ค๊ฒƒ์ด๋“  Dataํ˜•์œผ๋กœ ์†์‰ฝ๊ฒŒ ๋ณ€๊ฒฝ๊ฐ€๋Šฅ!

// Archive Data
let archivedData = NSKeyedArchiver.archivedData(withRootObject: file)

// Unarchive Data
let unarchivedData = NSKeyedUnarchiver.unarchiveObject(with: file as Data)

Q. ์• ํ”Œ ๋‚ด์žฅ ์•ฑ์ค‘ File App ์ด๋ž‘ ๊ด€๋ จ์ด ์žˆ๋‚˜?

A. Yes, ํ”„๋กœ์ ํŠธ๋‚ด์— ์„ค์ •์„ ํ•˜๋ฉด, File App์ด ํ•ด๋‹น ์–ดํ”Œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค!