Skip to content

Commit

Permalink
WIP Add SwiftUI Quickstart for Storage
Browse files Browse the repository at this point in the history
  • Loading branch information
codeblooded committed Mar 26, 2021
1 parent 5694c29 commit ac7f271
Show file tree
Hide file tree
Showing 9 changed files with 1,085 additions and 11 deletions.
5 changes: 4 additions & 1 deletion storage/Podfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# StorageExample

use_frameworks!
platform :ios, '10.0'
platform :ios, '14.4'

pod 'Firebase/Analytics'
pod 'Firebase/Auth'
Expand All @@ -12,5 +12,8 @@ end
target 'StorageExampleSwift' do
pod 'FirebaseStorageSwift', "~> 7.0-beta"
end
target 'StorageExampleSwiftUI' do
pod 'FirebaseStorageSwift', "~> 7.0-beta"
end
target 'StorageExampleTests' do
end
2 changes: 1 addition & 1 deletion storage/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,6 @@ SPEC CHECKSUMS:
nanopb: 59221d7f958fb711001e6a449489542d92ae113e
PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97

PODFILE CHECKSUM: 68e67a7f5b716247bf88b244cb2b3cc55d2e53ec
PODFILE CHECKSUM: d7464278d730121dc09bc1404e6cbe6af4459ccc

COCOAPODS: 1.10.1
783 changes: 774 additions & 9 deletions storage/StorageExample.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

78 changes: 78 additions & 0 deletions storage/StorageExampleSwiftUI/ImagePicker.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// ImagePicker.swift
// StorageExampleSwiftUI
//
// Created by Ben Reed on 3/25/21.
// Copyright © 2021 Google Inc. All rights reserved.
//

import SwiftUI
import Firebase

struct ImagePickerRepresentable {
enum Source {
case camera
case photoLibrary
}

var source: Source
@ObservedObject var store: ImageStore
@Binding var visible: Bool
var completion: () -> Void

class Coordinator: NSObject {
private var representable: ImagePickerRepresentable
private var store: ImageStore

init(representable: ImagePickerRepresentable, store: ImageStore) {
self.representable = representable
self.store = store
}
}
}

extension ImagePickerRepresentable.Coordinator: UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }

let imagePath = "\(Auth.auth().currentUser!.uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000)).jpg"
self.store.uploadImage(image, atPath: imagePath)
UserDefaults.standard.setValue(imagePath, forKey: self.store.imagePathKey)

self.store.image = image
self.representable.visible = false
picker.dismiss(animated: true, completion: self.representable.completion)
}

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.representable.visible = false
picker.dismiss(animated: true, completion: self.representable.completion)
}
}

extension ImagePickerRepresentable.Coordinator: UINavigationControllerDelegate { }

extension ImagePickerRepresentable: UIViewControllerRepresentable {
typealias UIViewControllerType = UIImagePickerController

func makeCoordinator() -> Coordinator {
Coordinator(representable: self, store: self.store)
}

func makeUIViewController(context: Context) -> UIImagePickerController {
guard UIImagePickerController.isSourceTypeAvailable(.camera) else { return UIImagePickerController() }
let imagePicker = UIImagePickerController()

if self.source == .camera {
imagePicker.sourceType = .camera
imagePicker.cameraCaptureMode = .photo
} else {
imagePicker.sourceType = .photoLibrary
}

imagePicker.delegate = context.coordinator
return imagePicker
}

func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) { }
}
60 changes: 60 additions & 0 deletions storage/StorageExampleSwiftUI/ImageStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// ImageStore.swift
// StorageExampleSwiftUI
//
// Created by Ben Reed on 3/25/21.
// Copyright © 2021 Google Inc. All rights reserved.
//

import Firebase
import FirebaseStorageSwift

public class ImageStore: ObservableObject {
private var storage: Storage

public var compressionQuality: CGFloat = 0.8

public let imagePathKey = "imagePath"

@Published var image: UIImage?

lazy var photoFileDirectory: String = {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0]
return "file:\(documentsDirectory)"
}()

public init(withStorage storage: Storage) {
self.storage = storage
}

public func uploadImage(_ image: UIImage, atPath imagePath: String) {
guard let imageData = image.jpegData(compressionQuality: 0.8) else { return }

let imageMetadata = StorageMetadata()
imageMetadata.contentType = "image/jpeg"

let storageRef = storage.reference(withPath: imagePath)
storageRef.putData(imageData, metadata: imageMetadata) { result in
switch result {
case .success:
break
case let .failure(error):
_ = error
break
}
}
}

public func downloadImage(atPath imagePath: String) {
guard let imageURL = URL(string: "\(self.photoFileDirectory)/\(imagePath)") else { return }
self.storage.reference().child(imagePath).write(toFile: imageURL) { result in
switch result {
case let .success(downloadedFileURL):
self.image = UIImage(contentsOfFile: downloadedFileURL.path)
case let .failure(error):
print("Error downloading: \(error)")
}
}
}
}
78 changes: 78 additions & 0 deletions storage/StorageExampleSwiftUI/ImageView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// ImageView.swift
// StorageExampleSwiftUI
//
// Created by Ben Reed on 2/10/21.
// Copyright © 2021 Google Inc. All rights reserved.
//

import SwiftUI
import Firebase

struct ImageView: View {
@StateObject private var photoStore = ImageStore(withStorage: Storage.storage())

@State var isSelectingImage = false
@State var isTakingPhoto = false
@State var showUploadMenu = false

var body: some View {
NavigationView {
VStack {
Image(uiImage: photoStore.image ?? UIImage())
.resizable()
.aspectRatio(contentMode: .fit)
}
.navigationTitle("Firebase Storage")
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
if !showUploadMenu {
Button("Upload") {
showUploadMenu = true
}
Spacer()
Button("Download") {
self.downloadImage()
}
} else {
Button("") {
showUploadMenu = false
}

Spacer()

Button("Take Photo") {
isTakingPhoto = true
}.sheet(isPresented: $isTakingPhoto) {
ImagePickerRepresentable(source: .camera, store: photoStore, visible: $isTakingPhoto, completion: {
showUploadMenu = false
})
}.disabled(!UIImagePickerController.isSourceTypeAvailable(.camera))

Button("Select Image") {
isSelectingImage = true
}.sheet(isPresented: $isSelectingImage) {
ImagePickerRepresentable(source: .photoLibrary, store: photoStore, visible: $isSelectingImage, completion: {
showUploadMenu = false
})
}
}
}
}
}.onAppear {
downloadImage()
}
}

func downloadImage() {
UserDefaults.standard.synchronize()
guard let imagePath = UserDefaults.standard.string(forKey: self.photoStore.imagePathKey) else { return }
self.photoStore.downloadImage(atPath: imagePath)
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ImageView()
}
}
54 changes: 54 additions & 0 deletions storage/StorageExampleSwiftUI/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>Take and upload photos to cloud storage</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Upload photos to cloud storage</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchScreen</key>
<dict/>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
30 changes: 30 additions & 0 deletions storage/StorageExampleSwiftUI/StorageExampleSwiftUIApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// StorageExampleSwiftUIApp.swift
// StorageExampleSwiftUI
//
// Created by Ben Reed on 2/10/21.
// Copyright © 2021 Google Inc. All rights reserved.
//

import SwiftUI
import Firebase

@main
struct StorageExampleSwiftUIApp: App {
init() {
FirebaseApp.configure()
if Auth.auth().currentUser == nil {
Auth.auth().signInAnonymously(completion: { (authResult, error) in
if let error = error {
print("Failed to sign in: \(error.localizedDescription)")
}
})
}
}

var body: some Scene {
WindowGroup {
ImageView()
}
}
}

0 comments on commit ac7f271

Please sign in to comment.