Tech Blog

グローバルな家族アプリFammを運営するTimers inc (タイマーズ) の公式Tech Blogです。弊社のエンジニアリングを支える記事を随時公開。エンジニア絶賛採用中!→ https://timers-inc.com/engineering

iOSでファイルアップロード時のメモリー節約Tips #ios #swift #firebase

f:id:fromkk:20200318155411p:plain

こんにちは。かっくん(@fromkk)です。
前に書いた記事が最後かと思った?残念でした🤪

Fammでは大量の写真・動画(最大写真50枚、動画3本)を一度にアップロードすることが可能ですが、そこで気になるのがメモリーの使用量です。
一度に大量の写真のデータをメモリーに格納してしまうとすぐにメモリーが枯渇してしまうのでどうにかしてメモリーの使用量を抑える必要があります。
現在のFammでどのように節約しながらファイル・動画をアップロードをしているかを紹介します。

グラフで比較してみる

モリーを節約しないでアップロードしたものがこちら。
右肩あがりにメモリーの使用量が増えていることが分かります。

f:id:fromkk:20200318145724p:plain

改善してみた場合のメモリー使用量がこちら。
右肩上がりに増えずに安定していることが分かります。

f:id:fromkk:20200318150419p:plain

どう対応したのか

今回はFirebase Cloud Storageを利用することを想定しています。
改善対応前のコードはこのような感じでした。(色々省略しています)

import UIKit
import FirebaseStorage

let localURL: URL = ...
let storagePath: String = ...

let data = try Data(contentsOf: localURL)
let storage = Storage.storage()
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
let reference = storage.reference(withPath: storagePath)
reference.putData(data, metadata: metadata) { _, _ in  }

改善後のコードはこのような感じになりました。

import UIKit
import FirebaseStorage

let localURL: URL = ...
let storagePath: String = ...

let storage = Storage.storage()
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
let reference = storage.reference(withPath: storagePath)
reference.putFile(from: localURL, metadata: metadata) { _, _ in  }

diffはこんな感じです。

import UIKit
import FirebaseStorage

let localURL: URL = ...
let storagePath: String = ...

- let data = try Data(contentsOf: localURL)
let storage = Storage.storage()
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
let reference = storage.reference(withPath: storagePath)
- reference.putData(data, metadata: metadata) { _, _ in  }
+ reference.putFile(from: localURL, metadata: metadata) { _, _ in  }

改善前は Data を生成してからそれを storage.putData に渡していましたが、ローカルに保存したURLをそのまま storage.putFile に渡すように変更しました。
ここの Data インスタンスを生成する処理がかなりメモリーを消費していたことがわかったのでこのような形に変更しました。
URLSession を利用する場合でも session.uploadTask(with: request, fromFile: localURL) を利用すれば同様のことが可能です。

まとめ

一つや二つ程度のファイルをアップロードする場合には Data 型を利用する方法でも問題無いかと思います。
ただ、50件以上をまとめて処理しようとするとメモリーの消費量に大きな差が出ることが分かりました。
モリーの容量は端末によってもサイズが異なりますし、多量のメモリーを消費することはユーザー体験を損ねる可能性につながります。
最近の端末は大量のメモリーを積んでるとはいえ、限りある資源は節約しながら良いユーザー体験を作っていきたいですね。

積極採用中!!

子育て家族アプリFammを運営するTimers inc.では、現在エンジニアを積極採用中!
急成長中のサービスの技術の話を少しでも聞いてみたい方、スタートアップで働きたい方など、是非お気軽にご連絡ください!
採用HP: http://timers-inc.com/engineerings

Timersでは各職種を積極採用中!

急成長スタートアップで、最高のものづくりをしよう。

募集の詳細をみる