Skip to content

Commit d818aa3

Browse files
committed
WIP Add SwiftUI Quickstart for Storage
1 parent 5694c29 commit d818aa3

File tree

9 files changed

+1084
-11
lines changed

9 files changed

+1084
-11
lines changed

storage/Podfile

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# StorageExample
22

33
use_frameworks!
4-
platform :ios, '10.0'
4+
platform :ios, '14.4'
55

66
pod 'Firebase/Analytics'
77
pod 'Firebase/Auth'
@@ -12,5 +12,8 @@ end
1212
target 'StorageExampleSwift' do
1313
pod 'FirebaseStorageSwift', "~> 7.0-beta"
1414
end
15+
target 'StorageExampleSwiftUI' do
16+
pod 'FirebaseStorageSwift', "~> 7.0-beta"
17+
end
1518
target 'StorageExampleTests' do
1619
end

storage/Podfile.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,6 @@ SPEC CHECKSUMS:
119119
nanopb: 59221d7f958fb711001e6a449489542d92ae113e
120120
PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97
121121

122-
PODFILE CHECKSUM: 68e67a7f5b716247bf88b244cb2b3cc55d2e53ec
122+
PODFILE CHECKSUM: d7464278d730121dc09bc1404e6cbe6af4459ccc
123123

124124
COCOAPODS: 1.10.1

storage/StorageExample.xcodeproj/project.pbxproj

+774-9
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//
2+
// ImagePicker.swift
3+
// StorageExampleSwiftUI
4+
//
5+
// Created by Ben Reed on 3/25/21.
6+
// Copyright © 2021 Google Inc. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
import Firebase
11+
12+
struct ImagePickerRepresentable {
13+
enum Source {
14+
case camera
15+
case photoLibrary
16+
}
17+
18+
var source: Source
19+
@ObservedObject var store: ImageStore
20+
@Binding var visible: Bool
21+
var completion: () -> Void
22+
23+
class Coordinator: NSObject {
24+
private var representable: ImagePickerRepresentable
25+
private var store: ImageStore
26+
27+
init(representable: ImagePickerRepresentable, store: ImageStore) {
28+
self.representable = representable
29+
self.store = store
30+
}
31+
}
32+
}
33+
34+
extension ImagePickerRepresentable.Coordinator: UIImagePickerControllerDelegate {
35+
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
36+
guard let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }
37+
38+
let imagePath = "\(Auth.auth().currentUser!.uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000)).jpg"
39+
self.store.uploadImage(image, atPath: imagePath)
40+
UserDefaults.standard.setValue(imagePath, forKey: self.store.imagePathKey)
41+
42+
self.store.image = image
43+
self.representable.visible = false
44+
picker.dismiss(animated: true, completion: self.representable.completion)
45+
}
46+
47+
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
48+
self.representable.visible = false
49+
picker.dismiss(animated: true, completion: self.representable.completion)
50+
}
51+
}
52+
53+
extension ImagePickerRepresentable.Coordinator: UINavigationControllerDelegate { }
54+
55+
extension ImagePickerRepresentable: UIViewControllerRepresentable {
56+
typealias UIViewControllerType = UIImagePickerController
57+
58+
func makeCoordinator() -> Coordinator {
59+
Coordinator(representable: self, store: self.store)
60+
}
61+
62+
func makeUIViewController(context: Context) -> UIImagePickerController {
63+
let imagePicker = UIImagePickerController()
64+
65+
if self.source == .camera {
66+
imagePicker.sourceType = .camera
67+
imagePicker.cameraCaptureMode = .photo
68+
} else {
69+
imagePicker.sourceType = .photoLibrary
70+
}
71+
72+
imagePicker.delegate = context.coordinator
73+
return imagePicker
74+
}
75+
76+
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) { }
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//
2+
// ImageStore.swift
3+
// StorageExampleSwiftUI
4+
//
5+
// Created by Ben Reed on 3/25/21.
6+
// Copyright © 2021 Google Inc. All rights reserved.
7+
//
8+
9+
import Firebase
10+
import FirebaseStorageSwift
11+
12+
public class ImageStore: ObservableObject {
13+
private var storage: Storage
14+
15+
public var compressionQuality: CGFloat = 0.8
16+
17+
public let imagePathKey = "imagePath"
18+
19+
@Published var image: UIImage?
20+
21+
lazy var localImageFileDirectory: String = {
22+
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
23+
let documentsDirectory = paths[0]
24+
return "file:\(documentsDirectory)"
25+
}()
26+
27+
public init(withStorage storage: Storage) {
28+
self.storage = storage
29+
}
30+
31+
public func uploadImage(_ image: UIImage, atPath imagePath: String) {
32+
guard let imageData = image.jpegData(compressionQuality: compressionQuality) else { return }
33+
34+
let imageMetadata = StorageMetadata()
35+
imageMetadata.contentType = "image/jpeg"
36+
37+
let storageRef = storage.reference(withPath: imagePath)
38+
storageRef.putData(imageData, metadata: imageMetadata) { result in
39+
switch result {
40+
case .success:
41+
break
42+
case let .failure(error):
43+
_ = error
44+
break
45+
}
46+
}
47+
}
48+
49+
public func downloadImage(atPath imagePath: String) {
50+
guard let imageURL = URL(string: "\(self.localImageFileDirectory)/\(imagePath)") else { return }
51+
self.storage.reference().child(imagePath).write(toFile: imageURL) { result in
52+
switch result {
53+
case let .success(downloadedFileURL):
54+
self.image = UIImage(contentsOfFile: downloadedFileURL.path)
55+
case let .failure(error):
56+
print("Error downloading: \(error)")
57+
}
58+
}
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// ImageView.swift
3+
// StorageExampleSwiftUI
4+
//
5+
// Created by Ben Reed on 2/10/21.
6+
// Copyright © 2021 Google Inc. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
import Firebase
11+
12+
struct ImageView: View {
13+
@StateObject private var photoStore = ImageStore(withStorage: Storage.storage())
14+
15+
@State var isSelectingImage = false
16+
@State var isTakingPhoto = false
17+
@State var showUploadMenu = false
18+
19+
var body: some View {
20+
NavigationView {
21+
VStack {
22+
Image(uiImage: photoStore.image ?? UIImage())
23+
.resizable()
24+
.aspectRatio(contentMode: .fit)
25+
}
26+
.navigationTitle("Firebase Storage")
27+
.toolbar {
28+
ToolbarItemGroup(placement: .bottomBar) {
29+
if !showUploadMenu {
30+
Button("Upload") {
31+
showUploadMenu = true
32+
}
33+
Spacer()
34+
Button("Download") {
35+
self.downloadImage()
36+
}
37+
} else {
38+
Button("") {
39+
showUploadMenu = false
40+
}
41+
42+
Spacer()
43+
44+
Button("Take Photo") {
45+
isTakingPhoto = true
46+
}.sheet(isPresented: $isTakingPhoto) {
47+
ImagePickerRepresentable(source: .camera, store: photoStore, visible: $isTakingPhoto, completion: {
48+
showUploadMenu = false
49+
})
50+
}.disabled(!UIImagePickerController.isSourceTypeAvailable(.camera))
51+
52+
Button("Select Image") {
53+
isSelectingImage = true
54+
}.sheet(isPresented: $isSelectingImage) {
55+
ImagePickerRepresentable(source: .photoLibrary, store: photoStore, visible: $isSelectingImage, completion: {
56+
showUploadMenu = false
57+
})
58+
}
59+
}
60+
}
61+
}
62+
}.onAppear {
63+
downloadImage()
64+
}
65+
}
66+
67+
func downloadImage() {
68+
UserDefaults.standard.synchronize()
69+
guard let imagePath = UserDefaults.standard.string(forKey: self.photoStore.imagePathKey) else { return }
70+
self.photoStore.downloadImage(atPath: imagePath)
71+
}
72+
}
73+
74+
struct ContentView_Previews: PreviewProvider {
75+
static var previews: some View {
76+
ImageView()
77+
}
78+
}
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleDevelopmentRegion</key>
6+
<string>$(DEVELOPMENT_LANGUAGE)</string>
7+
<key>CFBundleExecutable</key>
8+
<string>$(EXECUTABLE_NAME)</string>
9+
<key>CFBundleIdentifier</key>
10+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11+
<key>CFBundleInfoDictionaryVersion</key>
12+
<string>6.0</string>
13+
<key>CFBundleName</key>
14+
<string>$(PRODUCT_NAME)</string>
15+
<key>CFBundlePackageType</key>
16+
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
17+
<key>CFBundleShortVersionString</key>
18+
<string>1.0</string>
19+
<key>CFBundleVersion</key>
20+
<string>1</string>
21+
<key>LSRequiresIPhoneOS</key>
22+
<true/>
23+
<key>NSCameraUsageDescription</key>
24+
<string>Take and upload photos to cloud storage</string>
25+
<key>NSPhotoLibraryUsageDescription</key>
26+
<string>Upload photos to cloud storage</string>
27+
<key>UIApplicationSceneManifest</key>
28+
<dict>
29+
<key>UIApplicationSupportsMultipleScenes</key>
30+
<true/>
31+
</dict>
32+
<key>UIApplicationSupportsIndirectInputEvents</key>
33+
<true/>
34+
<key>UILaunchScreen</key>
35+
<dict/>
36+
<key>UIRequiredDeviceCapabilities</key>
37+
<array>
38+
<string>armv7</string>
39+
</array>
40+
<key>UISupportedInterfaceOrientations</key>
41+
<array>
42+
<string>UIInterfaceOrientationPortrait</string>
43+
<string>UIInterfaceOrientationLandscapeLeft</string>
44+
<string>UIInterfaceOrientationLandscapeRight</string>
45+
</array>
46+
<key>UISupportedInterfaceOrientations~ipad</key>
47+
<array>
48+
<string>UIInterfaceOrientationPortrait</string>
49+
<string>UIInterfaceOrientationPortraitUpsideDown</string>
50+
<string>UIInterfaceOrientationLandscapeLeft</string>
51+
<string>UIInterfaceOrientationLandscapeRight</string>
52+
</array>
53+
</dict>
54+
</plist>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//
2+
// StorageExampleSwiftUIApp.swift
3+
// StorageExampleSwiftUI
4+
//
5+
// Created by Ben Reed on 2/10/21.
6+
// Copyright © 2021 Google Inc. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
import Firebase
11+
12+
@main
13+
struct StorageExampleSwiftUIApp: App {
14+
init() {
15+
FirebaseApp.configure()
16+
if Auth.auth().currentUser == nil {
17+
Auth.auth().signInAnonymously(completion: { (authResult, error) in
18+
if let error = error {
19+
print("Failed to sign in: \(error.localizedDescription)")
20+
}
21+
})
22+
}
23+
}
24+
25+
var body: some Scene {
26+
WindowGroup {
27+
ImageView()
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)