├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── Alba.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── Alba-iOS.xcscheme │ ├── Alba-macOS.xcscheme │ ├── Alba-tvOS.xcscheme │ └── Alba-watchOS.xcscheme ├── Configs ├── Alba.plist └── AlbaTests.plist ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── Alba.swift ├── InformBureau.swift ├── Listener.swift ├── Publisher.swift ├── Subscribe.swift └── WeakSubscribe.swift └── Tests └── AlbaTests └── AlbaTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | *.DS_Store 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | .build/ 40 | 41 | # CocoaPods 42 | # 43 | # We recommend against adding the Pods directory to your .gitignore. However 44 | # you should judge for yourself, the pros and cons are mentioned at: 45 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 46 | # 47 | # Pods/ 48 | 49 | # Carthage 50 | # 51 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 52 | # Carthage/Checkouts 53 | 54 | Carthage/Build 55 | 56 | # fastlane 57 | # 58 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 59 | # screenshots whenever they are needed. 60 | # For more information about the recommended setup visit: 61 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 62 | 63 | fastlane/report.xml 64 | fastlane/Preview.html 65 | fastlane/screenshots 66 | fastlane/test_output -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Alba.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 47; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 52D6D9871BEFF229002C0205 /* Alba.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* Alba.framework */; }; 11 | 52D6D99B1BEFF375002C0205 /* AlbaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D6D9971BEFF375002C0205 /* AlbaTests.swift */; }; 12 | 7C08124B1DE4680100A49B21 /* Alba.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0812461DE4680100A49B21 /* Alba.swift */; }; 13 | 7C08124C1DE4680100A49B21 /* Alba.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0812461DE4680100A49B21 /* Alba.swift */; }; 14 | 7C08124D1DE4680100A49B21 /* Alba.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0812461DE4680100A49B21 /* Alba.swift */; }; 15 | 7C08124E1DE4680100A49B21 /* Alba.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0812461DE4680100A49B21 /* Alba.swift */; }; 16 | 7C08124F1DE4680100A49B21 /* Listener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0812471DE4680100A49B21 /* Listener.swift */; }; 17 | 7C0812501DE4680100A49B21 /* Listener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0812471DE4680100A49B21 /* Listener.swift */; }; 18 | 7C0812511DE4680100A49B21 /* Listener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0812471DE4680100A49B21 /* Listener.swift */; }; 19 | 7C0812521DE4680100A49B21 /* Listener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0812471DE4680100A49B21 /* Listener.swift */; }; 20 | 7C0812571DE4680100A49B21 /* Subscribe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0812491DE4680100A49B21 /* Subscribe.swift */; }; 21 | 7C0812581DE4680100A49B21 /* Subscribe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0812491DE4680100A49B21 /* Subscribe.swift */; }; 22 | 7C0812591DE4680100A49B21 /* Subscribe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0812491DE4680100A49B21 /* Subscribe.swift */; }; 23 | 7C08125A1DE4680100A49B21 /* Subscribe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0812491DE4680100A49B21 /* Subscribe.swift */; }; 24 | 7C08125B1DE4680100A49B21 /* Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C08124A1DE4680100A49B21 /* Publisher.swift */; }; 25 | 7C08125C1DE4680100A49B21 /* Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C08124A1DE4680100A49B21 /* Publisher.swift */; }; 26 | 7C08125D1DE4680100A49B21 /* Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C08124A1DE4680100A49B21 /* Publisher.swift */; }; 27 | 7C08125E1DE4680100A49B21 /* Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C08124A1DE4680100A49B21 /* Publisher.swift */; }; 28 | 7C1873D11E6F3669007241B7 /* WeakSubscribe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1873D01E6F3669007241B7 /* WeakSubscribe.swift */; }; 29 | 7C1873D21E6F3669007241B7 /* WeakSubscribe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1873D01E6F3669007241B7 /* WeakSubscribe.swift */; }; 30 | 7C1873D31E6F3669007241B7 /* WeakSubscribe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1873D01E6F3669007241B7 /* WeakSubscribe.swift */; }; 31 | 7C1873D41E6F3669007241B7 /* WeakSubscribe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1873D01E6F3669007241B7 /* WeakSubscribe.swift */; }; 32 | 7CBC662B1E5874E20033FF36 /* InformBureau.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CBC662A1E5874E20033FF36 /* InformBureau.swift */; }; 33 | 7CBC662C1E5874ED0033FF36 /* InformBureau.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CBC662A1E5874E20033FF36 /* InformBureau.swift */; }; 34 | 7CBC662D1E5874ED0033FF36 /* InformBureau.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CBC662A1E5874E20033FF36 /* InformBureau.swift */; }; 35 | 7CBC662E1E5874ED0033FF36 /* InformBureau.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CBC662A1E5874E20033FF36 /* InformBureau.swift */; }; 36 | DD7502861C68FDDC006590AF /* AlbaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D6D9971BEFF375002C0205 /* AlbaTests.swift */; }; 37 | DD7502881C68FEDE006590AF /* Alba.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6DA0F1BF000BD002C0205 /* Alba.framework */; }; 38 | DD7502921C690C7A006590AF /* Alba.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D9F01BEFFFBE002C0205 /* Alba.framework */; }; 39 | DD75029A1C690CBE006590AF /* AlbaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D6D9971BEFF375002C0205 /* AlbaTests.swift */; }; 40 | /* End PBXBuildFile section */ 41 | 42 | /* Begin PBXContainerItemProxy section */ 43 | 52D6D9881BEFF229002C0205 /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; 46 | proxyType = 1; 47 | remoteGlobalIDString = 52D6D97B1BEFF229002C0205; 48 | remoteInfo = Alba; 49 | }; 50 | DD7502801C68FCFC006590AF /* PBXContainerItemProxy */ = { 51 | isa = PBXContainerItemProxy; 52 | containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; 53 | proxyType = 1; 54 | remoteGlobalIDString = 52D6DA0E1BF000BD002C0205; 55 | remoteInfo = "Alba-macOS"; 56 | }; 57 | DD7502931C690C7A006590AF /* PBXContainerItemProxy */ = { 58 | isa = PBXContainerItemProxy; 59 | containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; 60 | proxyType = 1; 61 | remoteGlobalIDString = 52D6D9EF1BEFFFBE002C0205; 62 | remoteInfo = "Alba-tvOS"; 63 | }; 64 | /* End PBXContainerItemProxy section */ 65 | 66 | /* Begin PBXFileReference section */ 67 | 52D6D97C1BEFF229002C0205 /* Alba.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Alba.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | 52D6D9861BEFF229002C0205 /* Alba-iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Alba-iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | 52D6D9971BEFF375002C0205 /* AlbaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AlbaTests.swift; path = Tests/AlbaTests/AlbaTests.swift; sourceTree = ""; }; 70 | 52D6D9E21BEFFF6E002C0205 /* Alba.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Alba.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | 52D6D9F01BEFFFBE002C0205 /* Alba.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Alba.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 72 | 52D6DA0F1BF000BD002C0205 /* Alba.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Alba.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 73 | 7C0812461DE4680100A49B21 /* Alba.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Alba.swift; path = Sources/Alba.swift; sourceTree = ""; }; 74 | 7C0812471DE4680100A49B21 /* Listener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Listener.swift; path = Sources/Listener.swift; sourceTree = ""; }; 75 | 7C0812491DE4680100A49B21 /* Subscribe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Subscribe.swift; path = Sources/Subscribe.swift; sourceTree = ""; }; 76 | 7C08124A1DE4680100A49B21 /* Publisher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Publisher.swift; path = Sources/Publisher.swift; sourceTree = ""; }; 77 | 7C1873D01E6F3669007241B7 /* WeakSubscribe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WeakSubscribe.swift; path = Sources/WeakSubscribe.swift; sourceTree = ""; }; 78 | 7CBC662A1E5874E20033FF36 /* InformBureau.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InformBureau.swift; path = Sources/InformBureau.swift; sourceTree = ""; }; 79 | AD2FAA261CD0B6D800659CF4 /* Alba.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Alba.plist; sourceTree = ""; }; 80 | AD2FAA281CD0B6E100659CF4 /* AlbaTests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AlbaTests.plist; sourceTree = ""; }; 81 | DD75027A1C68FCFC006590AF /* Alba-macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Alba-macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 82 | DD75028D1C690C7A006590AF /* Alba-tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Alba-tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 83 | /* End PBXFileReference section */ 84 | 85 | /* Begin PBXFrameworksBuildPhase section */ 86 | 52D6D9781BEFF229002C0205 /* Frameworks */ = { 87 | isa = PBXFrameworksBuildPhase; 88 | buildActionMask = 2147483647; 89 | files = ( 90 | ); 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | 52D6D9831BEFF229002C0205 /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | 52D6D9871BEFF229002C0205 /* Alba.framework in Frameworks */, 98 | ); 99 | runOnlyForDeploymentPostprocessing = 0; 100 | }; 101 | 52D6D9DE1BEFFF6E002C0205 /* Frameworks */ = { 102 | isa = PBXFrameworksBuildPhase; 103 | buildActionMask = 2147483647; 104 | files = ( 105 | ); 106 | runOnlyForDeploymentPostprocessing = 0; 107 | }; 108 | 52D6D9EC1BEFFFBE002C0205 /* Frameworks */ = { 109 | isa = PBXFrameworksBuildPhase; 110 | buildActionMask = 2147483647; 111 | files = ( 112 | ); 113 | runOnlyForDeploymentPostprocessing = 0; 114 | }; 115 | 52D6DA0B1BF000BD002C0205 /* Frameworks */ = { 116 | isa = PBXFrameworksBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | ); 120 | runOnlyForDeploymentPostprocessing = 0; 121 | }; 122 | DD7502771C68FCFC006590AF /* Frameworks */ = { 123 | isa = PBXFrameworksBuildPhase; 124 | buildActionMask = 2147483647; 125 | files = ( 126 | DD7502881C68FEDE006590AF /* Alba.framework in Frameworks */, 127 | ); 128 | runOnlyForDeploymentPostprocessing = 0; 129 | }; 130 | DD75028A1C690C7A006590AF /* Frameworks */ = { 131 | isa = PBXFrameworksBuildPhase; 132 | buildActionMask = 2147483647; 133 | files = ( 134 | DD7502921C690C7A006590AF /* Alba.framework in Frameworks */, 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | /* End PBXFrameworksBuildPhase section */ 139 | 140 | /* Begin PBXGroup section */ 141 | 52D6D9721BEFF229002C0205 = { 142 | isa = PBXGroup; 143 | children = ( 144 | 7C0812451DE467C700A49B21 /* Sources */, 145 | 52D6D9971BEFF375002C0205 /* AlbaTests.swift */, 146 | 52D6D99C1BEFF38C002C0205 /* Configs */, 147 | 52D6D97D1BEFF229002C0205 /* Products */, 148 | ); 149 | sourceTree = ""; 150 | }; 151 | 52D6D97D1BEFF229002C0205 /* Products */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 52D6D97C1BEFF229002C0205 /* Alba.framework */, 155 | 52D6D9861BEFF229002C0205 /* Alba-iOS Tests.xctest */, 156 | 52D6D9E21BEFFF6E002C0205 /* Alba.framework */, 157 | 52D6D9F01BEFFFBE002C0205 /* Alba.framework */, 158 | 52D6DA0F1BF000BD002C0205 /* Alba.framework */, 159 | DD75027A1C68FCFC006590AF /* Alba-macOS Tests.xctest */, 160 | DD75028D1C690C7A006590AF /* Alba-tvOS Tests.xctest */, 161 | ); 162 | name = Products; 163 | sourceTree = ""; 164 | }; 165 | 52D6D99C1BEFF38C002C0205 /* Configs */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | DD7502721C68FC1B006590AF /* Frameworks */, 169 | DD7502731C68FC20006590AF /* Tests */, 170 | ); 171 | path = Configs; 172 | sourceTree = ""; 173 | }; 174 | 7C0812451DE467C700A49B21 /* Sources */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | 7C0812461DE4680100A49B21 /* Alba.swift */, 178 | 7C0812471DE4680100A49B21 /* Listener.swift */, 179 | 7C0812491DE4680100A49B21 /* Subscribe.swift */, 180 | 7C1873D01E6F3669007241B7 /* WeakSubscribe.swift */, 181 | 7C08124A1DE4680100A49B21 /* Publisher.swift */, 182 | 7CBC662A1E5874E20033FF36 /* InformBureau.swift */, 183 | ); 184 | name = Sources; 185 | sourceTree = ""; 186 | }; 187 | DD7502721C68FC1B006590AF /* Frameworks */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | AD2FAA261CD0B6D800659CF4 /* Alba.plist */, 191 | ); 192 | name = Frameworks; 193 | sourceTree = ""; 194 | }; 195 | DD7502731C68FC20006590AF /* Tests */ = { 196 | isa = PBXGroup; 197 | children = ( 198 | AD2FAA281CD0B6E100659CF4 /* AlbaTests.plist */, 199 | ); 200 | name = Tests; 201 | sourceTree = ""; 202 | }; 203 | /* End PBXGroup section */ 204 | 205 | /* Begin PBXHeadersBuildPhase section */ 206 | 52D6D9791BEFF229002C0205 /* Headers */ = { 207 | isa = PBXHeadersBuildPhase; 208 | buildActionMask = 2147483647; 209 | files = ( 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | }; 213 | 52D6D9DF1BEFFF6E002C0205 /* Headers */ = { 214 | isa = PBXHeadersBuildPhase; 215 | buildActionMask = 2147483647; 216 | files = ( 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | 52D6D9ED1BEFFFBE002C0205 /* Headers */ = { 221 | isa = PBXHeadersBuildPhase; 222 | buildActionMask = 2147483647; 223 | files = ( 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | }; 227 | 52D6DA0C1BF000BD002C0205 /* Headers */ = { 228 | isa = PBXHeadersBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | runOnlyForDeploymentPostprocessing = 0; 233 | }; 234 | /* End PBXHeadersBuildPhase section */ 235 | 236 | /* Begin PBXNativeTarget section */ 237 | 52D6D97B1BEFF229002C0205 /* Alba-iOS */ = { 238 | isa = PBXNativeTarget; 239 | buildConfigurationList = 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Alba-iOS" */; 240 | buildPhases = ( 241 | 52D6D9771BEFF229002C0205 /* Sources */, 242 | 52D6D9781BEFF229002C0205 /* Frameworks */, 243 | 52D6D9791BEFF229002C0205 /* Headers */, 244 | 52D6D97A1BEFF229002C0205 /* Resources */, 245 | ); 246 | buildRules = ( 247 | ); 248 | dependencies = ( 249 | ); 250 | name = "Alba-iOS"; 251 | productName = Alba; 252 | productReference = 52D6D97C1BEFF229002C0205 /* Alba.framework */; 253 | productType = "com.apple.product-type.framework"; 254 | }; 255 | 52D6D9851BEFF229002C0205 /* Alba-iOS Tests */ = { 256 | isa = PBXNativeTarget; 257 | buildConfigurationList = 52D6D9931BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Alba-iOS Tests" */; 258 | buildPhases = ( 259 | 52D6D9821BEFF229002C0205 /* Sources */, 260 | 52D6D9831BEFF229002C0205 /* Frameworks */, 261 | 52D6D9841BEFF229002C0205 /* Resources */, 262 | ); 263 | buildRules = ( 264 | ); 265 | dependencies = ( 266 | 52D6D9891BEFF229002C0205 /* PBXTargetDependency */, 267 | ); 268 | name = "Alba-iOS Tests"; 269 | productName = AlbaTests; 270 | productReference = 52D6D9861BEFF229002C0205 /* Alba-iOS Tests.xctest */; 271 | productType = "com.apple.product-type.bundle.unit-test"; 272 | }; 273 | 52D6D9E11BEFFF6E002C0205 /* Alba-watchOS */ = { 274 | isa = PBXNativeTarget; 275 | buildConfigurationList = 52D6D9E71BEFFF6E002C0205 /* Build configuration list for PBXNativeTarget "Alba-watchOS" */; 276 | buildPhases = ( 277 | 52D6D9DD1BEFFF6E002C0205 /* Sources */, 278 | 52D6D9DE1BEFFF6E002C0205 /* Frameworks */, 279 | 52D6D9DF1BEFFF6E002C0205 /* Headers */, 280 | 52D6D9E01BEFFF6E002C0205 /* Resources */, 281 | ); 282 | buildRules = ( 283 | ); 284 | dependencies = ( 285 | ); 286 | name = "Alba-watchOS"; 287 | productName = "Alba-watchOS"; 288 | productReference = 52D6D9E21BEFFF6E002C0205 /* Alba.framework */; 289 | productType = "com.apple.product-type.framework"; 290 | }; 291 | 52D6D9EF1BEFFFBE002C0205 /* Alba-tvOS */ = { 292 | isa = PBXNativeTarget; 293 | buildConfigurationList = 52D6DA011BEFFFBE002C0205 /* Build configuration list for PBXNativeTarget "Alba-tvOS" */; 294 | buildPhases = ( 295 | 52D6D9EB1BEFFFBE002C0205 /* Sources */, 296 | 52D6D9EC1BEFFFBE002C0205 /* Frameworks */, 297 | 52D6D9ED1BEFFFBE002C0205 /* Headers */, 298 | 52D6D9EE1BEFFFBE002C0205 /* Resources */, 299 | ); 300 | buildRules = ( 301 | ); 302 | dependencies = ( 303 | ); 304 | name = "Alba-tvOS"; 305 | productName = "Alba-tvOS"; 306 | productReference = 52D6D9F01BEFFFBE002C0205 /* Alba.framework */; 307 | productType = "com.apple.product-type.framework"; 308 | }; 309 | 52D6DA0E1BF000BD002C0205 /* Alba-macOS */ = { 310 | isa = PBXNativeTarget; 311 | buildConfigurationList = 52D6DA201BF000BD002C0205 /* Build configuration list for PBXNativeTarget "Alba-macOS" */; 312 | buildPhases = ( 313 | 52D6DA0A1BF000BD002C0205 /* Sources */, 314 | 52D6DA0B1BF000BD002C0205 /* Frameworks */, 315 | 52D6DA0C1BF000BD002C0205 /* Headers */, 316 | 52D6DA0D1BF000BD002C0205 /* Resources */, 317 | ); 318 | buildRules = ( 319 | ); 320 | dependencies = ( 321 | ); 322 | name = "Alba-macOS"; 323 | productName = "Alba-macOS"; 324 | productReference = 52D6DA0F1BF000BD002C0205 /* Alba.framework */; 325 | productType = "com.apple.product-type.framework"; 326 | }; 327 | DD7502791C68FCFC006590AF /* Alba-macOS Tests */ = { 328 | isa = PBXNativeTarget; 329 | buildConfigurationList = DD7502821C68FCFC006590AF /* Build configuration list for PBXNativeTarget "Alba-macOS Tests" */; 330 | buildPhases = ( 331 | DD7502761C68FCFC006590AF /* Sources */, 332 | DD7502771C68FCFC006590AF /* Frameworks */, 333 | DD7502781C68FCFC006590AF /* Resources */, 334 | ); 335 | buildRules = ( 336 | ); 337 | dependencies = ( 338 | DD7502811C68FCFC006590AF /* PBXTargetDependency */, 339 | ); 340 | name = "Alba-macOS Tests"; 341 | productName = "Alba-OS Tests"; 342 | productReference = DD75027A1C68FCFC006590AF /* Alba-macOS Tests.xctest */; 343 | productType = "com.apple.product-type.bundle.unit-test"; 344 | }; 345 | DD75028C1C690C7A006590AF /* Alba-tvOS Tests */ = { 346 | isa = PBXNativeTarget; 347 | buildConfigurationList = DD7502951C690C7A006590AF /* Build configuration list for PBXNativeTarget "Alba-tvOS Tests" */; 348 | buildPhases = ( 349 | DD7502891C690C7A006590AF /* Sources */, 350 | DD75028A1C690C7A006590AF /* Frameworks */, 351 | DD75028B1C690C7A006590AF /* Resources */, 352 | ); 353 | buildRules = ( 354 | ); 355 | dependencies = ( 356 | DD7502941C690C7A006590AF /* PBXTargetDependency */, 357 | ); 358 | name = "Alba-tvOS Tests"; 359 | productName = "Alba-tvOS Tests"; 360 | productReference = DD75028D1C690C7A006590AF /* Alba-tvOS Tests.xctest */; 361 | productType = "com.apple.product-type.bundle.unit-test"; 362 | }; 363 | /* End PBXNativeTarget section */ 364 | 365 | /* Begin PBXProject section */ 366 | 52D6D9731BEFF229002C0205 /* Project object */ = { 367 | isa = PBXProject; 368 | attributes = { 369 | LastSwiftUpdateCheck = 0720; 370 | LastUpgradeCheck = 1220; 371 | ORGANIZATIONNAME = "John Sundell"; 372 | TargetAttributes = { 373 | 52D6D97B1BEFF229002C0205 = { 374 | CreatedOnToolsVersion = 7.1; 375 | LastSwiftMigration = 1220; 376 | ProvisioningStyle = Automatic; 377 | }; 378 | 52D6D9851BEFF229002C0205 = { 379 | CreatedOnToolsVersion = 7.1; 380 | LastSwiftMigration = 1220; 381 | }; 382 | 52D6D9E11BEFFF6E002C0205 = { 383 | CreatedOnToolsVersion = 7.1; 384 | LastSwiftMigration = 1220; 385 | }; 386 | 52D6D9EF1BEFFFBE002C0205 = { 387 | CreatedOnToolsVersion = 7.1; 388 | LastSwiftMigration = 1220; 389 | }; 390 | 52D6DA0E1BF000BD002C0205 = { 391 | CreatedOnToolsVersion = 7.1; 392 | LastSwiftMigration = 1220; 393 | }; 394 | DD7502791C68FCFC006590AF = { 395 | CreatedOnToolsVersion = 7.2.1; 396 | LastSwiftMigration = 1220; 397 | }; 398 | DD75028C1C690C7A006590AF = { 399 | CreatedOnToolsVersion = 7.2.1; 400 | LastSwiftMigration = 1220; 401 | }; 402 | }; 403 | }; 404 | buildConfigurationList = 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "Alba" */; 405 | compatibilityVersion = "Xcode 6.3"; 406 | developmentRegion = en; 407 | hasScannedForEncodings = 0; 408 | knownRegions = ( 409 | en, 410 | Base, 411 | ); 412 | mainGroup = 52D6D9721BEFF229002C0205; 413 | productRefGroup = 52D6D97D1BEFF229002C0205 /* Products */; 414 | projectDirPath = ""; 415 | projectRoot = ""; 416 | targets = ( 417 | 52D6D97B1BEFF229002C0205 /* Alba-iOS */, 418 | 52D6DA0E1BF000BD002C0205 /* Alba-macOS */, 419 | 52D6D9E11BEFFF6E002C0205 /* Alba-watchOS */, 420 | 52D6D9EF1BEFFFBE002C0205 /* Alba-tvOS */, 421 | 52D6D9851BEFF229002C0205 /* Alba-iOS Tests */, 422 | DD7502791C68FCFC006590AF /* Alba-macOS Tests */, 423 | DD75028C1C690C7A006590AF /* Alba-tvOS Tests */, 424 | ); 425 | }; 426 | /* End PBXProject section */ 427 | 428 | /* Begin PBXResourcesBuildPhase section */ 429 | 52D6D97A1BEFF229002C0205 /* Resources */ = { 430 | isa = PBXResourcesBuildPhase; 431 | buildActionMask = 2147483647; 432 | files = ( 433 | ); 434 | runOnlyForDeploymentPostprocessing = 0; 435 | }; 436 | 52D6D9841BEFF229002C0205 /* Resources */ = { 437 | isa = PBXResourcesBuildPhase; 438 | buildActionMask = 2147483647; 439 | files = ( 440 | ); 441 | runOnlyForDeploymentPostprocessing = 0; 442 | }; 443 | 52D6D9E01BEFFF6E002C0205 /* Resources */ = { 444 | isa = PBXResourcesBuildPhase; 445 | buildActionMask = 2147483647; 446 | files = ( 447 | ); 448 | runOnlyForDeploymentPostprocessing = 0; 449 | }; 450 | 52D6D9EE1BEFFFBE002C0205 /* Resources */ = { 451 | isa = PBXResourcesBuildPhase; 452 | buildActionMask = 2147483647; 453 | files = ( 454 | ); 455 | runOnlyForDeploymentPostprocessing = 0; 456 | }; 457 | 52D6DA0D1BF000BD002C0205 /* Resources */ = { 458 | isa = PBXResourcesBuildPhase; 459 | buildActionMask = 2147483647; 460 | files = ( 461 | ); 462 | runOnlyForDeploymentPostprocessing = 0; 463 | }; 464 | DD7502781C68FCFC006590AF /* Resources */ = { 465 | isa = PBXResourcesBuildPhase; 466 | buildActionMask = 2147483647; 467 | files = ( 468 | ); 469 | runOnlyForDeploymentPostprocessing = 0; 470 | }; 471 | DD75028B1C690C7A006590AF /* Resources */ = { 472 | isa = PBXResourcesBuildPhase; 473 | buildActionMask = 2147483647; 474 | files = ( 475 | ); 476 | runOnlyForDeploymentPostprocessing = 0; 477 | }; 478 | /* End PBXResourcesBuildPhase section */ 479 | 480 | /* Begin PBXSourcesBuildPhase section */ 481 | 52D6D9771BEFF229002C0205 /* Sources */ = { 482 | isa = PBXSourcesBuildPhase; 483 | buildActionMask = 2147483647; 484 | files = ( 485 | 7C1873D11E6F3669007241B7 /* WeakSubscribe.swift in Sources */, 486 | 7CBC662B1E5874E20033FF36 /* InformBureau.swift in Sources */, 487 | 7C0812571DE4680100A49B21 /* Subscribe.swift in Sources */, 488 | 7C08124B1DE4680100A49B21 /* Alba.swift in Sources */, 489 | 7C08124F1DE4680100A49B21 /* Listener.swift in Sources */, 490 | 7C08125B1DE4680100A49B21 /* Publisher.swift in Sources */, 491 | ); 492 | runOnlyForDeploymentPostprocessing = 0; 493 | }; 494 | 52D6D9821BEFF229002C0205 /* Sources */ = { 495 | isa = PBXSourcesBuildPhase; 496 | buildActionMask = 2147483647; 497 | files = ( 498 | 52D6D99B1BEFF375002C0205 /* AlbaTests.swift in Sources */, 499 | ); 500 | runOnlyForDeploymentPostprocessing = 0; 501 | }; 502 | 52D6D9DD1BEFFF6E002C0205 /* Sources */ = { 503 | isa = PBXSourcesBuildPhase; 504 | buildActionMask = 2147483647; 505 | files = ( 506 | 7C1873D31E6F3669007241B7 /* WeakSubscribe.swift in Sources */, 507 | 7CBC662D1E5874ED0033FF36 /* InformBureau.swift in Sources */, 508 | 7C0812591DE4680100A49B21 /* Subscribe.swift in Sources */, 509 | 7C08124D1DE4680100A49B21 /* Alba.swift in Sources */, 510 | 7C0812511DE4680100A49B21 /* Listener.swift in Sources */, 511 | 7C08125D1DE4680100A49B21 /* Publisher.swift in Sources */, 512 | ); 513 | runOnlyForDeploymentPostprocessing = 0; 514 | }; 515 | 52D6D9EB1BEFFFBE002C0205 /* Sources */ = { 516 | isa = PBXSourcesBuildPhase; 517 | buildActionMask = 2147483647; 518 | files = ( 519 | 7C1873D41E6F3669007241B7 /* WeakSubscribe.swift in Sources */, 520 | 7CBC662E1E5874ED0033FF36 /* InformBureau.swift in Sources */, 521 | 7C08125A1DE4680100A49B21 /* Subscribe.swift in Sources */, 522 | 7C08124E1DE4680100A49B21 /* Alba.swift in Sources */, 523 | 7C0812521DE4680100A49B21 /* Listener.swift in Sources */, 524 | 7C08125E1DE4680100A49B21 /* Publisher.swift in Sources */, 525 | ); 526 | runOnlyForDeploymentPostprocessing = 0; 527 | }; 528 | 52D6DA0A1BF000BD002C0205 /* Sources */ = { 529 | isa = PBXSourcesBuildPhase; 530 | buildActionMask = 2147483647; 531 | files = ( 532 | 7C1873D21E6F3669007241B7 /* WeakSubscribe.swift in Sources */, 533 | 7CBC662C1E5874ED0033FF36 /* InformBureau.swift in Sources */, 534 | 7C0812581DE4680100A49B21 /* Subscribe.swift in Sources */, 535 | 7C08124C1DE4680100A49B21 /* Alba.swift in Sources */, 536 | 7C0812501DE4680100A49B21 /* Listener.swift in Sources */, 537 | 7C08125C1DE4680100A49B21 /* Publisher.swift in Sources */, 538 | ); 539 | runOnlyForDeploymentPostprocessing = 0; 540 | }; 541 | DD7502761C68FCFC006590AF /* Sources */ = { 542 | isa = PBXSourcesBuildPhase; 543 | buildActionMask = 2147483647; 544 | files = ( 545 | DD7502861C68FDDC006590AF /* AlbaTests.swift in Sources */, 546 | ); 547 | runOnlyForDeploymentPostprocessing = 0; 548 | }; 549 | DD7502891C690C7A006590AF /* Sources */ = { 550 | isa = PBXSourcesBuildPhase; 551 | buildActionMask = 2147483647; 552 | files = ( 553 | DD75029A1C690CBE006590AF /* AlbaTests.swift in Sources */, 554 | ); 555 | runOnlyForDeploymentPostprocessing = 0; 556 | }; 557 | /* End PBXSourcesBuildPhase section */ 558 | 559 | /* Begin PBXTargetDependency section */ 560 | 52D6D9891BEFF229002C0205 /* PBXTargetDependency */ = { 561 | isa = PBXTargetDependency; 562 | target = 52D6D97B1BEFF229002C0205 /* Alba-iOS */; 563 | targetProxy = 52D6D9881BEFF229002C0205 /* PBXContainerItemProxy */; 564 | }; 565 | DD7502811C68FCFC006590AF /* PBXTargetDependency */ = { 566 | isa = PBXTargetDependency; 567 | target = 52D6DA0E1BF000BD002C0205 /* Alba-macOS */; 568 | targetProxy = DD7502801C68FCFC006590AF /* PBXContainerItemProxy */; 569 | }; 570 | DD7502941C690C7A006590AF /* PBXTargetDependency */ = { 571 | isa = PBXTargetDependency; 572 | target = 52D6D9EF1BEFFFBE002C0205 /* Alba-tvOS */; 573 | targetProxy = DD7502931C690C7A006590AF /* PBXContainerItemProxy */; 574 | }; 575 | /* End PBXTargetDependency section */ 576 | 577 | /* Begin XCBuildConfiguration section */ 578 | 52D6D98E1BEFF229002C0205 /* Debug */ = { 579 | isa = XCBuildConfiguration; 580 | buildSettings = { 581 | ALWAYS_SEARCH_USER_PATHS = NO; 582 | ASSETCATALOG_COMPRESSION = lossless; 583 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 584 | CLANG_CXX_LIBRARY = "libc++"; 585 | CLANG_ENABLE_MODULES = YES; 586 | CLANG_ENABLE_OBJC_ARC = YES; 587 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 588 | CLANG_WARN_BOOL_CONVERSION = YES; 589 | CLANG_WARN_COMMA = YES; 590 | CLANG_WARN_CONSTANT_CONVERSION = YES; 591 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 592 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 593 | CLANG_WARN_EMPTY_BODY = YES; 594 | CLANG_WARN_ENUM_CONVERSION = YES; 595 | CLANG_WARN_INFINITE_RECURSION = YES; 596 | CLANG_WARN_INT_CONVERSION = YES; 597 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 598 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 599 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 600 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 601 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 602 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 603 | CLANG_WARN_STRICT_PROTOTYPES = YES; 604 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 605 | CLANG_WARN_UNREACHABLE_CODE = YES; 606 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 607 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 608 | COPY_PHASE_STRIP = NO; 609 | CURRENT_PROJECT_VERSION = 1; 610 | DEBUG_INFORMATION_FORMAT = dwarf; 611 | ENABLE_STRICT_OBJC_MSGSEND = YES; 612 | ENABLE_TESTABILITY = YES; 613 | GCC_C_LANGUAGE_STANDARD = gnu99; 614 | GCC_DYNAMIC_NO_PIC = NO; 615 | GCC_NO_COMMON_BLOCKS = YES; 616 | GCC_OPTIMIZATION_LEVEL = 0; 617 | GCC_PREPROCESSOR_DEFINITIONS = ( 618 | "DEBUG=1", 619 | "$(inherited)", 620 | ); 621 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 622 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 623 | GCC_WARN_UNDECLARED_SELECTOR = YES; 624 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 625 | GCC_WARN_UNUSED_FUNCTION = YES; 626 | GCC_WARN_UNUSED_VARIABLE = YES; 627 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 628 | MTL_ENABLE_DEBUG_INFO = YES; 629 | ONLY_ACTIVE_ARCH = YES; 630 | SDKROOT = iphoneos; 631 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 632 | SWIFT_VERSION = 3.0; 633 | TARGETED_DEVICE_FAMILY = "1,2"; 634 | VERSIONING_SYSTEM = "apple-generic"; 635 | VERSION_INFO_PREFIX = ""; 636 | }; 637 | name = Debug; 638 | }; 639 | 52D6D98F1BEFF229002C0205 /* Release */ = { 640 | isa = XCBuildConfiguration; 641 | buildSettings = { 642 | ALWAYS_SEARCH_USER_PATHS = NO; 643 | ASSETCATALOG_COMPRESSION = "respect-asset-catalog"; 644 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 645 | CLANG_CXX_LIBRARY = "libc++"; 646 | CLANG_ENABLE_MODULES = YES; 647 | CLANG_ENABLE_OBJC_ARC = YES; 648 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 649 | CLANG_WARN_BOOL_CONVERSION = YES; 650 | CLANG_WARN_COMMA = YES; 651 | CLANG_WARN_CONSTANT_CONVERSION = YES; 652 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 653 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 654 | CLANG_WARN_EMPTY_BODY = YES; 655 | CLANG_WARN_ENUM_CONVERSION = YES; 656 | CLANG_WARN_INFINITE_RECURSION = YES; 657 | CLANG_WARN_INT_CONVERSION = YES; 658 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 659 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 660 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 661 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 662 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 663 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 664 | CLANG_WARN_STRICT_PROTOTYPES = YES; 665 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 666 | CLANG_WARN_UNREACHABLE_CODE = YES; 667 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 668 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 669 | COPY_PHASE_STRIP = NO; 670 | CURRENT_PROJECT_VERSION = 1; 671 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 672 | ENABLE_NS_ASSERTIONS = NO; 673 | ENABLE_STRICT_OBJC_MSGSEND = YES; 674 | GCC_C_LANGUAGE_STANDARD = gnu99; 675 | GCC_NO_COMMON_BLOCKS = YES; 676 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 677 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 678 | GCC_WARN_UNDECLARED_SELECTOR = YES; 679 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 680 | GCC_WARN_UNUSED_FUNCTION = YES; 681 | GCC_WARN_UNUSED_VARIABLE = YES; 682 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 683 | MTL_ENABLE_DEBUG_INFO = NO; 684 | SDKROOT = iphoneos; 685 | SWIFT_VERSION = 3.0; 686 | TARGETED_DEVICE_FAMILY = "1,2"; 687 | VALIDATE_PRODUCT = YES; 688 | VERSIONING_SYSTEM = "apple-generic"; 689 | VERSION_INFO_PREFIX = ""; 690 | }; 691 | name = Release; 692 | }; 693 | 52D6D9911BEFF229002C0205 /* Debug */ = { 694 | isa = XCBuildConfiguration; 695 | buildSettings = { 696 | APPLICATION_EXTENSION_API_ONLY = YES; 697 | ASSETCATALOG_COMPRESSION = lossless; 698 | CLANG_ENABLE_MODULES = YES; 699 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 700 | DEFINES_MODULE = YES; 701 | DYLIB_COMPATIBILITY_VERSION = 1; 702 | DYLIB_CURRENT_VERSION = 1; 703 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 704 | INFOPLIST_FILE = Configs/Alba.plist; 705 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 706 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 707 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 708 | ONLY_ACTIVE_ARCH = NO; 709 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-iOS"; 710 | PRODUCT_NAME = Alba; 711 | SKIP_INSTALL = YES; 712 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 713 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 714 | SWIFT_VERSION = 5.0; 715 | }; 716 | name = Debug; 717 | }; 718 | 52D6D9921BEFF229002C0205 /* Release */ = { 719 | isa = XCBuildConfiguration; 720 | buildSettings = { 721 | APPLICATION_EXTENSION_API_ONLY = YES; 722 | ASSETCATALOG_COMPRESSION = "respect-asset-catalog"; 723 | CLANG_ENABLE_MODULES = YES; 724 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 725 | DEFINES_MODULE = YES; 726 | DYLIB_COMPATIBILITY_VERSION = 1; 727 | DYLIB_CURRENT_VERSION = 1; 728 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 729 | INFOPLIST_FILE = Configs/Alba.plist; 730 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 731 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 732 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 733 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-iOS"; 734 | PRODUCT_NAME = Alba; 735 | SKIP_INSTALL = YES; 736 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 737 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 738 | SWIFT_VERSION = 5.0; 739 | }; 740 | name = Release; 741 | }; 742 | 52D6D9941BEFF229002C0205 /* Debug */ = { 743 | isa = XCBuildConfiguration; 744 | buildSettings = { 745 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 746 | CLANG_ENABLE_MODULES = YES; 747 | INFOPLIST_FILE = Configs/AlbaTests.plist; 748 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 749 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 750 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-iOS-Tests"; 751 | PRODUCT_NAME = "$(TARGET_NAME)"; 752 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 753 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 754 | SWIFT_VERSION = 5.0; 755 | }; 756 | name = Debug; 757 | }; 758 | 52D6D9951BEFF229002C0205 /* Release */ = { 759 | isa = XCBuildConfiguration; 760 | buildSettings = { 761 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 762 | CLANG_ENABLE_MODULES = YES; 763 | INFOPLIST_FILE = Configs/AlbaTests.plist; 764 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 765 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 766 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-iOS-Tests"; 767 | PRODUCT_NAME = "$(TARGET_NAME)"; 768 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 769 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 770 | SWIFT_VERSION = 5.0; 771 | }; 772 | name = Release; 773 | }; 774 | 52D6D9E81BEFFF6E002C0205 /* Debug */ = { 775 | isa = XCBuildConfiguration; 776 | buildSettings = { 777 | APPLICATION_EXTENSION_API_ONLY = YES; 778 | CLANG_ENABLE_MODULES = YES; 779 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 780 | DEFINES_MODULE = YES; 781 | DYLIB_COMPATIBILITY_VERSION = 1; 782 | DYLIB_CURRENT_VERSION = 1; 783 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 784 | INFOPLIST_FILE = Configs/Alba.plist; 785 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 786 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 787 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-watchOS"; 788 | PRODUCT_NAME = Alba; 789 | SDKROOT = watchos; 790 | SKIP_INSTALL = YES; 791 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 792 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 793 | SWIFT_VERSION = 5.0; 794 | TARGETED_DEVICE_FAMILY = 4; 795 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 796 | }; 797 | name = Debug; 798 | }; 799 | 52D6D9E91BEFFF6E002C0205 /* Release */ = { 800 | isa = XCBuildConfiguration; 801 | buildSettings = { 802 | APPLICATION_EXTENSION_API_ONLY = YES; 803 | CLANG_ENABLE_MODULES = YES; 804 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 805 | DEFINES_MODULE = YES; 806 | DYLIB_COMPATIBILITY_VERSION = 1; 807 | DYLIB_CURRENT_VERSION = 1; 808 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 809 | INFOPLIST_FILE = Configs/Alba.plist; 810 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 811 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 812 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-watchOS"; 813 | PRODUCT_NAME = Alba; 814 | SDKROOT = watchos; 815 | SKIP_INSTALL = YES; 816 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 817 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 818 | SWIFT_VERSION = 5.0; 819 | TARGETED_DEVICE_FAMILY = 4; 820 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 821 | }; 822 | name = Release; 823 | }; 824 | 52D6DA021BEFFFBE002C0205 /* Debug */ = { 825 | isa = XCBuildConfiguration; 826 | buildSettings = { 827 | APPLICATION_EXTENSION_API_ONLY = YES; 828 | CLANG_ENABLE_MODULES = YES; 829 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 830 | DEFINES_MODULE = YES; 831 | DYLIB_COMPATIBILITY_VERSION = 1; 832 | DYLIB_CURRENT_VERSION = 1; 833 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 834 | INFOPLIST_FILE = Configs/Alba.plist; 835 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 836 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 837 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-tvOS"; 838 | PRODUCT_NAME = Alba; 839 | SDKROOT = appletvos; 840 | SKIP_INSTALL = YES; 841 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 842 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 843 | SWIFT_VERSION = 5.0; 844 | TARGETED_DEVICE_FAMILY = 3; 845 | TVOS_DEPLOYMENT_TARGET = 9.0; 846 | }; 847 | name = Debug; 848 | }; 849 | 52D6DA031BEFFFBE002C0205 /* Release */ = { 850 | isa = XCBuildConfiguration; 851 | buildSettings = { 852 | APPLICATION_EXTENSION_API_ONLY = YES; 853 | CLANG_ENABLE_MODULES = YES; 854 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 855 | DEFINES_MODULE = YES; 856 | DYLIB_COMPATIBILITY_VERSION = 1; 857 | DYLIB_CURRENT_VERSION = 1; 858 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 859 | INFOPLIST_FILE = Configs/Alba.plist; 860 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 861 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 862 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-tvOS"; 863 | PRODUCT_NAME = Alba; 864 | SDKROOT = appletvos; 865 | SKIP_INSTALL = YES; 866 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 867 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 868 | SWIFT_VERSION = 5.0; 869 | TARGETED_DEVICE_FAMILY = 3; 870 | TVOS_DEPLOYMENT_TARGET = 9.0; 871 | }; 872 | name = Release; 873 | }; 874 | 52D6DA211BF000BD002C0205 /* Debug */ = { 875 | isa = XCBuildConfiguration; 876 | buildSettings = { 877 | APPLICATION_EXTENSION_API_ONLY = YES; 878 | CLANG_ENABLE_MODULES = YES; 879 | CODE_SIGN_IDENTITY = "-"; 880 | COMBINE_HIDPI_IMAGES = YES; 881 | DEFINES_MODULE = YES; 882 | DYLIB_COMPATIBILITY_VERSION = 1; 883 | DYLIB_CURRENT_VERSION = 1; 884 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 885 | FRAMEWORK_VERSION = A; 886 | INFOPLIST_FILE = Configs/Alba.plist; 887 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 888 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 889 | MACOSX_DEPLOYMENT_TARGET = 10.10; 890 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-macOS"; 891 | PRODUCT_NAME = Alba; 892 | SDKROOT = macosx; 893 | SKIP_INSTALL = YES; 894 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 895 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 896 | SWIFT_VERSION = 5.0; 897 | }; 898 | name = Debug; 899 | }; 900 | 52D6DA221BF000BD002C0205 /* Release */ = { 901 | isa = XCBuildConfiguration; 902 | buildSettings = { 903 | APPLICATION_EXTENSION_API_ONLY = YES; 904 | CLANG_ENABLE_MODULES = YES; 905 | CODE_SIGN_IDENTITY = "-"; 906 | COMBINE_HIDPI_IMAGES = YES; 907 | DEFINES_MODULE = YES; 908 | DYLIB_COMPATIBILITY_VERSION = 1; 909 | DYLIB_CURRENT_VERSION = 1; 910 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 911 | FRAMEWORK_VERSION = A; 912 | INFOPLIST_FILE = Configs/Alba.plist; 913 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 914 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 915 | MACOSX_DEPLOYMENT_TARGET = 10.10; 916 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-macOS"; 917 | PRODUCT_NAME = Alba; 918 | SDKROOT = macosx; 919 | SKIP_INSTALL = YES; 920 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 921 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 922 | SWIFT_VERSION = 5.0; 923 | }; 924 | name = Release; 925 | }; 926 | DD7502831C68FCFC006590AF /* Debug */ = { 927 | isa = XCBuildConfiguration; 928 | buildSettings = { 929 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 930 | CODE_SIGN_IDENTITY = "-"; 931 | COMBINE_HIDPI_IMAGES = YES; 932 | INFOPLIST_FILE = Configs/AlbaTests.plist; 933 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 934 | MACOSX_DEPLOYMENT_TARGET = 10.11; 935 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-macOS-Tests"; 936 | PRODUCT_NAME = "$(TARGET_NAME)"; 937 | SDKROOT = macosx; 938 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 939 | SWIFT_VERSION = 5.0; 940 | }; 941 | name = Debug; 942 | }; 943 | DD7502841C68FCFC006590AF /* Release */ = { 944 | isa = XCBuildConfiguration; 945 | buildSettings = { 946 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 947 | CODE_SIGN_IDENTITY = "-"; 948 | COMBINE_HIDPI_IMAGES = YES; 949 | INFOPLIST_FILE = Configs/AlbaTests.plist; 950 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 951 | MACOSX_DEPLOYMENT_TARGET = 10.11; 952 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-macOS-Tests"; 953 | PRODUCT_NAME = "$(TARGET_NAME)"; 954 | SDKROOT = macosx; 955 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 956 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 957 | SWIFT_VERSION = 5.0; 958 | }; 959 | name = Release; 960 | }; 961 | DD7502961C690C7A006590AF /* Debug */ = { 962 | isa = XCBuildConfiguration; 963 | buildSettings = { 964 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 965 | INFOPLIST_FILE = Configs/AlbaTests.plist; 966 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 967 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-tvOS-Tests"; 968 | PRODUCT_NAME = "$(TARGET_NAME)"; 969 | SDKROOT = appletvos; 970 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 971 | SWIFT_VERSION = 5.0; 972 | TVOS_DEPLOYMENT_TARGET = 9.1; 973 | }; 974 | name = Debug; 975 | }; 976 | DD7502971C690C7A006590AF /* Release */ = { 977 | isa = XCBuildConfiguration; 978 | buildSettings = { 979 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 980 | INFOPLIST_FILE = Configs/AlbaTests.plist; 981 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 982 | PRODUCT_BUNDLE_IDENTIFIER = "com.Alba.Alba-tvOS-Tests"; 983 | PRODUCT_NAME = "$(TARGET_NAME)"; 984 | SDKROOT = appletvos; 985 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 986 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 987 | SWIFT_VERSION = 5.0; 988 | TVOS_DEPLOYMENT_TARGET = 9.1; 989 | }; 990 | name = Release; 991 | }; 992 | /* End XCBuildConfiguration section */ 993 | 994 | /* Begin XCConfigurationList section */ 995 | 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "Alba" */ = { 996 | isa = XCConfigurationList; 997 | buildConfigurations = ( 998 | 52D6D98E1BEFF229002C0205 /* Debug */, 999 | 52D6D98F1BEFF229002C0205 /* Release */, 1000 | ); 1001 | defaultConfigurationIsVisible = 0; 1002 | defaultConfigurationName = Release; 1003 | }; 1004 | 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Alba-iOS" */ = { 1005 | isa = XCConfigurationList; 1006 | buildConfigurations = ( 1007 | 52D6D9911BEFF229002C0205 /* Debug */, 1008 | 52D6D9921BEFF229002C0205 /* Release */, 1009 | ); 1010 | defaultConfigurationIsVisible = 0; 1011 | defaultConfigurationName = Release; 1012 | }; 1013 | 52D6D9931BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Alba-iOS Tests" */ = { 1014 | isa = XCConfigurationList; 1015 | buildConfigurations = ( 1016 | 52D6D9941BEFF229002C0205 /* Debug */, 1017 | 52D6D9951BEFF229002C0205 /* Release */, 1018 | ); 1019 | defaultConfigurationIsVisible = 0; 1020 | defaultConfigurationName = Release; 1021 | }; 1022 | 52D6D9E71BEFFF6E002C0205 /* Build configuration list for PBXNativeTarget "Alba-watchOS" */ = { 1023 | isa = XCConfigurationList; 1024 | buildConfigurations = ( 1025 | 52D6D9E81BEFFF6E002C0205 /* Debug */, 1026 | 52D6D9E91BEFFF6E002C0205 /* Release */, 1027 | ); 1028 | defaultConfigurationIsVisible = 0; 1029 | defaultConfigurationName = Release; 1030 | }; 1031 | 52D6DA011BEFFFBE002C0205 /* Build configuration list for PBXNativeTarget "Alba-tvOS" */ = { 1032 | isa = XCConfigurationList; 1033 | buildConfigurations = ( 1034 | 52D6DA021BEFFFBE002C0205 /* Debug */, 1035 | 52D6DA031BEFFFBE002C0205 /* Release */, 1036 | ); 1037 | defaultConfigurationIsVisible = 0; 1038 | defaultConfigurationName = Release; 1039 | }; 1040 | 52D6DA201BF000BD002C0205 /* Build configuration list for PBXNativeTarget "Alba-macOS" */ = { 1041 | isa = XCConfigurationList; 1042 | buildConfigurations = ( 1043 | 52D6DA211BF000BD002C0205 /* Debug */, 1044 | 52D6DA221BF000BD002C0205 /* Release */, 1045 | ); 1046 | defaultConfigurationIsVisible = 0; 1047 | defaultConfigurationName = Release; 1048 | }; 1049 | DD7502821C68FCFC006590AF /* Build configuration list for PBXNativeTarget "Alba-macOS Tests" */ = { 1050 | isa = XCConfigurationList; 1051 | buildConfigurations = ( 1052 | DD7502831C68FCFC006590AF /* Debug */, 1053 | DD7502841C68FCFC006590AF /* Release */, 1054 | ); 1055 | defaultConfigurationIsVisible = 0; 1056 | defaultConfigurationName = Release; 1057 | }; 1058 | DD7502951C690C7A006590AF /* Build configuration list for PBXNativeTarget "Alba-tvOS Tests" */ = { 1059 | isa = XCConfigurationList; 1060 | buildConfigurations = ( 1061 | DD7502961C690C7A006590AF /* Debug */, 1062 | DD7502971C690C7A006590AF /* Release */, 1063 | ); 1064 | defaultConfigurationIsVisible = 0; 1065 | defaultConfigurationName = Release; 1066 | }; 1067 | /* End XCConfigurationList section */ 1068 | }; 1069 | rootObject = 52D6D9731BEFF229002C0205 /* Project object */; 1070 | } 1071 | -------------------------------------------------------------------------------- /Alba.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Alba.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Alba.xcodeproj/xcshareddata/xcschemes/Alba-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /Alba.xcodeproj/xcshareddata/xcschemes/Alba-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /Alba.xcodeproj/xcshareddata/xcschemes/Alba-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /Alba.xcodeproj/xcshareddata/xcschemes/Alba-watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Configs/Alba.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2016 Oleg Dreyman. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Configs/AlbaTests.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Oleg Dreyman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Alba", 7 | platforms: [ 8 | .macOS(.v10_12), .iOS(.v10), .tvOS(.v10), .watchOS(.v3) 9 | ], 10 | products: [ 11 | .library(name: "Alba", type: .static, targets: ["Alba"]) 12 | ], 13 | dependencies: [], 14 | targets: [ 15 | .target( 16 | name: "Alba", 17 | dependencies: [], 18 | path: "Sources" 19 | ), 20 | .testTarget( 21 | name: "AlbaTests", 22 | dependencies: ["Alba"] 23 | ) 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alba 2 | 3 | **Alba** is a tiny yet powerful library which allows you to create sophisticated, decoupled and complex architecture using functional-reactive paradigms. **Alba** is designed to work mostly with reference semantics instances (classes). 4 | 5 | ## Usage 6 | 7 | #### Create publisher 8 | 9 | ```swift 10 | let publisher = Publisher() 11 | publisher.publish(UUID()) 12 | ``` 13 | 14 | #### Subscribing 15 | 16 | In order to subscribe, you should use `Subscribe` instances. The easiest way to get them is by using `.proxy` property on publishers: 17 | 18 | ```swift 19 | final class NumbersPrinter { 20 | 21 | init(numbersPublisher: Subscribe) { 22 | numbersPublisher.subscribe(self, with: NumbersPrinter.print) 23 | } 24 | 25 | func print(_ uuid: Int) { 26 | print(uuid) 27 | } 28 | 29 | } 30 | 31 | let printer = NumbersPrinter(numbersPublisher: publisher.proxy) 32 | publisher.publish(10) // prints "10" 33 | ``` 34 | 35 | If you're surprised by how `NumbersPrinter.print` looks - that's because this allows **Alba** to do some interesting stuff with reference cycles. Check out the [implementation](https://github.com/dreymonde/Alba/blob/master/Sources/Proxy.swift#L52) for details. 36 | 37 | #### That functional things 38 | 39 | The cool thing about publisher proxies is the ability to do interesting things on them, for example, filter and map: 40 | 41 | ```swift 42 | let stringPublisher = Publisher() 43 | 44 | final class Listener { 45 | 46 | init(publisher: Subscribe) { 47 | publisher 48 | .flatMap({ Int($0) }) 49 | .filter({ $0 > 0 }) 50 | .subscribe(self, with: Listener.didReceive) 51 | } 52 | 53 | func didReceive(positiveNumber: Int) { 54 | print(positiveNumber) 55 | } 56 | 57 | } 58 | 59 | let listener = Listener(publisher: stringPublisher.proxy) 60 | stringPublisher.publish("14aq") // nothing 61 | stringPublisher.publish("-5") // nothing 62 | stringPublisher.publish("15") // prints "15" 63 | ``` 64 | 65 | Cool, huh? 66 | 67 | #### Lightweight observing 68 | 69 | ```swift 70 | let publisher = Publisher() 71 | publisher.proxy.listen { (number) in 72 | print(number) 73 | } 74 | publisher.publish(112) // prints "112" 75 | ``` 76 | 77 | Be careful with `listen`. Don't prefer it over `subscribe` as it can introduce memory leaks to your application. 78 | 79 | ### Writing your own `Subscribe` extensions 80 | 81 | If you want to write your own `Subscribe` extensions, you should use `rawModify` method: 82 | 83 | ```swift 84 | public func rawModify(subscribe: (ObjectIdentifier, EventHandler) -> (), entry: @autoclosure @escaping ProxyPayload.Entry) -> Subscribe 85 | ``` 86 | 87 | Here is, for example, how you can implement `map`: 88 | 89 | ```swift 90 | public extension Subscribe { 91 | 92 | public func map(_ transform: @escaping (Event) -> OtherEvent) -> Subscribe { 93 | return rawModify(subscribe: { (identifier, handle) in 94 | let handler: EventHandler = { event in 95 | handle(transform(event)) 96 | } 97 | self._subscribe(identifier, handler) 98 | }, entry: .transformation(label: "mapped", .transformed(fromType: Event.self, toType: OtherEvent.self))) 99 | } 100 | 101 | } 102 | ``` 103 | 104 | `entry` here is purely for debugging purposes -- you're describing the intention of your method. 105 | 106 | ### Inform Bureau 107 | 108 | One of the main drawbacks of the functional-reactive style is an elevated level of indirection -- you can't easily detect the information flow in your application. **Alba** aims to solve this problem with the help of a handy feature called *Inform Bureau*. Inform Bureau collects information about every subscription and publishing inside your application, so it's easy for you to detect what's actually going on (and to detect any problems easily, of course). 109 | 110 | #### Enabling Inform Bureau 111 | 112 | Inform Bureau is an optional feature, so it should be enabled in your code in order to work. It's actually just one line of code -- make sure to put this in your `AppDelegate`'s `init` (`application(_:didFinishLaunchingWithOptions)` is too late): 113 | 114 | ```swift 115 | Alba.InformBureau.isEnabled = true 116 | ``` 117 | 118 | Just this line will no have any immediate effect -- in order for Inform Bureau to become useful, you should also enable it's `Logger`: 119 | 120 | ```swift 121 | Alba.InformBureau.Logger.enable() 122 | ``` 123 | 124 | And that's it! Now you're going to see beautiful messages like these in your output: 125 | 126 | ``` 127 | (S) ManagedObjectContextObserver.changes (Publisher<(NSChangeSet, Int)>) 128 | (S) --> mapped from (NSChangeSet, Int) to NSSpecificChangeSet 129 | (S) !-> subscribed by PointsListViewController:4929411136 130 | ``` 131 | 132 | ``` 133 | (S) +AppDelegate.applicationWillTerminate (Publisher) 134 | (S) --> mapped from UIApplication to () 135 | (S) merged with: 136 | (S) +AppDelegate.applicationDidEnterBackground (Publisher) 137 | (S) --> mapped from UIApplication to () 138 | (S) !-> subscribed by ContextSaver:6176536960 139 | ``` 140 | 141 | ``` 142 | (P) ContextSaver.didSaveContext (Publisher<()>) published () 143 | ``` 144 | 145 | *Hint*: `(S)` are subscriptions events, and `(P)` are publications. 146 | 147 | #### Getting your code ready for Inform Bureau 148 | 149 | Inform Bureau can be enabled with two lines of code. However, in order for it to be useful, there is a little amount of work required from you. First and foremost, you should create all your publishers with descriptive `label`: 150 | 151 | ```swift 152 | let didFailToSaveImage = Publisher(label: "ImageSaver.didFailToSaveImage") 153 | ``` 154 | 155 | You should name your publishers using the next convention: `[type_name].[publisher_name]` 156 | 157 | If your publisher is declared as `static`, then add `+` to the beginning: 158 | 159 | ```swift 160 | static let applicationWillTerminate = Publisher(label: "+AppDelegate.applicationWillTerminate") 161 | ``` 162 | 163 | #### OSLogger 164 | 165 | **Alba**'s Inform Bureau takes full advantage of Apple's latest [Unified Logging][unified-logging-wwdc] system. The support for this system comes via `Alba.OSLogger` object. If you want your app to write **Alba** logs via `os_log`, enable it after enabling `InformBureau`: 166 | 167 | ```swift 168 | Alba.InformBureau.isEnabled = true 169 | Alba.OSLogger.enable() 170 | ``` 171 | 172 | In order for `os_log` to work, you should also do [this](http://stackoverflow.com/a/40744462/5569590). 173 | 174 | Now you can see **Alba** logs of your program in a **Console** application. 175 | 176 | ## Installation 177 | 178 | **Alba** is available through [Carthage][carthage-url]. To install, just write into your Cartfile: 179 | 180 | ```ruby 181 | github "dreymonde/Alba" ~> 0.3.3 182 | ``` 183 | 184 | You can also use SwiftPM. Just add to your `Package.swift`: 185 | 186 | ```swift 187 | import PackageDescription 188 | 189 | let package = Package( 190 | dependencies: [ 191 | .Package(url: "https://github.com/dreymonde/Alba.git", majorVersion: 0, minor: 3), 192 | ] 193 | ) 194 | ``` 195 | 196 | ## Contributing 197 | 198 | **Alba** is in early stage of development and is opened for any ideas. If you want to contribute, you can: 199 | 200 | - Propose idea/bugfix in issues 201 | - Make a pull request 202 | - Review any other pull request (very appreciated!), since no change to this project is made without a PR. 203 | 204 | Actually, any help is welcomed! Feel free to contact us, ask questions and propose new ideas. If you don't want to raise a public issue, you can reach me at [dreymonde@me.com](mailto:dreymonde@me.com). 205 | 206 | [carthage-url]: https://github.com/Carthage/Carthage 207 | [unified-logging-wwdc]: https://developer.apple.com/videos/play/wwdc2016/721/ 208 | -------------------------------------------------------------------------------- /Sources/Alba.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Alba 3 | * 4 | * Copyright (c) 2016 Oleg Dreyman. Licensed under the MIT license, as follows: 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | // Alba - stateful event observing engine 26 | 27 | public typealias EventHandler = (Event) -> () 28 | 29 | public protocol SignedProtocol { 30 | 31 | associatedtype Wrapped 32 | 33 | var value: Wrapped { get set } 34 | var submittedBy: ObjectIdentifier? { get set } 35 | 36 | init(_ signed: Signed) 37 | 38 | } 39 | 40 | public struct Signed : SignedProtocol { 41 | public var value: Value 42 | public var submittedBy: ObjectIdentifier? 43 | 44 | public init(_ value: Value, _ submittedBy: ObjectIdentifier?) { 45 | self.value = value 46 | self.submittedBy = submittedBy 47 | } 48 | 49 | public func map(_ transform: (Value) -> T) -> Signed { 50 | return Signed(transform(self.value), self.submittedBy) 51 | } 52 | 53 | public init(_ signed: Signed) { 54 | self = signed 55 | } 56 | } 57 | 58 | public extension ObjectIdentifier { 59 | 60 | func belongsTo(_ object: AnyObject) -> Bool { 61 | return ObjectIdentifier(object) == self 62 | } 63 | 64 | } 65 | 66 | public extension Optional where Wrapped == ObjectIdentifier { 67 | 68 | func belongsTo(_ object: AnyObject) -> Bool { 69 | if let wrapped = self { 70 | return ObjectIdentifier(object) == wrapped 71 | } 72 | return false 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /Sources/InformBureau.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Alba 3 | * 4 | * Copyright (c) 2016 Oleg Dreyman. Licensed under the MIT license, as follows: 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | public protocol InformBureauPayload { 26 | 27 | associatedtype Entry 28 | 29 | init(entries: [Entry]) 30 | 31 | var entries: [Entry] { get set } 32 | 33 | } 34 | 35 | public extension InformBureauPayload { 36 | 37 | func adding(entry: @autoclosure () -> Entry) -> Self { 38 | if InformBureau.isEnabled { 39 | var updatedEntries = entries 40 | updatedEntries.append(entry()) 41 | return Self(entries: updatedEntries) 42 | } else { 43 | return .empty 44 | } 45 | } 46 | 47 | static var empty: Self { 48 | return Self(entries: []) 49 | } 50 | 51 | } 52 | 53 | fileprivate final class InformBureauPublisher : Subscribable { 54 | 55 | var handlers: [EventHandler] = [] 56 | 57 | func publish(_ event: Event) { 58 | handlers.forEach({ $0(event) }) 59 | } 60 | 61 | fileprivate var proxy: Subscribe { 62 | let payload = ProxyPayload.empty.adding(entry: .publisherLabel("Alba.InformBureau", type: InformBureauPublisher.self)) 63 | return Subscribe(subscribe: { (_, handler) in self.handlers.append(handler) }, 64 | unsubscribe: { _ in }, 65 | payload: payload) 66 | } 67 | 68 | } 69 | 70 | public final class InformBureau { 71 | 72 | public typealias SubscriptionLogMessage = ProxyPayload 73 | public typealias PublishingLogMessage = PublishingPayload 74 | public typealias GeneralWarningLogMessage = String 75 | 76 | public static var isEnabled = false 77 | 78 | fileprivate static let subscriptionPublisher = InformBureauPublisher() 79 | public static var didSubscribe: Subscribe { 80 | return subscriptionPublisher.proxy 81 | } 82 | 83 | fileprivate static let publishingPublisher = InformBureauPublisher() 84 | public static var didPublish: Subscribe { 85 | return publishingPublisher.proxy 86 | } 87 | 88 | fileprivate static let generalWarningsPublisher = InformBureauPublisher() 89 | public static var generalWarnings: Subscribe { 90 | return generalWarningsPublisher.proxy 91 | } 92 | 93 | static func submitSubscription(_ logMessage: SubscriptionLogMessage) { 94 | subscriptionPublisher.publish(logMessage) 95 | } 96 | 97 | static func submitPublishing(_ logMessage: PublishingLogMessage) { 98 | publishingPublisher.publish(logMessage) 99 | } 100 | 101 | static func submitGeneralWarning(_ logMessage: GeneralWarningLogMessage) { 102 | generalWarningsPublisher.publish(logMessage) 103 | } 104 | 105 | public final class Logger { 106 | 107 | static let shared = Logger() 108 | 109 | private init() { } 110 | 111 | public static func enable() { 112 | if !InformBureau.isEnabled { 113 | print("Enabling Alba.InformBureau...") 114 | InformBureau.isEnabled = true 115 | } 116 | InformBureau.didSubscribe.subscribe(shared, with: Logger.logSubMergeLevelZero) 117 | InformBureau.didPublish.subscribe(shared, with: Logger.logPub) 118 | InformBureau.generalWarnings.subscribe(shared, with: Logger.logGeneralWarning) 119 | } 120 | 121 | public static func disable() { 122 | InformBureau.didSubscribe.manual.unsubscribe(shared) 123 | InformBureau.didPublish.manual.unsubscribe(shared) 124 | InformBureau.generalWarnings.manual.unsubscribe(shared) 125 | } 126 | 127 | func logSubMergeLevelZero(_ logMessage: SubscriptionLogMessage) { 128 | logSub(logMessage) 129 | print("") 130 | } 131 | 132 | func logSub(_ logMessage: SubscriptionLogMessage, mergeLevel: Int = 0) { 133 | var mergeInset = "" 134 | (0 ..< mergeLevel).forEach { (_) in 135 | mergeInset += " " 136 | } 137 | let mark = "(S) " + mergeInset 138 | func mprint(_ item: String) { 139 | print(mark + item) 140 | } 141 | if mergeLevel == 0 { 142 | print("") 143 | } 144 | for entry in logMessage.entries { 145 | switch entry { 146 | case .publisherLabel(let label, let type): 147 | mprint("\(label) (\(type))") 148 | case .transformation(let label, let transformation): 149 | 150 | switch transformation { 151 | case .sameType: 152 | mprint("--> \(label)") 153 | case .transformed(let fromType, let toType): 154 | mprint("--> \(label) from \(fromType) to \(toType)") 155 | } 156 | case .custom(let custom): 157 | mprint("--> \(custom)") 158 | case .merged(let label, let otherPayload): 159 | mprint("\(label) with:") 160 | logSub(otherPayload, mergeLevel: mergeLevel + 1) 161 | case .subscription(let subscription): 162 | switch subscription { 163 | case .byObject(let identifier, let type): 164 | mprint("!-> subscribed by \(type):\(identifier.hashValue)") 165 | case .redirection(let label, let type): 166 | mprint("!-> redirected to \(label) (\(type))") 167 | case .listen(let type): 168 | mprint("!-> listened with EventHandler<\(type)>") 169 | } 170 | } 171 | } 172 | } 173 | 174 | func logPub(_ logMessage: PublishingLogMessage) { 175 | let mark = "(P) " 176 | print("") 177 | for entry in logMessage.entries { 178 | switch entry { 179 | case .published(publisherLabel: let publisherLabel, publisherType: let publisherType, event: let event): 180 | print(mark + "\(publisherLabel) (\(publisherType)) published \(event)") 181 | case .handled(handlerLabel: let handlerLabel): 182 | print(mark + "--> handled by \(handlerLabel)") 183 | } 184 | } 185 | } 186 | 187 | func logGeneralWarning(_ logMessage: GeneralWarningLogMessage) { 188 | print("") 189 | print("(W) \(logMessage)") 190 | } 191 | 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /Sources/Listener.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Alba 3 | * 4 | * Copyright (c) 2016 Oleg Dreyman. Licensed under the MIT license, as follows: 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | internal class BasicListener { 26 | 27 | internal let publisher: Subscribe 28 | internal let handler: EventHandler 29 | 30 | internal init(subscribingTo publisher: Subscribe, 31 | _ handler: @escaping EventHandler) { 32 | self.publisher = publisher 33 | self.handler = handler 34 | publisher.manual.subscribe(self, with: handler) 35 | } 36 | 37 | internal init(subscribingTo publisher: Pub, 38 | _ handler: @escaping EventHandler) where Pub.Event == Event { 39 | self.publisher = publisher.proxy 40 | self.handler = handler 41 | self.publisher.manual.subscribe(self, with: handler) 42 | } 43 | 44 | deinit { 45 | publisher.manual.unsubscribe(self) 46 | } 47 | 48 | } 49 | 50 | internal class NotGoingBasicListener { 51 | 52 | let publisher: Subscribe 53 | let handler: EventHandler 54 | 55 | init(subscribingTo publisher: Subscribe, 56 | _ handler: @escaping EventHandler) { 57 | self.publisher = publisher 58 | self.handler = handler 59 | publisher.manual.subscribe(self, with: self.handle) 60 | } 61 | 62 | init(subscribingTo publisher: Pub, 63 | _ handler: @escaping EventHandler) where Pub.Event == Event { 64 | self.publisher = publisher.proxy 65 | self.handler = handler 66 | self.publisher.manual.subscribe(self, with: self.handle) 67 | } 68 | 69 | func handle(_ event: Event) { 70 | self.handler(event) 71 | } 72 | 73 | deinit { 74 | publisher.manual.unsubscribe(self) 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /Sources/Publisher.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Alba 3 | * 4 | * Copyright (c) 2016 Oleg Dreyman. Licensed under the MIT license, as follows: 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | public protocol Subscribable : AnyObject { 26 | 27 | associatedtype Event 28 | 29 | var proxy: Subscribe { get } 30 | 31 | } 32 | 33 | public protocol PublisherProtocol : Subscribable { 34 | 35 | var label: String { get } 36 | 37 | var subscribers: [ObjectIdentifier : EventHandler] { get set } 38 | 39 | func publish(_ event: Event) 40 | 41 | } 42 | 43 | public extension PublisherProtocol { 44 | 45 | var proxy: Subscribe { 46 | fatalError() 47 | } 48 | 49 | } 50 | 51 | public extension PublisherProtocol { 52 | 53 | func publish(_ event: Event) { 54 | subscribers.values.forEach({ handle in handle(event) }) 55 | } 56 | 57 | } 58 | 59 | public class Publisher : PublisherProtocol { 60 | 61 | public var subscribers: [ObjectIdentifier : EventHandler] = [:] 62 | 63 | public var label: String 64 | 65 | public init(label: String = "unnamed") { 66 | self.proxy = Subscribe.empty() 67 | let initialPayload = ProxyPayload.empty.adding(entry: .publisherLabel(label, type: Publisher.self)) 68 | self.label = label 69 | self.proxy = Subscribe(subscribe: { [weak self] in self?.subscribers[$0] = $1 }, 70 | unsubscribe: { [weak self] in self?.subscribers[$0] = nil }, 71 | payload: initialPayload) 72 | } 73 | 74 | public private(set) var proxy: Subscribe 75 | 76 | public func publish(_ event: Event) { 77 | if !InformBureau.isEnabled { 78 | subscribers.values.forEach({ handle in handle(event) }) 79 | } else { 80 | let payload = PublishingPayload.empty.adding(entry: .published(publisherLabel: label, publisherType: Publisher.self, event: event)) 81 | InformBureau.submitPublishing(payload) 82 | subscribers.forEach { identifier, handle in 83 | handle(event) 84 | } 85 | } 86 | } 87 | 88 | } 89 | 90 | public struct PublishingPayload : InformBureauPayload { 91 | 92 | public enum Entry { 93 | case published(publisherLabel: String, publisherType: Any.Type, event: Any) 94 | case handled(handlerLabel: String) 95 | } 96 | 97 | public var entries: [Entry] 98 | 99 | public init(entries: [Entry]) { 100 | self.entries = entries 101 | } 102 | 103 | } 104 | 105 | public typealias SignedPublisher = Publisher> 106 | 107 | public extension Publisher where Event : SignedProtocol { 108 | 109 | func publish(_ event: Event.Wrapped, submitterIdentifier: ObjectIdentifier?) { 110 | let signed = Signed(event, submitterIdentifier) 111 | self.publish(.init(signed)) 112 | } 113 | 114 | func publish(_ event: Event.Wrapped, submittedBy submitter: AnyObject?) { 115 | publish(event, submitterIdentifier: submitter.map(ObjectIdentifier.init)) 116 | } 117 | 118 | } 119 | 120 | extension Publisher where Event == Void { 121 | 122 | public func publish() { 123 | self.publish(()) 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /Sources/Subscribe.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Alba 3 | * 4 | * Copyright (c) 2016 Oleg Dreyman. Licensed under the MIT license, as follows: 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | @available(*, unavailable, renamed: "Subscribe") 26 | public typealias PublisherProxy = Subscribe 27 | 28 | public struct ProxyPayload : InformBureauPayload { 29 | 30 | public enum Entry { 31 | 32 | public enum Subscription { 33 | case byObject(identifier: ObjectIdentifier, ofType: Any.Type) 34 | case redirection(to: String, ofType: Any.Type) 35 | case listen(eventType: Any.Type) 36 | } 37 | 38 | public enum Transformation { 39 | case sameType 40 | case transformed(fromType: Any.Type, toType: Any.Type) 41 | } 42 | 43 | public typealias TransformationLabel = String 44 | 45 | public enum MergeLabel { 46 | case merged 47 | case custom(String) 48 | } 49 | 50 | case publisherLabel(String, type: Any.Type) 51 | case subscription(Subscription) 52 | case transformation(label: TransformationLabel, Transformation) 53 | case merged(label: MergeLabel, otherPayload: ProxyPayload) 54 | case custom(String) 55 | 56 | } 57 | 58 | public var entries: [Entry] 59 | 60 | public init(entries: [Entry]) { 61 | self.entries = entries 62 | } 63 | 64 | } 65 | 66 | public extension ProxyPayload.Entry { 67 | 68 | static var filtered: ProxyPayload.Entry { 69 | return .transformation(label: "filtered", .sameType) 70 | } 71 | 72 | static func mapped(fromType: Any.Type, toType: Any.Type) -> ProxyPayload.Entry { 73 | return .transformation(label: "mapped", .transformed(fromType: fromType, toType: toType)) 74 | } 75 | 76 | static var interrupted: ProxyPayload.Entry { 77 | return .custom("interrupted") 78 | } 79 | 80 | static let emptyProxyLabel = "WARNING: Empty proxy" 81 | 82 | } 83 | 84 | public struct Subscribe { 85 | 86 | fileprivate let _subscribe: (ObjectIdentifier, @escaping EventHandler) -> () 87 | fileprivate let _unsubscribe: (ObjectIdentifier) -> () 88 | internal let payload: ProxyPayload 89 | 90 | public init(subscribe: @escaping (ObjectIdentifier, @escaping EventHandler) -> (), 91 | unsubscribe: @escaping (ObjectIdentifier) -> (), 92 | label: String = "unnnamed") { 93 | self._subscribe = subscribe 94 | self._unsubscribe = unsubscribe 95 | self.payload = ProxyPayload.empty.adding(entry: .publisherLabel(label, type: Subscribe.self)) 96 | } 97 | 98 | public init(subscribe: @escaping (ObjectIdentifier, @escaping EventHandler) -> (), 99 | unsubscribe: @escaping (ObjectIdentifier) -> (), 100 | payload: ProxyPayload) { 101 | self._subscribe = subscribe 102 | self._unsubscribe = unsubscribe 103 | self.payload = payload 104 | } 105 | 106 | public func subscribe(_ object: Object, 107 | with producer: @escaping (Object) -> EventHandler) { 108 | let identifier = ObjectIdentifier(object) 109 | if InformBureau.isEnabled, Object.self != Publisher.self { 110 | let entry = ProxyPayload.Entry.subscription(.byObject(identifier: identifier, ofType: Object.self)) 111 | InformBureau.submitSubscription(payload.adding(entry: entry)) 112 | } 113 | self._subscribe(identifier, { [weak object] in 114 | if let object = object { 115 | producer(object)($0) 116 | } else { 117 | self._unsubscribe(identifier) 118 | } 119 | }) 120 | } 121 | 122 | public func flatSubscribe(_ object: Object, with handler: @escaping (Object, Event) -> ()) { 123 | subscribe(object, with: unfold(handler)) 124 | } 125 | 126 | public func rawModify(subscribe: @escaping (ObjectIdentifier, @escaping EventHandler) -> (), 127 | entry: ProxyPayload.Entry) -> Subscribe { 128 | return Subscribe(subscribe: subscribe, 129 | unsubscribe: self._unsubscribe, 130 | payload: payload.adding(entry: entry)) 131 | } 132 | 133 | public func filter(_ condition: @escaping (Event) -> Bool) -> Subscribe { 134 | return rawModify(subscribe: { (identifier, handle) in 135 | let handler: EventHandler = { event in 136 | if condition(event) { handle(event) } 137 | } 138 | self._subscribe(identifier, handler) 139 | }, entry: .filtered) 140 | } 141 | 142 | public func map(_ transform: @escaping (Event) -> OtherEvent) -> Subscribe { 143 | return rawModify(subscribe: { (identifier, handle) in 144 | let handler: EventHandler = { event in 145 | handle(transform(event)) 146 | } 147 | self._subscribe(identifier, handler) 148 | }, entry: .mapped(fromType: Event.self, 149 | toType: OtherEvent.self)) 150 | } 151 | 152 | public func flatMap(_ transform: @escaping (Event) -> OtherEvent?) -> Subscribe { 153 | return rawModify(subscribe: { (identifier, handle) in 154 | let handler: EventHandler = { event in 155 | if let transformed = transform(event) { handle(transformed) } 156 | } 157 | self._subscribe(identifier, handler) 158 | }, entry: .mapped(fromType: Event.self, 159 | toType: OtherEvent.self)) 160 | } 161 | 162 | public func interrupted(with work: @escaping (Event) -> ()) -> Subscribe { 163 | return rawModify(subscribe: { (identifier, handle) in 164 | self._subscribe(identifier, { work($0); handle($0) }) 165 | }, entry: .interrupted) 166 | } 167 | 168 | public func merged(with other: Subscribe) -> Subscribe { 169 | return Subscribe(subscribe: { (identifier, handle) in 170 | self._subscribe(identifier, handle) 171 | other._subscribe(identifier, handle) 172 | }, unsubscribe: { (identifier) in 173 | self._unsubscribe(identifier) 174 | other._unsubscribe(identifier) 175 | }, payload: payload.adding(entry: .merged(label: .merged, otherPayload: other.payload))) 176 | } 177 | 178 | public func redirect(to publisher: Publisher) where Publisher.Event == Event { 179 | if InformBureau.isEnabled { 180 | InformBureau.submitSubscription(payload.adding(entry: .subscription(.redirection(to: publisher.label, ofType: Publisher.self)))) 181 | } 182 | subscribe(publisher, with: Publisher.publish) 183 | } 184 | 185 | public func listen(with handler: @escaping EventHandler) { 186 | let listener = NotGoingBasicListener(subscribingTo: self, handler) 187 | _silenceWarning(of: listener) 188 | if InformBureau.isEnabled { 189 | InformBureau.submitSubscription(payload.adding(entry: .subscription(.listen(eventType:Event.self)))) 190 | } 191 | } 192 | 193 | public func void() -> Subscribe { 194 | return map({ _ in }) 195 | } 196 | 197 | public var manual: ManualSubscribe { 198 | return ManualSubscribe(proxy: self) 199 | } 200 | 201 | } 202 | 203 | extension Subscribe where Event == Void { 204 | 205 | public func subscribe(_ object: Object, 206 | with producer: @escaping (Object) -> () -> ()) { 207 | self.flatSubscribe(object) { (obj, _) in 208 | producer(obj)() 209 | } 210 | } 211 | 212 | } 213 | 214 | public struct ManualSubscribe { 215 | 216 | fileprivate let proxy: Subscribe 217 | 218 | public func subscribe(_ object: AnyObject, with subscription: @escaping EventHandler) { 219 | let identifier = ObjectIdentifier(object) 220 | proxy._subscribe(identifier, subscription) 221 | } 222 | 223 | public func unsubscribe(_ object: AnyObject) { 224 | let identifier = ObjectIdentifier(object) 225 | proxy._unsubscribe(identifier) 226 | } 227 | 228 | public func subscribe(objectWith objectIdentifier: ObjectIdentifier, 229 | with handler: @escaping EventHandler) { 230 | proxy._subscribe(objectIdentifier, handler) 231 | } 232 | 233 | public func unsubscribe(objectWith objectIdentifier: ObjectIdentifier) { 234 | proxy._unsubscribe(objectIdentifier) 235 | } 236 | 237 | } 238 | 239 | public extension Subscribe where Event : SignedProtocol { 240 | 241 | func subscribe(_ object: Object, 242 | with producer: @escaping (Object) -> EventHandler<(Event.Wrapped, submitterIdentifier: ObjectIdentifier?)>) { 243 | let identifier = ObjectIdentifier(object) 244 | self._subscribe(identifier, { [weak object] event in 245 | if let object = object { 246 | let handler = producer(object) 247 | handler((event.value, event.submittedBy)) 248 | } else { 249 | self._unsubscribe(identifier) 250 | } 251 | }) 252 | } 253 | 254 | func subscribe(_ object: Object, 255 | with producer: @escaping (Object) -> EventHandler<(Event.Wrapped, submittedBySelf: Bool)>) { 256 | let identifier = ObjectIdentifier(object) 257 | self._subscribe(identifier, { [weak object] event in 258 | if let object = object { 259 | let handler = producer(object) 260 | handler((event.value, event.submittedBy == identifier)) 261 | } else { 262 | self._unsubscribe(identifier) 263 | } 264 | }) 265 | } 266 | 267 | func filterValue(_ condition: @escaping (Event.Wrapped) -> Bool) -> Subscribe { 268 | return filter({ condition($0.value) }) 269 | } 270 | 271 | func drop(eventsSignedBy signer: Object) -> Subscribe { 272 | return weak(signer).filter({ (object) in 273 | return { (event) in 274 | !event.submittedBy.belongsTo(object) 275 | } 276 | }).proxy 277 | } 278 | 279 | func mapValue(_ transform: @escaping (Event.Wrapped) -> OtherEvent) -> Subscribe> { 280 | return map({ (event) in 281 | let transformed = transform(event.value) 282 | let signed = Signed.init(transformed, event.submittedBy) 283 | return signed 284 | }) 285 | } 286 | 287 | func flatMapValue(_ transform: @escaping (Event.Wrapped) -> OtherEvent?) -> Subscribe> { 288 | return flatMap({ (event) in 289 | let transformed = transform(event.value) 290 | let signed = transformed.map({ Signed.init($0, event.submittedBy) }) 291 | return signed 292 | }) 293 | } 294 | 295 | var unsigned: Subscribe { 296 | return self.map({ $0.value }) 297 | } 298 | 299 | } 300 | 301 | public extension Subscribe { 302 | 303 | static func empty() -> Subscribe { 304 | let payload = ProxyPayload.empty.adding(entry: .publisherLabel(ProxyPayload.Entry.emptyProxyLabel, type: Subscribe.self)) 305 | return Subscribe(subscribe: { _,_ in }, 306 | unsubscribe: { _ in }, 307 | payload: payload) 308 | } 309 | 310 | } 311 | 312 | public extension Subscribe { 313 | 314 | func weak(_ object: Object) -> WeakSubscribe { 315 | return WeakSubscribe(proxy: self, object: object) 316 | } 317 | 318 | } 319 | 320 | public typealias SignedSubscribe = Subscribe> 321 | 322 | fileprivate func _silenceWarning(of unused: Any) { 323 | 324 | } 325 | -------------------------------------------------------------------------------- /Sources/WeakSubscribe.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Alba 3 | * 4 | * Copyright (c) 2016 Oleg Dreyman. Licensed under the MIT license, as follows: 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | public struct WeakSubscribe { 26 | 27 | public let proxy: Subscribe 28 | fileprivate let object: Object 29 | 30 | public init(proxy: Subscribe, 31 | object: Object) { 32 | self.proxy = proxy 33 | self.object = object 34 | } 35 | 36 | public func rawModify(subscribe: @escaping (Object, ObjectIdentifier, @escaping EventHandler) -> (), entry: ProxyPayload.Entry) -> WeakSubscribe { 37 | let selfproxy = self.proxy 38 | let newProxy: Subscribe = selfproxy.rawModify(subscribe: { [weak object] (identifier, handle) in 39 | if let objecta = object { 40 | subscribe(objecta, identifier, handle) 41 | } else { 42 | selfproxy.manual.unsubscribe(objectWith: identifier) 43 | } 44 | }, entry: entry) 45 | return WeakSubscribe(proxy: newProxy, object: object) 46 | } 47 | 48 | public func filter(_ condition: @escaping (Object) -> (Event) -> Bool) -> WeakSubscribe { 49 | let sproxy = self.proxy 50 | return rawModify(subscribe: { (object, identifier, handle) in 51 | let handler: EventHandler = { [weak object] event in 52 | if let object = object { 53 | if condition(object)(event) { handle(event) } 54 | } else { 55 | sproxy.manual.unsubscribe(objectWith: identifier) 56 | } 57 | } 58 | sproxy.manual.subscribe(objectWith: identifier, with: handler) 59 | }, entry: .filtered) 60 | } 61 | 62 | public func map(_ transform: @escaping (Object) -> (Event) -> (OtherEvent)) -> WeakSubscribe { 63 | let selfproxy = proxy 64 | return rawModify(subscribe: { (object, identifier, handle) in 65 | let handler: EventHandler = { [weak object] event in 66 | if let objecta = object { 67 | handle(transform(objecta)(event)) 68 | } else { 69 | selfproxy.manual.unsubscribe(objectWith: identifier) 70 | } 71 | } 72 | selfproxy.manual.subscribe(objectWith: identifier, with: handler) 73 | }, entry: .mapped(fromType: Event.self, toType: OtherEvent.self)) 74 | } 75 | 76 | public func flatMap(_ transform: @escaping (Object) -> (Event) -> (OtherEvent?)) -> WeakSubscribe { 77 | let selfproxy = proxy 78 | return rawModify(subscribe: { (object, identifier, handle) in 79 | let handler: EventHandler = { [weak object] event in 80 | if let objecta = object { 81 | if let transformed = transform(objecta)(event) { 82 | handle(transformed) 83 | } 84 | } else { 85 | selfproxy.manual.unsubscribe(objectWith: identifier) 86 | } 87 | } 88 | selfproxy.manual.subscribe(objectWith: identifier, with: handler) 89 | }, entry: .mapped(fromType: Event.self, toType: OtherEvent.self)) 90 | } 91 | 92 | public func subscribe(with producer: @escaping (Object) -> EventHandler) { 93 | proxy.subscribe(object, with: producer) 94 | } 95 | 96 | public var flat: FlatWeakSubscribe { 97 | return FlatWeakSubscribe(weakProxy: self) 98 | } 99 | 100 | } 101 | 102 | public struct FlatWeakSubscribe { 103 | 104 | public let weakProxy: WeakSubscribe 105 | 106 | public init(weakProxy: WeakSubscribe) { 107 | self.weakProxy = weakProxy 108 | } 109 | 110 | public var proxy: Subscribe { 111 | return weakProxy.proxy 112 | } 113 | 114 | public func filter(_ condition: @escaping (Object, Event) -> Bool) -> FlatWeakSubscribe { 115 | return weakProxy.filter(unfold(condition)).flat 116 | } 117 | 118 | public func map(_ transform: @escaping (Object, Event) -> OtherEvent) -> FlatWeakSubscribe { 119 | return weakProxy.map(unfold(transform)).flat 120 | } 121 | 122 | public func flatMap(_ transform: @escaping (Object, Event) -> OtherEvent?) -> FlatWeakSubscribe { 123 | return weakProxy.flatMap(unfold(transform)).flat 124 | } 125 | 126 | public func subscribe(with handler: @escaping (Object, Event) -> ()) { 127 | weakProxy.subscribe(with: unfold(handler)) 128 | } 129 | 130 | } 131 | 132 | internal func unfold(_ function: @escaping (First, Second) -> Output) -> (First) -> (Second) -> Output { 133 | return { first in 134 | return { second in 135 | return function(first, second) 136 | } 137 | } 138 | } 139 | 140 | public prefix func ! (boolFunc: @escaping (T) -> Bool) -> ((T) -> Bool) { 141 | return { 142 | return !boolFunc($0) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /Tests/AlbaTests/AlbaTests.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Alba 3 | * 4 | * Copyright (c) 2016 Oleg Dreyman. Licensed under the MIT license, as follows: 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | import Foundation 26 | import XCTest 27 | @testable import Alba 28 | 29 | var isBureauWorking = false 30 | 31 | class AlbaTests: XCTestCase { 32 | 33 | override func setUp() { 34 | if !isBureauWorking { 35 | print("Alba Inform Bureau on") 36 | Alba.InformBureau.isEnabled = true 37 | Alba.InformBureau.Logger.enable() 38 | // Alba.InformBureau.didPublish.listen(with: { print($0) }) 39 | isBureauWorking = true 40 | print("Now working") 41 | } 42 | } 43 | 44 | func testSimplest() { 45 | let pub = Publisher(label: "testSimplest.pub") 46 | let expectation = self.expectation(description: "On Sub") 47 | pub.proxy.listen { (number) in 48 | XCTAssertEqual(number, 5) 49 | expectation.fulfill() 50 | } 51 | pub.publish(5) 52 | waitForExpectations(timeout: 5.0) 53 | } 54 | 55 | class SignedThing { 56 | 57 | var expectation: XCTestExpectation 58 | 59 | init(expectation: XCTestExpectation) { 60 | self.expectation = expectation 61 | } 62 | 63 | func handle(_ a: (number: Int, submittedBySelf: Bool)) { 64 | if a.submittedBySelf { 65 | if a.number == 7 { 66 | XCTFail() 67 | } 68 | } else { 69 | if a.number == 5 { 70 | XCTFail() 71 | } 72 | if a.number == 7 { 73 | expectation.fulfill() 74 | } 75 | } 76 | } 77 | 78 | func handle_filtered(_ number: Int) { 79 | if number == 10 { 80 | XCTFail() 81 | } else if number == 5 { 82 | expectation.fulfill() 83 | } 84 | } 85 | 86 | } 87 | 88 | func testSigned() { 89 | let pub = SignedPublisher() 90 | let expectation = self.expectation(description: "On sub") 91 | let sub = SignedThing(expectation: expectation) 92 | pub.proxy.subscribe(sub, with: SignedThing.handle) 93 | pub.publish(5, submittedBy: sub) 94 | pub.publish(7, submittedBy: nil) 95 | waitForExpectations(timeout: 5.0) 96 | } 97 | 98 | func testFilterSigned() { 99 | let pub = SignedPublisher() 100 | let expectation = self.expectation(description: "on sub") 101 | let sub = SignedThing(expectation: expectation) 102 | pub.proxy.drop(eventsSignedBy: sub).unsigned.subscribe(sub, with: SignedThing.handle_filtered) 103 | pub.publish(10, submittedBy: sub) 104 | pub.publish(5, submittedBy: nil) 105 | waitForExpectations(timeout: 5.0) 106 | } 107 | 108 | class SignedThing2 { 109 | 110 | var expectation: XCTestExpectation 111 | 112 | init(expectation: XCTestExpectation) { 113 | self.expectation = expectation 114 | } 115 | 116 | func handle(a: (number: Int, identifier: ObjectIdentifier?)) { 117 | if a.identifier?.belongsTo(self) == true { 118 | if a.number == 7 { 119 | XCTFail() 120 | } 121 | } else { 122 | if a.number == 5 { 123 | XCTFail() 124 | } 125 | if a.number == 7 { 126 | expectation.fulfill() 127 | } 128 | } 129 | } 130 | 131 | } 132 | 133 | func testSigned2() { 134 | let pub = SignedPublisher() 135 | let expectation = self.expectation(description: "On sub") 136 | let sub = SignedThing2(expectation: expectation) 137 | pub.proxy.subscribe(sub, with: SignedThing2.handle) 138 | pub.publish(5, submittedBy: sub) 139 | pub.publish(7, submittedBy: nil) 140 | waitForExpectations(timeout: 5.0) 141 | } 142 | 143 | class DEA { 144 | let proxy: Subscribe 145 | let sproxy: SignedSubscribe 146 | let deinitBlock: () -> () 147 | init(proxy: Subscribe, sproxy: SignedSubscribe, signed: Bool = false, deinitBlock: @escaping () -> ()) { 148 | self.proxy = proxy 149 | self.sproxy = sproxy 150 | self.deinitBlock = deinitBlock 151 | proxy.subscribe(self, with: DEA.handle) 152 | if signed { 153 | sproxy.subscribe(self, with: DEA.handleSigned) 154 | } else { 155 | sproxy.unsigned.subscribe(self, with: DEA.handle) 156 | } 157 | } 158 | deinit { 159 | print("Dealloc") 160 | deinitBlock() 161 | } 162 | func handle(_ int: Int) { 163 | print(int) 164 | XCTAssertNotEqual(int, 10) 165 | } 166 | func handleSigned(a: (int: Int, submitter: ObjectIdentifier?)) { 167 | print(a.int) 168 | XCTAssertNotEqual(a.int, 10) 169 | } 170 | } 171 | 172 | func testDealloc() { 173 | let pub = Publisher() 174 | let spub = SignedPublisher() 175 | let expectation = self.expectation(description: "Deinit wait") 176 | var dea: DEA? = DEA.init(proxy: pub.proxy, sproxy: spub.proxy, deinitBlock: { expectation.fulfill() }) 177 | print(dea!) 178 | pub.publish(5) 179 | spub.publish(5, submittedBy: nil) 180 | dea = nil 181 | pub.publish(10) 182 | spub.publish(10, submittedBy: nil) 183 | waitForExpectations(timeout: 5.0) 184 | } 185 | 186 | func testDealloc2() { 187 | let pub = Publisher() 188 | let spub = SignedPublisher() 189 | let expectation = self.expectation(description: "Deinit wait") 190 | var dea: DEA? = DEA.init(proxy: pub.proxy, sproxy: spub.proxy, signed: true, deinitBlock: { expectation.fulfill() }) 191 | print(dea!) 192 | pub.publish(5) 193 | spub.publish(5, submittedBy: nil) 194 | dea = nil 195 | pub.publish(10) 196 | spub.publish(10, submittedBy: nil) 197 | waitForExpectations(timeout: 5.0) 198 | } 199 | 200 | func testFilter() { 201 | let pub = Publisher() 202 | let pospub = pub.proxy.filter({ $0 > 0 }) 203 | let expectation = self.expectation(description: "on sub") 204 | pospub.listen { (number) in 205 | XCTAssertGreaterThan(number, 0) 206 | if number == 10 { expectation.fulfill() } 207 | } 208 | [-1, -3, 5, 7, 9, 4, 3, -2, 0, 10].forEach(pub.publish) 209 | waitForExpectations(timeout: 5.0) 210 | } 211 | 212 | func testMap() { 213 | let pub = Publisher() 214 | let strpub = pub.proxy.map(String.init) 215 | let expectation = self.expectation(description: "onsub") 216 | strpub.listen { (string) in 217 | debugPrint(string) 218 | if string == "10" { expectation.fulfill() } 219 | } 220 | [-1, 2, 3, 9, 7, 4, 2, 57, 10].forEach(pub.publish) 221 | waitForExpectations(timeout: 5.0) 222 | } 223 | 224 | func testFlatMap() { 225 | let pub = Publisher() 226 | let intpub = pub.proxy.flatMap({ Int($0) }) 227 | let expectation = self.expectation(description: "onsub") 228 | intpub.listen { (number) in 229 | if number == 10 { expectation.fulfill() } 230 | } 231 | ["Abba", "Babbaa", "-7", "3", "10"].forEach(pub.publish) 232 | waitForExpectations(timeout: 5.0) 233 | } 234 | 235 | func testRedirect() { 236 | let pubOne = Publisher() 237 | let pubTwo = Publisher(label: "testRedirect.pubTwo") 238 | let expectation = self.expectation(description: "onsubtwo") 239 | pubOne.proxy 240 | .map({ $0 - 1 }) 241 | .map(String.init) 242 | .redirect(to: pubTwo) 243 | pubTwo.proxy.listen { (number) in 244 | debugPrint(number) 245 | if number == "10" { expectation.fulfill() } 246 | } 247 | pubOne.publish(3) 248 | pubOne.publish(5) 249 | pubOne.publish(11) 250 | waitForExpectations(timeout: 5.0) 251 | } 252 | 253 | func testIntercept() { 254 | let pub = Publisher() 255 | let expectation = self.expectation(description: "onsub") 256 | let proxy = pub.proxy 257 | .interrupted(with: { 258 | if $0 == 10 { expectation.fulfill() } 259 | }) 260 | proxy.listen { (_) in 261 | print("Yay") 262 | } 263 | pub.publish(5) 264 | pub.publish(7) 265 | pub.publish(10) 266 | waitForExpectations(timeout: 5.0) 267 | } 268 | 269 | func testListen() { 270 | let pub = Publisher() 271 | let expectation = self.expectation(description: "onlis") 272 | pub.proxy.listen { (number) in 273 | if number == 10 { expectation.fulfill() } 274 | } 275 | [0, 3, 4, -1, 5, 10].forEach(pub.publish) 276 | waitForExpectations(timeout: 5.0) 277 | } 278 | 279 | func testMapValue() { 280 | // let signed = SignedPublisher() 281 | // let strsgn = signed.proxy.mod_mapValue(String.init) 282 | } 283 | 284 | class Hand { 285 | 286 | func handle(_ int: Int) { 287 | print(int) 288 | } 289 | 290 | } 291 | 292 | func testBureau() { 293 | let hand = Hand() 294 | let publisher = Publisher(label: "Then-What") 295 | publisher.proxy 296 | .flatMap({ Int.init($0) }) 297 | .subscribe(hand, with: Hand.handle) 298 | } 299 | 300 | func testWarning() { 301 | let proxy = Subscribe.empty() 302 | let obj = Hand() 303 | let expectation = self.expectation(description: "on warning") 304 | Alba.InformBureau.didSubscribe 305 | .flatMap({ $0.entries.first }) 306 | .listen { (logEntry) in 307 | if case .publisherLabel(let name, _) = logEntry, name == "WARNING: Empty proxy" { 308 | expectation.fulfill() 309 | } 310 | } 311 | proxy.subscribe(obj, with: Hand.handle) 312 | waitForExpectations(timeout: 5.0) 313 | } 314 | 315 | func testMerged() { 316 | let first = Publisher(label: "testMerged.first") 317 | let second = Publisher(label: "testMerged.second") 318 | let merged = first.proxy.merged(with: second.proxy) 319 | 320 | let expectation = self.expectation(description: "on merged") 321 | var is5Present = false 322 | merged.listen { (number) in 323 | if number == 5 { 324 | is5Present = true 325 | } 326 | if number == 10 { 327 | XCTAssertTrue(is5Present) 328 | expectation.fulfill() 329 | } 330 | } 331 | first.publish(5) 332 | second.publish(10) 333 | waitForExpectations(timeout: 5.0, handler: nil) 334 | } 335 | 336 | class Filter { 337 | 338 | var numbers = [1, 2, 3, 4, 5] 339 | 340 | func subscribe(to publisher: Subscribe) { 341 | publisher 342 | .weak(self) 343 | .map({ $0.fromString }) 344 | .filter({ !$0.numbers.contains }) 345 | .subscribe(with: Filter.printNonExisting) 346 | } 347 | 348 | func printNonExisting(_ number: Int) { 349 | if number == 15 { 350 | XCTFail("Filter should be deallocated at this point") 351 | } 352 | if numbers.contains(number) { 353 | XCTFail("Where is actual filtering?") 354 | } 355 | print(number) 356 | } 357 | 358 | func fromString(_ string: String) -> Int { 359 | return Int(string)! 360 | } 361 | 362 | deinit { 363 | print("Deinit!") 364 | } 365 | 366 | } 367 | 368 | func testWeak() { 369 | let publisher = Publisher(label: "testWeakFilter.publisher") 370 | var filter: Filter? = Filter() 371 | filter!.subscribe(to: publisher.proxy) 372 | [1, 2, 3, 10, 12].map(String.init).forEach(publisher.publish) 373 | filter = nil 374 | publisher.publish("15") 375 | } 376 | 377 | class VoidTest { 378 | 379 | var tested = false 380 | 381 | init() { } 382 | 383 | func sub(sub: Subscribe) { 384 | sub.subscribe(self, with: VoidTest.test) 385 | } 386 | 387 | func test() { 388 | tested = true 389 | } 390 | 391 | } 392 | 393 | func testVoid() { 394 | let pub = Publisher() 395 | let voidTest = VoidTest() 396 | voidTest.sub(sub: pub.proxy) 397 | pub.publish() 398 | XCTAssertTrue(voidTest.tested) 399 | } 400 | 401 | } 402 | --------------------------------------------------------------------------------