├── .DS_Store ├── .gitignore ├── .swift-version ├── .travis.yml ├── CHANGELOG.md ├── Documentation ├── Documentation │ ├── DatabaseProviderDocs.md │ └── DatabaseTargetDocs.md └── Examples │ └── DatabaseProviderExample.md ├── LICENSE.md ├── Nora.podspec ├── Nora.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── Nora.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── Podfile ├── Podfile.lock ├── README.md ├── Screenshots ├── NoraBanner.png ├── Screen Shot 2017-04-05 at 4.07.48 AM.png └── Screen Shot 2017-04-05 at 4.10.29 AM.png ├── Sources ├── .DS_Store ├── Info.plist ├── NRDatabaseProvider.swift ├── NRDatabaseRequest.swift ├── NRDatabaseResponse.swift ├── NRError.swift ├── NRFirebaseTarget.swift ├── NRFirebaseTask.swift ├── NRStorageProvider.swift ├── NRStorageRequest.swift ├── NRStorageResponse.swift └── Nora.h ├── build.sh └── debug-time-function-bodies.sh /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SD10/Nora/54440a8800fa956e196659c423b21c34dfa0a1f2/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/swift 2 | 3 | ### Swift ### 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | .DS_Store 9 | 10 | ## Build generated 11 | build/ 12 | DerivedData/ 13 | 14 | ## Various settings 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata/ 24 | 25 | ## Other 26 | *.moved-aside 27 | *.xccheckout 28 | *.xcscmblueprint 29 | 30 | ## Obj-C/Swift specific 31 | *.hmap 32 | *.ipa 33 | *.dSYM.zip 34 | *.dSYM 35 | 36 | ## Playgrounds 37 | timeline.xctimeline 38 | playground.xcworkspace 39 | 40 | #CocoaPods 41 | Pods/* 42 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode9.3beta 2 | language: objective-c 3 | before_install: 4 | - gem install xcpretty 5 | script: sh build.sh /tmp/Nora 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | All notable changes to this project will be documented in this file. 3 | `Nora` adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | --- 6 | 7 | ## Versions 8 | 9 | --- 10 | 11 | ### Next Version 12 | 13 | ### API Breaking Changes 14 | N/A 15 | 16 | ### Enhancements 17 | N/A 18 | 19 | ### Bugfixes 20 | N/A 21 | 22 | --- 23 | 24 | ### [1.0.0]() 25 | 26 | ### API Breaking Changes 27 | - Updated to FirebaseDatabase `4.0.0` and FirebaseStorage `2.0.0`. 28 | - All Nora types are now prefixed with `NR` to prevent Firebase 3 naming collisions. [#25](https://github.com/SD10/Nora/pull/25) by [@SD10](https://github.com/SD10) 29 | 30 | --- 31 | 32 | ### [0.1.1](https://github.com/SD10/Nora/releases/tag/0.1.0) 33 | 34 | ### Bugfixes 35 | - Empty `String` path for `DatabaseTarget` & `StorageTarget` now uses `baseReference`. [#20](https://github.com/SD10/Nora/pull/20) by [@SD10](https://github.com/SD10) 36 | 37 | --- 38 | 39 | ### [0.1.0 Pre-Release](https://github.com/SD10/Nora/releases/tag/0.1.0) 40 | - Initial Release 41 | 42 | -------------------------------------------------------------------------------- /Documentation/Documentation/DatabaseProviderDocs.md: -------------------------------------------------------------------------------- 1 | # DatabaseTarget 2 | 3 | ## Table of Contents 4 | | Name | Type | 5 | |:--- | :--- | 6 | [**`baseReference`**](#basereference) | Read-Only Property | 7 | [**`path`**](#path) | Read-Only Property | 8 | [**`task`**](#task) | Read-Only Property | 9 | [**`queries`**](#queries) | Read-Only Property | 10 | [**`onDisconnect`**](#ondisconnect) | Read-Only Property | 11 | [**`localEvents`**](#localevents) | Read-Only Property | 12 | [**`transactionBlock`**](#tranasctionblock) | Read-Only Property | 13 | [**`uniqueID()`**](#uniqueid) | Method | 14 | 15 | --- 16 | 17 | ## `baseReference` 18 | Base reference for your target in Database. 19 | 20 | - **type**: Read-Only Property 21 | - **return type**: FIRDatabaseReference 22 | 23 | --- 24 | 25 | ## `path` 26 | Path to be appended to the base reference. 27 | 28 | - **type**: Read-Only Property 29 | - **return type**: String 30 | 31 | --- 32 | 33 | ## `task` 34 | Type of task you want to perform. ( Firebase method: eg. observe, observeOnce, setValue, etc. ) 35 | 36 | - **type**: Read-Only Property 37 | - **return type**: DatabaseTask 38 | 39 | --- 40 | 41 | ## `queries` 42 | Queries to append to the end of the path in order as listed. 43 | 44 | - **type**: Read-Only Property 45 | - **return type**: [DatabaseQuery]? 46 | 47 | Default Implementation: 48 | 49 | ```swift 50 | var queries: [DatabaseQuery]? { 51 | return nil 52 | } 53 | ``` 54 | 55 | --- 56 | 57 | ## `onDisconnect` 58 | Should task be performed on disconnect. ( Defaults to false ) 59 | 60 | - **type**: Read-Only Property 61 | - **return type**: Bool 62 | 63 | Default Implementation: 64 | 65 | ```swift 66 | var onDisconnect: Bool { 67 | return false 68 | } 69 | ``` 70 | 71 | --- 72 | 73 | ## `localEvents` 74 | Allow local events for transaction block. ( Defaults to false ) 75 | 76 | - **type**: Read-Only Property 77 | - **return type**: Bool 78 | 79 | Default Implementation: 80 | 81 | ```swift 82 | var localEvents: Bool { 83 | return false 84 | } 85 | ``` 86 | 87 | --- 88 | 89 | ## `transactionBlock` 90 | Transaction block to run for .transaction task. 91 | 92 | - **type**: Read-Only Property 93 | - **return type**: (FIRMutableData) -> FIRTransactionResult 94 | 95 | Default Implementation: 96 | 97 | ```swift 98 | var transactionBlock: (FIRMutableData) -> FIRTransactionResult { 99 | return { (data: FIRMutableData) in 100 | return FIRTransactionResult.success(withValue: data) 101 | } 102 | } 103 | ``` 104 | 105 | --- 106 | 107 | ## `uniqueID()` 108 | Generate unique id String. 109 | 110 | - **type**: Method 111 | - **return type**: String 112 | 113 | -------------------------------------------------------------------------------- /Documentation/Documentation/DatabaseTargetDocs.md: -------------------------------------------------------------------------------- 1 | # DatabaseProvider 2 | 3 | ## Table of Contents 4 | | Name | Type | 5 | |:--- | :--- | 6 | | [**`request(_ target: DatabaseTarget, completion: DatabaseCompletion)`**](#) | Method | 7 | | [**`request(_ target: DatabaseTarget)`**](#) | Method | 8 | 9 | --- 10 | 11 | ## `request(_ target: DatabaseTarget, completion: DatabaseCompletion)` 12 | Make a request to FirebaseDatabase. 13 | 14 | - **type**: Method 15 | - **parameters**: 16 | - **target**: target for the request. 17 | - **completion**: completion block with result of the request. 18 | - **returns**: a handle in the case of an observe request, used to unregister the observer (optional) 19 | - **return type**: UInt? (@discardableResult) 20 | 21 | Example 22 | 23 | ```swift 24 | let provider = DatabaseProvider() 25 | 26 | provider.request(.allUsers) { result in 27 | switch result { 28 | case .success(let response): 29 | let snapshot = response.snapshot 30 | case .failure(let error): 31 | print(error.localizedDescription) 32 | } 33 | } 34 | 35 | ``` 36 | 37 | --- 38 | 39 | ## `request(_ target: DatabaseTarget, completion: DatabaseCompletion = { _ in })` 40 | Make a request to FirebaseDatabase without a completion block. (Note: Do not use this for read requests) 41 | 42 | - **type**: Method 43 | - **parameters**: 44 | - **target**: target for the request. 45 | - **completion**: completion block with result of the request. 46 | - **returns**: a handle in the case of an observe request, used to unregister the observer (optional). 47 | - **return type**: UInt? (@discardableResult) 48 | 49 | Example 50 | 51 | ```swift 52 | let provider = DatabaseProvider() 53 | provider.request(.createUser(id: "1", email: "nora@email.com")) 54 | 55 | ``` 56 | -------------------------------------------------------------------------------- /Documentation/Examples/DatabaseProviderExample.md: -------------------------------------------------------------------------------- 1 | # Setting Up A DatabaseTarget 2 | 3 | This library revolves around using enums to model endpoints in your Firebase database. 4 | 5 | The enums must conform to the DatabaseTarget protocol. 6 | 7 | The `DatabaseTarget` protocol requires you to implement 3 properties: 8 | - baseReference: this is the base reference for all database tasks in this target 9 | - path: the path to be appended onto the base reference 10 | - task: this is an enum that represents FirebaseDatabase methods (eg. observe, observeOnce, setValue, etc.) 11 | 12 | --- 13 | 14 | ```swift 15 | 16 | enum Users: NRDatabaseTarget { 17 | 18 | case getUser(id: String) 19 | case createUser(email: String, name: String) 20 | case deleteUser(id: String) 21 | 22 | var baseReference: FIRDatabaseReference { 23 | return Database.database().reference().child("users") 24 | } 25 | 26 | var path: String { 27 | 28 | switch self { 29 | case .getUser(let id), .deleteUser(let id): 30 | return id 31 | case .createUser: 32 | return uniqueID() 33 | } 34 | 35 | } 36 | 37 | var task: NRDatabaseTask { 38 | 39 | switch self { 40 | case .getUser: 41 | return .observeOnce(.value) 42 | case .deleteUser: 43 | return .removeValue 44 | case .createUser(let email, let name): 45 | return .setValue(["email": email, "name": name]) 46 | } 47 | 48 | } 49 | 50 | } 51 | 52 | ``` 53 | 54 | --- 55 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Steven Deutsch (https://github.com/sd10) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Nora.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Nora' 3 | s.version = '1.1.1' 4 | s.summary = 'An abstraction layer for FirebaseDatabase and FirebaseStorage.' 5 | s.homepage = 'https://github.com/SD10/Nora' 6 | s.license = { :type => 'MIT', :file => 'LICENSE.md' } 7 | s.author = { 'Steven Deutsch' => 'stevensdeutsch@yahoo.com' } 8 | s.social_media_url = 'https://twitter.com/_SD10_' 9 | s.source = { :git => "https://github.com/SD10/Nora.git", :tag => s.version } 10 | s.source_files = 'Sources/*.swift' 11 | s.platform = :ios, '9.0' 12 | s.swift_version = '4.1' 13 | s.static_framework = true 14 | s.requires_arc = true 15 | s.dependency "Firebase/Core" 16 | s.dependency "Firebase/Database" 17 | s.dependency "Firebase/Storage" 18 | s.dependency "Result", "~> 3.0" 19 | end 20 | -------------------------------------------------------------------------------- /Nora.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1F9860FD2076EB7D00EB6CAE /* Nora.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F9860FB2076EB7D00EB6CAE /* Nora.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 1F98610D2076EB8900EB6CAE /* NRStorageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F9861032076EB8800EB6CAE /* NRStorageRequest.swift */; }; 12 | 1F98610E2076EB8900EB6CAE /* NRStorageResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F9861042076EB8800EB6CAE /* NRStorageResponse.swift */; }; 13 | 1F98610F2076EB8900EB6CAE /* NRDatabaseResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F9861052076EB8800EB6CAE /* NRDatabaseResponse.swift */; }; 14 | 1F9861102076EB8900EB6CAE /* NRFirebaseTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F9861062076EB8800EB6CAE /* NRFirebaseTarget.swift */; }; 15 | 1F9861112076EB8900EB6CAE /* NRStorageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F9861072076EB8800EB6CAE /* NRStorageProvider.swift */; }; 16 | 1F9861122076EB8900EB6CAE /* NRDatabaseProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F9861082076EB8900EB6CAE /* NRDatabaseProvider.swift */; }; 17 | 1F9861142076EB8900EB6CAE /* NRFirebaseTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F98610A2076EB8900EB6CAE /* NRFirebaseTask.swift */; }; 18 | 1F9861152076EB8900EB6CAE /* NRDatabaseRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F98610B2076EB8900EB6CAE /* NRDatabaseRequest.swift */; }; 19 | 1F9861162076EB8900EB6CAE /* NRError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F98610C2076EB8900EB6CAE /* NRError.swift */; }; 20 | A07D0FAFBE859B650D8F342E /* Pods_Nora.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE2BA8F9370352D1B2619345 /* Pods_Nora.framework */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 1F9860F82076EB7D00EB6CAE /* Nora.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Nora.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | 1F9860FB2076EB7D00EB6CAE /* Nora.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Nora.h; sourceTree = ""; }; 26 | 1F9860FC2076EB7D00EB6CAE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 27 | 1F9861032076EB8800EB6CAE /* NRStorageRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NRStorageRequest.swift; sourceTree = ""; }; 28 | 1F9861042076EB8800EB6CAE /* NRStorageResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NRStorageResponse.swift; sourceTree = ""; }; 29 | 1F9861052076EB8800EB6CAE /* NRDatabaseResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NRDatabaseResponse.swift; sourceTree = ""; }; 30 | 1F9861062076EB8800EB6CAE /* NRFirebaseTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NRFirebaseTarget.swift; sourceTree = ""; }; 31 | 1F9861072076EB8800EB6CAE /* NRStorageProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NRStorageProvider.swift; sourceTree = ""; }; 32 | 1F9861082076EB8900EB6CAE /* NRDatabaseProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NRDatabaseProvider.swift; sourceTree = ""; }; 33 | 1F98610A2076EB8900EB6CAE /* NRFirebaseTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NRFirebaseTask.swift; sourceTree = ""; }; 34 | 1F98610B2076EB8900EB6CAE /* NRDatabaseRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NRDatabaseRequest.swift; sourceTree = ""; }; 35 | 1F98610C2076EB8900EB6CAE /* NRError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NRError.swift; sourceTree = ""; }; 36 | AF8737A9720B68957699E4C7 /* Pods-Nora.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nora.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Nora/Pods-Nora.debug.xcconfig"; sourceTree = ""; }; 37 | DADC44E02F5A056F06492558 /* Pods-Nora.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nora.release.xcconfig"; path = "Pods/Target Support Files/Pods-Nora/Pods-Nora.release.xcconfig"; sourceTree = ""; }; 38 | EE2BA8F9370352D1B2619345 /* Pods_Nora.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Nora.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | 1F9860F42076EB7D00EB6CAE /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | A07D0FAFBE859B650D8F342E /* Pods_Nora.framework in Frameworks */, 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXFrameworksBuildPhase section */ 51 | 52 | /* Begin PBXGroup section */ 53 | 1F9860EE2076EB7D00EB6CAE = { 54 | isa = PBXGroup; 55 | children = ( 56 | 1F9860FA2076EB7D00EB6CAE /* Sources */, 57 | 1F9860F92076EB7D00EB6CAE /* Products */, 58 | EA7B3339399A9FC9B5E0C9DB /* Pods */, 59 | E2F60F89F7C0738BCEF3361D /* Frameworks */, 60 | ); 61 | sourceTree = ""; 62 | }; 63 | 1F9860F92076EB7D00EB6CAE /* Products */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 1F9860F82076EB7D00EB6CAE /* Nora.framework */, 67 | ); 68 | name = Products; 69 | sourceTree = ""; 70 | }; 71 | 1F9860FA2076EB7D00EB6CAE /* Sources */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 1F9861082076EB8900EB6CAE /* NRDatabaseProvider.swift */, 75 | 1F98610B2076EB8900EB6CAE /* NRDatabaseRequest.swift */, 76 | 1F9861052076EB8800EB6CAE /* NRDatabaseResponse.swift */, 77 | 1F98610C2076EB8900EB6CAE /* NRError.swift */, 78 | 1F9861062076EB8800EB6CAE /* NRFirebaseTarget.swift */, 79 | 1F98610A2076EB8900EB6CAE /* NRFirebaseTask.swift */, 80 | 1F9861072076EB8800EB6CAE /* NRStorageProvider.swift */, 81 | 1F9861032076EB8800EB6CAE /* NRStorageRequest.swift */, 82 | 1F9861042076EB8800EB6CAE /* NRStorageResponse.swift */, 83 | 1F9860FB2076EB7D00EB6CAE /* Nora.h */, 84 | 1F9860FC2076EB7D00EB6CAE /* Info.plist */, 85 | ); 86 | path = Sources; 87 | sourceTree = ""; 88 | }; 89 | E2F60F89F7C0738BCEF3361D /* Frameworks */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | EE2BA8F9370352D1B2619345 /* Pods_Nora.framework */, 93 | ); 94 | name = Frameworks; 95 | sourceTree = ""; 96 | }; 97 | EA7B3339399A9FC9B5E0C9DB /* Pods */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | AF8737A9720B68957699E4C7 /* Pods-Nora.debug.xcconfig */, 101 | DADC44E02F5A056F06492558 /* Pods-Nora.release.xcconfig */, 102 | ); 103 | name = Pods; 104 | sourceTree = ""; 105 | }; 106 | /* End PBXGroup section */ 107 | 108 | /* Begin PBXHeadersBuildPhase section */ 109 | 1F9860F52076EB7D00EB6CAE /* Headers */ = { 110 | isa = PBXHeadersBuildPhase; 111 | buildActionMask = 2147483647; 112 | files = ( 113 | 1F9860FD2076EB7D00EB6CAE /* Nora.h in Headers */, 114 | ); 115 | runOnlyForDeploymentPostprocessing = 0; 116 | }; 117 | /* End PBXHeadersBuildPhase section */ 118 | 119 | /* Begin PBXNativeTarget section */ 120 | 1F9860F72076EB7D00EB6CAE /* Nora */ = { 121 | isa = PBXNativeTarget; 122 | buildConfigurationList = 1F9861002076EB7D00EB6CAE /* Build configuration list for PBXNativeTarget "Nora" */; 123 | buildPhases = ( 124 | 883B09E10F5F9494EFD1A154 /* [CP] Check Pods Manifest.lock */, 125 | 1F9860F32076EB7D00EB6CAE /* Sources */, 126 | 1F9860F42076EB7D00EB6CAE /* Frameworks */, 127 | 1F9860F52076EB7D00EB6CAE /* Headers */, 128 | 1F9860F62076EB7D00EB6CAE /* Resources */, 129 | ); 130 | buildRules = ( 131 | ); 132 | dependencies = ( 133 | ); 134 | name = Nora; 135 | productName = Nora; 136 | productReference = 1F9860F82076EB7D00EB6CAE /* Nora.framework */; 137 | productType = "com.apple.product-type.framework"; 138 | }; 139 | /* End PBXNativeTarget section */ 140 | 141 | /* Begin PBXProject section */ 142 | 1F9860EF2076EB7D00EB6CAE /* Project object */ = { 143 | isa = PBXProject; 144 | attributes = { 145 | LastUpgradeCheck = 0930; 146 | ORGANIZATIONNAME = Nora; 147 | TargetAttributes = { 148 | 1F9860F72076EB7D00EB6CAE = { 149 | CreatedOnToolsVersion = 9.3; 150 | LastSwiftMigration = 0930; 151 | }; 152 | }; 153 | }; 154 | buildConfigurationList = 1F9860F22076EB7D00EB6CAE /* Build configuration list for PBXProject "Nora" */; 155 | compatibilityVersion = "Xcode 9.3"; 156 | developmentRegion = en; 157 | hasScannedForEncodings = 0; 158 | knownRegions = ( 159 | en, 160 | ); 161 | mainGroup = 1F9860EE2076EB7D00EB6CAE; 162 | productRefGroup = 1F9860F92076EB7D00EB6CAE /* Products */; 163 | projectDirPath = ""; 164 | projectRoot = ""; 165 | targets = ( 166 | 1F9860F72076EB7D00EB6CAE /* Nora */, 167 | ); 168 | }; 169 | /* End PBXProject section */ 170 | 171 | /* Begin PBXResourcesBuildPhase section */ 172 | 1F9860F62076EB7D00EB6CAE /* Resources */ = { 173 | isa = PBXResourcesBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | /* End PBXResourcesBuildPhase section */ 180 | 181 | /* Begin PBXShellScriptBuildPhase section */ 182 | 883B09E10F5F9494EFD1A154 /* [CP] Check Pods Manifest.lock */ = { 183 | isa = PBXShellScriptBuildPhase; 184 | buildActionMask = 2147483647; 185 | files = ( 186 | ); 187 | inputPaths = ( 188 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 189 | "${PODS_ROOT}/Manifest.lock", 190 | ); 191 | name = "[CP] Check Pods Manifest.lock"; 192 | outputPaths = ( 193 | "$(DERIVED_FILE_DIR)/Pods-Nora-checkManifestLockResult.txt", 194 | ); 195 | runOnlyForDeploymentPostprocessing = 0; 196 | shellPath = /bin/sh; 197 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 198 | showEnvVarsInLog = 0; 199 | }; 200 | /* End PBXShellScriptBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 1F9860F32076EB7D00EB6CAE /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 1F9861152076EB8900EB6CAE /* NRDatabaseRequest.swift in Sources */, 208 | 1F98610E2076EB8900EB6CAE /* NRStorageResponse.swift in Sources */, 209 | 1F9861102076EB8900EB6CAE /* NRFirebaseTarget.swift in Sources */, 210 | 1F9861112076EB8900EB6CAE /* NRStorageProvider.swift in Sources */, 211 | 1F9861162076EB8900EB6CAE /* NRError.swift in Sources */, 212 | 1F9861142076EB8900EB6CAE /* NRFirebaseTask.swift in Sources */, 213 | 1F98610F2076EB8900EB6CAE /* NRDatabaseResponse.swift in Sources */, 214 | 1F98610D2076EB8900EB6CAE /* NRStorageRequest.swift in Sources */, 215 | 1F9861122076EB8900EB6CAE /* NRDatabaseProvider.swift in Sources */, 216 | ); 217 | runOnlyForDeploymentPostprocessing = 0; 218 | }; 219 | /* End PBXSourcesBuildPhase section */ 220 | 221 | /* Begin XCBuildConfiguration section */ 222 | 1F9860FE2076EB7D00EB6CAE /* Debug */ = { 223 | isa = XCBuildConfiguration; 224 | buildSettings = { 225 | ALWAYS_SEARCH_USER_PATHS = NO; 226 | CLANG_ANALYZER_NONNULL = YES; 227 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 228 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 229 | CLANG_CXX_LIBRARY = "libc++"; 230 | CLANG_ENABLE_MODULES = YES; 231 | CLANG_ENABLE_OBJC_ARC = YES; 232 | CLANG_ENABLE_OBJC_WEAK = YES; 233 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 234 | CLANG_WARN_BOOL_CONVERSION = YES; 235 | CLANG_WARN_COMMA = YES; 236 | CLANG_WARN_CONSTANT_CONVERSION = YES; 237 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 238 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 239 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 240 | CLANG_WARN_EMPTY_BODY = YES; 241 | CLANG_WARN_ENUM_CONVERSION = YES; 242 | CLANG_WARN_INFINITE_RECURSION = YES; 243 | CLANG_WARN_INT_CONVERSION = YES; 244 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 245 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 246 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 247 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 248 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 249 | CLANG_WARN_STRICT_PROTOTYPES = YES; 250 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 251 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 252 | CLANG_WARN_UNREACHABLE_CODE = YES; 253 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 254 | CODE_SIGN_IDENTITY = "iPhone Developer"; 255 | COPY_PHASE_STRIP = NO; 256 | CURRENT_PROJECT_VERSION = 1; 257 | DEBUG_INFORMATION_FORMAT = dwarf; 258 | ENABLE_STRICT_OBJC_MSGSEND = YES; 259 | ENABLE_TESTABILITY = YES; 260 | GCC_C_LANGUAGE_STANDARD = gnu11; 261 | GCC_DYNAMIC_NO_PIC = NO; 262 | GCC_NO_COMMON_BLOCKS = YES; 263 | GCC_OPTIMIZATION_LEVEL = 0; 264 | GCC_PREPROCESSOR_DEFINITIONS = ( 265 | "DEBUG=1", 266 | "$(inherited)", 267 | ); 268 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 269 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 270 | GCC_WARN_UNDECLARED_SELECTOR = YES; 271 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 272 | GCC_WARN_UNUSED_FUNCTION = YES; 273 | GCC_WARN_UNUSED_VARIABLE = YES; 274 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 275 | MTL_ENABLE_DEBUG_INFO = YES; 276 | ONLY_ACTIVE_ARCH = YES; 277 | SDKROOT = iphoneos; 278 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 279 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 280 | VERSIONING_SYSTEM = "apple-generic"; 281 | VERSION_INFO_PREFIX = ""; 282 | }; 283 | name = Debug; 284 | }; 285 | 1F9860FF2076EB7D00EB6CAE /* Release */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | ALWAYS_SEARCH_USER_PATHS = NO; 289 | CLANG_ANALYZER_NONNULL = YES; 290 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 291 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 292 | CLANG_CXX_LIBRARY = "libc++"; 293 | CLANG_ENABLE_MODULES = YES; 294 | CLANG_ENABLE_OBJC_ARC = YES; 295 | CLANG_ENABLE_OBJC_WEAK = YES; 296 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 297 | CLANG_WARN_BOOL_CONVERSION = YES; 298 | CLANG_WARN_COMMA = YES; 299 | CLANG_WARN_CONSTANT_CONVERSION = YES; 300 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 301 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 302 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 303 | CLANG_WARN_EMPTY_BODY = YES; 304 | CLANG_WARN_ENUM_CONVERSION = YES; 305 | CLANG_WARN_INFINITE_RECURSION = YES; 306 | CLANG_WARN_INT_CONVERSION = YES; 307 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 308 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 309 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 310 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 311 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 312 | CLANG_WARN_STRICT_PROTOTYPES = YES; 313 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 314 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 315 | CLANG_WARN_UNREACHABLE_CODE = YES; 316 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 317 | CODE_SIGN_IDENTITY = "iPhone Developer"; 318 | COPY_PHASE_STRIP = NO; 319 | CURRENT_PROJECT_VERSION = 1; 320 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 321 | ENABLE_NS_ASSERTIONS = NO; 322 | ENABLE_STRICT_OBJC_MSGSEND = YES; 323 | GCC_C_LANGUAGE_STANDARD = gnu11; 324 | GCC_NO_COMMON_BLOCKS = YES; 325 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 326 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 327 | GCC_WARN_UNDECLARED_SELECTOR = YES; 328 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 329 | GCC_WARN_UNUSED_FUNCTION = YES; 330 | GCC_WARN_UNUSED_VARIABLE = YES; 331 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 332 | MTL_ENABLE_DEBUG_INFO = NO; 333 | SDKROOT = iphoneos; 334 | SWIFT_COMPILATION_MODE = wholemodule; 335 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 336 | VALIDATE_PRODUCT = YES; 337 | VERSIONING_SYSTEM = "apple-generic"; 338 | VERSION_INFO_PREFIX = ""; 339 | }; 340 | name = Release; 341 | }; 342 | 1F9861012076EB7D00EB6CAE /* Debug */ = { 343 | isa = XCBuildConfiguration; 344 | baseConfigurationReference = AF8737A9720B68957699E4C7 /* Pods-Nora.debug.xcconfig */; 345 | buildSettings = { 346 | CLANG_ENABLE_MODULES = YES; 347 | CODE_SIGN_IDENTITY = ""; 348 | CODE_SIGN_STYLE = Automatic; 349 | DEFINES_MODULE = YES; 350 | DYLIB_COMPATIBILITY_VERSION = 1; 351 | DYLIB_CURRENT_VERSION = 1; 352 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 353 | INFOPLIST_FILE = Sources/Info.plist; 354 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 355 | LD_RUNPATH_SEARCH_PATHS = ( 356 | "$(inherited)", 357 | "@executable_path/Frameworks", 358 | "@loader_path/Frameworks", 359 | ); 360 | PRODUCT_BUNDLE_IDENTIFIER = com.Nora.Nora; 361 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 362 | SKIP_INSTALL = YES; 363 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 364 | SWIFT_VERSION = 4.0; 365 | TARGETED_DEVICE_FAMILY = "1,2"; 366 | }; 367 | name = Debug; 368 | }; 369 | 1F9861022076EB7D00EB6CAE /* Release */ = { 370 | isa = XCBuildConfiguration; 371 | baseConfigurationReference = DADC44E02F5A056F06492558 /* Pods-Nora.release.xcconfig */; 372 | buildSettings = { 373 | CLANG_ENABLE_MODULES = YES; 374 | CODE_SIGN_IDENTITY = ""; 375 | CODE_SIGN_STYLE = Automatic; 376 | DEFINES_MODULE = YES; 377 | DYLIB_COMPATIBILITY_VERSION = 1; 378 | DYLIB_CURRENT_VERSION = 1; 379 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 380 | INFOPLIST_FILE = Sources/Info.plist; 381 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 382 | LD_RUNPATH_SEARCH_PATHS = ( 383 | "$(inherited)", 384 | "@executable_path/Frameworks", 385 | "@loader_path/Frameworks", 386 | ); 387 | PRODUCT_BUNDLE_IDENTIFIER = com.Nora.Nora; 388 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 389 | SKIP_INSTALL = YES; 390 | SWIFT_VERSION = 4.0; 391 | TARGETED_DEVICE_FAMILY = "1,2"; 392 | }; 393 | name = Release; 394 | }; 395 | /* End XCBuildConfiguration section */ 396 | 397 | /* Begin XCConfigurationList section */ 398 | 1F9860F22076EB7D00EB6CAE /* Build configuration list for PBXProject "Nora" */ = { 399 | isa = XCConfigurationList; 400 | buildConfigurations = ( 401 | 1F9860FE2076EB7D00EB6CAE /* Debug */, 402 | 1F9860FF2076EB7D00EB6CAE /* Release */, 403 | ); 404 | defaultConfigurationIsVisible = 0; 405 | defaultConfigurationName = Release; 406 | }; 407 | 1F9861002076EB7D00EB6CAE /* Build configuration list for PBXNativeTarget "Nora" */ = { 408 | isa = XCConfigurationList; 409 | buildConfigurations = ( 410 | 1F9861012076EB7D00EB6CAE /* Debug */, 411 | 1F9861022076EB7D00EB6CAE /* Release */, 412 | ); 413 | defaultConfigurationIsVisible = 0; 414 | defaultConfigurationName = Release; 415 | }; 416 | /* End XCConfigurationList section */ 417 | }; 418 | rootObject = 1F9860EF2076EB7D00EB6CAE /* Project object */; 419 | } 420 | -------------------------------------------------------------------------------- /Nora.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Nora.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Nora.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Nora.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | 3 | target 'Nora' do 4 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 5 | use_frameworks! 6 | 7 | # Pods for Nora 8 | pod 'Firebase/Database' 9 | pod 'Firebase/Storage' 10 | pod 'Result' 11 | end 12 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Firebase/Core (4.11.0): 3 | - FirebaseAnalytics (= 4.1.0) 4 | - FirebaseCore (= 4.0.18) 5 | - Firebase/Database (4.11.0): 6 | - Firebase/Core 7 | - FirebaseDatabase (= 4.1.5) 8 | - Firebase/Storage (4.11.0): 9 | - Firebase/Core 10 | - FirebaseStorage (= 2.1.3) 11 | - FirebaseAnalytics (4.1.0): 12 | - FirebaseCore (~> 4.0) 13 | - FirebaseInstanceID (~> 2.0) 14 | - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" 15 | - nanopb (~> 0.3) 16 | - FirebaseCore (4.0.18): 17 | - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" 18 | - FirebaseDatabase (4.1.5): 19 | - FirebaseAnalytics (~> 4.1) 20 | - FirebaseCore (~> 4.0) 21 | - leveldb-library (~> 1.18) 22 | - FirebaseInstanceID (2.0.10): 23 | - FirebaseCore (~> 4.0) 24 | - FirebaseStorage (2.1.3): 25 | - FirebaseAnalytics (~> 4.1) 26 | - FirebaseCore (~> 4.0) 27 | - GTMSessionFetcher/Core (~> 1.1) 28 | - GoogleToolboxForMac/Defines (2.1.3) 29 | - "GoogleToolboxForMac/NSData+zlib (2.1.3)": 30 | - GoogleToolboxForMac/Defines (= 2.1.3) 31 | - GTMSessionFetcher/Core (1.1.14) 32 | - leveldb-library (1.20) 33 | - nanopb (0.3.8): 34 | - nanopb/decode (= 0.3.8) 35 | - nanopb/encode (= 0.3.8) 36 | - nanopb/decode (0.3.8) 37 | - nanopb/encode (0.3.8) 38 | - Result (3.2.4) 39 | 40 | DEPENDENCIES: 41 | - Firebase/Database 42 | - Firebase/Storage 43 | - Result 44 | 45 | SPEC REPOS: 46 | https://github.com/CocoaPods/Specs: 47 | - Firebase 48 | - FirebaseAnalytics 49 | - FirebaseCore 50 | - FirebaseDatabase 51 | - FirebaseInstanceID 52 | - FirebaseStorage 53 | - GoogleToolboxForMac 54 | - GTMSessionFetcher 55 | - leveldb-library 56 | - nanopb 57 | - Result 58 | 59 | SPEC CHECKSUMS: 60 | Firebase: cc13dfab1038c8b45d7903e01fc690451d6d0b24 61 | FirebaseAnalytics: 3dfae28d4a5e06f86c4fae830efc2ad3fadb19bc 62 | FirebaseCore: b981f47e5254cbcfdeb483355300d743f6fcab2c 63 | FirebaseDatabase: 5f0bc6134c5c237cf55f9e1249d406770a75eafd 64 | FirebaseInstanceID: 8d20d890d65c917f9f7d9950b6e10a760ad34321 65 | FirebaseStorage: 9a863a2bb96c406958eeff7c2f1dfa9f44c44a13 66 | GoogleToolboxForMac: 2501e2ad72a52eb3dfe7bd9aee7dad11b858bd20 67 | GTMSessionFetcher: 390ea358e5a0d0133153806f744662dad933d06b 68 | leveldb-library: 08cba283675b7ed2d99629a4bc5fd052cd2bb6a5 69 | nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3 70 | Result: d2d07204ce72856f1fd9130bbe42c35a7b0fea10 71 | 72 | PODFILE CHECKSUM: c1a313749f5b8318c50f13a33eb24ed071a3b34a 73 | 74 | COCOAPODS: 1.5.0 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | 7 | codebeat badge 9 | 10 | 11 | Build Status 13 | 14 | 15 | Swift 17 | 18 | 19 | Xcode 21 | 22 | 23 | MIT 25 | 26 | 27 | Contributions Welcome 29 | 30 | 31 | Open Source Love 33 | 34 |

35 | 36 | --- 37 | 38 | Nora is a Firebase abstraction layer for working with FirebaseDatabase and FirebaseStorage. 39 | 40 | Stop spending all that time cleaning up your view controllers and trying to write reusable Firebase code. 41 | 42 | Let Nora handle that for you. Your time is better spent on what you do best. Writing great Apps! 43 | 44 | Simply put, working with Firebase just got whole lot easier. 45 | 46 | --- 47 | 48 | ## Installation 49 | 50 | ### [CocoaPods](https://cocoapods.org) Recommended 51 | 52 | ```rb 53 | pod 'Nora' 54 | ``` 55 | 56 | ## Example 57 | 58 | ### Working with FirebaseDatabase 59 | 60 | After [setting up a target](https://github.com/SD10/Nora/blob/master/Documentation/Examples/DatabaseProviderExample.md) making requests is simple. 61 | 62 | ```swift 63 | 64 | let database = DatabaseProvider() 65 | 66 | database.request(.getUser(id: "1")) { result in 67 | switch result { 68 | case .success(let response): 69 | let snapshot = response.snapshot 70 | case .failure(let error): 71 | print(error.localizedDescription) 72 | } 73 | } 74 | 75 | ``` 76 | 77 | ### Database Response Decoding 78 | 79 | Provide a JSON initializer for your custom types and easily convert the database response: 80 | 81 | ```swift 82 | 83 | let database = DatabaseProvider() 84 | 85 | database.request(.getUser(id: "1")) { result in 86 | switch result { 87 | case .success(let response): 88 | let user = try? response.mapTo(User.init) 89 | case .failure(let error): 90 | print(error.localizedDescription) 91 | } 92 | } 93 | 94 | ``` 95 | 96 | --- 97 | 98 | ### Working with FirebaseStorage 99 | 100 | ```swift 101 | 102 | let storage = StorageProvider() 103 | let avatarData = Data() 104 | var meta = FIRStorageMetadata? 105 | 106 | storage.request(.upload(avatarData, meta)) { result in 107 | switch result { 108 | case .success(_): 109 | print("Upload Success!") 110 | case .failure(let error): 111 | print(error.localizedDescription) 112 | } 113 | } 114 | 115 | ``` 116 | 117 | --- 118 | 119 | ## Documentation (In Progress) 120 | 121 | [DatabaseTarget Documentation](https://github.com/SD10/Nora/blob/master/Documentation/Documentation/DatabaseTargetDocs.md) 122 | 123 | [DatabaseProvider Documentation](https://github.com/SD10/Nora/blob/master/Documentation/Documentation/DatabaseProviderDocs.md) 124 | 125 | --- 126 | 127 | ## Get involved 128 | 129 | You can contribute to this project in more ways than just code: 130 | 131 | - Improving the README 132 | - Improving Documentation 133 | - Bug reporting 134 | - New feature suggestions 135 | - Answering questions 136 | 137 | We really encourage everyone to get involved in open source. 138 | 139 | Your feedback is always welcome and much appreciated! 140 | 141 | REGARDLESS, of your level of experience. 142 | 143 | You will never be put down for having an opinion. So take a chance, open a pull request, or submit that issue! 144 | 145 | --- 146 | 147 | ## Inspiration 148 | 149 | This project was inspired and based off the [Moya](https://github.com/Moya/Moya) networking abstraction layer in Swift. 150 | 151 | Check out their project if you're using traditional networking! 152 | 153 | --- 154 | 155 | ## License 156 | 157 | Nora is released under an MIT license. See [LICENSE.md](https://github.com/SD10/Nora/blob/master/LICENSE.md) for more information. 158 | -------------------------------------------------------------------------------- /Screenshots/NoraBanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SD10/Nora/54440a8800fa956e196659c423b21c34dfa0a1f2/Screenshots/NoraBanner.png -------------------------------------------------------------------------------- /Screenshots/Screen Shot 2017-04-05 at 4.07.48 AM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SD10/Nora/54440a8800fa956e196659c423b21c34dfa0a1f2/Screenshots/Screen Shot 2017-04-05 at 4.07.48 AM.png -------------------------------------------------------------------------------- /Screenshots/Screen Shot 2017-04-05 at 4.10.29 AM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SD10/Nora/54440a8800fa956e196659c423b21c34dfa0a1f2/Screenshots/Screen Shot 2017-04-05 at 4.10.29 AM.png -------------------------------------------------------------------------------- /Sources/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SD10/Nora/54440a8800fa956e196659c423b21c34dfa0a1f2/Sources/.DS_Store -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/NRDatabaseProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NRDatabaseProvider.swift 3 | // Nora 4 | // 5 | // Created by Steven on 4/4/17. 6 | // Copyright © 2017 NoraFirebase. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import FirebaseDatabase 11 | import Result 12 | 13 | public typealias DatabaseCompletion = (Result) -> Void 14 | public typealias TransactionBlock = (MutableData) -> TransactionResult 15 | 16 | public class NRDatabaseProvider { 17 | 18 | /// Default init 19 | public init() {} 20 | /// Make a request to FirebaseDatabase 21 | /// - Parameter target: target for the request 22 | /// - Parameter completion: completion block with result of the request 23 | /// - Returns: a handle in the case of an observe request, used to deregister the observer (optional) 24 | @discardableResult 25 | public func request(_ target: Target, completion: @escaping DatabaseCompletion = { _ in }) -> UInt? { 26 | 27 | var handle: UInt? 28 | 29 | switch target.task { 30 | case .observe, .observeOnce: 31 | let request = NRDatabaseQueryRequest(target) 32 | handle = processObserve(request, completion) 33 | case .setValue, .updateChildValues, .removeValue: 34 | let request = NRDatabaseRequest(target) 35 | processWrite(request, completion) 36 | case .transaction: 37 | let request = NRDatabaseRequest(target) 38 | processTransaction(request, completion) 39 | } 40 | return handle 41 | } 42 | 43 | private func processObserve(_ request: NRDatabaseQueryRequest, _ completion: @escaping DatabaseCompletion) -> UInt? { 44 | 45 | let successMapping = { (snapshot: DataSnapshot) in 46 | let result = self.convertResponseToResult(snapshot: snapshot, reference: request.query.ref, error: nil) 47 | completion(result) 48 | } 49 | 50 | let failureMapping = { (error: Error?) in 51 | let result = self.convertResponseToResult(snapshot: nil, reference: nil, error: error) 52 | completion(result) 53 | } 54 | 55 | var handle: UInt? 56 | 57 | switch request.task { 58 | case .observe(let event): 59 | handle = request.query.observe(event, with: successMapping, withCancel: failureMapping) 60 | case .observeOnce(let event): 61 | request.query.observeSingleEvent(of: event, with: successMapping, withCancel: failureMapping) 62 | default: 63 | completion(.failure(NRError.requestMapping)) 64 | } 65 | 66 | return handle 67 | } 68 | 69 | private func processWrite(_ request: NRDatabaseRequest, _ completion: @escaping DatabaseCompletion) { 70 | 71 | let completionBlock = { (error: Error?, reference: DatabaseReference) in 72 | let result = self.convertResponseToResult(snapshot: nil, reference: reference, error: error) 73 | completion(result) 74 | } 75 | 76 | switch request.task { 77 | case .setValue(let value): 78 | 79 | if request.onDisconnect { 80 | request.reference.onDisconnectSetValue(value, withCompletionBlock: completionBlock) 81 | } else { 82 | request.reference.setValue(value, withCompletionBlock: completionBlock) 83 | } 84 | 85 | case .updateChildValues(let values): 86 | 87 | if request.onDisconnect { 88 | request.reference.onDisconnectUpdateChildValues(values, withCompletionBlock: completionBlock) 89 | } else { 90 | request.reference.updateChildValues(values, withCompletionBlock: completionBlock) 91 | } 92 | 93 | case .removeValue: 94 | 95 | if request.onDisconnect { 96 | request.reference.onDisconnectRemoveValue(completionBlock: completionBlock) 97 | } else { 98 | request.reference.removeValue(completionBlock: completionBlock) 99 | } 100 | 101 | default: 102 | completion(.failure(NRError.requestMapping)) 103 | } 104 | } 105 | 106 | private func processTransaction(_ request: NRDatabaseRequest, _ completion: @escaping DatabaseCompletion) { 107 | 108 | let transactionCompletion = { (error: Error?, committed: Bool, snapshot: DataSnapshot?) in 109 | let result = self.convertResponseToResult(snapshot: snapshot, reference: request.reference, error: error, committed: committed) 110 | completion(result) 111 | } 112 | 113 | request.reference.runTransactionBlock(request.transactionBlock, andCompletionBlock: transactionCompletion, withLocalEvents: request.localEvents) 114 | } 115 | } 116 | 117 | private extension NRDatabaseProvider { 118 | 119 | func convertResponseToResult(snapshot: DataSnapshot?, reference: DatabaseReference?, error: Error?, committed: Bool? = nil) -> Result { 120 | 121 | switch (snapshot, reference, error, committed) { 122 | case let (snapshot, .some(reference), .none, .some(committed)): 123 | let response = NRDatabaseResponse(reference: reference, snapshot: snapshot, isCommitted: committed) 124 | return .success(response) 125 | case let (.some(snapshot), .some(reference), .none, _): 126 | let response = NRDatabaseResponse(reference: reference, snapshot: snapshot, isCommitted: true) 127 | return .success(response) 128 | case let (.none, .some(reference), .none, _): 129 | let response = NRDatabaseResponse(reference: reference, isCommitted: true) 130 | return .success(response) 131 | case let (.none, _, .some(error), _): 132 | return .failure(NRError.underlying(error)) 133 | default: 134 | return .failure(NRError.resultConversion) 135 | } 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /Sources/NRDatabaseRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NRDatabaseRequest.swift 3 | // Nora 4 | // 5 | // Created by Steven on 4/8/17. 6 | // Copyright © 2017 NoraFirebase. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import FirebaseDatabase 11 | 12 | // MARK: - NRDatabaseRequest 13 | 14 | struct NRDatabaseRequest { 15 | 16 | var reference: DatabaseReference 17 | var task: NRDatabaseTask 18 | var transactionBlock: (MutableData) -> TransactionResult 19 | var onDisconnect: Bool 20 | var localEvents: Bool 21 | 22 | } 23 | 24 | extension NRDatabaseRequest { 25 | 26 | init(_ target: NRDatabaseTarget) { 27 | self.reference = target.path == "" ? target.baseReference : target.baseReference.child(target.path) 28 | self.task = target.task 29 | self.transactionBlock = target.transactionBlock 30 | self.onDisconnect = target.onDisconnect 31 | self.localEvents = target.localEvents 32 | } 33 | } 34 | 35 | // MARK: - DatabaseQueryRequest 36 | 37 | struct NRDatabaseQueryRequest { 38 | 39 | var query: DatabaseQuery 40 | var task: NRDatabaseTask 41 | 42 | } 43 | 44 | extension NRDatabaseQueryRequest { 45 | 46 | init(_ target: NRDatabaseTarget) { 47 | let reference = target.path == "" ? target.baseReference : target.baseReference.child(target.path) 48 | self.query = target.queries?.reduce(reference) { $1.prepare($0) } ?? reference 49 | self.task = target.task 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /Sources/NRDatabaseResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NRDatabaseResponse.swift 3 | // Nora 4 | // 5 | // Created by Steven on 4/4/17. 6 | // Copyright © 2017 NoraFirebase. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import FirebaseDatabase 11 | 12 | // MARK: - JSONDecodeable 13 | 14 | public protocol JSONDecodeable { 15 | 16 | init?(_ json: [String: Any]) 17 | 18 | } 19 | 20 | // MARK: - NRDatabaseResponse 21 | 22 | public struct NRDatabaseResponse { 23 | 24 | public let snapshot: DataSnapshot? 25 | public let reference: DatabaseReference 26 | public let isCommitted: Bool 27 | } 28 | 29 | public extension NRDatabaseResponse { 30 | 31 | init(reference: DatabaseReference, snapshot: DataSnapshot? = nil, isCommitted: Bool = false) { 32 | self.reference = reference 33 | self.snapshot = snapshot 34 | self.isCommitted = isCommitted 35 | } 36 | 37 | } 38 | 39 | // MARK: - Response Decoding 40 | 41 | public extension NRDatabaseResponse { 42 | 43 | /// The FIRDataSnapshot of the response as JSON 44 | var json: [String: Any]? { 45 | return snapshot?.value as? [String: Any] 46 | } 47 | 48 | /// Retrieve value for FIRDataSnapshot for specified key 49 | /// - Parameter key: key to use to look up value in dictionary 50 | /// - Returns: value for specified key 51 | public func mapKey(_ key: String) throws -> Any { 52 | 53 | guard let snapshot = snapshot, snapshot.exists() else { 54 | throw NRError.nullSnapshot 55 | } 56 | 57 | guard let json = snapshot.value as? [String: Any] else { 58 | throw NRError.jsonMapping 59 | } 60 | 61 | guard let result = json[key] else { 62 | throw NRError.objectDecoding 63 | } 64 | 65 | return result 66 | } 67 | 68 | /// Decode the DataSnapshot to a JSONDecodeable type 69 | /// - Parameter transform: closure that takes in JSON and returns a JSONDecodeable type 70 | /// - Returns: decoded object 71 | public func mapTo(_ transform: ([String: Any]) -> T?) throws -> T { 72 | 73 | guard let snapshot = snapshot, snapshot.exists() else { 74 | throw NRError.nullSnapshot 75 | } 76 | 77 | guard let json = snapshot.value as? [String: Any] else { 78 | throw NRError.jsonMapping 79 | } 80 | 81 | guard let result = T(json) else { 82 | throw NRError.objectDecoding 83 | } 84 | 85 | return result 86 | 87 | } 88 | 89 | /// Convert the children of a DataSnapshot to JSON 90 | /// - Returns: an array of the child snapshots as JSON 91 | public func childrenAsJSON() throws -> [[String: Any]] { 92 | 93 | guard let snapshot = snapshot, snapshot.exists() else { 94 | throw NRError.nullSnapshot 95 | } 96 | 97 | var result: [[String: Any]] = [] 98 | 99 | for child in snapshot.children { 100 | guard let snapshot = child as? DataSnapshot, let json = snapshot.value as? [String: Any] else { 101 | throw NRError.jsonMapping 102 | } 103 | 104 | result.append(json) 105 | } 106 | 107 | return result 108 | 109 | } 110 | 111 | /// Decode the children of DataSnapshot to a JSONDecodeable type 112 | /// - Parameter transform: a closure taking in JSON and returning a JSONDecodeable type 113 | /// - Returns: an array of decoded objects 114 | public func mapChildrenTo(_ transform: ([String: Any]) -> T?) throws -> [T] { 115 | 116 | let childJSON = try childrenAsJSON() 117 | 118 | var result: [T] = [] 119 | 120 | for json in childJSON { 121 | guard let decoded = transform(json) else { 122 | throw NRError.objectDecoding 123 | } 124 | result.append(decoded) 125 | } 126 | 127 | return result 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /Sources/NRError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NRError.swift 3 | // Nora 4 | // 5 | // Created by Steven on 4/4/17. 6 | // Copyright © 2017 NoraFirebase. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - NRError 12 | 13 | public enum NRError: Error { 14 | 15 | case resultConversion 16 | 17 | case requestMapping 18 | 19 | case nullSnapshot 20 | 21 | case jsonMapping 22 | 23 | case objectDecoding 24 | 25 | case underlying(Error) 26 | 27 | } 28 | 29 | // MARK: - LocalizedError 30 | 31 | extension NRError: LocalizedError { 32 | 33 | public var errorDescription: String? { 34 | switch self { 35 | case .resultConversion: 36 | return "There was an error converting the Response to a Result." 37 | case .requestMapping: 38 | return "There was an error mapping the Request to Firebase." 39 | case .nullSnapshot: 40 | return "The FIRDataSnapshot is empty." 41 | case .jsonMapping: 42 | return "The FIRDataSnapshot to JSON conversion failed." 43 | case .objectDecoding: 44 | return "The object decoding for the Response failed." 45 | case .underlying(let error): 46 | return error.localizedDescription 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/NRFirebaseTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirebaseTarget.swift 3 | // Nora 4 | // 5 | // Created by Steven on 4/4/17. 6 | // Copyright © 2017 NoraFirebase. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import FirebaseDatabase 11 | import FirebaseStorage 12 | 13 | // MARK: - NRFirebaseTarget 14 | 15 | public protocol NRFirebaseTarget {} 16 | 17 | public extension NRFirebaseTarget { 18 | 19 | /// Generate unique id String 20 | func uniqueID() -> String { 21 | return UUID().uuidString.lowercased() 22 | } 23 | 24 | } 25 | 26 | // MARK: - NRDatabaseTarget 27 | 28 | public protocol NRDatabaseTarget: NRFirebaseTarget { 29 | 30 | /// Base reference for your target in Database 31 | var baseReference: DatabaseReference { get } 32 | 33 | /// Path to be appended to the base reference 34 | var path: String { get } 35 | 36 | /// Type of task you want to perform ( Firebase method: eg. observe, observeOnce, setValue, etc. ) 37 | var task: NRDatabaseTask { get } 38 | 39 | /// Queries to append to the end of the path in order as listed 40 | var queries: [NRDatabaseQuery]? { get } 41 | 42 | /// Transaction block to run for .transaction task ( Defaults to FIRTransactionResult.success(withValue: FIRMutableData) ) 43 | var transactionBlock: (MutableData) -> TransactionResult { get } 44 | 45 | /// Should task be performed on disconnect ( Defaults to false ) 46 | var onDisconnect: Bool { get } 47 | 48 | /// Allow local events for transaction block ( Defaults to false ) 49 | var localEvents: Bool { get } 50 | 51 | } 52 | 53 | public extension NRDatabaseTarget { 54 | 55 | /// Queries to append to the end of the path in order as listed 56 | var queries: [NRDatabaseQuery]? { 57 | return nil 58 | } 59 | 60 | /// Should task be performed on disconnect ( Defaults to false ) 61 | var onDisconnect: Bool { 62 | return false 63 | } 64 | 65 | /// Allow local events for transaction block ( Defaults to false ) 66 | var localEvents: Bool { 67 | return false 68 | } 69 | 70 | /// Transaction block to run for .transaction task ( Defaults to FIRTransactionResult.success(withValue: FIRMutableData) ) 71 | var transactionBlock: (MutableData) -> TransactionResult { 72 | return { (data: MutableData) in 73 | return TransactionResult.success(withValue: data) 74 | } 75 | } 76 | } 77 | 78 | // MARK: - NRStorageTarget 79 | 80 | public protocol NRStorageTarget: NRFirebaseTarget { 81 | 82 | /// Base reference for your target in Storage 83 | var baseReference: StorageReference { get } 84 | 85 | /// Path to be appended to the base reference 86 | var path: String { get } 87 | 88 | /// Type of task you want to perform ( Firebase storage method: eg. put, putFile, write, delete ) 89 | var task: NRStorageTask { get } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /Sources/NRFirebaseTask.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirebaseTask.swift 3 | // Nora 4 | // 5 | // Created by Steven on 4/4/17. 6 | // Copyright © 2017 NoraFirebase. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import FirebaseDatabase 11 | import FirebaseStorage 12 | 13 | // MARK: - NRDatabaseTask 14 | 15 | /// Represents the read/write methods of Firebase 16 | public enum NRDatabaseTask { 17 | 18 | case observe(DataEventType) 19 | case observeOnce(DataEventType) 20 | case setValue(Any?) 21 | case updateChildValues([AnyHashable: Any]) 22 | case removeValue 23 | case transaction 24 | 25 | } 26 | 27 | // MARK: - NRStorageTask 28 | 29 | /// Represents the read/write methods of FirebaseStorage 30 | public enum NRStorageTask { 31 | 32 | case upload(Data, StorageMetadata?) // put 33 | case uploadFile(to: URL, StorageMetadata?) // putFile 34 | case downloadData(maxSize: Int64) // data 35 | case downloadToURL(URL) // write 36 | case downloadURL // downloadURL 37 | case downloadMetadata // metadata 38 | case update(StorageMetadata) // update 39 | case delete // delete 40 | 41 | } 42 | 43 | // MARK: - NRDatabaseQuery 44 | 45 | /// Represents the DatabaseQuery methods 46 | public enum NRDatabaseQuery { 47 | 48 | case limitedFirst(UInt) // queryLimited(toFirst limit: UInt) 49 | case limitedLast(UInt) // queryLimited(toLast limit: UInt) 50 | case orderedByChild(key: String) // queryOrdered(byChild key: String) 51 | case orderedByKey // queryOrderedByKey() 52 | case orderedByValue // queryOrderedByValue() 53 | case orderedByPriority // queryOrderedByPriority 54 | case startingAtValue(Any?) 55 | case startingAt(value: Any?, childKey: String?) // queryStarting(atValue: Any?, childKey: String?) 56 | case endingAtValue(value: Any?) // queryEnding(atValue: Any?) 57 | case endingAt(value: Any?, childKey: String?) // queryEnding(atValue: Any?, childKey: String?) 58 | case equalToValue(Any?) // queryEqual(toValue: Any?) 59 | case equalTo(value: Any, childKey: String?) // queryEqual(toValue value: Any?, childKey: String?) 60 | 61 | } 62 | 63 | public extension NRDatabaseQuery { 64 | 65 | func prepare(_ query: DatabaseQuery) -> DatabaseQuery { 66 | 67 | switch self { 68 | case .limitedFirst(let n): 69 | return query.queryLimited(toFirst: n) 70 | case .limitedLast(let n): 71 | return query.queryLimited(toLast: n) 72 | case .orderedByChild(let key): 73 | return query.queryOrdered(byChild: key) 74 | case .orderedByKey: 75 | return query.queryOrderedByKey() 76 | case .orderedByValue: 77 | return query.queryOrderedByValue() 78 | case .orderedByPriority: 79 | return query.queryOrderedByPriority() 80 | case .startingAtValue(let value): 81 | return query.queryStarting(atValue: value) 82 | case .startingAt(let value, let childKey): 83 | return query.queryStarting(atValue: value, childKey: childKey) 84 | case .endingAtValue(let value): 85 | return query.queryEnding(atValue: value) 86 | case .endingAt(let value, let childKey): 87 | return query.queryEnding(atValue: value, childKey: childKey) 88 | case .equalToValue(let value): 89 | return query.queryEqual(toValue: value) 90 | case .equalTo(let value, let childKey): 91 | return query.queryEqual(toValue: value, childKey: childKey) 92 | } 93 | } 94 | 95 | } 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /Sources/NRStorageProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NRStorageProvider.swift 3 | // Nora 4 | // 5 | // Created by Steven on 4/4/17. 6 | // Copyright © 2017 NoraFirebase. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import FirebaseStorage 11 | import Result 12 | 13 | public typealias StorageCompletion = (Result) -> Void 14 | 15 | public class NRStorageProvider { 16 | 17 | /// Default init 18 | public init() {} 19 | 20 | @discardableResult 21 | public func request(_ target: Target, completion: @escaping StorageCompletion) -> StorageObservableTask? { 22 | 23 | let request = NRStorageRequest(target) 24 | 25 | switch request.task { 26 | case .upload, .uploadFile, .update, .downloadMetadata, .delete: 27 | return processUpload(request, completion) 28 | case .downloadToURL, .downloadData, .downloadURL: 29 | return processDownload(request, completion) 30 | } 31 | } 32 | 33 | private func processUpload(_ request: NRStorageRequest, _ completion: @escaping StorageCompletion) -> StorageUploadTask? { 34 | 35 | let writeBlock = { (metaData: StorageMetadata?, error: Error?) in 36 | let response = self.convertResponseToResult(data: nil, metaData: metaData, url: nil, error: error) 37 | completion(response) 38 | } 39 | 40 | let deleteBlock = { (error: Error?) in 41 | let response = self.convertResponseToResult(data: nil, metaData: nil, url: nil, error: error) 42 | completion(response) 43 | } 44 | 45 | var uploadTask: StorageUploadTask? 46 | 47 | switch request.task { 48 | case .upload(let data, let metaData): 49 | uploadTask = request.reference.putData(data, metadata: metaData, completion: writeBlock) 50 | case .uploadFile(let url, let metaData): 51 | uploadTask = request.reference.putFile(from: url, metadata: metaData, completion: writeBlock) 52 | case .update(let metadata): 53 | request.reference.updateMetadata(metadata, completion: writeBlock) 54 | case .downloadMetadata: // this is really a download task 55 | request.reference.getMetadata(completion: writeBlock) 56 | case .delete: 57 | request.reference.delete(completion: deleteBlock) 58 | default: 59 | completion(.failure(NRError.requestMapping)) 60 | } 61 | 62 | return uploadTask 63 | } 64 | 65 | private func processDownload(_ request: NRStorageRequest, _ completion: @escaping StorageCompletion) -> StorageDownloadTask? { 66 | 67 | let dataCompletion = { (data: Data?, error: Error?) in 68 | let response = self.convertResponseToResult(data: data, metaData: nil, url: nil, error: error) 69 | completion(response) 70 | } 71 | 72 | let urlCompletion = { (url: URL?, error: Error?) in 73 | let response = self.convertResponseToResult(data: nil, metaData: nil, url: url, error: error) 74 | completion(response) 75 | } 76 | 77 | var downloadTask: StorageDownloadTask? 78 | 79 | switch request.task { 80 | case .downloadData(let maxSize): 81 | downloadTask = request.reference.getData(maxSize: maxSize, completion: dataCompletion) 82 | case .downloadToURL(let url): 83 | downloadTask = request.reference.write(toFile: url, completion: urlCompletion) 84 | case .downloadURL: 85 | request.reference.downloadURL(completion: urlCompletion) 86 | default: 87 | completion(.failure(NRError.requestMapping)) 88 | } 89 | 90 | return downloadTask 91 | } 92 | } 93 | 94 | private extension NRStorageProvider { 95 | 96 | func convertResponseToResult(data: Data?, metaData: StorageMetadata?, url: URL?, error: Error?) -> Result { 97 | 98 | switch (data, metaData, url, error) { 99 | case let (.some(data), _, _, .none): 100 | let response = NRStorageResponse(data: data) 101 | return .success(response) 102 | case let (_, _, .some(url), .none): 103 | let response = NRStorageResponse(url: url) 104 | return .success(response) 105 | case let (_, .some(metaData), _, .none): 106 | let response = NRStorageResponse(metaData: metaData) 107 | return .success(response) 108 | case let (_, _, _, .some(error)): 109 | return .failure(NRError.underlying(error)) 110 | default: 111 | return .failure(NRError.resultConversion) 112 | } 113 | 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /Sources/NRStorageRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NRStorageRequest.swift 3 | // Nora 4 | // 5 | // Created by Steven on 4/8/17. 6 | // Copyright © 2017 NoraFirebase. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import FirebaseStorage 11 | 12 | // MARK: - NRStorageRequest 13 | 14 | struct NRStorageRequest { 15 | 16 | let reference: StorageReference 17 | let task: NRStorageTask 18 | 19 | } 20 | 21 | extension NRStorageRequest { 22 | 23 | init(_ target: NRStorageTarget) { 24 | self.reference = target.path == "" ? target.baseReference : target.baseReference.child(target.path) 25 | self.task = target.task 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Sources/NRStorageResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NRStorageResponse.swift 3 | // Nora 4 | // 5 | // Created by Steven on 4/4/17. 6 | // Copyright © 2017 NoraFirebase. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import FirebaseStorage 11 | 12 | // MARK: - StorageResponse 13 | 14 | public struct NRStorageResponse { 15 | 16 | let data: Data? 17 | let metaData: StorageMetadata? 18 | let url: URL? 19 | } 20 | 21 | 22 | public extension NRStorageResponse { 23 | 24 | init(data: Data) { 25 | self.data = data 26 | self.metaData = nil 27 | self.url = nil 28 | } 29 | 30 | init(url: URL) { 31 | self.url = url 32 | self.metaData = nil 33 | self.data = nil 34 | } 35 | 36 | init(metaData: StorageMetadata) { 37 | self.metaData = metaData 38 | self.url = nil 39 | self.data = nil 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Nora.h: -------------------------------------------------------------------------------- 1 | // 2 | // Nora.h 3 | // Nora 4 | // 5 | // Created by Steven Deutsch on 4/5/18. 6 | // Copyright © 2018 Nora. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Nora. 12 | FOUNDATION_EXPORT double NoraVersionNumber; 13 | 14 | //! Project version string for Nora. 15 | FOUNDATION_EXPORT const unsigned char NoraVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Builds all targets and runs tests. 3 | 4 | DERIVED_DATA=${1:-/tmp/Nora} 5 | echo "Derived data location: $DERIVED_DATA"; 6 | 7 | set -o pipefail && 8 | rm -rf $DERIVED_DATA && 9 | time xcodebuild clean test \ 10 | -workspace Nora.xcworkspace \ 11 | -scheme 'Nora' \ 12 | -sdk iphonesimulator \ 13 | -derivedDataPath $DERIVED_DATA \ 14 | -destination 'platform=iOS Simulator,name=iPhone 7,OS=10.2' \ 15 | OTHER_SWIFT_FLAGS='-Xfrontend -debug-time-function-bodies' \ 16 | | tee build.log \ 17 | | xcpretty && 18 | cat build.log | sh debug-time-function-bodies.sh 19 | -------------------------------------------------------------------------------- /debug-time-function-bodies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Pipe the output of xcodebuild to this script to see the slowest 10 functions to compile. 4 | # Use this flag with xcodebuild: OTHER_SWIFT_FLAGS='-Xfrontend -debug-time-function-bodies' 5 | 6 | echo "\nSlowest functions to compile:\n" 7 | grep '^[0-9]\+\.[0-9]\+' | sed 's/\(^[0-9]*.[0-9]*\)\([a-z]*\)/\1 \2/' | sort -k2,2r -k1,1nr | sed 's/\(^[0-9]*.[0-9]*\) \([a-z]*\)/\1\2/' | head -n 10 | sed 's/^/ /' 8 | echo "\n" 9 | --------------------------------------------------------------------------------