写真・ビデオの撮影日時や再生時間を取得する

# はじめに
カメラやフォトライブラリを使ったアプリを作ったときに、撮影日時と再生時間を取得する必要があって色々調べてみたので、書き残しておこうと思います。

UIImagePickerControllerを使用しているのですが、カメラで撮影した時と、フォトライブラリに保存されたものを選択した時とで取得方法が違ったので、少し苦戦しました。

とりあえず、これで目的は果たせているのですが、もっといい方法があるような気がしています。

# 環境：
Xcode 11.2.1
Swift 5.1.2

# カメラで撮影したとき
撮影後に現在時刻を取得しても大差ない気はするんですけど、なんか意地で。

## 写真の場合
撮影日時という訳ではなさそうですが、もっとも近い値かと。
簡単と思いきや、Exifの日付形式が意外な落とし穴。

````swift:ViewController.swift
import UIKit

class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    
    // 〜カメラ・フォトライブラリの呼び出しや終了については省略〜

    //　撮影または写真の選択が完了した時に呼ばれる
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]){

        var shootingDate: String = ""    // 撮影日時

        // メタデータからExifを取り出し、撮影日時を取得
        guard let metadata = info[.mediaMetadata] as? NSDictionary else { return }
        guard let exif = metadata.object(forKey: kCGImagePropertyExifDictionary) as? NSDictionary else { return }
        let dateTimeOriginal = exif.object(forKey: kCGImagePropertyExifDateTimeOriginal)
        shootingDate = String(describing: dateTimeOriginal)
        
        // 取得したデータはなぜか「0000:00:00 00:00:00」という形式なので、最初の2つだけ「:」を「-」に置き換え
        var count = 0
        while count < 2 {
            count += 1
            if let range = shootingDate.range(of: ":"){
                shootingDate.replaceSubrange(range, with:"-")
            }
        }
        print("shootingDate: \(shootingDate)")
    }
}

````



## ビデオの場合
ビデオはExifデータを持っていないので、AVURLAssetから撮影日時を取得しています。

````swift:ViewController.swift
import UIKit
import Photos    // AVURLAsset()やPHAssetを使うのに必要

class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    
    // 〜カメラ・フォトライブラリの呼び出しはここでは省略〜

    //　撮影または写真の選択が完了した時に呼ばれる
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]){

        var shootingDate: String = ""    // 撮影日時
        var duration: String = ""        // 再生時間

        // 日時の表示形式はお好みで
        let formatter = DateFormatter()
        formatter.timeZone = TimeZone.current
        formatter.locale = Locale.current
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

        // AVURLAssetを使ってファイルから撮影日時を取得
        guard let fileUrl = info[.mediaURL] as? URL else { return }
        let video = AVURLAsset(url: fileUrl)
        let creationDate = video.creationDate?.value as! Date
        shootingDate = formatter.string(from: creationDate)

        // ついでにビデオの再生時間も取得
        let durationTime = TimeInterval(round(Float(video.duration.value) / Float(video.duration.timescale)))
        duration = generateDuration(timeInterval: durationTime)

        print("shootingDate: \(shootingDate)")
        print("duration: \(duration)")
    }

    // ビデオの時間を「00:00」表示に変換して返す
    func generateDuration(timeInterval: TimeInterval) -> String {
        
        let min = Int(timeInterval / 60)
        let sec = Int(round(timeInterval.truncatingRemainder(dividingBy: 60)))
        let duration = String(format: "%02d:%02d", min, sec)
        return duration
    }
}
````


# フォトライブラリから選択したとき

info[.referenceURL]がDeprecateになって、代わりにinfo[.phAsset]を使うことになったのかな？
でも情報が見つけられなくて、意外と苦労した箇所（情弱の可能性）。

写真もビデオも同じ方法で取得できます。
写真の場合はdurationのくだりをカットしてください。

````swift:ViewController.swift
import UIKit
import Photos    // AVURLAsset()やPHAssetを使うのに必要

class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    
    // 〜カメラ・フォトライブラリの呼び出しはここでは省略〜

    //　撮影または写真の選択が完了した時に呼ばれる
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]){

        var shootingDate: String = ""    // 撮影日時
        var duration: String = ""        // 再生時間

        // 日時の表示形式はお好みで
        let formatter = DateFormatter()
        formatter.timeZone = TimeZone.current
        formatter.locale = Locale.current
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

        guard let asset = info[.phAsset] as? PHAsset else { return }
        shootingDate = formatter.string(from: asset.creationDate!)
        duration = generateDuration(timeInterval: asset.duration)

        print("shootingDate: \(shootingDate)")
        print("duration: \(duration)")
    }

    // ビデオの時間を「00:00」表示に変換して返す
    func generateDuration(timeInterval: TimeInterval) -> String {
        
        let min = Int(timeInterval / 60)
        let sec = Int(round(timeInterval.truncatingRemainder(dividingBy: 60)))
        let duration = String(format: "%02d:%02d", min, sec)
        return duration
    }
}
````

# 参考にしたURL
[Swiftで画像のExifデータから作成日時の情報を得る
](https://qiita.com/kojiro_ueda/items/e03ed67d85a838706052)

[【Swift】動画撮影＆保存＆取得【UIImagePickerController】](https://hatsunem.hatenablog.com/entry/2018/12/04/004823)

[AVURLAsset - AVFoundation | Apple Developer Documentation](https://developer.apple.com/documentation/avfoundation/avurlasset)

[AVAsset - AVFoundation | Apple Developer Documentation](https://developer.apple.com/documentation/avfoundation/avasset)

# 最後に
至らない点や無駄な記述など、お気付きの点はご指摘いただけるとありがたいです！