TIL_20180723 ~ 20180730
23 Jul 2018 | TIL2018.07.23
- ํ
์ด๋ธ๋ทฐ๋ฅผ reloadํ ๋์
async
๋ฌธ์ private func resetTableView(indexPaths: [IndexPath]) { DispatchQueue.main.sync { [weak self] in // async๋ก ํ๋ฉด ์๋ฌ๋จ self?.tableView.beginUpdates() self?.tableView.insertRows(at: indexPaths, with: .automatic) self?.tableView.endUpdates() } }
- beginUpdates()์ endUpdates() ์ฌ์ด์ ์์ ์์๋ ๋ชจ๋ธ์ด ๋ณ๊ฒฝ๋๋ฉด ์๋จ.
- ํ
์ด๋ธ๋ทฐ ๋ฐ์ดํฐ์์ค๋ reload๋ insert๋ฅผ ํ ๋ ๋ณ๊ฒฝ์ด ํ์ํ ๋ถ๋ถ(ํ
์ด๋ธ ๋ทฐ ๋ด์ ํน์ ์น์
์ด๋ ์
)์ ๋ด๋นํ๋ ๋ชจ๋ธ์ด ๊ฐ์ ์์ธ์ง ๋ด๋ถ์ ์ผ๋ก ํ์ธํ๋ ๊ณผ์ ์ ๊ฑฐ์น๋ค.
- ์ด๋ ๋ณ๊ฒฝ๋์์ด ํ์์๋ ๊ณณ์๋ ๋์์ ํ์ง ์๊ณ ๋ญ๋น๋ฅผ ๋ง์ผ๋ ค๊ณ ์ด๋ ๊ฒ ๋์ํจ.
- ์์ ์ฝ๋์์ ์์ ์ฑ์ด ์ฃฝ๋ ์๋ฌ ๋ฐ์
insertSection/insertRows
, ํน์reloadSection/rows
๋ก ์ธํด์ ๋ณ๊ฒฝ๋ ํ ์ด๋ธ๋ทฐ์ ๋ฐ์ดํฐ ์๊ฐ ๋ณ๊ฒฝ ์ ๊ณผ ๋ค๋ฅด๋ค๋ ์๋ฌ.Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the table view after the update (0) must be equal to the number of sections contained in the table view before the update (2), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).
- ์๋ฌ์๋ฃ ๋งํฌ 1- Error โInvalid update: invalid number of rows in section 0โ attempting to delete row in table
- ์๋ฌ์๋ฃ ๋งํฌ 2 - Insert rows to UITableView crash
- ํด๊ฒฐ:
- ํ์ธํด๋ณด๋ insertRows์ ์ ๋ฌ๋๋ IndexPath๋ ๋ชจ๋ ์ ๋ง๋ค์ด์ ธ ์ ๋ฌ๋์๋ค.
- ํ์ง๋ง update๋ฅผ ์์ํ๊ณ insertํ๊ณ update๊ฐ ๋๋๋ ๋์์ด async๋ก ์๋ํ์ฌ ๋ชจ๋ธ ๋ณ๊ฒฝ์ด ์ธ์ ๋๋์ง ๋ชจ๋ฅด๋ ๋ฌธ์ ๊ฐ ์์๋ค.
- URLSession์ผ๋ก ๋ชจ๋ธ ์ ๋ฐ์ดํธ(async) - 0๊ฐ์ ๋ฐ์ดํฐ๋ก ํ ์ด๋ธ๋ทฐ ๊ทธ๋ฆผ - ๋ชจ๋ธ ์ ๋ฐ์ดํธ(async) noti๋ก ํ ์ด๋ธ๋ทฐ ์ ๋ฐ์ดํธ ์์ - ๋ค๋ฅธ ๋ชจ๋ธ๋ ์ ๋ฐ์ดํธ ๋๋ ์ค
- ํด๋น ๋์์ asyncํ๊ฒ ๋์ํ๋ฉด, URLSession์์ ๋ชจ๋ธ์ ๋ฐ์์ค๋๊ฒ ์ ๋ถ ์๋ฃ๋์ง ์๊ณ ๋ค๋ฅธ ์ค๋ ๋์์ ๋ค๋ฅธ ์น์ ์ ๋ด๋นํ๋ ๋ชจ๋ธ์ด ์ ๋ฐ์ดํธ๋๊ณ ์๋ ์ํ์์ ๋ค๋ฅธ ์น์ ์ ์ ๋ฐ์ดํธ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ์์ฒญํ๊ณ , ํ ์ด๋ธ๋ทฐ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ์ ๋ฐ์ดํธํ๋ ค๊ณ ํ๋๊น ์ ๋ฐ์ดํธ ์ ํ์ ๋ฐ์ดํฐ ์๊ฐ ์ผ์นํ์ง ์๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. (1์น์ ์ ํ ์ด๋ธ๋ทฐ๋ฅผ ์ ๋ฐ์ดํธํ๊ณ ์๋๋ฐ 2์ ๋ฐ์ดํฐ๊ฐ ๋ง๋ค์ด์ ธ์ ๋ 2 ์น์ ์ ์ ๋ฐ์ดํธํ๋ผ๊ณ noti๊ฐ ์ค๋ ์ํฉ)
beginUpdates()
์endUpdates()
์ฌ์ด์์ ๋ชจ๋ธ๊ณผ ํ ์ด๋ธ๋ทฐ๊ฐ ๊ฐ์ง ๋ฐ์ดํฐ์ ์๊ฐ ๊ฐ์์ผํ๋๊ฒ ํฌ์ธํธ! (์ ๋ฐ์ดํธ ์ ์ ๋ฐ์ดํฐ๊ฐ 0๊ฐ์ด๋ฉด ํ ์ด๋ธ๋ทฐ์ rows๋ 0, ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋์ 3๊ฐ๊ฐ ๋๋ฉด rows๋ 3๊ฐ์ฌ์ผํ๋ฉฐ, ์๋ฌ๋ฉ์์ง์์๋ ์ธ๊ธํ๊ณ ์๋ ๋ด์ฉ์ด๋ค.)- ๋ฐ๋ผ์ ๋ชจ๋ธ์ ๋น๋๊ธฐ๋ก ์ ๋ฐ์ดํธ๋๋ค๊ณ ํ๋๋ผ๋, ํ ์ด๋ธ๋ทฐ ์ ๋ฐ์ดํธ๋ syncํ๊ฒ ๋์ํ๋๋ก ํ์ฌ ๋ฉ์ธ์ค๋ ๋์์ ์ง๋ ฌ์ ์ผ๋ก(์ฐจ๋ก๋๋ก) ์ ๋ฐ์ดํธ๋๊ฒํ์ฌ ๋ชจ๋ธ์ ๋ฐ์ดํธ์ ํ ์ด๋ธ๋ทฐ ์ ๋ฐ์ดํธ์ ํ์ด๋ฐ์ ๋ง์ถ๋ค!
2018.07.24
-
์ฝ์ด๋ณด๊ธฐ- tableview editing
-
debug
// DEBUG Path1: /Users/jeonmijin/Library/Developer/CoreSimulator/Devices/DE1DE4FA-2208-4062-8C55-0673E3019F6C/data/Containers/Data/Application/34D0E5CC-B577-49E2-913A-82DDBA91CB59/tmp/CFNetworkDownload_nEtNzv.tmp Path2: /Users/jeonmijin/Library/Developer/CoreSimulator/Devices/DE1DE4FA-2208-4062-8C55-0673E3019F6C/data/Containers/Data/Application/34D0E5CC-B577-49E2-913A-82DDBA91CB59/Library/Caches/fdb0d5fcfb86e332505785225a6d9ade.jpg ์์ดํ ์ด๋ฏธ์ง: https://cdn.bmf.kr/_data/product/HBBCC/fdb0d5fcfb86e332505785225a6d9ade.jpg
2018.07.25
downloadTask() ๋์๋ฐฉ์
func downloadTask(with url: URL, completionHandler: @escaping (URL?, URLResponse?, Error?) -> Void) -> URLSessionDownloadTask
- ์ด๋ ๊ฒ URLSession์์ downloadTask๋ง๋ค๋์, ์ผ๋ฐ dataTask๋์ ๋ค๋ฅด๊ฒ ์ปดํ๋ฆฌ์ ํธ๋ค๋ฌ์์ data๊ฐ ์๋ location(URL)์ ๋ฐ๋๋ฐ, ์ ๋ ์ด task๋ฅผ resume()ํ์๋ ์ด๋ป๊ฒ ๋ค์ด๋ก๋๊ฐ ๋๊ฑด์ง ๊ถ๊ธํฉ๋๋น. ์ปดํ๋ฆฌ์ ํธ๋ค๋ฌ๋ฅผ ๊ฑฐ์น๋ฉด์ ์ด๋ฏธ ๋ด๋ถ์ ์ผ๋ก ๋ค์ด๋ก๋๋ location์ ๋๊ฑฐ๊ณ location์ด ๋ค์ด๋ก๋ ๋ฐ์ ์์ํ์ผ์ด ์ ์ฅ๋ ๊ฒฝ๋ก์ธ๊ฑด๊ฐ์? ๊ทธ๋ฌ๋ฉด data๋ก ๋ฐ๋ก ์ ๊ทผํ๋๊ฒ ์๋๋ผ location์ธ ์์๊ฒฝ๋ก๋ก ๊ฐ์ ๋ค์ด๋ฐ์์ง ์ด๋ฏธ์ง ํ์ผ์ ๊ฐ์ ธ์์ผํ๋๊ฑด๊ฐ์?
- Answer: ํ์ผ๋ก ์ ์ฅ๋๊ณ ์์น๊ฐ ๋์ด์ค๋ฉฐ, ํด๋น ์์น์๋ ์ด๋ฏธ url๋ก๋ถํฐ ๋ค์ด๋ฐ์์ง ์ด๋ฏธ์งํ์ผ์ด ์กด์ฌํจ.
- ์์๊ฒฝ๋ก์๋
/Users/jeonmijin/Library/Developer/CoreSimulator/Devices/DE1DE4FA-2208-4062-8C55-0673E3019F6C/data/Containers/Data/Application/34D0E5CC-B577-49E2-913A-82DDBA91CB59/tmp/CFNetworkDownload_nEtNzv.tmp
์ด๋ฐ์์ผ๋ก ์์ ํ์ผ์ด ์ ์ฅ๋จ
// ์์ ํ์ผ location์ผ๋ก ๋ฐ๋ก ์ด๋ฏธ์ง ๊ฐ์ ธ์๋ด
func test() {
let url = URL(string: "https://cdn.bmf.kr/_data/product/HCCFE/757878b14ee5a8d5af905c154fc38f01.jpg")!
URLSession.shared.downloadTask(with: url) { (location, response, error) in
if let error = error {
print("\(error)")
}
if let location = location {
let img = UIImage(contentsOfFile: location.path)
DispatchQueue.main.sync {
self.view.addSubview(UIImageView(image: img))
}
}
}.resume()
}
์บ์ํด๋๋ก ํ์ผ ์ฎ๊ธฐ๊ธฐ
- downloadTask์ ์ปดํ๋ฆฌ์ ํธ๋ค๋ฌ์์ location - tmp์ ์ฅ
- tmp path์์ cache path๋ก move
- cache์ jpgํ์ผ ์ ์ฅํ ๊ฒฝ๋ก๋ฅผ UIImage(contentsOf:)๋ก ๋๊น
- cache ํ์ผ ์์ ์๋๊ฑฐ ์์ง์ฐ๋ฉด moveError๋๋๋ท!?
do {
try fileManager.moveItem(at: tmpLocation, to: imageSavingPath)
let imageData = try? Data(contentsOf: imageSavingPath)
handler(imageData)
} catch {
if FileManager().fileExists(atPath: imageSavingPath.path) {
let imageData = try? Data(contentsOf: imageSavingPath)
handler(imageData)
} else { print("MOVE Error!") }
class ImageSetter {
class func download(with url: String, handler: @escaping((Data?) -> Void)) {
let fileManager = FileManager.default
let cacheURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
let imageSavingPath = cacheURL.appendingPathComponent(URL(string: url)!.lastPathComponent)
URLSession.shared.downloadTask(with: URL(string: url)!) { (tmpLocation, response, error) in
if let error = error {
print("Image download Error log: \(error)\n")
}
if let response = response as? HTTPURLResponse, response.statusCode == 200, let tmpLocation = tmpLocation {
do {
try fileManager.moveItem(at: tmpLocation, to: imageSavingPath)
let sss = try? fileManager.contentsOfDirectory(atPath: imageSavingPath.path)
print(sss) // nil - ํ์ผ๋ช
๊น์ง์ ๊ฒฝ๋ก๋ผ nil
let fff = try? fileManager.contentsOfDirectory(atPath: cacheURL.path)
print(fff)
let ddd = fileManager.contents(atPath: imageSavingPath.path)
print(ddd) // ์ ์ฅ๋ ์ด๋ฏธ์ง์ data์์ฒด ํ๋ฆฐํธ๋จ
let imageData = try? Data(contentsOf: imageSavingPath)
handler(imageData)
} catch {
if FileManager().fileExists(atPath: imageSavingPath.path) {
let imageData = try? Data(contentsOf: imageSavingPath)
handler(imageData)
} else { print("MOVE Error!") }
}
}
}.resume()
}
}
์ด๋ฏธ์ง ์ค์
private func setItemImage(imageURL: String) {
ImageSetter.download(with: imageURL, handler: { imageData in
DispatchQueue.main.sync { [weak self] in
// self.itemImage.image = UIImage(contentsOfFile: imagePath)
guard let data = imageData else { return }
self?.itemImage.image = UIImage(data: data)
}
})
}
- self.imageView?.image = UIImage(contentsOfFile: imagePath) > ๋ฐ๋ก ์๋๊ณ ์ ์ ๋ค์ ์ฌ์ฌ์ฉํ ๋ ๊ทธ๋ฆผ ๋ธ
- self.itemImage.image = UIImage(contentsOfFile: imagePath)
- 1,2์ ์ฐจ์ด: ์ ์ด ์ฒ์ ์ฌ์ฌ์ฉ๋ ๋ UIImageView ํ๋กํผํฐ ์์ฒด๋ ์คํ ๋ฆฌ๋ณด๋์์ ๋ง๋ค์ด์ก๊ธฐ๋๋ฌธ์, ์คํ ๋ฆฌ๋ณด๋๋๋ก ์ฒ์์ ์ธํ ๋๊ณ ๋ฐ์ดํ์์ค๊ฐ์ฒด์ ์ํด์ ์์ฑ์ด ํ ๋น๋ ๋๊ฐ ๋์์ผ ์ด๋ฏธ์ง๊ฐ ์ธํ ๋จ(dequeํ ๋)
2018.07.26
- ๋ฐ๋๋ฝ ์ํฉ ๋ฐ๊ฒฌ!
- main queue์์ ๋ main.sync๋ก ๋์๊ฐ๋ ์ฝ๋๋ธ๋ญ์ ํธ์ถ
- setItemImage() ํจ์๋ ImageSetter์ download()๋ฅผ ํธ์ถํ๋ฉฐ, ์ธ์๋ก url๊ณผ ํจ๊ป ์ปดํ๋ฆฌ์ ํธ๋ค๋ฌ๋ฅผ ๋๊ธด๋ค. (main queue์์ ์คํ)
- ํด๋น ์ปดํ๋ฆฌ์
ํธ๋ค๋ฌ์ ์ฝ๋ ๋ธ๋ญ์ ํธ์์
A๋ธ๋ญ
์ด๋ผ๊ณ ์นญํ๋ค. - ์ด๋
A๋ธ๋ญ
์ ๊ด๋ จ worker๋ ๋ค์๊ณผ ๊ฐ๋ค.Assignor : setItemImage()
,Assignee : download()
- download()๋ ํจ์๋ฅผ ๋น ์ ธ๋์ค๋ ์์ ์
A๋ธ๋ญ
์ ์คํํ๋ค. - ์ด๋์
A๋ธ๋ญ
์ worker๋Assignor : download()
,Assignee : setItemImage()
- ๋ worker๊ฐ ๋ชจ๋ main queue์์ sync๋ก ๋์ํ๊ธฐ๋๋ฌธ์, ์๋ก๊ฐ ๊ต์ฐจํ์ฌ ์๋ก๋ฅผ ๊ฐ๋ฆฌํค๊ณ ์๊ณ ์๋ก ์ผ์ด ๋๋๊ธฐ๋ง์ ๊ธฐ๋ค๋ฆฌ๋ ๋ฐ๋๋ฝ ์ํฉ์ด ๋ฐ์.
- handler์ ์ฝ๋๋ฅผ async๋ก ๋ณ๊ฒฝํ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
- ๋ฌธ์ ์ฝ๋
// TableViewCell.swift private func setItemImage(imageURL: String) { // 1. ImageSetter์ download()ํธ์ถ ImageSetter.download(with: imageURL, handler: { imageData in // 3. download()ํจ์๊ฐ ์ข ๋ฃ๋๋ฉด์ ํธ๋ค๋ฌ๊ฐ์คํ๋จ. ๊ฐ์ main queue๋ผ์ ๋ฌธ์ ๋ฐ์ DispatchQueue.main.sync { [weak self] in guard let data = imageData else { return } self?.itemImage.image = UIImage(data: data) } }) }
// TableViewCell์์ ImageSetter์ download๊ฐ ํธ์ถ
class func download(with url: String, handler: @escaping((Data) -> Void)) {
let cacheURL = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
let imageSavingPath = cacheURL.appendingPathComponent(URL(string: url)!.lastPathComponent)
// 2. ์๋ ์ค ์ฝ๋์ existFile์ด ์ฒดํฌ๋๊ณ handler๊ฐ ์คํ๋จ
if let imageData = existFile(at: imageSavingPath) {
handler(imageData)
} else {
URLSession.shared.downloadTask(with: URL(string: url)!) { (tmpLocation, response, error) in
// do something...
}.resume()
}
}
2018.07.27
- ์คํ ์ด์ฑ 7๋จ๊ณ ์งํ
- UINavigationController ๋ธ๋ฆฌ๊ฒ์ดํธ ๊ณต๋ถ (์์ธํ๋ฉด์์ ์ผ์ด๋๋ ์ก์ ์ ๋ฐ๋ผ ๋ธ๋ฆฌ๊ฒ์ดํธ ๋ฉ์๋ ์ฐ๊ฒฐ)
- ์คํฌ๋กค๋ทฐ ๊ณต๋ถ
- ์ํ ์์ธ ์ ๋ณด ๋ฐ์์ค๋ ๋ชจ๋ธ ๊ฐ์ฒด ์ค๊ณ
- HTTP & Network Basic ์ฑ ์ฝ์ด๋ณด๊ธฐ
2018.07.28
- ๋ถ์คํธ์ฝ์ค ๋ ์จ์ฑ ํ๋ฉด ๊ตฌํ ๋ฏธ์
- NSDataAsset: ์์ ์นดํ๋ก๊ทธ์์ ์๋ฃ๋ฅผ ๊ฐ์ ธ์ฌ๋ ์ฌ์ฉํ๋ ๋ฐ์ดํฐํ์
Comments